| // 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. |
| |
| /// ----------------------------------------------------------------------- |
| /// WHEN CHANGING THIS FILE: |
| /// ----------------------------------------------------------------------- |
| /// |
| /// If you are adding/removing/modifying fields/classes of the AST, you must |
| /// also update the following files: |
| /// |
| /// - binary/ast_to_binary.dart |
| /// - binary/ast_from_binary.dart |
| /// - text/ast_to_text.dart |
| /// - clone.dart |
| /// - binary.md |
| /// - type_checker.dart (if relevant) |
| /// |
| /// ----------------------------------------------------------------------- |
| /// ERROR HANDLING |
| /// ----------------------------------------------------------------------- |
| /// |
| /// As a rule of thumb, errors that can be detected statically are handled by |
| /// the frontend, typically by translating the erroneous code into a 'throw' or |
| /// a call to 'noSuchMethod'. |
| /// |
| /// For example, there are no arity mismatches in static invocations, and |
| /// there are no direct invocations of a constructor on a abstract class. |
| /// |
| /// ----------------------------------------------------------------------- |
| /// STATIC vs TOP-LEVEL |
| /// ----------------------------------------------------------------------- |
| /// |
| /// The term `static` includes both static class members and top-level members. |
| /// |
| /// "Static class member" is the preferred term for non-top level statics. |
| /// |
| /// Static class members are not lifted to the library level because mirrors |
| /// and stack traces can observe that they are class members. |
| /// |
| /// ----------------------------------------------------------------------- |
| /// PROCEDURES |
| /// ----------------------------------------------------------------------- |
| /// |
| /// "Procedure" is an umbrella term for method, getter, setter, index-getter, |
| /// index-setter, operator overloader, and factory constructor. |
| /// |
| /// Generative constructors, field initializers, local functions are NOT |
| /// procedures. |
| /// |
| /// ----------------------------------------------------------------------- |
| /// TRANSFORMATIONS |
| /// ----------------------------------------------------------------------- |
| /// |
| /// AST transformations can be performed using [TreeNode.replaceWith] or the |
| /// [Transformer] visitor class. |
| /// |
| /// Use [Transformer] for bulk transformations that are likely to transform lots |
| /// of nodes, and [TreeNode.replaceWith] for sparse transformations that mutate |
| /// relatively few nodes. Or use whichever is more convenient. |
| /// |
| /// The AST can also be mutated by direct field manipulation, but the user then |
| /// has to update parent pointers manually. |
| /// |
| library kernel.ast; |
| |
| import 'dart:collection' show ListBase; |
| import 'dart:convert' show utf8; |
| |
| import 'visitor.dart'; |
| export 'visitor.dart'; |
| |
| import 'canonical_name.dart' show CanonicalName, Reference; |
| export 'canonical_name.dart' show CanonicalName, Reference; |
| |
| import 'default_language_version.dart' show defaultLanguageVersion; |
| export 'default_language_version.dart' show defaultLanguageVersion; |
| |
| import 'transformations/flags.dart'; |
| import 'text/ast_to_text.dart' as astToText; |
| import 'core_types.dart'; |
| import 'class_hierarchy.dart'; |
| import 'type_algebra.dart'; |
| import 'type_environment.dart'; |
| import 'src/assumptions.dart'; |
| import 'src/non_null.dart'; |
| import 'src/printer.dart'; |
| import 'src/text_util.dart'; |
| |
| part 'src/ast/patterns.dart'; |
| |
| /// Any type of node in the IR. |
| abstract class Node { |
| const Node(); |
| |
| R accept<R>(Visitor<R> v); |
| R accept1<R, A>(Visitor1<R, A> v, A arg); |
| void visitChildren(Visitor v); |
| |
| /// Returns the textual representation of this node for use in debugging. |
| /// |
| /// [toString] should only be used for debugging, but should not leak. |
| /// |
| /// The data is generally bare-bones, but can easily be updated for your |
| /// specific debugging needs. |
| @override |
| String toString(); |
| |
| /// Returns the textual representation of this node for use in debugging. |
| /// |
| /// [toStringInternal] should only be used for debugging, but should not leak. |
| /// |
| /// The data is generally bare-bones, but can easily be updated for your |
| /// specific debugging needs. |
| /// |
| /// This method is called internally by toString methods to create conciser |
| /// textual representations. |
| String toStringInternal() => toText(defaultAstTextStrategy); |
| |
| /// Returns the textual representation of this node for use in debugging. |
| /// |
| /// Note that this adds some nodes to a static map to ensure consistent |
| /// naming, but that it thus also leaks memory. [leakingDebugToString] should |
| /// thus only be used for debugging and short-running test tools. |
| /// |
| /// Synthetic names are cached globally to retain consistency across different |
| /// [leakingDebugToString] calls (hence the memory leak). |
| String leakingDebugToString() => astToText.debugNodeToString(this); |
| |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| toTextInternal(printer); |
| return printer.getText(); |
| } |
| |
| void toTextInternal(AstPrinter printer); |
| } |
| |
| /// A mutable AST node with a parent pointer. |
| /// |
| /// This is anything other than [Name] and [DartType] nodes. |
| abstract class TreeNode extends Node { |
| static int _hashCounter = 0; |
| @override |
| final int hashCode = _hashCounter = (_hashCounter + 1) & 0x3fffffff; |
| static const int noOffset = -1; |
| |
| TreeNode? parent; |
| |
| /// Offset in the source file it comes from. |
| /// |
| /// Valid values are from 0 and up, or -1 ([noOffset]) if the file offset is |
| /// not available (this is the default if none is specifically set). |
| int fileOffset = noOffset; |
| |
| @override |
| R accept<R>(TreeVisitor<R> v); |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg); |
| @override |
| void visitChildren(Visitor v); |
| void transformChildren(Transformer v); |
| void transformOrRemoveChildren(RemovingTransformer v); |
| |
| /// Replaces [child] with [replacement]. |
| /// |
| /// The caller is responsible for ensuring that the AST remains a tree. In |
| /// particular, [replacement] should be an orphan or be part of an orphaned |
| /// subtree. |
| /// |
| /// Has no effect if [child] is not actually a child of this node. |
| /// |
| /// [replacement] must be non-null. |
| void replaceChild(TreeNode child, TreeNode replacement) { |
| // ignore: unnecessary_null_comparison |
| assert(replacement != null); |
| transformChildren(new _ChildReplacer(child, replacement)); |
| } |
| |
| /// Inserts another node in place of this one. |
| /// |
| /// The caller is responsible for ensuring that the AST remains a tree. In |
| /// particular, [replacement] should be an orphan or be part of an orphaned |
| /// subtree. |
| /// |
| /// [replacement] must be non-null. |
| void replaceWith(TreeNode replacement) { |
| // ignore: unnecessary_null_comparison |
| assert(replacement != null); |
| parent!.replaceChild(this, replacement); |
| parent = null; |
| } |
| |
| // TODO(johnniwinther): Make this non-nullable. |
| Component? get enclosingComponent => parent?.enclosingComponent; |
| |
| /// Returns the best known source location of the given AST node, or `null` if |
| /// the node is orphaned. |
| /// |
| /// This getter is intended for diagnostics and debugging, and should be |
| /// avoided in production code. |
| Location? get location { |
| if (fileOffset == noOffset) return parent?.location; |
| return _getLocationInEnclosingFile(fileOffset); |
| } |
| |
| Location? _getLocationInEnclosingFile(int offset) { |
| return parent?._getLocationInEnclosingFile(offset); |
| } |
| } |
| |
| /// An AST node that can be referenced by other nodes. |
| /// |
| /// There is a single [reference] belonging to this node, providing a level of |
| /// indirection that is needed during serialization. |
| abstract class NamedNode extends TreeNode { |
| final Reference reference; |
| |
| NamedNode(Reference? reference) |
| : this.reference = reference ?? new Reference() { |
| this.reference.node = this; |
| } |
| |
| /// This is an advanced feature. |
| /// |
| /// See [Component.relink] for a comprehensive description. |
| /// |
| /// Makes sure the reference in this named node points to itself. |
| void _relinkNode() { |
| this.reference.node = this; |
| } |
| |
| /// Computes the canonical names for this node using the [parent] as the |
| /// canonical name of the parent node. |
| void bindCanonicalNames(CanonicalName parent); |
| } |
| |
| abstract class FileUriNode extends TreeNode { |
| /// The URI of the source file this node was loaded from. |
| Uri get fileUri; |
| } |
| |
| abstract class Annotatable extends TreeNode { |
| List<Expression> get annotations; |
| void addAnnotation(Expression node); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // LIBRARIES and CLASSES |
| // ------------------------------------------------------------------------ |
| |
| enum NonNullableByDefaultCompiledMode { Weak, Strong, Agnostic, Invalid } |
| |
| class Library extends NamedNode |
| implements Annotatable, Comparable<Library>, FileUriNode { |
| /// An import path to this library. |
| /// |
| /// The [Uri] should have the `dart`, `package`, `app`, or `file` scheme. |
| /// |
| /// If the URI has the `app` scheme, it is relative to the application root. |
| Uri importUri; |
| |
| /// The URI of the source file this library was loaded from. |
| @override |
| Uri fileUri; |
| |
| Version? _languageVersion; |
| Version get languageVersion => _languageVersion ?? defaultLanguageVersion; |
| |
| void setLanguageVersion(Version languageVersion) { |
| // ignore: unnecessary_null_comparison |
| if (languageVersion == null) { |
| throw new StateError("Trying to set language version 'null'"); |
| } |
| _languageVersion = languageVersion; |
| } |
| |
| static const int SyntheticFlag = 1 << 0; |
| static const int NonNullableByDefaultFlag = 1 << 1; |
| static const int NonNullableByDefaultModeBit1 = 1 << 2; |
| static const int NonNullableByDefaultModeBit2 = 1 << 3; |
| static const int IsUnsupportedFlag = 1 << 4; |
| |
| int flags = 0; |
| |
| /// If true, the library is synthetic, for instance library that doesn't |
| /// represents an actual file and is created as the result of error recovery. |
| bool get isSynthetic => flags & SyntheticFlag != 0; |
| void set isSynthetic(bool value) { |
| flags = value ? (flags | SyntheticFlag) : (flags & ~SyntheticFlag); |
| } |
| |
| bool get isNonNullableByDefault => (flags & NonNullableByDefaultFlag) != 0; |
| void set isNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | NonNullableByDefaultFlag) |
| : (flags & ~NonNullableByDefaultFlag); |
| } |
| |
| NonNullableByDefaultCompiledMode get nonNullableByDefaultCompiledMode { |
| bool bit1 = (flags & NonNullableByDefaultModeBit1) != 0; |
| bool bit2 = (flags & NonNullableByDefaultModeBit2) != 0; |
| if (!bit1 && !bit2) return NonNullableByDefaultCompiledMode.Weak; |
| if (bit1 && !bit2) return NonNullableByDefaultCompiledMode.Strong; |
| if (bit1 && bit2) return NonNullableByDefaultCompiledMode.Agnostic; |
| if (!bit1 && bit2) return NonNullableByDefaultCompiledMode.Invalid; |
| throw new StateError("Unused bit-pattern for compilation mode"); |
| } |
| |
| void set nonNullableByDefaultCompiledMode( |
| NonNullableByDefaultCompiledMode mode) { |
| switch (mode) { |
| case NonNullableByDefaultCompiledMode.Weak: |
| flags = (flags & ~NonNullableByDefaultModeBit1) & |
| ~NonNullableByDefaultModeBit2; |
| break; |
| case NonNullableByDefaultCompiledMode.Strong: |
| flags = (flags | NonNullableByDefaultModeBit1) & |
| ~NonNullableByDefaultModeBit2; |
| break; |
| case NonNullableByDefaultCompiledMode.Agnostic: |
| flags = (flags | NonNullableByDefaultModeBit1) | |
| NonNullableByDefaultModeBit2; |
| break; |
| case NonNullableByDefaultCompiledMode.Invalid: |
| flags = (flags & ~NonNullableByDefaultModeBit1) | |
| NonNullableByDefaultModeBit2; |
| break; |
| } |
| } |
| |
| /// If true, the library is not supported through the 'dart.library.*' value |
| /// used in conditional imports and `bool.fromEnvironment` constants. |
| bool get isUnsupported => flags & IsUnsupportedFlag != 0; |
| void set isUnsupported(bool value) { |
| flags = value ? (flags | IsUnsupportedFlag) : (flags & ~IsUnsupportedFlag); |
| } |
| |
| String? name; |
| |
| /// Problems in this [Library] encoded as json objects. |
| /// |
| /// Note that this field can be null, and by convention should be null if the |
| /// list is empty. |
| List<String>? problemsAsJson; |
| |
| @override |
| List<Expression> annotations; |
| |
| List<LibraryDependency> dependencies; |
| |
| /// References to nodes exported by `export` declarations that: |
| /// - aren't ambiguous, or |
| /// - aren't hidden by local declarations. |
| final List<Reference> additionalExports = <Reference>[]; |
| |
| @informative |
| List<LibraryPart> parts; |
| |
| List<Typedef> _typedefs; |
| List<Class> _classes; |
| List<Extension> _extensions; |
| List<InlineClass> _inlineClasses; |
| List<Procedure> _procedures; |
| List<Field> _fields; |
| |
| Library(this.importUri, |
| {this.name, |
| List<Expression>? annotations, |
| List<LibraryDependency>? dependencies, |
| List<LibraryPart>? parts, |
| List<Typedef>? typedefs, |
| List<Class>? classes, |
| List<Extension>? extensions, |
| List<InlineClass>? inlineClasses, |
| List<Procedure>? procedures, |
| List<Field>? fields, |
| required this.fileUri, |
| Reference? reference}) |
| // ignore: unnecessary_null_comparison |
| : assert(fileUri != null), |
| this.annotations = annotations ?? <Expression>[], |
| this.dependencies = dependencies ?? <LibraryDependency>[], |
| this.parts = parts ?? <LibraryPart>[], |
| this._typedefs = typedefs ?? <Typedef>[], |
| this._classes = classes ?? <Class>[], |
| this._extensions = extensions ?? <Extension>[], |
| this._inlineClasses = inlineClasses ?? <InlineClass>[], |
| this._procedures = procedures ?? <Procedure>[], |
| this._fields = fields ?? <Field>[], |
| super(reference) { |
| setParents(this.dependencies, this); |
| setParents(this.parts, this); |
| setParents(this._typedefs, this); |
| setParents(this._classes, this); |
| setParents(this._extensions, this); |
| setParents(this._procedures, this); |
| setParents(this._fields, this); |
| } |
| |
| List<Typedef> get typedefs => _typedefs; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding typedefs when reading the dill file. |
| void set typedefsInternal(List<Typedef> typedefs) { |
| _typedefs = typedefs; |
| } |
| |
| List<Class> get classes => _classes; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding classes when reading the dill file. |
| void set classesInternal(List<Class> classes) { |
| _classes = classes; |
| } |
| |
| List<Extension> get extensions => _extensions; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding extensions when reading the dill file. |
| void set extensionsInternal(List<Extension> extensions) { |
| _extensions = extensions; |
| } |
| |
| List<InlineClass> get inlineClasses => _inlineClasses; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding inline classes when reading the dill file. |
| void set inlineClassesInternal(List<InlineClass> inlineClasses) { |
| _inlineClasses = inlineClasses; |
| } |
| |
| List<Procedure> get procedures => _procedures; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding procedures when reading the dill file. |
| void set proceduresInternal(List<Procedure> procedures) { |
| _procedures = procedures; |
| } |
| |
| List<Field> get fields => _fields; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding fields when reading the dill file. |
| void set fieldsInternal(List<Field> fields) { |
| _fields = fields; |
| } |
| |
| Nullability get nullable { |
| return isNonNullableByDefault ? Nullability.nullable : Nullability.legacy; |
| } |
| |
| Nullability get nonNullable { |
| return isNonNullableByDefault |
| ? Nullability.nonNullable |
| : Nullability.legacy; |
| } |
| |
| Nullability nullableIfTrue(bool isNullable) { |
| if (isNonNullableByDefault) { |
| return isNullable ? Nullability.nullable : Nullability.nonNullable; |
| } |
| return Nullability.legacy; |
| } |
| |
| /// Returns the top-level fields and procedures defined in this library. |
| /// |
| /// This getter is for convenience, not efficiency. Consider manually |
| /// iterating the members to speed up code in production. |
| Iterable<Member> get members => |
| <Iterable<Member>>[fields, procedures].expand((x) => x); |
| |
| @override |
| void addAnnotation(Expression node) { |
| node.parent = this; |
| annotations.add(node); |
| } |
| |
| void addClass(Class class_) { |
| class_.parent = this; |
| classes.add(class_); |
| } |
| |
| void addExtension(Extension extension) { |
| extension.parent = this; |
| extensions.add(extension); |
| } |
| |
| void addInlineClass(InlineClass inlineClass) { |
| inlineClass.parent = this; |
| inlineClasses.add(inlineClass); |
| } |
| |
| void addField(Field field) { |
| field.parent = this; |
| fields.add(field); |
| } |
| |
| void addProcedure(Procedure procedure) { |
| procedure.parent = this; |
| procedures.add(procedure); |
| } |
| |
| void addTypedef(Typedef typedef_) { |
| typedef_.parent = this; |
| typedefs.add(typedef_); |
| } |
| |
| @override |
| CanonicalName bindCanonicalNames(CanonicalName parent) { |
| return parent.getChildFromUri(importUri)..bindTo(reference); |
| } |
| |
| /// Computes the canonical name for this library and all its members. |
| void ensureCanonicalNames(CanonicalName parent) { |
| CanonicalName canonicalName = bindCanonicalNames(parent); |
| for (int i = 0; i < typedefs.length; ++i) { |
| typedefs[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < fields.length; ++i) { |
| fields[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < procedures.length; ++i) { |
| procedures[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < classes.length; ++i) { |
| classes[i].ensureCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < extensions.length; ++i) { |
| extensions[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < inlineClasses.length; ++i) { |
| inlineClasses[i].bindCanonicalNames(canonicalName); |
| } |
| } |
| |
| /// This is an advanced feature. Use of this method should be coordinated |
| /// with the kernel team. |
| /// |
| /// See [Component.relink] for a comprehensive description. |
| /// |
| /// Makes sure all references in named nodes in this library points to said |
| /// named node. |
| void relink() { |
| _relinkNode(); |
| for (int i = 0; i < typedefs.length; ++i) { |
| Typedef typedef_ = typedefs[i]; |
| typedef_._relinkNode(); |
| } |
| for (int i = 0; i < fields.length; ++i) { |
| Field field = fields[i]; |
| field._relinkNode(); |
| } |
| for (int i = 0; i < procedures.length; ++i) { |
| Procedure member = procedures[i]; |
| member._relinkNode(); |
| } |
| for (int i = 0; i < classes.length; ++i) { |
| Class class_ = classes[i]; |
| class_.relink(); |
| } |
| for (int i = 0; i < extensions.length; ++i) { |
| Extension extension = extensions[i]; |
| extension._relinkNode(); |
| } |
| for (int i = 0; i < inlineClasses.length; ++i) { |
| InlineClass inlineClass = inlineClasses[i]; |
| inlineClass._relinkNode(); |
| } |
| } |
| |
| void addDependency(LibraryDependency node) { |
| dependencies.add(node..parent = this); |
| } |
| |
| void addPart(LibraryPart node) { |
| parts.add(node..parent = this); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitLibrary(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitLibrary(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(dependencies, v); |
| visitList(parts, v); |
| visitList(typedefs, v); |
| visitList(classes, v); |
| visitList(extensions, v); |
| visitList(inlineClasses, v); |
| visitList(procedures, v); |
| visitList(fields, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(dependencies, this); |
| v.transformList(parts, this); |
| v.transformList(typedefs, this); |
| v.transformList(classes, this); |
| v.transformList(extensions, this); |
| v.transformList(inlineClasses, this); |
| v.transformList(procedures, this); |
| v.transformList(fields, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformLibraryDependencyList(dependencies, this); |
| v.transformLibraryPartList(parts, this); |
| v.transformTypedefList(typedefs, this); |
| v.transformClassList(classes, this); |
| v.transformExtensionList(extensions, this); |
| v.transformInlineClassList(inlineClasses, this); |
| v.transformProcedureList(procedures, this); |
| v.transformFieldList(fields, this); |
| } |
| |
| static int _libraryIdCounter = 0; |
| int _libraryId = ++_libraryIdCounter; |
| int get libraryIdForTesting => _libraryId; |
| |
| @override |
| int compareTo(Library other) => _libraryId - other._libraryId; |
| |
| /// Returns a possibly synthesized name for this library, consistent with |
| /// the names across all [toString] calls. |
| @override |
| String toString() => libraryNameToString(this); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(libraryNameToString(this)); |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| String leakingDebugToString() => astToText.debugLibraryToString(this); |
| } |
| |
| /// An import or export declaration in a library. |
| /// |
| /// It can represent any of the following forms, |
| /// |
| /// import <url>; |
| /// import <url> as <name>; |
| /// import <url> deferred as <name>; |
| /// export <url>; |
| /// |
| /// optionally with metadata and [Combinators]. |
| class LibraryDependency extends TreeNode implements Annotatable { |
| int flags; |
| |
| @override |
| final List<Expression> annotations; |
| |
| Reference importedLibraryReference; |
| |
| /// The name of the import prefix, if any, or `null` if this is not an import |
| /// with a prefix. |
| /// |
| /// Must be non-null for deferred imports, and must be null for exports. |
| String? name; |
| |
| final List<Combinator> combinators; |
| |
| LibraryDependency(int flags, List<Expression> annotations, |
| Library importedLibrary, String name, List<Combinator> combinators) |
| : this.byReference( |
| flags, annotations, importedLibrary.reference, name, combinators); |
| |
| LibraryDependency.deferredImport(Library importedLibrary, String name, |
| {List<Combinator>? combinators, List<Expression>? annotations}) |
| : this.byReference(DeferredFlag, annotations ?? <Expression>[], |
| importedLibrary.reference, name, combinators ?? <Combinator>[]); |
| |
| LibraryDependency.import(Library importedLibrary, |
| {String? name, |
| List<Combinator>? combinators, |
| List<Expression>? annotations}) |
| : this.byReference(0, annotations ?? <Expression>[], |
| importedLibrary.reference, name, combinators ?? <Combinator>[]); |
| |
| LibraryDependency.export(Library importedLibrary, |
| {List<Combinator>? combinators, List<Expression>? annotations}) |
| : this.byReference(ExportFlag, annotations ?? <Expression>[], |
| importedLibrary.reference, null, combinators ?? <Combinator>[]); |
| |
| LibraryDependency.byReference(this.flags, this.annotations, |
| this.importedLibraryReference, this.name, this.combinators) { |
| setParents(annotations, this); |
| setParents(combinators, this); |
| } |
| |
| Library get enclosingLibrary => parent as Library; |
| Library get targetLibrary => importedLibraryReference.asLibrary; |
| |
| static const int ExportFlag = 1 << 0; |
| static const int DeferredFlag = 1 << 1; |
| |
| bool get isExport => flags & ExportFlag != 0; |
| bool get isImport => !isExport; |
| bool get isDeferred => flags & DeferredFlag != 0; |
| |
| @override |
| void addAnnotation(Expression annotation) { |
| annotations.add(annotation..parent = this); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitLibraryDependency(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => |
| v.visitLibraryDependency(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(combinators, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(combinators, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformCombinatorList(combinators, this); |
| } |
| |
| @override |
| String toString() { |
| return "LibraryDependency(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// A part declaration in a library. |
| /// |
| /// part <url>; |
| /// |
| /// optionally with metadata. |
| class LibraryPart extends TreeNode implements Annotatable { |
| @override |
| final List<Expression> annotations; |
| |
| final String partUri; |
| |
| LibraryPart(this.annotations, this.partUri) { |
| setParents(annotations, this); |
| } |
| |
| @override |
| void addAnnotation(Expression annotation) { |
| annotations.add(annotation..parent = this); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitLibraryPart(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitLibraryPart(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| } |
| |
| @override |
| String toString() { |
| return "LibraryPart(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// A `show` or `hide` clause for an import or export. |
| class Combinator extends TreeNode { |
| bool isShow; |
| |
| final List<String> names; |
| |
| LibraryDependency get dependency => parent as LibraryDependency; |
| |
| Combinator(this.isShow, this.names); |
| Combinator.show(this.names) : isShow = true; |
| Combinator.hide(this.names) : isShow = false; |
| |
| bool get isHide => !isShow; |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitCombinator(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitCombinator(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "Combinator(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// Declaration of a type alias. |
| class Typedef extends NamedNode implements FileUriNode, Annotatable { |
| /// The URI of the source file that contains the declaration of this typedef. |
| @override |
| Uri fileUri; |
| |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| String name; |
| final List<TypeParameter> typeParameters; |
| // TODO(johnniwinther): Make this non-nullable. |
| DartType? type; |
| |
| Typedef(this.name, this.type, |
| {Reference? reference, |
| required this.fileUri, |
| List<TypeParameter>? typeParameters, |
| List<TypeParameter>? typeParametersOfFunctionType, |
| List<VariableDeclaration>? positionalParameters, |
| List<VariableDeclaration>? namedParameters}) |
| // ignore: unnecessary_null_comparison |
| : assert(fileUri != null), |
| this.typeParameters = typeParameters ?? <TypeParameter>[], |
| super(reference) { |
| setParents(this.typeParameters, this); |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChildFromTypedef(this).bindTo(reference); |
| } |
| |
| Library get enclosingLibrary => parent as Library; |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitTypedef(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitTypedef(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(typeParameters, v); |
| type?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(typeParameters, this); |
| if (type != null) { |
| type = v.visitDartType(type!); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformTypeParameterList(typeParameters, this); |
| if (type != null) { |
| DartType newType = v.visitDartType(type!, dummyDartType); |
| if (identical(newType, dummyDartType)) { |
| type = null; |
| } else { |
| type = newType; |
| } |
| } |
| } |
| |
| @override |
| void addAnnotation(Expression node) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(node); |
| node.parent = this; |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| String toString() { |
| return "Typedef(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypedefName(reference); |
| } |
| } |
| |
| /// List-wrapper that marks the parent-class as dirty if the list is modified. |
| /// |
| /// The idea being, that for non-dirty classes (classes just loaded from dill) |
| /// the canonical names has already been calculated, and recalculating them is |
| /// not needed. If, however, we change anything, recalculation of the canonical |
| /// names can be needed. |
| class DirtifyingList<E> extends ListBase<E> { |
| final Class dirtifyClass; |
| final List<E> wrapped; |
| |
| DirtifyingList(this.dirtifyClass, this.wrapped); |
| |
| @override |
| int get length { |
| return wrapped.length; |
| } |
| |
| @override |
| void set length(int length) { |
| dirtifyClass.dirty = true; |
| wrapped.length = length; |
| } |
| |
| @override |
| E operator [](int index) { |
| return wrapped[index]; |
| } |
| |
| @override |
| void operator []=(int index, E value) { |
| dirtifyClass.dirty = true; |
| wrapped[index] = value; |
| } |
| } |
| |
| /// Declaration of a regular class or a mixin application. |
| /// |
| /// Mixin applications may not contain fields or procedures, as they implicitly |
| /// use those from its mixed-in type. However, the IR does not enforce this |
| /// rule directly, as doing so can obstruct transformations. It is possible to |
| /// transform a mixin application to become a regular class, and vice versa. |
| class Class extends NamedNode implements Annotatable, FileUriNode { |
| /// Start offset of the class in the source file it comes from. |
| /// |
| /// Note that this includes annotations if any. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file |
| /// start offset is not available (this is the default if none is specifically |
| /// set). |
| int startFileOffset = TreeNode.noOffset; |
| |
| /// End offset in the source file it comes from. Valid values are from 0 and |
| /// up, or -1 ([TreeNode.noOffset]) if the file end offset is not available |
| /// (this is the default if none is specifically set). |
| int fileEndOffset = TreeNode.noOffset; |
| |
| /// List of metadata annotations on the class. |
| /// |
| /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| /// annotations if needed. |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| /// Name of the class. |
| /// |
| /// Must be non-null and must be unique within the library. |
| /// |
| /// The name may contain characters that are not valid in a Dart identifier, |
| /// in particular, the symbol '&' is used in class names generated for mixin |
| /// applications. |
| String name; |
| |
| // Must match serialized bit positions. |
| static const int FlagAbstract = 1 << 0; |
| static const int FlagEnum = 1 << 1; |
| static const int FlagAnonymousMixin = 1 << 2; |
| static const int FlagEliminatedMixin = 1 << 3; |
| static const int FlagMixinDeclaration = 1 << 4; |
| static const int FlagHasConstConstructor = 1 << 5; |
| static const int FlagMacro = 1 << 6; |
| static const int FlagSealed = 1 << 7; |
| static const int FlagMixinClass = 1 << 8; |
| static const int FlagBase = 1 << 9; |
| static const int FlagInterface = 1 << 10; |
| static const int FlagFinal = 1 << 11; |
| |
| int flags = 0; |
| |
| bool get isAbstract => flags & FlagAbstract != 0; |
| |
| void set isAbstract(bool value) { |
| flags = value ? (flags | FlagAbstract) : (flags & ~FlagAbstract); |
| } |
| |
| /// Whether this class is an enum. |
| bool get isEnum => flags & FlagEnum != 0; |
| |
| void set isEnum(bool value) { |
| flags = value ? (flags | FlagEnum) : (flags & ~FlagEnum); |
| } |
| |
| /// Whether this class is a macro class. |
| bool get isMacro => flags & FlagMacro != 0; |
| |
| void set isMacro(bool value) { |
| flags = value ? (flags | FlagMacro) : (flags & ~FlagMacro); |
| } |
| |
| /// Whether this class is a sealed class. |
| bool get isSealed => flags & FlagSealed != 0; |
| |
| void set isSealed(bool value) { |
| flags = value ? (flags | FlagSealed) : (flags & ~FlagSealed); |
| } |
| |
| /// Whether this class is a base class. |
| bool get isBase => flags & FlagBase != 0; |
| |
| void set isBase(bool value) { |
| flags = value ? (flags | FlagBase) : (flags & ~FlagBase); |
| } |
| |
| /// Whether this class is an interface class. |
| bool get isInterface => flags & FlagInterface != 0; |
| |
| void set isInterface(bool value) { |
| flags = value ? (flags | FlagInterface) : (flags & ~FlagInterface); |
| } |
| |
| /// Whether this class is a final class. |
| bool get isFinal => flags & FlagFinal != 0; |
| |
| void set isFinal(bool value) { |
| flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal); |
| } |
| |
| /// Whether this class is a synthetic implementation created for each |
| /// mixed-in class. For example the following code: |
| /// class Z extends A with B, C, D {} |
| /// class A {} |
| /// class B {} |
| /// class C {} |
| /// class D {} |
| /// ...creates: |
| /// abstract class _Z&A&B extends A mixedIn B {} |
| /// abstract class _Z&A&B&C extends A&B mixedIn C {} |
| /// abstract class _Z&A&B&C&D extends A&B&C mixedIn D {} |
| /// class Z extends _Z&A&B&C&D {} |
| /// All X&Y classes are marked as synthetic. |
| bool get isAnonymousMixin => flags & FlagAnonymousMixin != 0; |
| |
| void set isAnonymousMixin(bool value) { |
| flags = |
| value ? (flags | FlagAnonymousMixin) : (flags & ~FlagAnonymousMixin); |
| } |
| |
| /// Whether this class was transformed from a mixin application. |
| /// In such case, its mixed-in type was pulled into the end of implemented |
| /// types list. |
| bool get isEliminatedMixin => flags & FlagEliminatedMixin != 0; |
| |
| void set isEliminatedMixin(bool value) { |
| flags = |
| value ? (flags | FlagEliminatedMixin) : (flags & ~FlagEliminatedMixin); |
| } |
| |
| /// Whether this class is a mixin class. |
| /// |
| /// The `mixin` modifier was added to the class declaration which allows the |
| /// class to be used as a mixin. The class can be mixed in by other classes |
| /// outside of its library. Otherwise, classes are not able to be used as a |
| /// mixin outside of its library from version 3.0 and later. |
| bool get isMixinClass => flags & FlagMixinClass != 0; |
| |
| void set isMixinClass(bool value) { |
| flags = value ? (flags | FlagMixinClass) : (flags & ~FlagMixinClass); |
| } |
| |
| /// True if this class was a mixin declaration in Dart. |
| /// |
| /// Mixins are declared in Dart with the `mixin` keyword. They are compiled |
| /// to Kernel classes. |
| bool get isMixinDeclaration => flags & FlagMixinDeclaration != 0; |
| |
| void set isMixinDeclaration(bool value) { |
| flags = value |
| ? (flags | FlagMixinDeclaration) |
| : (flags & ~FlagMixinDeclaration); |
| } |
| |
| /// True if this class declares one or more constant constructors. |
| bool get hasConstConstructor => flags & FlagHasConstConstructor != 0; |
| |
| void set hasConstConstructor(bool value) { |
| flags = value |
| ? (flags | FlagHasConstConstructor) |
| : (flags & ~FlagHasConstConstructor); |
| } |
| |
| /// If this class is a mixin declaration, this list contains the types from |
| /// the `on` clause. Otherwise the list is empty. |
| List<Supertype> get onClause => _onClause ??= _computeOnClause(); |
| |
| List<Supertype> _computeOnClause() { |
| List<Supertype> constraints = <Supertype>[]; |
| |
| // Not a mixin declaration. |
| if (!isMixinDeclaration) return constraints; |
| |
| // Otherwise we have a left-linear binary tree (subtrees are supertype and |
| // mixedInType) of constraints, where all the interior nodes are anonymous |
| // mixin applications. |
| Supertype? current = supertype; |
| while (current != null && current.classNode.isAnonymousMixin) { |
| Class currentClass = current.classNode; |
| assert(currentClass.implementedTypes.length == 2); |
| Substitution substitution = Substitution.fromSupertype(current); |
| constraints.add( |
| substitution.substituteSupertype(currentClass.implementedTypes[1])); |
| current = |
| substitution.substituteSupertype(currentClass.implementedTypes[0]); |
| } |
| return constraints..add(current!); |
| } |
| |
| /// The URI of the source file this class was loaded from. |
| @override |
| Uri fileUri; |
| |
| final List<TypeParameter> typeParameters; |
| |
| /// The immediate super type, or `null` if this is the root class. |
| Supertype? supertype; |
| |
| /// The mixed-in type if this is a mixin application, otherwise `null`. |
| Supertype? mixedInType; |
| |
| /// The types from the `implements` clause. |
| List<Supertype> implementedTypes; |
| |
| List<Supertype>? _onClause; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// If non-null, the function that will have to be called to fill-out the |
| /// content of this class. Note that this should not be called directly |
| /// though. |
| void Function()? lazyBuilder; |
| |
| /// Makes sure the class is loaded, i.e. the fields, procedures etc have been |
| /// loaded from the dill. Generally, one should not need to call this as it is |
| /// done automatically when accessing the lists. |
| void ensureLoaded() { |
| void Function()? lazyBuilderLocal = lazyBuilder; |
| if (lazyBuilderLocal != null) { |
| lazyBuilder = null; |
| lazyBuilderLocal(); |
| } |
| } |
| |
| List<Field> _fieldsInternal; |
| DirtifyingList<Field>? _fieldsView; |
| |
| /// Fields declared in the class. |
| /// |
| /// For mixin applications this should be empty. |
| List<Field> get fields { |
| ensureLoaded(); |
| // If already dirty the caller just might as well add stuff directly too. |
| if (dirty) return _fieldsInternal; |
| return _fieldsView ??= new DirtifyingList(this, _fieldsInternal); |
| } |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding fields when reading the dill file. |
| void set fieldsInternal(List<Field> fields) { |
| _fieldsInternal = fields; |
| _fieldsView = null; |
| } |
| |
| List<Constructor> _constructorsInternal; |
| DirtifyingList<Constructor>? _constructorsView; |
| |
| /// Constructors declared in the class. |
| List<Constructor> get constructors { |
| ensureLoaded(); |
| // If already dirty the caller just might as well add stuff directly too. |
| if (dirty) return _constructorsInternal; |
| return _constructorsView ??= |
| new DirtifyingList(this, _constructorsInternal); |
| } |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding constructors when reading the dill file. |
| void set constructorsInternal(List<Constructor> constructors) { |
| _constructorsInternal = constructors; |
| _constructorsView = null; |
| } |
| |
| List<Procedure> _proceduresInternal; |
| DirtifyingList<Procedure>? _proceduresView; |
| |
| /// Procedures declared in the class. |
| /// |
| /// For mixin applications this should only contain forwarding stubs. |
| List<Procedure> get procedures { |
| ensureLoaded(); |
| // If already dirty the caller just might as well add stuff directly too. |
| if (dirty) return _proceduresInternal; |
| return _proceduresView ??= new DirtifyingList(this, _proceduresInternal); |
| } |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding procedures when reading the dill file. |
| void set proceduresInternal(List<Procedure> procedures) { |
| _proceduresInternal = procedures; |
| _proceduresView = null; |
| } |
| |
| List<RedirectingFactory> _redirectingFactoriesInternal; |
| DirtifyingList<RedirectingFactory>? _redirectingFactoriesView; |
| |
| /// Redirecting factory constructors declared in the class. |
| /// |
| /// For mixin applications this should be empty. |
| List<RedirectingFactory> get redirectingFactories { |
| ensureLoaded(); |
| // If already dirty the caller just might as well add stuff directly too. |
| if (dirty) return _redirectingFactoriesInternal; |
| return _redirectingFactoriesView ??= |
| new DirtifyingList(this, _redirectingFactoriesInternal); |
| } |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// Used for adding redirecting factory constructor when reading the dill |
| /// file. |
| void set redirectingFactoryConstructorsInternal( |
| List<RedirectingFactory> redirectingFactoryConstructors) { |
| _redirectingFactoriesInternal = redirectingFactoryConstructors; |
| _redirectingFactoriesView = null; |
| } |
| |
| Class( |
| {required this.name, |
| bool isAbstract = false, |
| bool isAnonymousMixin = false, |
| this.supertype, |
| this.mixedInType, |
| List<TypeParameter>? typeParameters, |
| List<Supertype>? implementedTypes, |
| List<Constructor>? constructors, |
| List<Procedure>? procedures, |
| List<Field>? fields, |
| List<RedirectingFactory>? redirectingFactoryConstructors, |
| required this.fileUri, |
| Reference? reference}) |
| // ignore: unnecessary_null_comparison |
| : assert(name != null), |
| // ignore: unnecessary_null_comparison |
| assert(fileUri != null), |
| this.typeParameters = typeParameters ?? <TypeParameter>[], |
| this.implementedTypes = implementedTypes ?? <Supertype>[], |
| this._fieldsInternal = fields ?? <Field>[], |
| this._constructorsInternal = constructors ?? <Constructor>[], |
| this._proceduresInternal = procedures ?? <Procedure>[], |
| this._redirectingFactoriesInternal = |
| redirectingFactoryConstructors ?? <RedirectingFactory>[], |
| super(reference) { |
| setParents(this.typeParameters, this); |
| setParents(this._constructorsInternal, this); |
| setParents(this._proceduresInternal, this); |
| setParents(this._fieldsInternal, this); |
| setParents(this._redirectingFactoriesInternal, this); |
| this.isAbstract = isAbstract; |
| this.isAnonymousMixin = isAnonymousMixin; |
| } |
| |
| @override |
| CanonicalName bindCanonicalNames(CanonicalName parent) { |
| return parent.getChild(name)..bindTo(reference); |
| } |
| |
| /// Computes the canonical name for this class and all its members. |
| void ensureCanonicalNames(CanonicalName parent) { |
| CanonicalName canonicalName = bindCanonicalNames(parent); |
| if (!dirty) return; |
| for (int i = 0; i < fields.length; ++i) { |
| fields[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < procedures.length; ++i) { |
| procedures[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < constructors.length; ++i) { |
| constructors[i].bindCanonicalNames(canonicalName); |
| } |
| for (int i = 0; i < redirectingFactories.length; ++i) { |
| redirectingFactories[i].bindCanonicalNames(canonicalName); |
| } |
| dirty = false; |
| } |
| |
| /// This is an advanced feature. Use of this method should be coordinated |
| /// with the kernel team. |
| /// |
| /// See [Component.relink] for a comprehensive description. |
| /// |
| /// Makes sure all references in named nodes in this class points to said |
| /// named node. |
| void relink() { |
| this.reference.node = this; |
| for (int i = 0; i < fields.length; ++i) { |
| Field member = fields[i]; |
| member._relinkNode(); |
| } |
| for (int i = 0; i < procedures.length; ++i) { |
| Procedure member = procedures[i]; |
| member._relinkNode(); |
| } |
| for (int i = 0; i < constructors.length; ++i) { |
| Constructor member = constructors[i]; |
| member._relinkNode(); |
| } |
| for (int i = 0; i < redirectingFactories.length; ++i) { |
| RedirectingFactory member = redirectingFactories[i]; |
| member._relinkNode(); |
| } |
| dirty = false; |
| } |
| |
| /// The immediate super class, or `null` if this is the root class. |
| Class? get superclass => supertype?.classNode; |
| |
| /// The mixed-in class if this is a mixin application, otherwise `null`. |
| /// |
| /// Note that this may itself be a mixin application. Use [mixin] to get the |
| /// class that has the fields and procedures. |
| Class? get mixedInClass => mixedInType?.classNode; |
| |
| /// The class that declares the field and procedures of this class. |
| Class get mixin => mixedInClass?.mixin ?? this; |
| |
| bool get isMixinApplication => mixedInType != null; |
| |
| String get demangledName { |
| if (isAnonymousMixin) return nameAsMixinApplication; |
| assert(!name.contains('&')); |
| return name; |
| } |
| |
| String get nameAsMixinApplication { |
| assert(isAnonymousMixin); |
| return demangleMixinApplicationName(name); |
| } |
| |
| String get nameAsMixinApplicationSubclass { |
| assert(isAnonymousMixin); |
| return demangleMixinApplicationSubclassName(name); |
| } |
| |
| /// Members declared in this class. |
| /// |
| /// This getter is for convenience, not efficiency. Consider manually |
| /// iterating the members to speed up code in production. |
| Iterable<Member> get members => <Iterable<Member>>[ |
| fields, |
| constructors, |
| procedures, |
| redirectingFactories |
| ].expand((x) => x); |
| |
| /// The immediately extended, mixed-in, and implemented types. |
| /// |
| /// This getter is for convenience, not efficiency. Consider manually |
| /// iterating the super types to speed up code in production. |
| Iterable<Supertype> get supers => <Iterable<Supertype>>[ |
| supertype == null ? const [] : [supertype!], |
| mixedInType == null ? const [] : [mixedInType!], |
| implementedTypes |
| ].expand((x) => x); |
| |
| /// The library containing this class. |
| Library get enclosingLibrary => parent as Library; |
| |
| /// Internal. Should *ONLY* be used from within kernel. |
| /// |
| /// If true we have to compute canonical names for all children of this class. |
| /// if false we can skip it. |
| bool dirty = true; |
| |
| /// Adds a constructor to this class. |
| void addConstructor(Constructor constructor) { |
| dirty = true; |
| constructor.parent = this; |
| _constructorsInternal.add(constructor); |
| } |
| |
| /// Adds a procedure to this class. |
| void addProcedure(Procedure procedure) { |
| dirty = true; |
| procedure.parent = this; |
| _proceduresInternal.add(procedure); |
| } |
| |
| /// Adds a field to this class. |
| void addField(Field field) { |
| dirty = true; |
| field.parent = this; |
| _fieldsInternal.add(field); |
| } |
| |
| /// Adds a field to this class. |
| void addRedirectingFactory(RedirectingFactory redirectingFactory) { |
| dirty = true; |
| redirectingFactory.parent = this; |
| _redirectingFactoriesInternal.add(redirectingFactory); |
| } |
| |
| @override |
| void addAnnotation(Expression node) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(node); |
| node.parent = this; |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitClass(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitClass(this, arg); |
| |
| R acceptReference<R>(Visitor<R> v) => v.visitClassReference(this); |
| |
| Supertype get asRawSupertype { |
| return new Supertype(this, |
| new List<DartType>.filled(typeParameters.length, const DynamicType())); |
| } |
| |
| Supertype get asThisSupertype { |
| return new Supertype( |
| this, getAsTypeArguments(typeParameters, this.enclosingLibrary)); |
| } |
| |
| /// Returns the type of `this` for the class using [coreTypes] for caching. |
| InterfaceType getThisType(CoreTypes coreTypes, Nullability nullability) { |
| return coreTypes.thisInterfaceType(this, nullability); |
| } |
| |
| @override |
| String toString() => 'Class(${toStringInternal()})'; |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeClassName(reference); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(typeParameters, v); |
| supertype?.accept(v); |
| mixedInType?.accept(v); |
| visitList(implementedTypes, v); |
| visitList(constructors, v); |
| visitList(procedures, v); |
| visitList(fields, v); |
| visitList(redirectingFactories, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(typeParameters, this); |
| if (supertype != null) { |
| supertype = v.visitSupertype(supertype!); |
| } |
| if (mixedInType != null) { |
| mixedInType = v.visitSupertype(mixedInType!); |
| } |
| v.transformSupertypeList(implementedTypes); |
| v.transformList(constructors, this); |
| v.transformList(procedures, this); |
| v.transformList(fields, this); |
| v.transformList(redirectingFactories, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformTypeParameterList(typeParameters, this); |
| if (supertype != null) { |
| Supertype newSupertype = v.visitSupertype(supertype!, dummySupertype); |
| if (identical(newSupertype, dummySupertype)) { |
| supertype = null; |
| } else { |
| supertype = newSupertype; |
| } |
| } |
| if (mixedInType != null) { |
| Supertype newMixedInType = v.visitSupertype(mixedInType!, dummySupertype); |
| if (identical(newMixedInType, dummySupertype)) { |
| mixedInType = null; |
| } else { |
| mixedInType = newMixedInType; |
| } |
| } |
| v.transformSupertypeList(implementedTypes); |
| v.transformConstructorList(constructors, this); |
| v.transformProcedureList(procedures, this); |
| v.transformFieldList(fields, this); |
| v.transformRedirectingFactoryList(redirectingFactories, this); |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| } |
| |
| /// Declaration of an extension. |
| /// |
| /// The members are converted into top-level procedures and only accessible |
| /// by reference in the [Extension] node. |
| class Extension extends NamedNode implements Annotatable, FileUriNode { |
| /// Name of the extension. |
| /// |
| /// If unnamed, the extension will be given a synthesized name by the |
| /// front end. |
| String name; |
| |
| /// The URI of the source file this class was loaded from. |
| @override |
| Uri fileUri; |
| |
| /// Type parameters declared on the extension. |
| final List<TypeParameter> typeParameters; |
| |
| /// The type in the 'on clause' of the extension declaration. |
| /// |
| /// For instance A in: |
| /// |
| /// class A {} |
| /// extension B on A {} |
| /// |
| /// The 'on clause' appears also in the experimental feature 'extension |
| /// types' as a part of an extension type declaration, for example: |
| /// |
| /// class A {} |
| /// extension type B on A {} |
| late DartType onType; |
| |
| /// The 'show' and 'hide' clauses of an extension type declaration. |
| ExtensionTypeShowHideClause? showHideClause; |
| |
| /// The members declared by the extension. |
| /// |
| /// The members are converted into top-level members and only accessible |
| /// by reference through [ExtensionMemberDescriptor]. |
| List<ExtensionMemberDescriptor> members; |
| |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| // Must match serialized bit positions. |
| static const int FlagExtensionTypeDeclaration = 1 << 0; |
| static const int FlagUnnamedExtension = 1 << 1; |
| |
| int flags = 0; |
| |
| @override |
| void addAnnotation(Expression node) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(node); |
| node.parent = this; |
| } |
| |
| Extension( |
| {required this.name, |
| List<TypeParameter>? typeParameters, |
| DartType? onType, |
| List<ExtensionMemberDescriptor>? members, |
| required this.fileUri, |
| Reference? reference}) |
| // ignore: unnecessary_null_comparison |
| : assert(name != null), |
| // ignore: unnecessary_null_comparison |
| assert(fileUri != null), |
| this.typeParameters = typeParameters ?? <TypeParameter>[], |
| this.members = members ?? <ExtensionMemberDescriptor>[], |
| super(reference) { |
| setParents(this.typeParameters, this); |
| if (onType != null) { |
| this.onType = onType; |
| } |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChild(name).bindTo(reference); |
| } |
| |
| Library get enclosingLibrary => parent as Library; |
| |
| bool get isExtensionTypeDeclaration { |
| return flags & FlagExtensionTypeDeclaration != 0; |
| } |
| |
| void set isExtensionTypeDeclaration(bool value) { |
| flags = value |
| ? (flags | FlagExtensionTypeDeclaration) |
| : (flags & ~FlagExtensionTypeDeclaration); |
| } |
| |
| bool get isUnnamedExtension { |
| return flags & FlagUnnamedExtension != 0; |
| } |
| |
| void set isUnnamedExtension(bool value) { |
| flags = value |
| ? (flags | FlagUnnamedExtension) |
| : (flags & ~FlagUnnamedExtension); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitExtension(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitExtension(this, arg); |
| |
| R acceptReference<R>(Visitor<R> v) => v.visitExtensionReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(typeParameters, v); |
| onType.accept(v); |
| if (showHideClause != null) { |
| visitList(showHideClause!.shownSupertypes, v); |
| visitList(showHideClause!.hiddenSupertypes, v); |
| } |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(typeParameters, this); |
| // ignore: unnecessary_null_comparison |
| if (onType != null) { |
| onType = v.visitDartType(onType); |
| } |
| if (showHideClause != null) { |
| v.transformSupertypeList(showHideClause!.shownSupertypes); |
| v.transformSupertypeList(showHideClause!.hiddenSupertypes); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformTypeParameterList(typeParameters, this); |
| // ignore: unnecessary_null_comparison |
| if (onType != null) { |
| onType = v.visitDartType(onType, cannotRemoveSentinel); |
| } |
| if (showHideClause != null) { |
| v.transformSupertypeList(showHideClause!.shownSupertypes); |
| v.transformSupertypeList(showHideClause!.hiddenSupertypes); |
| } |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| String toString() { |
| return "Extension(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExtensionName(reference); |
| } |
| } |
| |
| enum ExtensionMemberKind { |
| Field, |
| Method, |
| Getter, |
| Setter, |
| Operator, |
| TearOff, |
| } |
| |
| /// Information about an member declaration in an extension. |
| class ExtensionMemberDescriptor { |
| static const int FlagStatic = 1 << 0; // Must match serialized bit positions. |
| |
| /// The name of the extension member. |
| /// |
| /// The name of the generated top-level member is mangled to ensure |
| /// uniqueness. This name is used to lookup an extension method in the |
| /// extension itself. |
| Name name; |
| |
| /// [ExtensionMemberKind] kind of the original member. |
| /// |
| /// An extension method is converted into a regular top-level method. For |
| /// instance: |
| /// |
| /// class A { |
| /// var foo; |
| /// } |
| /// extension B on A { |
| /// get bar => this.foo; |
| /// } |
| /// |
| /// will be converted into |
| /// |
| /// class A {} |
| /// B|get#bar(A #this) => #this.foo; |
| /// |
| /// where `B|get#bar` is the synthesized name of the top-level method and |
| /// `#this` is the synthesized parameter that holds represents `this`. |
| /// |
| ExtensionMemberKind kind; |
| |
| int flags = 0; |
| |
| /// Reference to the top-level member created for the extension method. |
| final Reference member; |
| |
| ExtensionMemberDescriptor( |
| {required this.name, |
| required this.kind, |
| bool isStatic = false, |
| required this.member}) { |
| this.isStatic = isStatic; |
| } |
| |
| /// Return `true` if the extension method was declared as `static`. |
| bool get isStatic => flags & FlagStatic != 0; |
| |
| void set isStatic(bool value) { |
| flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
| } |
| |
| @override |
| String toString() { |
| return 'ExtensionMemberDescriptor($name,$kind,' |
| '${member.toStringInternal()},isStatic=${isStatic})'; |
| } |
| } |
| |
| enum CallSiteAccessKind { |
| methodInvocation, |
| getterInvocation, |
| setterInvocation, |
| operatorInvocation, |
| } |
| |
| /// Elements of the 'show' and 'hide' clauses of an extension type declaration. |
| class ExtensionTypeShowHideClause { |
| /// The types in the 'show clause' of the extension type declaration. |
| /// |
| /// For instance A, B in: |
| /// |
| /// class A {} |
| /// class B {} |
| /// class C extends B implements A {} |
| /// extension type E on C show B, A {} |
| final List<Supertype> shownSupertypes = <Supertype>[]; |
| |
| /// The methods in the 'show clause' of the extension type declaration. |
| /// |
| /// For instance foo in |
| /// |
| /// class A { |
| /// void foo() {} |
| /// } |
| /// extension type E on A show foo {} |
| final List<Reference> shownMethods = <Reference>[]; |
| |
| /// The getters in the 'show clause' of the extension type declaration. |
| /// |
| /// For instance foo, bar, baz in |
| /// |
| /// class A { |
| /// void foo() {} |
| /// int? bar; |
| /// int get baz => 42; |
| /// } |
| /// extension type E on A show get foo, get bar, get baz {} |
| final List<Reference> shownGetters = <Reference>[]; |
| |
| /// The setters in the 'show clause' of the extension type declaration. |
| /// |
| /// For instance foo, bar in |
| /// |
| /// class A { |
| /// int? foo; |
| /// void set bar(int value) {} |
| /// } |
| /// extension type E on A show set foo, set bar {} |
| final List<Reference> shownSetters = <Reference>[]; |
| |
| /// The operators in the 'show clause' of the extension type declaration. |
| /// |
| /// For instance +, * in |
| /// |
| /// class A { |
| /// A operator+(A other) => other; |
| /// A operator*(A other) => this; |
| /// } |
| /// extension type E on A show operator +, operator * {} |
| final List<Reference> shownOperators = <Reference>[]; |
| |
| /// The types in the 'hide clause' of the extension type declaration. |
| /// |
| /// For instance A, B in: |
| /// |
| /// class A {} |
| /// class B {} |
| /// class C extends B implements A {} |
| /// extension E on C hide A, B {} |
| final List<Supertype> hiddenSupertypes = <Supertype>[]; |
| |
| /// The methods in the 'hide clause' of the extension type declaration. |
| /// |
| /// For instance foo in |
| /// |
| /// class A { |
| /// void foo() {} |
| /// } |
| /// extension type E on A hide foo {} |
| final List<Reference> hiddenMethods = <Reference>[]; |
| |
| /// The getters in the 'hide clause' of the extension type declaration. |
| /// |
| /// For instance foo, bar, baz in |
| /// |
| /// class A { |
| /// void foo() {} |
| /// int? bar; |
| /// int get baz => 42; |
| /// } |
| /// extension type E on A hide get foo, get bar, get baz {} |
| final List<Reference> hiddenGetters = <Reference>[]; |
| |
| /// The setters in the 'hide clause' of the extension type declaration. |
| /// |
| /// For instance foo, bar in |
| /// |
| /// class A { |
| /// int? foo; |
| /// void set bar(int value) {} |
| /// } |
| /// extension type E on A hide set foo, set bar {} |
| final List<Reference> hiddenSetters = <Reference>[]; |
| |
| /// The operators in the 'hide clause' of the extension type declaration. |
| /// |
| /// For instance +, * in |
| /// |
| /// class A { |
| /// A operator+(A other) => other; |
| /// A operator*(A other) => this; |
| /// } |
| /// extension type E on A hide operator +, operator * {} |
| final List<Reference> hiddenOperators = <Reference>[]; |
| |
| Reference? findShownReference(Name name, |
| CallSiteAccessKind callSiteAccessKind, ClassHierarchyMembers hierarchy) { |
| List<Reference> shownReferences; |
| List<Reference> hiddenReferences; |
| switch (callSiteAccessKind) { |
| case CallSiteAccessKind.getterInvocation: |
| shownReferences = shownGetters; |
| hiddenReferences = hiddenGetters; |
| break; |
| case CallSiteAccessKind.setterInvocation: |
| shownReferences = shownSetters; |
| hiddenReferences = hiddenSetters; |
| break; |
| case CallSiteAccessKind.methodInvocation: |
| shownReferences = shownMethods; |
| hiddenReferences = hiddenMethods; |
| break; |
| case CallSiteAccessKind.operatorInvocation: |
| shownReferences = shownOperators; |
| hiddenReferences = hiddenOperators; |
| break; |
| } |
| |
| Reference? reference = _findMember( |
| name, shownReferences, shownSupertypes, hierarchy, callSiteAccessKind); |
| if (reference != null && |
| _findMember(name, hiddenReferences, hiddenSupertypes, hierarchy, |
| callSiteAccessKind) == |
| null) { |
| return reference; |
| } |
| |
| return null; |
| } |
| |
| Reference? _findMember( |
| Name name, |
| List<Reference> references, |
| List<Supertype> interfaces, |
| ClassHierarchyMembers hierarchy, |
| CallSiteAccessKind callSiteAccessKind) { |
| for (Reference reference in references) { |
| if (reference.asMember.name == name) { |
| return reference; |
| } |
| } |
| for (Supertype interface in interfaces) { |
| Member? member = hierarchy.getInterfaceMember(interface.classNode, name, |
| setter: callSiteAccessKind == CallSiteAccessKind.setterInvocation); |
| if (member != null) { |
| return member.reference; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /// Declaration of an inline class. |
| /// |
| /// The members are converted into top-level procedures and only accessible |
| /// by reference in the [InlineClass] node. |
| class InlineClass extends NamedNode implements Annotatable, FileUriNode { |
| /// Name of the inline class. |
| String name; |
| |
| /// The URI of the source file this class was loaded from. |
| @override |
| Uri fileUri; |
| |
| /// Type parameters declared on the extension. |
| final List<TypeParameter> typeParameters; |
| |
| /// The type in the underlying representation of the inline class declaration. |
| /// |
| /// For instance A in the inline class B: |
| /// |
| /// class A {} |
| /// inline class B { |
| /// final A it; |
| /// B(this.it) |
| /// } |
| /// |
| late DartType declaredRepresentationType; |
| |
| /// The name of the representation field. |
| /// |
| /// For instance 'it' in the inline class B: |
| /// |
| /// class A {} |
| /// inline class B { |
| /// final A it; |
| /// B(this.it) |
| /// } |
| /// |
| /// This name is used for accessing underlying representation from an inline |
| /// type. If the name starts with '_' is private wrt. the enclosing library |
| /// of the inline class. |
| late String representationName; |
| |
| /// The members declared by the inline class. |
| /// |
| /// The members are converted into top-level members and only accessible |
| /// by reference through [InlineClassMemberDescriptor]. |
| List<InlineClassMemberDescriptor> members; |
| |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| List<InlineType> implements; |
| |
| int flags = 0; |
| |
| @override |
| void addAnnotation(Expression node) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(node); |
| node.parent = this; |
| } |
| |
| InlineClass( |
| {required this.name, |
| List<TypeParameter>? typeParameters, |
| DartType? declaredRepresentationType, |
| List<InlineClassMemberDescriptor>? members, |
| List<InlineType>? implements, |
| required this.fileUri, |
| Reference? reference}) |
| // ignore: unnecessary_null_comparison |
| : assert(name != null), |
| // ignore: unnecessary_null_comparison |
| assert(fileUri != null), |
| this.typeParameters = typeParameters ?? <TypeParameter>[], |
| this.members = members ?? <InlineClassMemberDescriptor>[], |
| this.implements = implements ?? <InlineType>[], |
| super(reference) { |
| setParents(this.typeParameters, this); |
| if (declaredRepresentationType != null) { |
| this.declaredRepresentationType = declaredRepresentationType; |
| } |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChild(name).bindTo(reference); |
| } |
| |
| Library get enclosingLibrary => parent as Library; |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitInlineClass(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitInlineClass(this, arg); |
| |
| R acceptReference<R>(Visitor<R> v) => v.visitInlineClassReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| visitList(typeParameters, v); |
| declaredRepresentationType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(typeParameters, this); |
| // ignore: unnecessary_null_comparison |
| if (declaredRepresentationType != null) { |
| declaredRepresentationType = v.visitDartType(declaredRepresentationType); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformTypeParameterList(typeParameters, this); |
| // ignore: unnecessary_null_comparison |
| if (declaredRepresentationType != null) { |
| declaredRepresentationType = |
| v.visitDartType(declaredRepresentationType, cannotRemoveSentinel); |
| } |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| String toString() { |
| return "InlineClass(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeInlineClassName(reference); |
| } |
| } |
| |
| enum InlineClassMemberKind { |
| Constructor, |
| Factory, |
| Field, |
| Method, |
| Getter, |
| Setter, |
| Operator, |
| TearOff, |
| } |
| |
| /// Information about an member declaration in an inline class. |
| class InlineClassMemberDescriptor { |
| static const int FlagStatic = 1 << 0; // Must match serialized bit positions. |
| |
| /// The name of the inline class member. |
| /// |
| /// The name of the generated top-level member is mangled to ensure |
| /// uniqueness. This name is used to lookup a member in the inline class |
| /// itself. |
| Name name; |
| |
| /// [InlineClassMemberKind] kind of the original member. |
| /// |
| /// An inline class member is converted into a regular top-level method. For |
| /// instance: |
| /// |
| /// class A { |
| /// var foo; |
| /// } |
| /// inline class B { |
| /// final A it; |
| /// B(this.it); |
| /// get bar => this.foo; |
| /// } |
| /// |
| /// will be converted into |
| /// |
| /// class A {} |
| /// B|get#bar(A #this) => #this.foo; |
| /// |
| /// where `B|get#bar` is the synthesized name of the top-level method and |
| /// `#this` is the synthesized parameter that holds represents `this`. |
| /// |
| InlineClassMemberKind kind; |
| |
| int flags = 0; |
| |
| /// Reference to the top-level member created for the inline class member. |
| final Reference member; |
| |
| InlineClassMemberDescriptor( |
| {required this.name, |
| required this.kind, |
| bool isStatic = false, |
| required this.member}) { |
| this.isStatic = isStatic; |
| } |
| |
| /// Return `true` if the inline class member was declared as `static`. |
| bool get isStatic => flags & FlagStatic != 0; |
| |
| void set isStatic(bool value) { |
| flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
| } |
| |
| @override |
| String toString() { |
| return 'InlineClassMemberDescriptor($name,$kind,' |
| '${member.toStringInternal()},isStatic=${isStatic})'; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // MEMBERS |
| // ------------------------------------------------------------------------ |
| |
| abstract class Member extends NamedNode implements Annotatable, FileUriNode { |
| /// End offset in the source file it comes from. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file |
| /// end offset is not available (this is the default if none is specifically |
| /// set). |
| int fileEndOffset = TreeNode.noOffset; |
| |
| /// List of metadata annotations on the member. |
| /// |
| /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| /// annotations if needed. |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| Name name; |
| |
| /// The URI of the source file this member was loaded from. |
| @override |
| Uri fileUri; |
| |
| /// Flags summarizing the kinds of AST nodes contained in this member, for |
| /// speeding up transformations that only affect certain types of nodes. |
| /// |
| /// See [TransformerFlag] for the meaning of each bit. |
| /// |
| /// These should not be used for any purpose other than skipping certain |
| /// members if it can be determined that no work is needed in there. |
| /// |
| /// It is valid for these flags to be false positives in rare cases, so |
| /// transformers must tolerate the case where a flag is spuriously set. |
| /// |
| /// This value is not serialized; it is populated by the frontend and the |
| /// deserializer. |
| // |
| // TODO(asgerf): It might be worthwhile to put this on classes as well. |
| int transformerFlags = 0; |
| |
| Member(this.name, this.fileUri, Reference? reference) |
| // ignore: unnecessary_null_comparison |
| : assert(name != null), |
| // ignore: unnecessary_null_comparison |
| assert(fileUri != null), |
| super(reference); |
| |
| Class? get enclosingClass => parent is Class ? parent as Class : null; |
| Library get enclosingLibrary => |
| (parent is Class ? parent!.parent : parent) as Library; |
| |
| @override |
| R accept<R>(MemberVisitor<R> v); |
| |
| @override |
| R accept1<R, A>(MemberVisitor1<R, A> v, A arg); |
| |
| R acceptReference<R>(MemberReferenceVisitor<R> v); |
| |
| /// Returns true if this is an abstract procedure. |
| bool get isAbstract => false; |
| |
| /// Returns true if the member has the 'const' modifier. |
| bool get isConst; |
| |
| /// True if this is a field or non-setter procedure. |
| /// |
| /// Note that operators and factories return `true`, even though there are |
| /// normally no calls to their getter. |
| bool get hasGetter; |
| |
| /// True if this is a setter or a mutable field. |
| bool get hasSetter; |
| |
| /// True if this is a non-static field or procedure. |
| bool get isInstanceMember; |
| |
| /// True if the member has the `external` modifier, implying that the |
| /// implementation is provided by the backend, and is not necessarily written |
| /// in Dart. |
| /// |
| /// Members can have this modifier independently of whether the enclosing |
| /// library is external. |
| bool get isExternal; |
| void set isExternal(bool value); |
| |
| /// If `true` this member is compiled from a member declared in an extension |
| /// declaration. |
| /// |
| /// For instance `field`, `method1` and `method2` in: |
| /// |
| /// extension A on B { |
| /// static var field; |
| /// B method1() => this; |
| /// static B method2() => new B(); |
| /// } |
| /// |
| bool get isExtensionMember; |
| |
| /// If `true` this member is compiled from a member declared in an inline |
| /// class declaration. |
| /// |
| /// For instance `field`, `method1` and `method2` in: |
| /// |
| /// inline class A { |
| /// final B it; |
| /// A(this.it); |
| /// static var field; |
| /// B method1() => this; |
| /// static B method2() => new B(); |
| /// } |
| /// |
| bool get isInlineClassMember; |
| |
| /// If `true` this member is defined in a library for which non-nullable by |
| /// default is enabled. |
| bool get isNonNullableByDefault; |
| void set isNonNullableByDefault(bool value); |
| |
| /// If `true` this procedure is not part of the interface but only part of the |
| /// class members. |
| /// |
| /// This is `true` for instance for augmented procedures and synthesized |
| /// fields added for the late lowering. |
| bool get isInternalImplementation => false; |
| |
| /// The function signature and body of the procedure or constructor, or `null` |
| /// if this is a field. |
| FunctionNode? get function => null; |
| |
| /// Returns a possibly synthesized name for this member, consistent with |
| /// the names used across all [toString] calls. |
| @override |
| String toString() => toStringInternal(); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(reference); |
| } |
| |
| @override |
| void addAnnotation(Expression node) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(node); |
| node.parent = this; |
| } |
| |
| /// Returns the type of this member when accessed as a getter. |
| /// |
| /// For a field, this is the field type. For a getter, this is the return |
| /// type. For a method or constructor, this is the tear off type. |
| /// |
| /// For a setter, this is undefined. Currently, non-nullable `Never` is |
| /// returned. |
| // TODO(johnniwinther): Should we use `InvalidType` for the undefined cases? |
| DartType get getterType; |
| |
| /// Returns the type of this member when access as a getter on a super class. |
| /// |
| /// This is in most cases the same as for [getterType]. |
| /// |
| /// An exception is for forwarding semi stubs: |
| /// |
| /// class Super { |
| /// void method(num a) {} |
| /// } |
| /// class Class extends Super { |
| /// void method(covariant int a); |
| /// } |
| /// class Subclass extends Class { |
| /// void method(int a) { |
| /// super.method; // Type `void Function(num)`. |
| /// Class().method; // Type `void Function(int)`. |
| /// } |
| /// } |
| /// |
| /// Here, `Class.method` is turned into a forwarding semi stub |
| /// |
| /// void method(covariant num a) => super.method(a); |
| /// |
| /// with [signatureType] `void Function(int)`. When `Class.method` is used |
| /// as the target of a super get, it has getter type `void Function(num)` and |
| /// as the target of an instance get, it has getter type `void Function(int)`. |
| DartType get superGetterType => getterType; |
| |
| /// Returns the type of this member when accessed as a setter. |
| /// |
| /// For an assignable field, this is the field type. For a setter this is the |
| /// parameter type. |
| /// |
| /// For other members, including unassignable fields, this is undefined. |
| /// Currently, non-nullable `Never` is returned. |
| // TODO(johnniwinther): Should we use `InvalidType` for the undefined cases? |
| DartType get setterType; |
| |
| /// Returns the type of this member when access as a setter on a super class. |
| /// |
| /// This is in most cases the same as for [setterType]. |
| /// |
| /// An exception is for forwarding semi stubs: |
| /// |
| /// class Super { |
| /// void set setter(num a) {} |
| /// } |
| /// class Class extends Super { |
| /// void set setter(covariant int a); |
| /// } |
| /// class Subclass extends Class { |
| /// void set setter(int a) { |
| /// super.setter = 0.5; // Valid. |
| /// Class().setter = 0.5; // Invalid. |
| /// } |
| /// } |
| /// |
| /// Here, `Class.setter` is turned into a forwarding semi stub |
| /// |
| /// void set setter(covariant num a) => super.setter = a; |
| /// |
| /// with [signatureType] `void Function(int)`. When `Class.setter` is used |
| /// as the target of a super set, it has setter type `num` and as the target |
| /// of an instance set, it has setter type `int`. |
| DartType get superSetterType => setterType; |
| |
| bool get containsSuperCalls { |
| return transformerFlags & TransformerFlag.superCalls != 0; |
| } |
| |
| /// If this member is a member signature, [memberSignatureOrigin] is one of |
| /// the non-member signature members from which it was created. |
| Member? get memberSignatureOrigin => null; |
| } |
| |
| /// A field declaration. |
| /// |
| /// The implied getter and setter for the field are not represented explicitly, |
| /// but can be made explicit if needed. |
| class Field extends Member { |
| DartType type; // Not null. Defaults to DynamicType. |
| int flags = 0; |
| Expression? initializer; // May be null. |
| |
| /// Reference used for reading from this field. |
| /// |
| /// This should be used as the target in [StaticGet], [InstanceGet], and |
| /// [SuperPropertyGet]. |
| final Reference getterReference; |
| |
| /// Reference used for writing to this field. |
| /// |
| /// This should be used as the target in [StaticSet], [InstanceSet], and |
| /// [SuperPropertySet]. |
| final Reference? setterReference; |
| |
| @override |
| @Deprecated("Use the specific getterReference/setterReference instead") |
| Reference get reference => super.reference; |
| |
| /// Reference used for initializing this field. |
| /// |
| /// This should be used as the target in [FieldInitializer] and as the key |
| /// in the field values of [InstanceConstant]. |
| Reference get fieldReference => super.reference; |
| |
| Field.mutable(Name name, |
| {this.type = const DynamicType(), |
| this.initializer, |
| bool isCovariantByDeclaration = false, |
| bool isFinal = false, |
| bool isStatic = false, |
| bool isLate = false, |
| int transformerFlags = 0, |
| required Uri fileUri, |
| Reference? fieldReference, |
| Reference? getterReference, |
| Reference? setterReference}) |
| : this.getterReference = getterReference ?? new Reference(), |
| this.setterReference = setterReference ?? new Reference(), |
| super(name, fileUri, fieldReference) { |
| this.getterReference.node = this; |
| this.setterReference!.node = this; |
| // ignore: unnecessary_null_comparison |
| assert(type != null); |
| initializer?.parent = this; |
| this.isCovariantByDeclaration = isCovariantByDeclaration; |
| this.isFinal = isFinal; |
| this.isStatic = isStatic; |
| this.isLate = isLate; |
| this.transformerFlags = transformerFlags; |
| } |
| |
| Field.immutable(Name name, |
| {this.type = const DynamicType(), |
| this.initializer, |
| bool isCovariantByDeclaration = false, |
| bool isFinal = false, |
| bool isConst = false, |
| bool isStatic = false, |
| bool isLate = false, |
| int transformerFlags = 0, |
| required Uri fileUri, |
| Reference? fieldReference, |
| Reference? getterReference, |
| bool isEnumElement = false}) |
| : this.getterReference = getterReference ?? new Reference(), |
| this.setterReference = null, |
| super(name, fileUri, fieldReference) { |
| this.getterReference.node = this; |
| // ignore: unnecessary_null_comparison |
| assert(type != null); |
| initializer?.parent = this; |
| this.isCovariantByDeclaration = isCovariantByDeclaration; |
| this.isFinal = isFinal; |
| this.isConst = isConst; |
| this.isStatic = isStatic; |
| this.isLate = isLate; |
| this.isEnumElement = isEnumElement; |
| this.transformerFlags = transformerFlags; |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChildFromField(this).bindTo(fieldReference); |
| parent.getChildFromFieldGetter(this).bindTo(getterReference); |
| if (hasSetter) { |
| parent.getChildFromFieldSetter(this).bindTo(setterReference!); |
| } |
| } |
| |
| @override |
| void _relinkNode() { |
| this.fieldReference.node = this; |
| this.getterReference.node = this; |
| if (hasSetter) { |
| this.setterReference!.node = this; |
| } |
| } |
| |
| static const int FlagFinal = 1 << 0; // Must match serialized bit positions. |
| static const int FlagConst = 1 << 1; |
| static const int FlagStatic = 1 << 2; |
| static const int FlagCovariant = 1 << 3; |
| static const int FlagCovariantByClass = 1 << 4; |
| static const int FlagLate = 1 << 5; |
| static const int FlagExtensionMember = 1 << 6; |
| static const int FlagNonNullableByDefault = 1 << 7; |
| static const int FlagInternalImplementation = 1 << 8; |
| static const int FlagEnumElement = 1 << 9; |
| static const int FlagInlineClassMember = 1 << 10; |
| |
| /// Whether the field is declared with the `covariant` keyword. |
| bool get isCovariantByDeclaration => flags & FlagCovariant != 0; |
| |
| bool get isFinal => flags & FlagFinal != 0; |
| |
| @override |
| bool get isConst => flags & FlagConst != 0; |
| |
| bool get isStatic => flags & FlagStatic != 0; |
| |
| @override |
| bool get isExtensionMember => flags & FlagExtensionMember != 0; |
| |
| @override |
| bool get isInlineClassMember => flags & FlagInlineClassMember != 0; |
| |
| /// Indicates whether the implicit setter associated with this field needs to |
| /// contain a runtime type check to deal with generic covariance. |
| /// |
| /// When `true`, runtime checks may need to be performed. |
| bool get isCovariantByClass => flags & FlagCovariantByClass != 0; |
| |
| /// Whether the field is declared with the `late` keyword. |
| bool get isLate => flags & FlagLate != 0; |
| |
| /// If `true` this field is not part of the interface but only part of the |
| /// class members. |
| /// |
| /// This is `true` for instance for synthesized fields added for the late |
| /// lowering. |
| @override |
| bool get isInternalImplementation => flags & FlagInternalImplementation != 0; |
| |
| /// If `true` this field is an enum element. |
| /// |
| /// For instance |
| /// |
| /// enum A { |
| /// a, b; |
| /// static const A c = A.a; |
| /// } |
| /// |
| /// the fields `a` and `b` are enum elements whereas `c` is a regular field. |
| bool get isEnumElement => flags & FlagEnumElement != 0; |
| |
| void set isCovariantByDeclaration(bool value) { |
| flags = value ? (flags | FlagCovariant) : (flags & ~FlagCovariant); |
| } |
| |
| void set isFinal(bool value) { |
| flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal); |
| } |
| |
| void set isConst(bool value) { |
| flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
| } |
| |
| void set isStatic(bool value) { |
| flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
| } |
| |
| void set isExtensionMember(bool value) { |
| flags = |
| value ? (flags | FlagExtensionMember) : (flags & ~FlagExtensionMember); |
| } |
| |
| void set isCovariantByClass(bool value) { |
| flags = value |
| ? (flags | FlagCovariantByClass) |
| : (flags & ~FlagCovariantByClass); |
| } |
| |
| void set isLate(bool value) { |
| flags = value ? (flags | FlagLate) : (flags & ~FlagLate); |
| } |
| |
| void set isInternalImplementation(bool value) { |
| flags = value |
| ? (flags | FlagInternalImplementation) |
| : (flags & ~FlagInternalImplementation); |
| } |
| |
| void set isEnumElement(bool value) { |
| flags = value ? (flags | FlagEnumElement) : (flags & ~FlagEnumElement); |
| } |
| |
| void set isInlineClassMember(bool value) { |
| flags = value |
| ? (flags | FlagInlineClassMember) |
| : (flags & ~FlagInlineClassMember); |
| } |
| |
| @override |
| bool get isInstanceMember => !isStatic; |
| |
| @override |
| bool get hasGetter => true; |
| |
| @override |
| bool get hasSetter => setterReference != null; |
| |
| @override |
| bool get isExternal => false; |
| |
| @override |
| void set isExternal(bool value) { |
| if (value) throw 'Fields cannot be external'; |
| } |
| |
| @override |
| bool get isNonNullableByDefault => flags & FlagNonNullableByDefault != 0; |
| |
| @override |
| void set isNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagNonNullableByDefault) |
| : (flags & ~FlagNonNullableByDefault); |
| } |
| |
| @override |
| R accept<R>(MemberVisitor<R> v) => v.visitField(this); |
| |
| @override |
| R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => v.visitField(this, arg); |
| |
| @override |
| R acceptReference<R>(MemberReferenceVisitor<R> v) => |
| v.visitFieldReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| type.accept(v); |
| name.accept(v); |
| initializer?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| type = v.visitDartType(type); |
| v.transformList(annotations, this); |
| if (initializer != null) { |
| initializer = v.transform(initializer!); |
| initializer?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| type = v.visitDartType(type, null); |
| v.transformExpressionList(annotations, this); |
| if (initializer != null) { |
| initializer = v.transformOrRemoveExpression(initializer!); |
| initializer?.parent = this; |
| } |
| } |
| |
| @override |
| DartType get getterType => type; |
| |
| @override |
| DartType get setterType => hasSetter ? type : const NeverType.nonNullable(); |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(fieldReference); |
| } |
| } |
| |
| /// A generative constructor, possibly redirecting. |
| /// |
| /// Note that factory constructors are treated as [Procedure]s. |
| /// |
| /// Constructors do not take type parameters. Type arguments from a constructor |
| /// invocation should be matched with the type parameters declared in the class. |
| /// |
| /// For unnamed constructors, the name is an empty string (in a [Name]). |
| class Constructor extends Member { |
| /// Start offset of the constructor in the source file it comes from. |
| /// |
| /// Note that this includes annotations if any. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file |
| /// start offset is not available (this is the default if none is specifically |
| /// set). |
| int startFileOffset = TreeNode.noOffset; |
| |
| int flags = 0; |
| |
| @override |
| FunctionNode function; |
| |
| List<Initializer> initializers; |
| |
| Constructor(this.function, |
| {required Name name, |
| bool isConst = false, |
| bool isExternal = false, |
| bool isSynthetic = false, |
| List<Initializer>? initializers, |
| int transformerFlags = 0, |
| required Uri fileUri, |
| Reference? reference}) |
| : this.initializers = initializers ?? <Initializer>[], |
| // ignore: unnecessary_null_comparison |
| assert(function != null), |
| super(name, fileUri, reference) { |
| function.parent = this; |
| setParents(this.initializers, this); |
| this.isConst = isConst; |
| this.isExternal = isExternal; |
| this.isSynthetic = isSynthetic; |
| this.transformerFlags = transformerFlags; |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChildFromConstructor(this).bindTo(reference); |
| } |
| |
| @override |
| Class get enclosingClass => parent as Class; |
| |
| static const int FlagConst = 1 << 0; // Must match serialized bit positions. |
| static const int FlagExternal = 1 << 1; |
| static const int FlagSynthetic = 1 << 2; |
| static const int FlagNonNullableByDefault = 1 << 3; |
| |
| @override |
| bool get isConst => flags & FlagConst != 0; |
| |
| @override |
| bool get isExternal => flags & FlagExternal != 0; |
| |
| /// True if this is a synthetic constructor inserted in a class that |
| /// does not otherwise declare any constructors. |
| bool get isSynthetic => flags & FlagSynthetic != 0; |
| |
| void set isConst(bool value) { |
| flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
| } |
| |
| @override |
| void set isExternal(bool value) { |
| flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal); |
| } |
| |
| void set isSynthetic(bool value) { |
| flags = value ? (flags | FlagSynthetic) : (flags & ~FlagSynthetic); |
| } |
| |
| @override |
| bool get isInstanceMember => false; |
| |
| @override |
| bool get hasGetter => false; |
| |
| @override |
| bool get hasSetter => false; |
| |
| @override |
| bool get isExtensionMember => false; |
| |
| @override |
| bool get isInlineClassMember => false; |
| |
| @override |
| bool get isNonNullableByDefault => flags & FlagNonNullableByDefault != 0; |
| |
| @override |
| void set isNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagNonNullableByDefault) |
| : (flags & ~FlagNonNullableByDefault); |
| } |
| |
| @override |
| R accept<R>(MemberVisitor<R> v) => v.visitConstructor(this); |
| |
| @override |
| R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => |
| v.visitConstructor(this, arg); |
| |
| @override |
| R acceptReference<R>(MemberReferenceVisitor<R> v) => |
| v.visitConstructorReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| name.accept(v); |
| visitList(initializers, v); |
| function.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformList(initializers, this); |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformInitializerList(initializers, this); |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| // TODO(johnniwinther): Provide the tear off type here. |
| @override |
| DartType get getterType => const NeverType.nonNullable(); |
| |
| @override |
| DartType get setterType => const NeverType.nonNullable(); |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| } |
| |
| /// Residue of a redirecting factory constructor for the linking phase. |
| /// |
| /// In the following example, `bar` is a redirecting factory constructor. |
| /// |
| /// class A { |
| /// A.foo(); |
| /// factory A.bar() = A.foo; |
| /// } |
| /// |
| /// An invocation of `new A.bar()` has the same effect as an invocation of |
| /// `new A.foo()`. In Kernel, the invocations of `bar` are replaced with |
| /// invocations of `foo`, and after it is done, the redirecting constructor can |
| /// be removed from the class. However, it is needed during the linking phase, |
| /// because other modules can refer to that constructor. |
| /// |
| /// [RedirectingFactory]s contain the necessary information for |
| /// linking and are treated as non-runnable members of classes that merely serve |
| /// as containers for that information. |
| /// |
| /// Redirecting factory constructors can be unnamed. In this case, the name is |
| /// an empty string (in a [Name]). |
| class RedirectingFactory extends Member { |
| int flags = 0; |
| |
| /// [RedirectingFactory]s may redirect to constructors or factories |
| /// of instantiated generic types, that is, generic types with supplied type |
| /// arguments. The supplied type arguments are stored in this field. |
| final List<DartType> typeArguments; |
| |
| /// Reference to the constructor or the factory that this |
| /// [RedirectingFactory] redirects to. |
| // TODO(johnniwinther): Make this non-nullable. |
| Reference? targetReference; |
| |
| /// [FunctionNode] that holds the type parameters, copied from the enclosing |
| /// class, and the parameters defined on the redirecting factory. |
| /// |
| /// The `FunctionNode.body` is `null` or a synthesized [ConstructorInvocation] |
| /// of the [targetReference] constructor using the [typeArguments] and |
| /// [VariableGet] of the parameters. |
| @override |
| FunctionNode function; |
| |
| RedirectingFactory(this.targetReference, |
| {required Name name, |
| bool isConst = false, |
| bool isExternal = false, |
| int transformerFlags = 0, |
| List<DartType>? typeArguments, |
| required this.function, |
| required Uri fileUri, |
| Reference? reference}) |
| : this.typeArguments = typeArguments ?? <DartType>[], |
| super(name, fileUri, reference) { |
| function.parent = this; |
| this.isConst = isConst; |
| this.isExternal = isExternal; |
| this.transformerFlags = transformerFlags; |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChildFromRedirectingFactory(this).bindTo(reference); |
| } |
| |
| @override |
| Class get enclosingClass => parent as Class; |
| |
| static const int FlagConst = 1 << 0; // Must match serialized bit positions. |
| static const int FlagExternal = 1 << 1; |
| static const int FlagNonNullableByDefault = 1 << 2; |
| |
| @override |
| bool get isConst => flags & FlagConst != 0; |
| |
| @override |
| bool get isExternal => flags & FlagExternal != 0; |
| |
| void set isConst(bool value) { |
| flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
| } |
| |
| @override |
| void set isExternal(bool value) { |
| flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal); |
| } |
| |
| @override |
| bool get isInstanceMember => false; |
| |
| @override |
| bool get hasGetter => false; |
| |
| @override |
| bool get hasSetter => false; |
| |
| @override |
| bool get isExtensionMember => false; |
| |
| @override |
| bool get isInlineClassMember => false; |
| |
| bool get isUnresolved => targetReference == null; |
| |
| @override |
| bool get isNonNullableByDefault => flags & FlagNonNullableByDefault != 0; |
| |
| @override |
| void set isNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagNonNullableByDefault) |
| : (flags & ~FlagNonNullableByDefault); |
| } |
| |
| Member? get target => targetReference?.asMember; |
| |
| void set target(Member? member) { |
| assert(member is Constructor || |
| (member is Procedure && member.kind == ProcedureKind.Factory)); |
| targetReference = getMemberReferenceGetter(member); |
| } |
| |
| @override |
| R accept<R>(MemberVisitor<R> v) => v.visitRedirectingFactory(this); |
| |
| @override |
| R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => |
| v.visitRedirectingFactory(this, arg); |
| |
| @override |
| R acceptReference<R>(MemberReferenceVisitor<R> v) => |
| v.visitRedirectingFactoryReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| target?.acceptReference(v); |
| visitList(typeArguments, v); |
| name.accept(v); |
| function.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| v.transformDartTypeList(typeArguments); |
| function = v.transform(function)..parent = this; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| v.transformDartTypeList(typeArguments); |
| function = v.transform(function)..parent = this; |
| } |
| |
| @override |
| DartType get getterType => |
| function.computeFunctionType(enclosingLibrary.nonNullable); |
| |
| @override |
| DartType get setterType => const NeverType.nonNullable(); |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| } |
| |
| /// Enum for the semantics of the `Procedure.stubTarget` property. |
| enum ProcedureStubKind { |
| /// A regular procedure declared in source code. |
| /// |
| /// The stub target is `null`. |
| Regular, |
| |
| /// An abstract procedure inserted to add `isCovariantByDeclaration` and |
| /// `isCovariantByClass` to parameters for a set of overridden members. |
| /// |
| /// The stub is inserted when not all of the overridden members agree on |
| /// the covariance flags. For instance: |
| /// |
| /// class A<T> { |
| /// void method1(num o) {} |
| /// void method2(T o) {} |
| /// } |
| /// class B { |
| /// void method1(covariant int o) {} |
| /// void method2(int o) {} |
| /// } |
| /// class C implements A<int>, B { |
| /// // Abstract forwarding stub needed because the parameter is |
| /// // covariant in `B.method1` but not in `A.method1`. |
| /// void method1(covariant num o); |
| /// // Abstract forwarding stub needed because the parameter is a |
| /// // generic covariant impl in `A.method2` but not in `B.method2`. |
| /// void method2(/*generic-covariant-impl*/ int o); |
| /// } |
| /// |
| /// The stub target is one of the overridden members. |
| AbstractForwardingStub, |
| |
| /// A concrete procedure inserted to add `isCovariantByDeclaration` and |
| /// `isCovariantByClass` checks to parameters before calling the |
| /// overridden member in the superclass. |
| /// |
| /// The stub is inserted when not all of the overridden members agree on |
| /// the covariance flags and the overridden super class member does not |
| /// have the same covariance flags. For instance: |
| /// |
| /// class A<T> { |
| /// void method1(num o) {} |
| /// void method2(T o) {} |
| /// } |
| /// class B { |
| /// void method1(covariant int o) {} |
| /// void method2(int o) {} |
| /// } |
| /// class C extends A<int> implements B { |
| /// // Concrete forwarding stub needed because the parameter is |
| /// // covariant in `B.method1` but not in `A.method1`. |
| /// void method1(covariant num o) => super.method1(o); |
| /// // No need for a concrete forwarding stub for `A.method2` because |
| /// // it has the right covariance flags already. |
| /// } |
| /// |
| /// The stub target is the called superclass member. |
| ConcreteForwardingStub, |
| |
| /// A concrete procedure inserted to forward calls to `noSuchMethod` for |
| /// an inherited member that it does not implement. |
| /// |
| /// The stub is inserted when a class implements private members of another |
| /// library or declares/inherits a user-defined `noSuchMethod` method. For |
| /// instance: |
| /// |
| /// // lib1: |
| /// class A { |
| /// void _privateMethod() {} |
| /// } |
| /// // lib2: |
| /// class B implements A { |
| /// // Forwarding stub inserted to forward calls to `A._privateMethod`. |
| /// void _privateMethod() => noSuchMethod(#_privateMethod, ...); |
| /// } |
| /// class C { |
| /// void method() {} |
| /// } |
| /// class D implements C { |
| /// noSuchMethod(o) { ... } |
| /// // Forwarding stub inserted to forward calls to `C.method`. |
| /// void method() => noSuchMethod(#method, ...); |
| /// } |
| /// |
| /// |
| /// The stub target is `null` if the procedure preexisted as an abstract |
| /// procedure. Otherwise the stub target is one of the inherited members. |
| NoSuchMethodForwarder, |
| |
| /// An abstract procedure inserted to show the combined member signature type |
| /// of set of overridden members. |
| /// |
| /// The stub is inserted when an opt-in member is inherited into an opt-out |
| /// library or when NNBD_TOP_MERGE was used to compute the type of a merge |
| /// point in an opt-in library. For instance: |
| /// |
| /// // lib1: opt-in |
| /// class A { |
| /// int? method1() => null; |
| /// void method2(Object? o) {} |
| /// } |
| /// class B { |
| /// dynamic method2(dynamic o); |
| /// } |
| /// class C implements A, B { |
| /// // Member signature inserted for the NNBD_TOP_MERGE type of |
| /// // `A.method2` and `B.method2`. |
| /// Object? method2(Object? o); |
| /// } |
| /// // lib2: opt-out |
| /// class D extends A { |
| /// // Member signature inserted for the LEGACY_ERASURE type of |
| /// // `A.method1` and `A.method2` with types `int* Function()` |
| /// // and `void Function(Object*)`, respectively. |
| /// int method1(); |
| /// void method2(Object o); |
| /// } |
| /// |
| /// The stub target is one of the overridden members. |
| MemberSignature, |
| |
| /// An abstract procedure inserted for the application of an abstract mixin |
| /// member. |
| /// |
| /// The stub is inserted when an abstract member is mixed into a mixin |
| /// application. For instance: |
| /// |
| /// class Super {} |
| /// abstract class Mixin { |
| /// void method(); |
| /// } |
| /// class Class = Super with Mixin |
| /// // An abstract mixin stub for `A.method` is added to `Class` |
| /// void method(); |
| /// ; |
| /// |
| /// This is added to ensure that interface targets are resolved consistently |
| /// in face of cloning. For instance, without the abstract mixin stub, this |
| /// call: |
| /// |
| /// method(Class c) => c.method(); |
| /// |
| /// would use `Mixin.method` as its target, but after loading from a VM .dill |
| /// (which clones all mixin members) the call would resolve to `Class.method` |
| /// instead. By adding the mixin stub to `Class`, all accesses both before |
| /// and after .dill will point to `Class.method`. |
| /// |
| /// The stub target is the mixin member. |
| AbstractMixinStub, |
| |
| /// A concrete procedure inserted for the application of a concrete mixin |
| /// member. The implementation calls the mixin member via a super-call. |
| /// |
| /// The stub is inserted when a concrete member is mixed into a mixin |
| /// application. For instance: |
| /// |
| /// class Super {} |
| /// abstract class Mixin { |
| /// void method() {} |
| /// } |
| /// class Class = Super with Mixin |
| /// // A concrete mixin stub for `A.method` is added to `Class` which |
| /// // calls `A.method`. |
| /// void method() => super.method(); |
| /// ; |
| /// |
| /// This is added to ensure that super accesses are resolved correctly, even |
| /// in face of cloning. For instance, without the concrete mixin stub, this |
| /// super call: |
| /// |
| /// class Subclass extends Class { |
| /// method(Class c) => super.method(); |
| /// } |
| /// |
| /// would use `Mixin.method` as its target, which would need to be updated to |
| /// match the clone of the mixin member performed for instance by the VM. By |
| /// adding the concrete mixin stub to `Class`, all accesses both before and |
| /// after cloning will point to `Class.method`. |
| /// |
| /// The stub target is the called mixin member. |
| ConcreteMixinStub, |
| } |
| |
| /// A method, getter, setter, index-getter, index-setter, operator overloader, |
| /// or factory. |
| /// |
| /// Procedures can have the static, abstract, and/or external modifier, although |
| /// only the static and external modifiers may be used together. |
| /// |
| /// For non-static procedures the name is required for dynamic dispatch. |
| /// For external procedures the name is required for identifying the external |
| /// implementation. |
| /// |
| /// For methods, getters, and setters the name is just as it was declared. |
| /// For setters this does not include a trailing `=`. |
| /// For index-getters/setters, this is `[]` and `[]=`. |
| /// For operators, this is the token for the operator, e.g. `+` or `==`, |
| /// except for the unary minus operator, whose name is `unary-`. |
| class Procedure extends Member { |
| /// Start offset of the function in the source file it comes from. |
| /// |
| /// Note that this includes annotations if any. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file |
| /// start offset is not available (this is the default if none is specifically |
| /// set). |
| int fileStartOffset = TreeNode.noOffset; |
| |
| final ProcedureKind kind; |
| int flags = 0; |
| |
| @override |
| FunctionNode function; |
| |
| ProcedureStubKind stubKind; |
| Reference? stubTargetReference; |
| |
| /// The interface member signature type of this procedure. |
| /// |
| /// Normally this is derived from the parameter types and return type of |
| /// [function]. In rare cases, the interface member signature type is |
| /// different from the class member type, in which case the interface member |
| /// signature type is stored here. |
| /// |
| /// For instance |
| /// |
| /// class Super { |
| /// void method(num a) {} |
| /// } |
| /// class Class extends Super { |
| /// void method(covariant int a); |
| /// } |
| /// |
| /// Here the member `Class.method` is turned into a forwarding semi stub to |
| /// ensure that arguments passed to `Super.method` are checked as covariant. |
| /// Since `Super.method` allows `num` as argument, the inserted covariant |
| /// check must be against `num` and not `int`, and the parameter type of the |
| /// forwarding semi stub must be changed to `num`. Still, the interface of |
| /// `Class` requires that `Class.method` is `void Function(int)`, so for this, |
| /// it is stored explicitly as the [signatureType] on the procedure. |
| FunctionType? signatureType; |
| |
| Procedure(Name name, ProcedureKind kind, FunctionNode function, |
| {bool isAbstract = false, |
| bool isStatic = false, |
| bool isExternal = false, |
| bool isConst = false, |
| bool isExtensionMember = false, |
| bool isInlineClassMember = false, |
| bool isSynthetic = false, |
| bool isAbstractFieldAccessor = false, |
| int transformerFlags = 0, |
| required Uri fileUri, |
| Reference? reference, |
| ProcedureStubKind stubKind = ProcedureStubKind.Regular, |
| Member? stubTarget}) |
| : this._byReferenceRenamed(name, kind, function, |
| isAbstract: isAbstract, |
| isStatic: isStatic, |
| isExternal: isExternal, |
| isConst: isConst, |
| isExtensionMember: isExtensionMember, |
| isInlineClassMember: isInlineClassMember, |
| isSynthetic: isSynthetic, |
| isAbstractFieldAccessor: isAbstractFieldAccessor, |
| transformerFlags: transformerFlags, |
| fileUri: fileUri, |
| reference: reference, |
| stubKind: stubKind, |
| stubTargetReference: |
| getMemberReferenceBasedOnProcedureKind(stubTarget, kind)); |
| |
| Procedure._byReferenceRenamed(Name name, this.kind, this.function, |
| {bool isAbstract = false, |
| bool isStatic = false, |
| bool isExternal = false, |
| bool isConst = false, |
| bool isExtensionMember = false, |
| bool isInlineClassMember = false, |
| bool isSynthetic = false, |
| bool isAbstractFieldAccessor = false, |
| int transformerFlags = 0, |
| required Uri fileUri, |
| Reference? reference, |
| this.stubKind = ProcedureStubKind.Regular, |
| this.stubTargetReference}) |
| // ignore: unnecessary_null_comparison |
| : assert(kind != null), |
| // ignore: unnecessary_null_comparison |
| assert(function != null), |
| super(name, fileUri, reference) { |
| function.parent = this; |
| this.isAbstract = isAbstract; |
| this.isStatic = isStatic; |
| this.isExternal = isExternal; |
| this.isConst = isConst; |
| this.isExtensionMember = isExtensionMember; |
| this.isInlineClassMember = isInlineClassMember; |
| this.isSynthetic = isSynthetic; |
| this.isAbstractFieldAccessor = isAbstractFieldAccessor; |
| setTransformerFlagsWithoutLazyLoading(transformerFlags); |
| assert(!(isMemberSignature && stubTargetReference == null), |
| "No member signature origin for member signature $this."); |
| assert( |
| !(memberSignatureOrigin is Procedure && |
| (memberSignatureOrigin as Procedure).isMemberSignature), |
| "Member signature origin cannot be a member signature " |
| "$memberSignatureOrigin for $this."); |
| } |
| |
| // The function node's body might be lazily loaded, meaning that this value |
| // might not be set correctly yet. Make sure the body is loaded before |
| // returning anything. |
| @override |
| int get transformerFlags { |
| function.body; |
| return super.transformerFlags; |
| } |
| |
| // The function node's body might be lazily loaded, meaning that this value |
| // might get overwritten later (when the body is read). To avoid that read the |
| // body now and only set the value afterwards. |
| @override |
| void set transformerFlags(int newValue) { |
| function.body; |
| super.transformerFlags = newValue; |
| } |
| |
| // This function will set the transformer flags without loading the body. |
| // Used when reading the binary. For other cases one should probably use |
| // `transformerFlags = value;`. |
| void setTransformerFlagsWithoutLazyLoading(int newValue) { |
| super.transformerFlags = newValue; |
| } |
| |
| @override |
| void bindCanonicalNames(CanonicalName parent) { |
| parent.getChildFromProcedure(this).bindTo(reference); |
| } |
| |
| static const int FlagStatic = 1 << 0; // Must match serialized bit positions. |
| static const int FlagAbstract = 1 << 1; |
| static const int FlagExternal = 1 << 2; |
| static const int FlagConst = 1 << 3; // Only for external const factories. |
| // TODO(29841): Remove this flag after the issue is resolved. |
| static const int FlagRedirectingFactory = 1 << 4; |
| static const int FlagExtensionMember = 1 << 5; |
| static const int FlagNonNullableByDefault = 1 << 6; |
| static const int FlagSynthetic = 1 << 7; |
| static const int FlagInternalImplementation = 1 << 8; |
| static const int FlagIsAbstractFieldAccessor = 1 << 9; |
| static const int FlagInlineMember = 1 << 10; |
| static const int FlagHasWeakTearoffReferencePragma = 1 << 11; |
| |
| bool get isStatic => flags & FlagStatic != 0; |
| |
| @override |
| bool get isAbstract => flags & FlagAbstract != 0; |
| |
| @override |
| bool get isExternal => flags & FlagExternal != 0; |
| |
| /// True if this has the `const` modifier. This is only possible for external |
| /// constant factories, such as `String.fromEnvironment`. |
| @override |
| bool get isConst => flags & FlagConst != 0; |
| |
| /// If set, this flag indicates that this function's implementation exists |
| /// solely for the purpose of type checking arguments and forwarding to |
| /// [concreteForwardingStubTarget]. |
| /// |
| /// Note that just because this bit is set doesn't mean that the function was |
| /// not declared in the source; it's possible that this is a forwarding |
| /// semi-stub (see isForwardingSemiStub). To determine whether this function |
| /// was present in the source, consult [isSyntheticForwarder]. |
| bool get isForwardingStub => |
| stubKind == ProcedureStubKind.AbstractForwardingStub || |
| stubKind == ProcedureStubKind.ConcreteForwardingStub; |
| |
| /// If set, this flag indicates that although this function is a forwarding |
| /// stub, it was present in the original source as an abstract method. |
| bool get isForwardingSemiStub => !isSynthetic && isForwardingStub; |
| |
| /// If set, this method is a class member added to show the type of an |
| /// inherited member. |
| /// |
| /// This is used when the type of the inherited member cannot be computed |
| /// directly from the member(s) in the supertypes. For instance in case of |
| /// an nnbd opt-out class inheriting from an nnbd opt-in class; here all nnbd- |
| /// aware types are replaced with legacy types in the inherited signature. |
| bool get isMemberSignature => stubKind == ProcedureStubKind.MemberSignature; |
| |
| // Indicates if this [Procedure] represents a redirecting factory constructor |
| // and doesn't have a runnable body. |
| bool get isRedirectingFactory { |
| return flags & FlagRedirectingFactory != 0; |
| } |
| |
| /// If set, this flag indicates that this function was not present in the |
| /// source, and it exists solely for the purpose of type checking arguments |
| /// and forwarding to [concreteForwardingStubTarget]. |
| bool get isSyntheticForwarder => isForwardingStub && !isForwardingSemiStub; |
| bool get isSynthetic => flags & FlagSynthetic != 0; |
| |
| bool get isNoSuchMethodForwarder => |
| stubKind == ProcedureStubKind.NoSuchMethodForwarder; |
| |
| /// If `true` this procedure is not part of the interface but only part of the |
| /// class members. |
| /// |
| /// This is `true` for instance for augmented procedures. |
| @override |
| bool get isInternalImplementation => flags & FlagInternalImplementation != 0; |
| |
| void set isInternalImplementation(bool value) { |
| flags = value |
| ? (flags | FlagInternalImplementation) |
| : (flags & ~FlagInternalImplementation); |
| } |
| |
| /// If `true` this procedure was generated from an abstract field. |
| bool get isAbstractFieldAccessor => flags & FlagIsAbstractFieldAccessor != 0; |
| |
| void set isAbstractFieldAccessor(bool value) { |
| flags = value |
| ? (flags | FlagIsAbstractFieldAccessor) |
| : (flags & ~FlagIsAbstractFieldAccessor); |
| } |
| |
| @override |
| bool get isExtensionMember => flags & FlagExtensionMember != 0; |
| |
| @override |
| bool get isInlineClassMember => flags & FlagInlineMember != 0; |
| |
| void set isStatic(bool value) { |
| flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
| } |
| |
| void set isAbstract(bool value) { |
| flags = value ? (flags | FlagAbstract) : (flags & ~FlagAbstract); |
| } |
| |
| @override |
| void set isExternal(bool value) { |
| flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal); |
| } |
| |
| void set isConst(bool value) { |
| flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
| } |
| |
| void set isRedirectingFactory(bool value) { |
| flags = value |
| ? (flags | FlagRedirectingFactory) |
| : (flags & ~FlagRedirectingFactory); |
| } |
| |
| void set isExtensionMember(bool value) { |
| flags = |
| value ? (flags | FlagExtensionMember) : (flags & ~FlagExtensionMember); |
| } |
| |
| void set isInlineClassMember(bool value) { |
| flags = value ? (flags | FlagInlineMember) : (flags & ~FlagInlineMember); |
| } |
| |
| void set isSynthetic(bool value) { |
| flags = value ? (flags | FlagSynthetic) : (flags & ~FlagSynthetic); |
| } |
| |
| @override |
| bool get isInstanceMember => !isStatic; |
| |
| bool get isGetter => kind == ProcedureKind.Getter; |
| bool get isSetter => kind == ProcedureKind.Setter; |
| bool get isAccessor => isGetter || isSetter; |
| |
| @override |
| bool get hasGetter => kind != ProcedureKind.Setter; |
| |
| @override |
| bool get hasSetter => kind == ProcedureKind.Setter; |
| |
| bool get isFactory => kind == ProcedureKind.Factory; |
| |
| @override |
| bool get isNonNullableByDefault => flags & FlagNonNullableByDefault != 0; |
| |
| @override |
| void set isNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagNonNullableByDefault) |
| : (flags & ~FlagNonNullableByDefault); |
| } |
| |
| Member? get concreteForwardingStubTarget => |
| stubKind == ProcedureStubKind.ConcreteForwardingStub |
| ? stubTargetReference?.asMember |
| : null; |
| |
| Member? get abstractForwardingStubTarget => |
| stubKind == ProcedureStubKind.AbstractForwardingStub |
| ? stubTargetReference?.asMember |
| : null; |
| |
| Member? get stubTarget => stubTargetReference?.asMember; |
| |
| void set stubTarget(Member? target) { |
| stubTargetReference = getMemberReferenceBasedOnProcedureKind(target, kind); |
| } |
| |
| @override |
| Member? get memberSignatureOrigin => |
| stubKind == ProcedureStubKind.MemberSignature |
| ? stubTargetReference?.asMember |
| : null; |
| |
| bool get hasWeakTearoffReferencePragma => |
| flags & FlagHasWeakTearoffReferencePragma != 0; |
| |
| void set hasWeakTearoffReferencePragma(bool value) { |
| flags = value |
| ? (flags | FlagHasWeakTearoffReferencePragma) |
| : (flags & ~FlagHasWeakTearoffReferencePragma); |
| } |
| |
| @override |
| R accept<R>(MemberVisitor<R> v) => v.visitProcedure(this); |
| |
| @override |
| R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => v.visitProcedure(this, arg); |
| |
| @override |
| R acceptReference<R>(MemberReferenceVisitor<R> v) => |
| v.visitProcedureReference(this); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| name.accept(v); |
| function.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| if (signatureType != null) { |
| signatureType = v.visitDartType(signatureType!) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| if (signatureType != null) { |
| DartType newSignatureType = |
| v.visitDartType(signatureType!, dummyDartType); |
| if (identical(newSignatureType, dummyDartType)) { |
| signatureType = null; |
| } else { |
| signatureType = newSignatureType as FunctionType; |
| } |
| } |
| } |
| |
| @override |
| DartType get getterType { |
| return isGetter |
| ? (signatureType?.returnType ?? function.returnType) |
| : (signatureType ?? |
| function.computeFunctionType(enclosingLibrary.nonNullable)); |
| } |
| |
| @override |
| DartType get superGetterType { |
| return isGetter |
| ? function.returnType |
| : function.computeFunctionType(enclosingLibrary.nonNullable); |
| } |
| |
| @override |
| DartType get setterType { |
| return isSetter |
| ? (signatureType?.positionalParameters[0] ?? |
| function.positionalParameters[0].type) |
| : const NeverType.nonNullable(); |
| } |
| |
| @override |
| DartType get superSetterType { |
| return isSetter |
| ? function.positionalParameters[0].type |
| : const NeverType.nonNullable(); |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| } |
| |
| enum ProcedureKind { |
| Method, |
| Getter, |
| Setter, |
| Operator, |
| Factory, |
| } |
| |
| // ------------------------------------------------------------------------ |
| // CONSTRUCTOR INITIALIZERS |
| // ------------------------------------------------------------------------ |
| |
| /// Part of an initializer list in a constructor. |
| abstract class Initializer extends TreeNode { |
| /// True if this is a synthetic constructor initializer. |
| @informative |
| bool isSynthetic = false; |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg); |
| } |
| |
| /// An initializer with a compile-time error. |
| /// |
| /// Should throw an exception at runtime. |
| // |
| // DESIGN TODO: The frontend should use this in a lot more cases to catch |
| // invalid cases. |
| class InvalidInitializer extends Initializer { |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitInvalidInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitInvalidInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "InvalidInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// A field assignment `field = value` occurring in the initializer list of |
| /// a constructor. |
| /// |
| /// This node has nothing to do with declaration-site field initializers; those |
| /// are [Expression]s stored in [Field.initializer]. |
| // |
| // TODO: The frontend should check that all final fields are initialized |
| // exactly once, and that no fields are assigned twice in the initializer list. |
| class FieldInitializer extends Initializer { |
| /// Reference to the field being initialized. Not null. |
| Reference fieldReference; |
| Expression value; |
| |
| FieldInitializer(Field field, Expression value) |
| : this.byReference(field.fieldReference, value); |
| |
| FieldInitializer.byReference(this.fieldReference, this.value) { |
| value.parent = this; |
| } |
| |
| Field get field => fieldReference.asField; |
| |
| void set field(Field field) { |
| fieldReference = field.fieldReference; |
| } |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitFieldInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitFieldInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| field.acceptReference(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FieldInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// A super call `super(x,y)` occurring in the initializer list of a |
| /// constructor. |
| /// |
| /// There are no type arguments on this call. |
| // |
| // TODO: The frontend should check that there is no more than one super call. |
| // |
| // DESIGN TODO: Consider if the frontend should insert type arguments derived |
| // from the extends clause. |
| class SuperInitializer extends Initializer { |
| /// Reference to the constructor being invoked in the super class. Not null. |
| Reference targetReference; |
| Arguments arguments; |
| |
| SuperInitializer(Constructor target, Arguments arguments) |
| : this.byReference( |
| // Getter vs setter doesn't matter for constructors. |
| getNonNullableMemberReferenceGetter(target), |
| arguments); |
| |
| SuperInitializer.byReference(this.targetReference, this.arguments) { |
| arguments.parent = this; |
| } |
| |
| Constructor get target => targetReference.asConstructor; |
| |
| void set target(Constructor target) { |
| // Getter vs setter doesn't matter for constructors. |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitSuperInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitSuperInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "SuperInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super'); |
| if (target.name.text.isNotEmpty) { |
| printer.write('.'); |
| printer.write(target.name.text); |
| } |
| printer.writeArguments(arguments, includeTypeArguments: false); |
| } |
| } |
| |
| /// A redirecting call `this(x,y)` occurring in the initializer list of |
| /// a constructor. |
| // |
| // TODO: The frontend should check that this is the only initializer and if the |
| // constructor has a body or if there is a cycle in the initializer calls. |
| class RedirectingInitializer extends Initializer { |
| /// Reference to the constructor being invoked in the same class. Not null. |
| Reference targetReference; |
| Arguments arguments; |
| |
| RedirectingInitializer(Constructor target, Arguments arguments) |
| : this.byReference( |
| // Getter vs setter doesn't matter for constructors. |
| getNonNullableMemberReferenceGetter(target), |
| arguments); |
| |
| RedirectingInitializer.byReference(this.targetReference, this.arguments) { |
| arguments.parent = this; |
| } |
| |
| Constructor get target => targetReference.asConstructor; |
| |
| void set target(Constructor target) { |
| // Getter vs setter doesn't matter for constructors. |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitRedirectingInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitRedirectingInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "RedirectingInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| /// Binding of a temporary variable in the initializer list of a constructor. |
| /// |
| /// The variable is in scope for the remainder of the initializer list, but is |
| /// not in scope in the constructor body. |
| class LocalInitializer extends Initializer { |
| VariableDeclaration variable; |
| |
| LocalInitializer(this.variable) { |
| variable.parent = this; |
| } |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitLocalInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitLocalInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| variable.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "LocalInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| class AssertInitializer extends Initializer { |
| AssertStatement statement; |
| |
| AssertInitializer(this.statement) { |
| statement.parent = this; |
| } |
| |
| @override |
| R accept<R>(InitializerVisitor<R> v) => v.visitAssertInitializer(this); |
| |
| @override |
| R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) => |
| v.visitAssertInitializer(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| statement.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| statement = v.transform(statement); |
| statement.parent = this; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| statement = v.transform(statement); |
| statement.parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "AssertInitializer(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| statement.toTextInternal(printer); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // FUNCTIONS |
| // ------------------------------------------------------------------------ |
| |
| /// A function declares parameters and has a body. |
| /// |
| /// This may occur in a procedure, constructor, function expression, or local |
| /// function declaration. |
| class FunctionNode extends TreeNode { |
| /// End offset in the source file it comes from. Valid values are from 0 and |
| /// up, or -1 ([TreeNode.noOffset]) if the file end offset is not available |
| /// (this is the default if none is specifically set). |
| int fileEndOffset = TreeNode.noOffset; |
| |
| /// Kernel async marker for the function. |
| /// |
| /// See also [dartAsyncMarker]. |
| AsyncMarker asyncMarker; |
| |
| /// Dart async marker for the function. |
| /// |
| /// See also [asyncMarker]. |
| /// |
| /// A Kernel function can represent a Dart function with a different async |
| /// marker. |
| /// |
| /// For example, when async/await is translated away, |
| /// a Dart async function might be represented by a Kernel sync function. |
| AsyncMarker dartAsyncMarker; |
| |
| List<TypeParameter> typeParameters; |
| int requiredParameterCount; |
| List<VariableDeclaration> positionalParameters; |
| List<VariableDeclaration> namedParameters; |
| DartType returnType; // Not null. |
| Statement? _body; |
| |
| /// The future value type of this is an async function, otherwise `null`. |
| /// |
| /// The future value type is the element type returned by an async function. |
| /// For instance |
| /// |
| /// Future<Foo> method1() async => new Foo(); |
| /// FutureOr<Foo> method2() async => new Foo(); |
| /// |
| /// here the return types are `Future<Foo>` and `FutureOr<Foo>` for `method1` |
| /// and `method2`, respectively, but the future value type is in both cases |
| /// `Foo`. |
| /// |
| /// For pre-nnbd libraries, this is set to `flatten(T)` of the return type |
| /// `T`, which can be seen as the pre-nnbd equivalent of the future value |
| /// type. |
| DartType? futureValueType; |
| |
| void Function()? lazyBuilder; |
| |
| void _buildLazy() { |
| void Function()? lazyBuilderLocal = lazyBuilder; |
| if (lazyBuilderLocal != null) { |
| lazyBuilder = null; |
| lazyBuilderLocal(); |
| } |
| } |
| |
| Statement? get body { |
| _buildLazy(); |
| return _body; |
| } |
| |
| void set body(Statement? body) { |
| _buildLazy(); |
| _body = body; |
| } |
| |
| FunctionNode(this._body, |
| {List<TypeParameter>? typeParameters, |
| List<VariableDeclaration>? positionalParameters, |
| List<VariableDeclaration>? namedParameters, |
| int? requiredParameterCount, |
| this.returnType = const DynamicType(), |
| this.asyncMarker = AsyncMarker.Sync, |
| AsyncMarker? dartAsyncMarker, |
| this.futureValueType}) |
| : this.positionalParameters = |
| positionalParameters ?? <VariableDeclaration>[], |
| this.requiredParameterCount = |
| requiredParameterCount ?? positionalParameters?.length ?? 0, |
| this.namedParameters = namedParameters ?? <VariableDeclaration>[], |
| this.typeParameters = typeParameters ?? <TypeParameter>[], |
| this.dartAsyncMarker = dartAsyncMarker ?? asyncMarker { |
| // ignore: unnecessary_null_comparison |
| assert(returnType != null); |
| setParents(this.typeParameters, this); |
| setParents(this.positionalParameters, this); |
| setParents(this.namedParameters, this); |
| _body?.parent = this; |
| } |
| |
| static DartType _getTypeOfVariable(VariableDeclaration node) => node.type; |
| |
| static NamedType _getNamedTypeOfVariable(VariableDeclaration node) { |
| return new NamedType(node.name!, node.type, isRequired: node.isRequired); |
| } |
| |
| /// Returns the function type of the node reusing its type parameters. |
| /// |
| /// This getter works similarly to [functionType], but reuses type parameters |
| /// of the function node (or the class enclosing it -- see the comment on |
| /// [functionType] about constructors of generic classes) in the result. It |
| /// is useful in some contexts, especially when reasoning about the function |
| /// type of the enclosing generic function and in combination with |
| /// [FunctionType.withoutTypeParameters]. |
| FunctionType computeThisFunctionType(Nullability nullability) { |
| TreeNode? parent = this.parent; |
| List<NamedType> named; |
| if (namedParameters.isEmpty) { |
| named = const <NamedType>[]; |
| } else { |
| named = |
| namedParameters.map(_getNamedTypeOfVariable).toList(growable: false); |
| named.sort(); |
| } |
| |
| List<TypeParameter> typeParametersCopy; |
| List<TypeParameter> typeParametersToCopy = parent is Constructor |
| ? parent.enclosingClass.typeParameters |
| : typeParameters; |
| if (typeParametersToCopy.isEmpty) { |
| typeParametersCopy = const <TypeParameter>[]; |
| } else { |
| // We need create a copy of the list of type parameters, otherwise |
| // transformations like erasure don't work. |
| typeParametersCopy = |
| new List<TypeParameter>.of(typeParametersToCopy, growable: false); |
| } |
| return new FunctionType( |
| positionalParameters.map(_getTypeOfVariable).toList(growable: false), |
| returnType, |
| nullability, |
| namedParameters: named, |
| typeParameters: typeParametersCopy, |
| requiredParameterCount: requiredParameterCount); |
| } |
| |
| /// Returns the function type of the function node. |
| /// |
| /// If the function node describes a generic function, the resulting function |
| /// type will be generic. If the function node describes a constructor of a |
| /// generic class, the resulting function type will be generic with its type |
| /// parameters constructed after those of the class. In both cases, if the |
| /// resulting function type is generic, a fresh set of type parameters is used |
| /// in it. |
| FunctionType computeFunctionType(Nullability nullability) { |
| TreeNode? parent = this.parent; |
| List<TypeParameter> typeParameters; |
| if (parent is Constructor) { |
| assert(this.typeParameters.isEmpty); |
| typeParameters = parent.enclosingClass.typeParameters; |
| } else { |
| typeParameters = this.typeParameters; |
| } |
| return typeParameters.isEmpty |
| ? computeThisFunctionType(nullability) |
| : getFreshTypeParameters(typeParameters) |
| .applyToFunctionType(computeThisFunctionType(nullability)); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitFunctionNode(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => |
| v.visitFunctionNode(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(typeParameters, v); |
| visitList(positionalParameters, v); |
| visitList(namedParameters, v); |
| returnType.accept(v); |
| futureValueType?.accept(v); |
| body?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(typeParameters, this); |
| v.transformList(positionalParameters, this); |
| v.transformList(namedParameters, this); |
| returnType = v.visitDartType(returnType); |
| if (futureValueType != null) { |
| futureValueType = v.visitDartType(futureValueType!); |
| } |
| if (body != null) { |
| body = v.transform(body!); |
| body?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformTypeParameterList(typeParameters, this); |
| v.transformVariableDeclarationList(positionalParameters, this); |
| v.transformVariableDeclarationList(namedParameters, this); |
| returnType = v.visitDartType(returnType, cannotRemoveSentinel); |
| if (futureValueType != null) { |
| futureValueType = v.visitDartType(futureValueType!, cannotRemoveSentinel); |
| } |
| if (body != null) { |
| body = v.transformOrRemoveStatement(body!); |
| body?.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FunctionNode(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| } |
| |
| enum AsyncMarker { |
| // Do not change the order of these, the frontends depend on it. |
| Sync, |
| SyncStar, |
| Async, |
| AsyncStar, |
| } |
| |
| // ------------------------------------------------------------------------ |
| // EXPRESSIONS |
| // ------------------------------------------------------------------------ |
| |
| abstract class Expression extends TreeNode { |
| /// Returns the static type of the expression. |
| /// |
| /// This calls `StaticTypeContext.getExpressionType` which calls |
| /// [getStaticTypeInternal] to compute the type of not already cached in |
| /// [context]. |
| DartType getStaticType(StaticTypeContext context) { |
| return context.getExpressionType(this); |
| } |
| |
| /// Computes the static type of this expression. |
| /// |
| /// This is called by `StaticTypeContext.getExpressionType` if the static |
| /// type of this expression is not already cached in [context]. |
| DartType getStaticTypeInternal(StaticTypeContext context); |
| |
| /// Returns the static type of the expression as an instantiation of |
| /// [superclass]. |
| /// |
| /// Shouldn't be used on code compiled in legacy mode, as this method assumes |
| /// the IR is strongly typed. |
| /// |
| /// This method furthermore assumes that the type of the expression actually |
| /// is a subtype of (some instantiation of) the given [superclass]. |
| /// If this is not the case, either an exception is thrown or the raw type of |
| /// [superclass] is returned. |
| InterfaceType getStaticTypeAsInstanceOf( |
| Class superclass, StaticTypeContext context) { |
| // This method assumes the program is correctly typed, so if the superclass |
| // is not generic, we can just return its raw type without computing the |
| // type of this expression. It also ensures that all types are considered |
| // subtypes of Object (not just interface types), and function types are |
| // considered subtypes of Function. |
| if (superclass.typeParameters.isEmpty) { |
| return context.typeEnvironment.coreTypes |
| .rawType(superclass, context.nonNullable); |
| } |
| DartType type = getStaticType(context).resolveTypeParameterType; |
| if (type is NullType) { |
| return context.typeEnvironment.coreTypes |
| .bottomInterfaceType(superclass, context.nullable); |
| } else if (type is NeverType) { |
| return context.typeEnvironment.coreTypes |
| .bottomInterfaceType(superclass, type.nullability); |
| } |
| if (type is InterfaceType) { |
| List<DartType>? upcastTypeArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf(type, superclass); |
| if (upcastTypeArguments != null) { |
| return new InterfaceType( |
| superclass, type.nullability, upcastTypeArguments); |
| } |
| } |
| |
| // The static type of this expression is not a subtype of [superclass]. The |
| // means that the static type of this expression is not the same as when |
| // the parent [PropertyGet] or [MethodInvocation] was created. |
| // |
| // For instance when cloning generic mixin methods, the substitution can |
| // render some of the code paths as dead code: |
| // |
| // mixin M<T> { |
| // int method(T t) => t is String ? t.length : 0; |
| // } |
| // class C with M<int> {} |
| // |
| // The mixin transformation will clone the `M.method` method into the |
| // unnamed mixin application for `Object&M<int>` as this: |
| // |
| // int method(int t) => t is String ? t.length : 0; |
| // |
| // Now `t.length`, which was originally an access to `String.length` on a |
| // receiver of type `T & String`, is an access to `String.length` on `int`. |
| // When computing the static type of `t.length` we will try to compute the |
| // type of `int` as an instance of `String`, and we do not find it to be |
| // an instance of `String`. |
| // |
| // To resolve this case we compute the type of `t.length` to be the type |
| // as if accessed on an unknown subtype `String`. |
| return context.typeEnvironment.coreTypes |
| .rawType(superclass, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg); |
| |
| int get precedence => astToText.Precedence.of(this); |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeExpression(this); |
| return printer.getText(); |
| } |
| } |
| |
| /// An expression containing compile-time errors. |
| /// |
| /// Should throw a runtime error when evaluated. |
| /// |
| /// The [fileOffset] of an [InvalidExpression] indicates the location in the |
| /// tree where the expression occurs, rather than the location of the error. |
| class InvalidExpression extends Expression { |
| // TODO(johnniwinther): Avoid using `null` as the empty string. |
| String? message; |
| |
| /// The expression containing the error. |
| Expression? expression; |
| |
| InvalidExpression(this.message, [this.expression]) { |
| expression?.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| const NeverType.nonNullable(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInvalidExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInvalidExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| if (expression != null) { |
| expression = v.transform(expression!); |
| expression?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| if (expression != null) { |
| expression = v.transformOrRemoveExpression(expression!); |
| expression?.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InvalidExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('<invalid:'); |
| printer.write(message ?? ''); |
| if (expression != null) { |
| printer.write(', '); |
| printer.writeExpression(expression!); |
| } |
| printer.write('>'); |
| } |
| } |
| |
| /// Read a local variable, a local function, or a function parameter. |
| class VariableGet extends Expression { |
| VariableDeclaration variable; |
| DartType? promotedType; // Null if not promoted. |
| |
| VariableGet(this.variable, [this.promotedType]) |
| // ignore: unnecessary_null_comparison |
| : assert(variable != null); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return promotedType ?? variable.type; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitVariableGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitVariableGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| promotedType?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| if (promotedType != null) { |
| promotedType = v.visitDartType(promotedType!); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| if (promotedType != null) { |
| DartType newPromotedType = v.visitDartType(promotedType!, dummyDartType); |
| if (identical(newPromotedType, dummyDartType)) { |
| promotedType = null; |
| } else { |
| promotedType = newPromotedType; |
| } |
| } |
| } |
| |
| @override |
| String toString() { |
| return "VariableGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(printer.getVariableName(variable)); |
| if (promotedType != null) { |
| printer.write('{'); |
| printer.writeType(promotedType!); |
| printer.write('}'); |
| } |
| } |
| } |
| |
| /// Assign a local variable or function parameter. |
| /// |
| /// Evaluates to the value of [value]. |
| class VariableSet extends Expression { |
| VariableDeclaration variable; |
| Expression value; |
| |
| VariableSet(this.variable, this.value) |
| // ignore: unnecessary_null_comparison |
| : assert(variable != null) { |
| value.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitVariableSet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitVariableSet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "VariableSet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(printer.getVariableName(variable)); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| class RecordIndexGet extends Expression { |
| Expression receiver; |
| RecordType receiverType; |
| final int index; |
| |
| RecordIndexGet(this.receiver, this.receiverType, this.index) |
| : assert(0 <= index && index < receiverType.positional.length) { |
| receiver.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| assert(index < receiverType.positional.length); |
| return receiverType.positional[index]; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordIndexGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitRecordIndexGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| receiver = v.transform(receiver)..parent = this; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| receiver = v.transform(receiver)..parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "RecordIndexGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver); |
| printer.write(".\$${index + 1}"); |
| } |
| } |
| |
| class RecordNameGet extends Expression { |
| Expression receiver; |
| RecordType receiverType; |
| final String name; |
| |
| RecordNameGet(this.receiver, this.receiverType, this.name) |
| : assert(receiverType.named |
| .singleWhere((element) => element.name == name) |
| .name == |
| name) { |
| receiver.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| DartType? result; |
| for (NamedType namedType in receiverType.named) { |
| if (namedType.name == name) { |
| result = namedType.type; |
| break; |
| } |
| } |
| assert(result != null); |
| |
| return result!; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordNameGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitRecordNameGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| receiver = v.transform(receiver)..parent = this; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| receiver = v.transform(receiver)..parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "RecordNameGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver); |
| printer.write(".${name}"); |
| } |
| } |
| |
| enum DynamicAccessKind { |
| /// An access on a receiver of type dynamic. |
| /// |
| /// An access of this kind always results in a value of static type dynamic. |
| /// |
| /// Valid accesses to Object members on receivers of type dynamic are encoded |
| /// as an [InstanceInvocation] of kind [InstanceAccessKind.Object]. |
| Dynamic, |
| |
| /// An access on a receiver of type Never. |
| /// |
| /// An access of this kind always results in a value of static type Never. |
| /// |
| /// Valid accesses to Object members on receivers of type Never are also |
| /// encoded as [DynamicInvocation] of kind [DynamicAccessKind.Never] and _not_ |
| /// as an [InstanceInvocation] of kind [InstanceAccessKind.Object]. |
| Never, |
| |
| /// An access on a receiver of an invalid type. |
| /// |
| /// An access of this kind always results in a value of an invalid static |
| /// type. |
| Invalid, |
| |
| /// An access of an unresolved target. |
| /// |
| /// An access of this kind always results in a value of an invalid static |
| /// type. |
| Unresolved, |
| } |
| |
| class DynamicGet extends Expression { |
| final DynamicAccessKind kind; |
| Expression receiver; |
| Name name; |
| |
| DynamicGet(this.kind, this.receiver, this.name) { |
| receiver.parent = this; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitDynamicGet(this, arg); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| switch (kind) { |
| case DynamicAccessKind.Dynamic: |
| return const DynamicType(); |
| case DynamicAccessKind.Never: |
| return const NeverType.nonNullable(); |
| case DynamicAccessKind.Invalid: |
| case DynamicAccessKind.Unresolved: |
| return const InvalidType(); |
| } |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| name.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "DynamicGet($kind,${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeName(name); |
| } |
| } |
| |
| /// A property read of an instance getter or field with a statically known |
| /// interface target. |
| class InstanceGet extends Expression { |
| final InstanceAccessKind kind; |
| Expression receiver; |
| |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? |
| Name name; |
| |
| /// The static type of result of the property read. |
| /// |
| /// This includes substituted type parameters from the static receiver type. |
| /// |
| /// For instance |
| /// |
| /// class A<T> { |
| /// T get t; |
| /// } |
| /// m(A<String> a) { |
| /// a.t; // The result type is `String`. |
| /// } |
| /// |
| DartType resultType; |
| |
| Reference interfaceTargetReference; |
| |
| InstanceGet(InstanceAccessKind kind, Expression receiver, Name name, |
| {required Member interfaceTarget, required DartType resultType}) |
| : this.byReference(kind, receiver, name, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceGetter(interfaceTarget), |
| resultType: resultType); |
| |
| InstanceGet.byReference(this.kind, this.receiver, this.name, |
| {required this.interfaceTargetReference, required this.resultType}) |
| // ignore: unnecessary_null_comparison |
| : assert(interfaceTargetReference != null), |
| // ignore: unnecessary_null_comparison |
| assert(resultType != null) { |
| receiver.parent = this; |
| } |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => resultType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| resultType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (resultType != null) { |
| resultType = v.visitDartType(resultType); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (resultType != null) { |
| resultType = v.visitDartType(resultType, cannotRemoveSentinel); |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InstanceGet($kind,${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| } |
| } |
| |
| /// A tear-off of the 'call' method on an expression whose static type is |
| /// a function type or the type 'Function'. |
| class FunctionTearOff extends Expression { |
| Expression receiver; |
| |
| FunctionTearOff(this.receiver) { |
| receiver.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| receiver.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitFunctionTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FunctionTearOff(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeName(Name.callName); |
| } |
| } |
| |
| /// A tear-off of an instance method with a statically known interface target. |
| class InstanceTearOff extends Expression { |
| final InstanceAccessKind kind; |
| Expression receiver; |
| |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? |
| Name name; |
| |
| /// The static type of result of the tear-off. |
| /// |
| /// This includes substituted type parameters from the static receiver type. |
| /// |
| /// For instance |
| /// |
| /// class A<T, S> { |
| /// T method<U>(S s, U u) { ... } |
| /// } |
| /// m(A<String, int> a) { |
| /// a.method; // The result type is `String Function<U>(int, U)`. |
| /// } |
| /// |
| DartType resultType; |
| |
| Reference interfaceTargetReference; |
| |
| InstanceTearOff(InstanceAccessKind kind, Expression receiver, Name name, |
| {required Procedure interfaceTarget, required DartType resultType}) |
| : this.byReference(kind, receiver, name, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceGetter(interfaceTarget), |
| resultType: resultType); |
| |
| InstanceTearOff.byReference(this.kind, this.receiver, this.name, |
| {required this.interfaceTargetReference, required this.resultType}) { |
| receiver.parent = this; |
| } |
| |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; |
| |
| void set interfaceTarget(Procedure procedure) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(procedure); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => resultType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| resultType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (resultType != null) { |
| resultType = v.visitDartType(resultType); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (resultType != null) { |
| resultType = v.visitDartType(resultType, cannotRemoveSentinel); |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InstanceTearOff($kind, ${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| } |
| } |
| |
| class DynamicSet extends Expression { |
| final DynamicAccessKind kind; |
| Expression receiver; |
| Name name; |
| Expression value; |
| |
| DynamicSet(this.kind, this.receiver, this.name, this.value) { |
| receiver.parent = this; |
| value.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicSet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitDynamicSet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| name.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "DynamicSet($kind,${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeName(name); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// An property write of an instance setter or field with a statically known |
| /// interface target. |
| class InstanceSet extends Expression { |
| final InstanceAccessKind kind; |
| Expression receiver; |
| |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? |
| Name name; |
| Expression value; |
| |
| Reference interfaceTargetReference; |
| |
| InstanceSet( |
| InstanceAccessKind kind, Expression receiver, Name name, Expression value, |
| {required Member interfaceTarget}) |
| : this.byReference(kind, receiver, name, value, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceSetter(interfaceTarget)); |
| |
| InstanceSet.byReference(this.kind, this.receiver, this.name, this.value, |
| {required this.interfaceTargetReference}) |
| // ignore: unnecessary_null_comparison |
| : assert(interfaceTargetReference != null) { |
| receiver.parent = this; |
| value.parent = this; |
| } |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceSet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceSet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InstanceSet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// Expression of form `super.foo` occurring in a mixin declaration. |
| /// |
| /// In this setting, the target is looked up on the types in the mixin 'on' |
| /// clause and are therefore not necessary the runtime targets of the read. An |
| /// [AbstractSuperPropertyGet] must be converted into a [SuperPropertyGet] to |
| /// statically bind the target. |
| /// |
| /// For instance |
| /// |
| /// abstract class Interface { |
| /// get getter; |
| /// } |
| /// mixin Mixin on Interface { |
| /// get getter { |
| /// // This is an [AbstractSuperPropertyGet] with interface target |
| /// // `Interface.getter`. |
| /// return super.getter; |
| /// } |
| /// } |
| /// class Super implements Interface { |
| /// // This is the target when `Mixin` is applied to `Class`. |
| /// get getter => 42; |
| /// } |
| /// class Class extends Super with Mixin {} |
| /// |
| /// This may invoke a getter, read a field, or tear off a method. |
| class AbstractSuperPropertyGet extends Expression { |
| Name name; |
| |
| Reference interfaceTargetReference; |
| |
| AbstractSuperPropertyGet(Name name, Member interfaceTarget) |
| : this.byReference( |
| name, getNonNullableMemberReferenceGetter(interfaceTarget)); |
| |
| AbstractSuperPropertyGet.byReference( |
| this.name, this.interfaceTargetReference); |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| Class declaringClass = interfaceTarget.enclosingClass!; |
| if (declaringClass.typeParameters.isEmpty) { |
| return interfaceTarget.getterType; |
| } |
| List<DartType>? receiverArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass); |
| return Substitution.fromPairs( |
| declaringClass.typeParameters, receiverArguments!) |
| .substituteType(interfaceTarget.getterType); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertyGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitAbstractSuperPropertyGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "AbstractSuperPropertyGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.{abstract}'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| } |
| } |
| |
| /// Expression of form `super.field`. |
| /// |
| /// This may invoke a getter, read a field, or tear off a method. |
| class SuperPropertyGet extends Expression { |
| Name name; |
| |
| Reference interfaceTargetReference; |
| |
| SuperPropertyGet(Name name, Member interfaceTarget) |
| : this.byReference( |
| name, getNonNullableMemberReferenceGetter(interfaceTarget)); |
| |
| SuperPropertyGet.byReference(this.name, this.interfaceTargetReference); |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| Class declaringClass = interfaceTarget.enclosingClass!; |
| if (declaringClass.typeParameters.isEmpty) { |
| return interfaceTarget.getterType; |
| } |
| List<DartType>? receiverArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass); |
| return Substitution.fromPairs( |
| declaringClass.typeParameters, receiverArguments!) |
| .substituteType(interfaceTarget.getterType); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertyGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSuperPropertyGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "SuperPropertyGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| } |
| } |
| |
| /// Expression of form `super.foo = x` occurring in a mixin declaration. |
| /// |
| /// In this setting, the target is looked up on the types in the mixin 'on' |
| /// clause and are therefore not necessary the runtime targets of the |
| /// assignment. An [AbstractSuperPropertySet] must be converted into a |
| /// [SuperPropertySet] to statically bind the target. |
| /// |
| /// For instance |
| /// |
| /// abstract class Interface { |
| /// void set setter(value); |
| /// } |
| /// mixin Mixin on Interface { |
| /// void set setter(value) { |
| /// // This is an [AbstractSuperPropertySet] with interface target |
| /// // `Interface.setter`. |
| /// super.setter = value; |
| /// } |
| /// } |
| /// class Super implements Interface { |
| /// // This is the target when `Mixin` is applied to `Class`. |
| /// void set setter(value) {} |
| /// } |
| /// class Class extends Super with Mixin {} |
| /// |
| /// This may invoke a setter or assign a field. |
| class AbstractSuperPropertySet extends Expression { |
| Name name; |
| Expression value; |
| |
| Reference interfaceTargetReference; |
| |
| AbstractSuperPropertySet(Name name, Expression value, Member interfaceTarget) |
| : this.byReference( |
| name, value, getNonNullableMemberReferenceSetter(interfaceTarget)); |
| |
| AbstractSuperPropertySet.byReference( |
| this.name, this.value, this.interfaceTargetReference) { |
| value.parent = this; |
| } |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAbstractSuperPropertySet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitAbstractSuperPropertySet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "AbstractSuperPropertySet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.{abstract}'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// Expression of form `super.field = value`. |
| /// |
| /// This may invoke a setter or assign a field. |
| /// |
| /// Evaluates to the value of [value]. |
| class SuperPropertySet extends Expression { |
| Name name; |
| Expression value; |
| |
| Reference interfaceTargetReference; |
| |
| SuperPropertySet(Name name, Expression value, Member interfaceTarget) |
| : this.byReference( |
| name, value, getNonNullableMemberReferenceSetter(interfaceTarget)); |
| |
| SuperPropertySet.byReference( |
| this.name, this.value, this.interfaceTargetReference) { |
| value.parent = this; |
| } |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member member) { |
| interfaceTargetReference = getNonNullableMemberReferenceSetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertySet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSuperPropertySet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "SuperPropertySet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// Read a static field, call a static getter, or tear off a static method. |
| class StaticGet extends Expression { |
| /// A static field, getter, or method (for tear-off). |
| Reference targetReference; |
| |
| StaticGet(Member target) |
| : assert(target is Field || (target is Procedure && target.isGetter)), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| StaticGet.byReference(this.targetReference); |
| |
| Member get target => targetReference.asMember; |
| |
| void set target(Member target) { |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| target.getterType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticGet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStaticGet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "StaticGet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| } |
| |
| /// Tear-off of a static method. |
| class StaticTearOff extends Expression { |
| Reference targetReference; |
| |
| StaticTearOff(Procedure target) |
| : assert(target.isStatic, "Unexpected static tear off target: $target"), |
| assert(target.kind == ProcedureKind.Method, |
| "Unexpected static tear off target: $target"), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| StaticTearOff.byReference(this.targetReference); |
| |
| Procedure get target => targetReference.asProcedure; |
| |
| void set target(Procedure target) { |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| target.getterType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStaticTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "StaticTearOff(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| } |
| |
| /// Assign a static field or call a static setter. |
| /// |
| /// Evaluates to the value of [value]. |
| class StaticSet extends Expression { |
| /// A mutable static field or a static setter. |
| Reference targetReference; |
| Expression value; |
| |
| StaticSet(Member target, Expression value) |
| : this.byReference(getNonNullableMemberReferenceSetter(target), value); |
| |
| StaticSet.byReference(this.targetReference, this.value) { |
| value.parent = this; |
| } |
| |
| Member get target => targetReference.asMember; |
| |
| void set target(Member target) { |
| targetReference = getNonNullableMemberReferenceSetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticSet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStaticSet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "StaticSet(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| printer.write(' = '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// The arguments to a function call, divided into type arguments, |
| /// positional arguments, and named arguments. |
| class Arguments extends TreeNode { |
| final List<DartType> types; |
| final List<Expression> positional; |
| List<NamedExpression> named; |
| |
| Arguments(this.positional, |
| {List<DartType>? types, List<NamedExpression>? named}) |
| : this.types = types ?? <DartType>[], |
| this.named = named ?? <NamedExpression>[] { |
| setParents(this.positional, this); |
| setParents(this.named, this); |
| } |
| |
| Arguments.empty() |
| : types = <DartType>[], |
| positional = <Expression>[], |
| named = <NamedExpression>[]; |
| |
| factory Arguments.forwarded(FunctionNode function, Library library) { |
| return new Arguments( |
| function.positionalParameters |
| .map<Expression>((p) => new VariableGet(p)) |
| .toList(), |
| named: function.namedParameters |
| .map((p) => new NamedExpression(p.name!, new VariableGet(p))) |
| .toList(), |
| types: function.typeParameters |
| .map<DartType>((p) => |
| new TypeParameterType.withDefaultNullabilityForLibrary( |
| p, library)) |
| .toList()); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitArguments(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitArguments(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(types, v); |
| visitList(positional, v); |
| visitList(named, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformDartTypeList(types); |
| v.transformList(positional, this); |
| v.transformList(named, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformDartTypeList(types); |
| v.transformExpressionList(positional, this); |
| v.transformNamedExpressionList(named, this); |
| } |
| |
| @override |
| String toString() { |
| return "Arguments(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeArguments(this); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer, {bool includeTypeArguments = true}) { |
| if (includeTypeArguments) { |
| printer.writeTypeArguments(types); |
| } |
| printer.write('('); |
| for (int index = 0; index < positional.length; index++) { |
| if (index > 0) { |
| printer.write(', '); |
| } |
| printer.writeExpression(positional[index]); |
| } |
| if (named.isNotEmpty) { |
| if (positional.isNotEmpty) { |
| printer.write(', '); |
| } |
| for (int index = 0; index < named.length; index++) { |
| if (index > 0) { |
| printer.write(', '); |
| } |
| printer.writeNamedExpression(named[index]); |
| } |
| } |
| printer.write(')'); |
| } |
| } |
| |
| /// A named argument, `name: value`. |
| class NamedExpression extends TreeNode { |
| String name; |
| Expression value; |
| |
| NamedExpression(this.name, this.value) { |
| value.parent = this; |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitNamedExpression(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => |
| v.visitNamedExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "NamedExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| toTextInternal(printer); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(name); |
| printer.write(': '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// Common super class for [DirectMethodInvocation], [MethodInvocation], |
| /// [SuperMethodInvocation], [StaticInvocation], and [ConstructorInvocation]. |
| abstract class InvocationExpression extends Expression { |
| Arguments get arguments; |
| void set arguments(Arguments value); |
| |
| /// Name of the invoked method. |
| Name get name; |
| } |
| |
| abstract class InstanceInvocationExpression extends InvocationExpression { |
| Expression get receiver; |
| } |
| |
| class DynamicInvocation extends InstanceInvocationExpression { |
| final DynamicAccessKind kind; |
| |
| @override |
| Expression receiver; |
| |
| @override |
| Name name; |
| |
| @override |
| Arguments arguments; |
| |
| DynamicInvocation(this.kind, this.receiver, this.name, this.arguments) { |
| receiver.parent = this; |
| arguments.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| switch (kind) { |
| case DynamicAccessKind.Dynamic: |
| return const DynamicType(); |
| case DynamicAccessKind.Never: |
| return const NeverType.nonNullable(); |
| case DynamicAccessKind.Invalid: |
| case DynamicAccessKind.Unresolved: |
| return const InvalidType(); |
| } |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitDynamicInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| name.accept(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "DynamicInvocation($kind,${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeName(name); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Access kind used by [InstanceInvocation], [InstanceGet], [InstanceSet], |
| /// and [InstanceTearOff]. |
| enum InstanceAccessKind { |
| /// An access to a member on a static receiver type which is an interface |
| /// type. |
| /// |
| /// In null safe libraries the static receiver type is non-nullable. |
| /// |
| /// For instance: |
| /// |
| /// class C { void method() {} } |
| /// main() => new C().method(); |
| /// |
| Instance, |
| |
| /// An access to a member defined on Object on a static receiver type that |
| /// is either a non-interface type or a nullable type. |
| /// |
| /// For instance: |
| /// |
| /// test1(String? s) => s.toString(); |
| /// test1(dynamic s) => s.hashCode; |
| /// |
| Object, |
| |
| /// An access to a method on a static receiver type which is an interface |
| /// type which is inapplicable, that is, whose arguments don't match the |
| /// required parameter structure. |
| /// |
| /// This is an error case which is only used on expression nested within |
| /// [InvalidExpression]s. |
| /// |
| /// For instance: |
| /// |
| /// class C { void method() {} } |
| /// main() => new C().method(0); // Too many arguments. |
| /// |
| Inapplicable, |
| |
| /// An access to a non-Object member on a static receiver type which is a |
| /// nullable interface type. |
| /// |
| /// This is an error case which is only used on expression nested within |
| /// [InvalidExpression]s. |
| /// |
| /// For instance: |
| /// |
| /// class C { void method() {} } |
| /// test(C? c) => c.method(0); // 'c' is nullable. |
| /// |
| Nullable, |
| } |
| |
| /// An invocation of an instance method with a statically known interface |
| /// target. |
| class InstanceInvocation extends InstanceInvocationExpression { |
| // Must match serialized bit positions. |
| static const int FlagInvariant = 1 << 0; |
| static const int FlagBoundsSafe = 1 << 1; |
| |
| final InstanceAccessKind kind; |
| @override |
| Expression receiver; |
| |
| // TODO(johnniwinther): Can we pull this from the [interfaceTarget] instead? |
| @override |
| Name name; |
| |
| @override |
| Arguments arguments; |
| |
| int flags = 0; |
| |
| /// The static type of the invocation. |
| /// |
| /// This includes substituted type parameters from the static receiver type |
| /// and generic type arguments. |
| /// |
| /// For instance |
| /// |
| /// class A<T> { |
| /// Map<T, S> map<S>(S s) { ... } |
| /// } |
| /// m(A<String> a) { |
| /// a.map(0); // The function type is `Map<String, int> Function(int)`. |
| /// } |
| /// |
| FunctionType functionType; |
| |
| Reference interfaceTargetReference; |
| |
| InstanceInvocation(InstanceAccessKind kind, Expression receiver, Name name, |
| Arguments arguments, |
| {required Procedure interfaceTarget, required FunctionType functionType}) |
| : this.byReference(kind, receiver, name, arguments, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceGetter(interfaceTarget), |
| functionType: functionType); |
| |
| InstanceInvocation.byReference( |
| this.kind, this.receiver, this.name, this.arguments, |
| {required this.interfaceTargetReference, required this.functionType}) |
| // ignore: unnecessary_null_comparison |
| : assert(interfaceTargetReference != null), |
| // ignore: unnecessary_null_comparison |
| assert(functionType != null), |
| assert(functionType.typeParameters.isEmpty) { |
| receiver.parent = this; |
| arguments.parent = this; |
| } |
| |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; |
| |
| void set interfaceTarget(Procedure target) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. |
| /// |
| /// This is for instance the case in code patterns like this |
| /// |
| /// List<int> list = <int>[]; |
| /// list.add(0); |
| /// |
| /// where the `list` variable is known to hold a value of the same type as |
| /// the static type. In contrast the would not be the case in code patterns |
| /// like this |
| /// |
| /// List<num> list = <double>[]; |
| /// list.add(0); // Runtime error `int` is not a subtype of `double`. |
| /// |
| bool get isInvariant => flags & FlagInvariant != 0; |
| |
| void set isInvariant(bool value) { |
| flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant); |
| } |
| |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. |
| /// |
| /// This is for instance the case in code patterns like this |
| /// |
| /// List list = new List.filled(2, 0); |
| /// list[1] = 42; |
| /// |
| /// where the `list` is known to have a sufficient length for the update |
| /// in `list[1] = 42`. |
| bool get isBoundsSafe => flags & FlagBoundsSafe != 0; |
| |
| void set isBoundsSafe(bool value) { |
| flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| functionType.returnType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| arguments.accept(v); |
| functionType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = v.visitDartType(functionType) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InstanceInvocation($kind, ${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// An invocation of an instance getter or field with a statically known |
| /// interface target. |
| /// |
| /// This is used only for web backend in order to support invocation of |
| /// native properties as functions. This node will be removed when this |
| /// invocation style is no longer supported. |
| class InstanceGetterInvocation extends InstanceInvocationExpression { |
| // Must match serialized bit positions. |
| static const int FlagInvariant = 1 << 0; |
| static const int FlagBoundsSafe = 1 << 1; |
| |
| final InstanceAccessKind kind; |
| @override |
| Expression receiver; |
| |
| @override |
| Name name; |
| |
| @override |
| Arguments arguments; |
| |
| int flags = 0; |
| |
| /// The static type of the invocation, or `dynamic` is of the type is unknown. |
| /// |
| /// This includes substituted type parameters from the static receiver type |
| /// and generic type arguments. |
| /// |
| /// For instance |
| /// |
| /// class A<T> { |
| /// Map<T, S> Function<S>(S) get map => ... |
| /// dynamic get dyn => ... |
| /// } |
| /// m(A<String> a) { |
| /// a.map(0); // The function type is `Map<String, int> Function(int)`. |
| /// a.dyn(0); // The function type is `null`. |
| /// } |
| /// |
| FunctionType? functionType; |
| |
| Reference interfaceTargetReference; |
| |
| InstanceGetterInvocation(InstanceAccessKind kind, Expression receiver, |
| Name name, Arguments arguments, |
| {required Member interfaceTarget, required FunctionType? functionType}) |
| : this.byReference(kind, receiver, name, arguments, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceGetter(interfaceTarget), |
| functionType: functionType); |
| |
| InstanceGetterInvocation.byReference( |
| this.kind, this.receiver, this.name, this.arguments, |
| {required this.interfaceTargetReference, required this.functionType}) |
| // ignore: unnecessary_null_comparison |
| : assert(interfaceTargetReference != null), |
| assert(functionType == null || functionType.typeParameters.isEmpty) { |
| receiver.parent = this; |
| arguments.parent = this; |
| } |
| |
| Member get interfaceTarget => interfaceTargetReference.asMember; |
| |
| void set interfaceTarget(Member target) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. |
| /// |
| /// This is for instance the case in code patterns like this |
| /// |
| /// List<int> list = <int>[]; |
| /// list.add(0); |
| /// |
| /// where the `list` variable is known to hold a value of the same type as |
| /// the static type. In contrast the would not be the case in code patterns |
| /// like this |
| /// |
| /// List<num> list = <double>[]; |
| /// list.add(0); // Runtime error `int` is not a subtype of `double`. |
| /// |
| bool get isInvariant => flags & FlagInvariant != 0; |
| |
| void set isInvariant(bool value) { |
| flags = value ? (flags | FlagInvariant) : (flags & ~FlagInvariant); |
| } |
| |
| /// If `true`, this call is known to be safe wrt. parameter covariance checks. |
| /// |
| /// This is for instance the case in code patterns like this |
| /// |
| /// List list = new List.filled(2, 0); |
| /// list[1] = 42; |
| /// |
| /// where the `list` is known to have a sufficient length for the update |
| /// in `list[1] = 42`. |
| bool get isBoundsSafe => flags & FlagBoundsSafe != 0; |
| |
| void set isBoundsSafe(bool value) { |
| flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| functionType?.returnType ?? const DynamicType(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGetterInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceGetterInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| arguments.accept(v); |
| functionType?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| if (functionType != null) { |
| functionType = v.visitDartType(functionType!) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| if (functionType != null) { |
| functionType = |
| v.visitDartType(functionType!, cannotRemoveSentinel) as FunctionType; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "InstanceGetterInvocation($kind, ${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.write('.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Access kind used by [FunctionInvocation] and [FunctionTearOff]. |
| enum FunctionAccessKind { |
| /// An access to the 'call' method on an expression of static type `Function`. |
| /// |
| /// For instance |
| /// |
| /// method(Function f) => f(); |
| /// |
| Function, |
| |
| /// An access to the 'call' method on an expression whose static type is a |
| /// function type. |
| /// |
| /// For instance |
| /// |
| /// method(void Function() f) => f(); |
| /// |
| FunctionType, |
| |
| /// An access to the 'call' method on an expression whose static type is a |
| /// function type which is inapplicable, that is, whose arguments don't match |
| /// the required parameter structure. |
| /// |
| /// This is an error case which is only used on expression nested within |
| /// [InvalidExpression]s. |
| /// |
| /// For instance: |
| /// |
| /// test(void Function() f) => f(0); // Too many arguments. |
| /// |
| Inapplicable, |
| |
| /// An access to the 'call' method on an expression whose static type is a |
| /// nullable function type or `Function?`. |
| /// |
| /// This is an error case which is only used on expression nested within |
| /// [InvalidExpression]s. |
| /// |
| /// For instance: |
| /// |
| /// test(void Function()? f) => f(); // 'f' is nullable. |
| /// |
| Nullable, |
| } |
| |
| /// An invocation of the 'call' method on an expression whose static type is |
| /// a function type or the type 'Function'. |
| class FunctionInvocation extends InstanceInvocationExpression { |
| final FunctionAccessKind kind; |
| |
| @override |
| Expression receiver; |
| |
| @override |
| Arguments arguments; |
| |
| /// The static type of the invocation. |
| /// |
| /// This is `null` if the static type of the receiver is not a function type |
| /// or is not bounded by a function type. |
| /// |
| /// For instance |
| /// |
| /// m<T extends Function, S extends int Function()>(T t, S s, Function f) { |
| /// X local<X>(X t) => t; |
| /// t(); // The function type is `null`. |
| /// s(); // The function type is `int Function()`. |
| /// f(); // The function type is `null`. |
| /// local(0); // The function type is `int Function(int)`. |
| /// } |
| /// |
| FunctionType? functionType; |
| |
| FunctionInvocation(this.kind, this.receiver, this.arguments, |
| {required this.functionType}) { |
| receiver.parent = this; |
| arguments.parent = this; |
| } |
| |
| @override |
| Name get name => Name.callName; |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| functionType?.returnType ?? const DynamicType(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitFunctionInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| receiver.accept(v); |
| name.accept(v); |
| arguments.accept(v); |
| functionType?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| FunctionType? type = functionType; |
| if (type != null) { |
| functionType = v.visitDartType(type) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (receiver != null) { |
| receiver = v.transform(receiver); |
| receiver.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| FunctionType? type = functionType; |
| if (type != null) { |
| functionType = |
| v.visitDartType(type, cannotRemoveSentinel) as FunctionType; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FunctionInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(receiver, |
| minimumPrecedence: astToText.Precedence.PRIMARY); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// An invocation of a local function declaration. |
| class LocalFunctionInvocation extends InvocationExpression { |
| /// The variable declaration for the function declaration. |
| VariableDeclaration variable; |
| |
| @override |
| Arguments arguments; |
| |
| /// The static type of the invocation. |
| /// |
| /// This might differ from the static type of [variable] for generic |
| /// functions. |
| /// |
| /// For instance |
| /// |
| /// m() { |
| /// T local<T>(T t) => t; |
| /// local(0); // The static type is `int Function(int)`. |
| /// } |
| /// |
| FunctionType functionType; |
| |
| LocalFunctionInvocation(this.variable, this.arguments, |
| {required this.functionType}) |
| // ignore: unnecessary_null_comparison |
| : assert(functionType != null) { |
| arguments.parent = this; |
| } |
| |
| /// The declaration for the invoked local function. |
| FunctionDeclaration get localFunction => |
| variable.parent as FunctionDeclaration; |
| |
| @override |
| Name get name => Name.callName; |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| functionType.returnType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLocalFunctionInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitLocalFunctionInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| arguments.accept(v); |
| functionType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = v.visitDartType(functionType) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "LocalFunctionInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(printer.getVariableName(variable)); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Nullness test of an expression, that is `e == null`. |
| /// |
| /// This is generated for code like `e1 == e2` where `e1` or `e2` is `null`. |
| class EqualsNull extends Expression { |
| /// The expression tested for nullness. |
| Expression expression; |
| |
| EqualsNull(this.expression) { |
| expression.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsNull(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitEqualsNull(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "EqualsNull(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(expression, minimumPrecedence: precedence); |
| printer.write(' == null'); |
| } |
| } |
| |
| /// A test of equality, that is `e1 == e2`. |
| /// |
| /// This is generated for code like `e1 == e2` where neither `e1` nor `e2` is |
| /// `null`. |
| class EqualsCall extends Expression { |
| Expression left; |
| Expression right; |
| |
| /// The static type of the invocation. |
| /// |
| /// This might differ from the static type of [Object.==] for covariant |
| /// parameters. |
| /// |
| /// For instance |
| /// |
| /// class C<T> { |
| /// bool operator(covariant C<T> other) { ... } |
| /// } |
| /// // The function type is `bool Function(C<num>)`. |
| /// method(C<num> a, C<int> b) => a == b; |
| /// |
| FunctionType functionType; |
| |
| Reference interfaceTargetReference; |
| |
| EqualsCall(Expression left, Expression right, |
| {required FunctionType functionType, required Procedure interfaceTarget}) |
| : this.byReference(left, right, |
| functionType: functionType, |
| interfaceTargetReference: |
| getNonNullableMemberReferenceGetter(interfaceTarget)); |
| |
| EqualsCall.byReference(this.left, this.right, |
| {required this.functionType, required this.interfaceTargetReference}) { |
| left.parent = this; |
| right.parent = this; |
| } |
| |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; |
| |
| void set interfaceTarget(Procedure target) { |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return functionType.returnType; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsCall(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitEqualsCall(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| left.accept(v); |
| interfaceTarget.acceptReference(v); |
| right.accept(v); |
| functionType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (left != null) { |
| left = v.transform(left); |
| left.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (right != null) { |
| right = v.transform(right); |
| right.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = v.visitDartType(functionType) as FunctionType; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (left != null) { |
| left = v.transform(left); |
| left.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (right != null) { |
| right = v.transform(right); |
| right.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (functionType != null) { |
| functionType = |
| v.visitDartType(functionType, cannotRemoveSentinel) as FunctionType; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "EqualsCall(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| int minimumPrecedence = precedence; |
| printer.writeExpression(left, minimumPrecedence: minimumPrecedence); |
| printer.write(' == '); |
| printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1); |
| } |
| } |
| |
| /// Expression of form `super.foo(x)` occurring in a mixin declaration. |
| /// |
| /// In this setting, the target is looked up on the types in the mixin 'on' |
| /// clause and are therefore not necessary the runtime targets of the |
| /// invocation. An [AbstractSuperMethodInvocation] must be converted into |
| /// a [SuperMethodInvocation] to statically bind the target. |
| /// |
| /// For instance |
| /// |
| /// abstract class Interface { |
| /// void method(); |
| /// } |
| /// mixin Mixin on Interface { |
| /// void method() { |
| /// // This is an [AbstractSuperMethodInvocation] with interface target |
| /// // `Interface.method`. |
| /// super.method(); // This targets Super.method. |
| /// } |
| /// } |
| /// class Super implements Interface { |
| /// // This is the target when `Mixin` is applied to `Class`. |
| /// void method() {} |
| /// } |
| /// class Class extends Super with Mixin {} |
| /// |
| class AbstractSuperMethodInvocation extends InvocationExpression { |
| @override |
| Name name; |
| |
| @override |
| Arguments arguments; |
| |
| Reference interfaceTargetReference; |
| |
| AbstractSuperMethodInvocation( |
| Name name, Arguments arguments, Procedure interfaceTarget) |
| : this.byReference( |
| name, |
| arguments, |
| // An invocation doesn't refer to the setter. |
| getNonNullableMemberReferenceGetter(interfaceTarget)); |
| |
| AbstractSuperMethodInvocation.byReference( |
| this.name, this.arguments, this.interfaceTargetReference) { |
| arguments.parent = this; |
| } |
| |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; |
| |
| void set interfaceTarget(Procedure target) { |
| // An invocation doesn't refer to the setter. |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| Class superclass = interfaceTarget.enclosingClass!; |
| List<DartType>? receiverTypeArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf(context.thisType!, superclass); |
| DartType returnType = Substitution.fromPairs( |
| superclass.typeParameters, receiverTypeArguments!) |
| .substituteType(interfaceTarget.function.returnType); |
| return Substitution.fromPairs( |
| interfaceTarget.function.typeParameters, arguments.types) |
| .substituteType(returnType); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => |
| v.visitAbstractSuperMethodInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitAbstractSuperMethodInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "AbstractSuperMethodInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.{abstract}'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Expression of form `super.foo(x)`. |
| /// |
| /// The provided arguments might not match the parameters of the target. |
| class SuperMethodInvocation extends InvocationExpression { |
| @override |
| Name name; |
| |
| @override |
| Arguments arguments; |
| |
| Reference interfaceTargetReference; |
| |
| SuperMethodInvocation( |
| Name name, Arguments arguments, Procedure interfaceTarget) |
| : this.byReference( |
| name, |
| arguments, |
| // An invocation doesn't refer to the setter. |
| getNonNullableMemberReferenceGetter(interfaceTarget)); |
| |
| SuperMethodInvocation.byReference( |
| this.name, this.arguments, this.interfaceTargetReference) { |
| arguments.parent = this; |
| } |
| |
| Procedure get interfaceTarget => interfaceTargetReference.asProcedure; |
| |
| void set interfaceTarget(Procedure target) { |
| // An invocation doesn't refer to the setter. |
| interfaceTargetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| Class superclass = interfaceTarget.enclosingClass!; |
| List<DartType>? receiverTypeArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf(context.thisType!, superclass); |
| DartType returnType = Substitution.fromPairs( |
| superclass.typeParameters, receiverTypeArguments!) |
| .substituteType(interfaceTarget.function.returnType); |
| return Substitution.fromPairs( |
| interfaceTarget.function.typeParameters, arguments.types) |
| .substituteType(returnType); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSuperMethodInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSuperMethodInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| interfaceTarget.acceptReference(v); |
| name.accept(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "SuperMethodInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('super.'); |
| printer.writeInterfaceMemberName(interfaceTargetReference, name); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Expression of form `foo(x)`, or `const foo(x)` if the target is an |
| /// external constant factory. |
| /// |
| /// The provided arguments might not match the parameters of the target. |
| class StaticInvocation extends InvocationExpression { |
| Reference targetReference; |
| |
| @override |
| Arguments arguments; |
| |
| /// True if this is a constant call to an external constant factory. |
| bool isConst; |
| |
| @override |
| Name get name => target.name; |
| |
| StaticInvocation(Procedure target, Arguments arguments, |
| {bool isConst = false}) |
| : this.byReference( |
| // An invocation doesn't refer to the setter. |
| getNonNullableMemberReferenceGetter(target), |
| arguments, |
| isConst: isConst); |
| |
| StaticInvocation.byReference(this.targetReference, this.arguments, |
| {this.isConst = false}) { |
| arguments.parent = this; |
| } |
| |
| Procedure get target => targetReference.asProcedure; |
| |
| void set target(Procedure target) { |
| // An invocation doesn't refer to the setter. |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return Substitution.fromPairs( |
| target.function.typeParameters, arguments.types) |
| .substituteType(target.function.returnType); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStaticInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStaticInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "StaticInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| printer.writeArguments(arguments); |
| } |
| } |
| |
| /// Expression of form `new Foo(x)` or `const Foo(x)`. |
| /// |
| /// The provided arguments might not match the parameters of the target. |
| // |
| // DESIGN TODO: Should we pass type arguments in a separate field |
| // `classTypeArguments`? They are quite different from type arguments to |
| // generic functions. |
| class ConstructorInvocation extends InvocationExpression { |
| Reference targetReference; |
| |
| @override |
| Arguments arguments; |
| |
| bool isConst; |
| |
| @override |
| Name get name => target.name; |
| |
| ConstructorInvocation(Constructor target, Arguments arguments, |
| {bool isConst = false}) |
| : this.byReference( |
| // A constructor doesn't refer to the setter. |
| getNonNullableMemberReferenceGetter(target), |
| arguments, |
| isConst: isConst); |
| |
| ConstructorInvocation.byReference(this.targetReference, this.arguments, |
| {this.isConst = false}) { |
| arguments.parent = this; |
| } |
| |
| Constructor get target => targetReference.asConstructor; |
| |
| void set target(Constructor target) { |
| // A constructor doesn't refer to the setter. |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return arguments.types.isEmpty |
| ? context.typeEnvironment.coreTypes |
| .rawType(target.enclosingClass, context.nonNullable) |
| : new InterfaceType( |
| target.enclosingClass, context.nonNullable, arguments.types); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorInvocation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitConstructorInvocation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| arguments.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (arguments != null) { |
| arguments = v.transform(arguments); |
| arguments.parent = this; |
| } |
| } |
| |
| // TODO(cstefantsova): Change the getter into a method that accepts a |
| // CoreTypes. |
| InterfaceType get constructedType { |
| Class enclosingClass = target.enclosingClass; |
| // TODO(cstefantsova): Get raw type from a CoreTypes object if arguments is |
| // empty. |
| return arguments.types.isEmpty |
| ? new InterfaceType( |
| enclosingClass, Nullability.legacy, const <DartType>[]) |
| : new InterfaceType( |
| enclosingClass, Nullability.legacy, arguments.types); |
| } |
| |
| @override |
| String toString() { |
| return "ConstructorInvocation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isConst) { |
| printer.write('const '); |
| } else { |
| printer.write('new '); |
| } |
| printer.writeClassName(target.enclosingClass.reference); |
| printer.writeTypeArguments(arguments.types); |
| if (target.name.text.isNotEmpty) { |
| printer.write('.'); |
| printer.write(target.name.text); |
| } |
| printer.writeArguments(arguments, includeTypeArguments: false); |
| } |
| } |
| |
| /// An explicit type instantiation of a generic function. |
| class Instantiation extends Expression { |
| Expression expression; |
| final List<DartType> typeArguments; |
| |
| Instantiation(this.expression, this.typeArguments) { |
| expression.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| DartType type = expression.getStaticType(context); |
| if (type is FunctionType) { |
| return Substitution.fromPairs(type.typeParameters, typeArguments) |
| .substituteType(type.withoutTypeParameters); |
| } |
| assert(type is InvalidType || type is NeverType, |
| "Unexpected operand type $type for $expression"); |
| return type; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstantiation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstantiation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| visitList(typeArguments, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformDartTypeList(typeArguments); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformDartTypeList(typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "Instantiation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(expression); |
| printer.writeTypeArguments(typeArguments); |
| } |
| } |
| |
| /// Expression of form `!x`. |
| /// |
| /// The `is!` and `!=` operators are desugared into [Not] nodes with `is` and |
| /// `==` expressions inside, respectively. |
| class Not extends Expression { |
| Expression operand; |
| |
| Not(this.operand) { |
| operand.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNot(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitNot(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| operand.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "Not(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('!'); |
| printer.writeExpression(operand, |
| minimumPrecedence: astToText.Precedence.PREFIX); |
| } |
| } |
| |
| enum LogicalExpressionOperator { AND, OR } |
| |
| String logicalExpressionOperatorToString(LogicalExpressionOperator operator) { |
| switch (operator) { |
| case LogicalExpressionOperator.AND: |
| return "&&"; |
| case LogicalExpressionOperator.OR: |
| return "||"; |
| } |
| } |
| |
| /// Expression of form `x && y` or `x || y` |
| class LogicalExpression extends Expression { |
| Expression left; |
| LogicalExpressionOperator operatorEnum; // AND (&&) or OR (||). |
| Expression right; |
| |
| LogicalExpression(this.left, this.operatorEnum, this.right) { |
| left.parent = this; |
| right.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLogicalExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitLogicalExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| left.accept(v); |
| right.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (left != null) { |
| left = v.transform(left); |
| left.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (right != null) { |
| right = v.transform(right); |
| right.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (left != null) { |
| left = v.transform(left); |
| left.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (right != null) { |
| right = v.transform(right); |
| right.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "LogicalExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| int minimumPrecedence = precedence; |
| printer.writeExpression(left, minimumPrecedence: minimumPrecedence); |
| printer.write(' ${logicalExpressionOperatorToString(operatorEnum)} '); |
| printer.writeExpression(right, minimumPrecedence: minimumPrecedence + 1); |
| } |
| } |
| |
| /// Expression of form `x ? y : z`. |
| class ConditionalExpression extends Expression { |
| Expression condition; |
| Expression then; |
| Expression otherwise; |
| |
| /// The static type of the expression. |
| DartType staticType; |
| |
| ConditionalExpression( |
| this.condition, this.then, this.otherwise, this.staticType) { |
| condition.parent = this; |
| then.parent = this; |
| otherwise.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => staticType; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConditionalExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitConditionalExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| condition.accept(v); |
| then.accept(v); |
| otherwise.accept(v); |
| staticType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (then != null) { |
| then = v.transform(then); |
| then.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (otherwise != null) { |
| otherwise = v.transform(otherwise); |
| otherwise.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (staticType != null) { |
| staticType = v.visitDartType(staticType); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (then != null) { |
| then = v.transform(then); |
| then.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (otherwise != null) { |
| otherwise = v.transform(otherwise); |
| otherwise.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (staticType != null) { |
| staticType = v.visitDartType(staticType, cannotRemoveSentinel); |
| } |
| } |
| |
| @override |
| String toString() { |
| return "ConditionalExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(condition, |
| minimumPrecedence: astToText.Precedence.LOGICAL_OR); |
| printer.write(' ?'); |
| // ignore: unnecessary_null_comparison |
| if (staticType != null) { |
| printer.write('{'); |
| printer.writeType(staticType); |
| printer.write('}'); |
| } |
| printer.write(' '); |
| printer.writeExpression(then); |
| printer.write(' : '); |
| printer.writeExpression(otherwise); |
| } |
| } |
| |
| /// Convert expressions to strings and concatenate them. Semantically, calls |
| /// `toString` on every argument, checks that a string is returned, and returns |
| /// the concatenation of all the strings. |
| /// |
| /// If [expressions] is empty then an empty string is returned. |
| /// |
| /// These arise from string interpolations and adjacent string literals. |
| class StringConcatenation extends Expression { |
| final List<Expression> expressions; |
| |
| StringConcatenation(this.expressions) { |
| setParents(expressions, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.stringRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStringConcatenation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStringConcatenation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(expressions, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(expressions, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(expressions, this); |
| } |
| |
| @override |
| String toString() { |
| return "StringConcatenation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('"'); |
| for (Expression part in expressions) { |
| if (part is StringLiteral) { |
| printer.write(escapeString(part.value)); |
| } else { |
| printer.write(r'${'); |
| printer.writeExpression(part); |
| printer.write('}'); |
| } |
| } |
| printer.write('"'); |
| } |
| } |
| |
| /// Concatenate lists into a single list. |
| /// |
| /// If [lists] is empty then an empty list is returned. |
| /// |
| /// These arise from spread and control-flow elements in const list literals. |
| /// They are only present before constant evaluation, or within unevaluated |
| /// constants in constant expressions. |
| class ListConcatenation extends Expression { |
| DartType typeArgument; |
| final List<Expression> lists; |
| |
| ListConcatenation(this.lists, {this.typeArgument = const DynamicType()}) { |
| setParents(lists, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.listType(typeArgument, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitListConcatenation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitListConcatenation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| visitList(lists, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| typeArgument = v.visitDartType(typeArgument); |
| v.transformList(lists, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); |
| v.transformExpressionList(lists, this); |
| } |
| |
| @override |
| String toString() { |
| return "ListConcatenation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| bool first = true; |
| for (Expression part in lists) { |
| if (!first) { |
| printer.write(' + '); |
| } |
| printer.writeExpression(part); |
| first = false; |
| } |
| } |
| } |
| |
| /// Concatenate sets into a single set. |
| /// |
| /// If [sets] is empty then an empty set is returned. |
| /// |
| /// These arise from spread and control-flow elements in const set literals. |
| /// They are only present before constant evaluation, or within unevaluated |
| /// constants in constant expressions. |
| /// |
| /// Duplicated values in or across the sets will result in a compile-time error |
| /// during constant evaluation. |
| class SetConcatenation extends Expression { |
| DartType typeArgument; |
| final List<Expression> sets; |
| |
| SetConcatenation(this.sets, {this.typeArgument = const DynamicType()}) { |
| setParents(sets, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.setType(typeArgument, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSetConcatenation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSetConcatenation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| visitList(sets, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| typeArgument = v.visitDartType(typeArgument); |
| v.transformList(sets, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); |
| v.transformExpressionList(sets, this); |
| } |
| |
| @override |
| String toString() { |
| return "SetConcatenation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| bool first = true; |
| for (Expression part in sets) { |
| if (!first) { |
| printer.write(' + '); |
| } |
| printer.writeExpression(part); |
| first = false; |
| } |
| } |
| } |
| |
| /// Concatenate maps into a single map. |
| /// |
| /// If [maps] is empty then an empty map is returned. |
| /// |
| /// These arise from spread and control-flow elements in const map literals. |
| /// They are only present before constant evaluation, or within unevaluated |
| /// constants in constant expressions. |
| /// |
| /// Duplicated keys in or across the maps will result in a compile-time error |
| /// during constant evaluation. |
| class MapConcatenation extends Expression { |
| DartType keyType; |
| DartType valueType; |
| final List<Expression> maps; |
| |
| MapConcatenation(this.maps, |
| {this.keyType = const DynamicType(), |
| this.valueType = const DynamicType()}) { |
| setParents(maps, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment |
| .mapType(keyType, valueType, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitMapConcatenation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitMapConcatenation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| keyType.accept(v); |
| valueType.accept(v); |
| visitList(maps, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| keyType = v.visitDartType(keyType); |
| valueType = v.visitDartType(valueType); |
| v.transformList(maps, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| keyType = v.visitDartType(keyType, cannotRemoveSentinel); |
| valueType = v.visitDartType(valueType, cannotRemoveSentinel); |
| v.transformExpressionList(maps, this); |
| } |
| |
| @override |
| String toString() { |
| return "MapConcatenation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| bool first = true; |
| for (Expression part in maps) { |
| if (!first) { |
| printer.write(' + '); |
| } |
| printer.writeExpression(part); |
| first = false; |
| } |
| } |
| } |
| |
| /// Create an instance directly from the field values. |
| /// |
| /// These expressions arise from const constructor calls when one or more field |
| /// initializing expressions, field initializers, assert initializers or unused |
| /// arguments contain unevaluated expressions. They only ever occur within |
| /// unevaluated constants in constant expressions. |
| class InstanceCreation extends Expression { |
| final Reference classReference; |
| final List<DartType> typeArguments; |
| final Map<Reference, Expression> fieldValues; |
| final List<AssertStatement> asserts; |
| final List<Expression> unusedArguments; |
| |
| InstanceCreation(this.classReference, this.typeArguments, this.fieldValues, |
| this.asserts, this.unusedArguments) { |
| setParents(fieldValues.values.toList(), this); |
| setParents(asserts, this); |
| setParents(unusedArguments, this); |
| } |
| |
| Class get classNode => classReference.asClass; |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return typeArguments.isEmpty |
| ? context.typeEnvironment.coreTypes |
| .rawType(classNode, context.nonNullable) |
| : new InterfaceType(classNode, context.nonNullable, typeArguments); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceCreation(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitInstanceCreation(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| classReference.asClass.acceptReference(v); |
| visitList(typeArguments, v); |
| for (final Reference reference in fieldValues.keys) { |
| reference.asField.acceptReference(v); |
| } |
| for (final Expression value in fieldValues.values) { |
| value.accept(v); |
| } |
| visitList(asserts, v); |
| visitList(unusedArguments, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| fieldValues.forEach((Reference fieldRef, Expression value) { |
| Expression transformed = v.transform(value); |
| // ignore: unnecessary_null_comparison |
| if (transformed != null && !identical(value, transformed)) { |
| fieldValues[fieldRef] = transformed; |
| transformed.parent = this; |
| } |
| }); |
| v.transformList(asserts, this); |
| v.transformList(unusedArguments, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| fieldValues.forEach((Reference fieldRef, Expression value) { |
| Expression transformed = v.transform(value); |
| if (!identical(value, transformed)) { |
| fieldValues[fieldRef] = transformed; |
| transformed.parent = this; |
| } |
| }); |
| v.transformList(asserts, this, dummyAssertStatement); |
| v.transformExpressionList(unusedArguments, this); |
| } |
| |
| @override |
| String toString() { |
| return "InstanceCreation(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeClassName(classReference); |
| printer.writeTypeArguments(typeArguments); |
| printer.write('{'); |
| bool first = true; |
| fieldValues.forEach((Reference fieldRef, Expression value) { |
| if (!first) { |
| printer.write(', '); |
| } |
| printer.writeName(fieldRef.asField.name); |
| printer.write(': '); |
| printer.writeExpression(value); |
| first = false; |
| }); |
| for (AssertStatement assert_ in asserts) { |
| if (!first) { |
| printer.write(', '); |
| } |
| printer.write('assert('); |
| printer.writeExpression(assert_.condition); |
| if (assert_.message != null) { |
| printer.write(', '); |
| printer.writeExpression(assert_.message!); |
| } |
| printer.write(')'); |
| first = false; |
| } |
| for (Expression unusedArgument in unusedArguments) { |
| if (!first) { |
| printer.write(', '); |
| } |
| printer.writeExpression(unusedArgument); |
| first = false; |
| } |
| printer.write('}'); |
| } |
| } |
| |
| /// A marker indicating that a subexpression originates in a different source |
| /// file than the surrounding context. |
| /// |
| /// These expressions arise from inlining of const variables during constant |
| /// evaluation. They only ever occur within unevaluated constants in constant |
| /// expressions. |
| class FileUriExpression extends Expression implements FileUriNode { |
| /// The URI of the source file in which the subexpression is located. |
| /// Can be different from the file containing the [FileUriExpression]. |
| @override |
| Uri fileUri; |
| |
| Expression expression; |
| |
| FileUriExpression(this.expression, this.fileUri) { |
| expression.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| expression.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFileUriExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitFileUriExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| expression = v.transform(expression)..parent = this; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| expression = v.transform(expression)..parent = this; |
| } |
| |
| @override |
| Location? _getLocationInEnclosingFile(int offset) { |
| return _getLocationInComponent(enclosingComponent, fileUri, offset); |
| } |
| |
| @override |
| String toString() { |
| return "FileUriExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (printer.includeAuxiliaryProperties) { |
| printer.write('{'); |
| printer.write(fileUri.toString()); |
| printer.write('}'); |
| } |
| printer.writeExpression(expression); |
| } |
| } |
| |
| /// Expression of form `x is T`. |
| class IsExpression extends Expression { |
| int flags = 0; |
| Expression operand; |
| DartType type; |
| |
| IsExpression(this.operand, this.type) { |
| operand.parent = this; |
| } |
| |
| // Must match serialized bit positions. |
| static const int FlagForNonNullableByDefault = 1 << 0; |
| |
| /// If `true`, this test take the nullability of [type] into account. |
| /// |
| /// This is the case for is-tests written in libraries that are opted in to |
| /// the non nullable by default feature. |
| bool get isForNonNullableByDefault => |
| flags & FlagForNonNullableByDefault != 0; |
| |
| void set isForNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagForNonNullableByDefault) |
| : (flags & ~FlagForNonNullableByDefault); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitIsExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitIsExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| operand.accept(v); |
| type.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| type = v.visitDartType(type); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| type = v.visitDartType(type, cannotRemoveSentinel); |
| } |
| |
| @override |
| String toString() { |
| return "IsExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(operand, |
| minimumPrecedence: astToText.Precedence.BITWISE_OR); |
| printer.write(' is'); |
| if (printer.includeAuxiliaryProperties && isForNonNullableByDefault) { |
| printer.write('{ForNonNullableByDefault}'); |
| } |
| printer.write(' '); |
| printer.writeType(type); |
| } |
| } |
| |
| /// Expression of form `x as T`. |
| class AsExpression extends Expression { |
| int flags = 0; |
| Expression operand; |
| DartType type; |
| |
| AsExpression(this.operand, this.type) { |
| operand.parent = this; |
| } |
| |
| // Must match serialized bit positions. |
| static const int FlagTypeError = 1 << 0; |
| static const int FlagCovarianceCheck = 1 << 1; |
| static const int FlagForDynamic = 1 << 2; |
| static const int FlagForNonNullableByDefault = 1 << 3; |
| static const int FlagUnchecked = 1 << 4; |
| |
| /// If `true`, this test is an implicit down cast. |
| /// |
| /// If `true` a TypeError should be thrown. If `false` a CastError should be |
| /// thrown. |
| bool get isTypeError => flags & FlagTypeError != 0; |
| |
| void set isTypeError(bool value) { |
| flags = value ? (flags | FlagTypeError) : (flags & ~FlagTypeError); |
| } |
| |
| /// If `true`, this test is needed to ensure soundness of covariant type |
| /// variables using in contravariant positions. |
| /// |
| /// For instance |
| /// |
| /// class Class<T> { |
| /// void Function(T) field; |
| /// Class(this.field); |
| /// } |
| /// main() { |
| /// Class<num> c = new Class<int>((int i) {}); |
| /// void Function<num> field = c.field; // Check needed on `c.field` |
| /// field(0.5); |
| /// } |
| /// |
| /// Here a covariant check `c.field as void Function(num)` is needed because |
| /// the field could be (and indeed is) not a subtype of the static type of |
| /// the expression. |
| bool get isCovarianceCheck => flags & FlagCovarianceCheck != 0; |
| |
| void set isCovarianceCheck(bool value) { |
| flags = |
| value ? (flags | FlagCovarianceCheck) : (flags & ~FlagCovarianceCheck); |
| } |
| |
| /// If `true`, this is an implicit down cast from an expression of type |
| /// `dynamic`. |
| bool get isForDynamic => flags & FlagForDynamic != 0; |
| |
| void set isForDynamic(bool value) { |
| flags = value ? (flags | FlagForDynamic) : (flags & ~FlagForDynamic); |
| } |
| |
| /// If `true`, this test take the nullability of [type] into account. |
| /// |
| /// This is the case for is-tests written in libraries that are opted in to |
| /// the non nullable by default feature. |
| bool get isForNonNullableByDefault => |
| flags & FlagForNonNullableByDefault != 0; |
| |
| void set isForNonNullableByDefault(bool value) { |
| flags = value |
| ? (flags | FlagForNonNullableByDefault) |
| : (flags & ~FlagForNonNullableByDefault); |
| } |
| |
| /// If `true`, this test is added to show the known static type of the |
| /// expression and should not be performed at runtime. |
| /// |
| /// This is the case for instance for access to inline class representation |
| /// fields on an inline type, where this node shows that the static type |
| /// changes from the inline type of the declared representation type. |
| bool get isUnchecked => flags & FlagUnchecked != 0; |
| |
| void set isUnchecked(bool value) { |
| flags = value ? (flags | FlagUnchecked) : (flags & ~FlagUnchecked); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => type; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAsExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitAsExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| operand.accept(v); |
| type.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| type = v.visitDartType(type); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| type = v.visitDartType(type, cannotRemoveSentinel); |
| } |
| |
| @override |
| String toString() { |
| return "AsExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(operand, |
| minimumPrecedence: astToText.Precedence.BITWISE_OR); |
| printer.write(' as'); |
| if (printer.includeAuxiliaryProperties) { |
| List<String> flags = <String>[]; |
| if (isTypeError) { |
| flags.add('TypeError'); |
| } |
| if (isCovarianceCheck) { |
| flags.add('CovarianceCheck'); |
| } |
| if (isForDynamic) { |
| flags.add('ForDynamic'); |
| } |
| if (isForNonNullableByDefault) { |
| flags.add('ForNonNullableByDefault'); |
| } |
| if (flags.isNotEmpty) { |
| printer.write('{${flags.join(',')}}'); |
| } |
| } |
| printer.write(' '); |
| printer.writeType(type); |
| } |
| } |
| |
| /// Null check expression of form `x!`. |
| /// |
| /// This expression was added as part of NNBD and is currently only created when |
| /// the 'non-nullable' experimental feature is enabled. |
| class NullCheck extends Expression { |
| Expression operand; |
| |
| NullCheck(this.operand) { |
| operand.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| DartType operandType = operand.getStaticType(context); |
| return operandType is NullType |
| ? const NeverType.nonNullable() |
| : operandType.withDeclaredNullability(Nullability.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNullCheck(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitNullCheck(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| operand.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "NullCheck(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(operand, |
| minimumPrecedence: astToText.Precedence.POSTFIX); |
| printer.write('!'); |
| } |
| } |
| |
| /// An integer, double, boolean, string, or null constant. |
| abstract class BasicLiteral extends Expression { |
| Object? get value; |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| } |
| |
| class StringLiteral extends BasicLiteral { |
| @override |
| String value; |
| |
| StringLiteral(this.value); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.stringRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitStringLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitStringLiteral(this, arg); |
| |
| @override |
| String toString() { |
| return "StringLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('"'); |
| printer.write(escapeString(value)); |
| printer.write('"'); |
| } |
| } |
| |
| class IntLiteral extends BasicLiteral { |
| /// Note that this value holds a uint64 value. |
| /// E.g. "0x8000000000000000" will be saved as "-9223372036854775808" despite |
| /// technically (on some platforms, particularly JavaScript) being positive. |
| /// If the number is meant to be negative it will be wrapped in a "unary-". |
| @override |
| int value; |
| |
| IntLiteral(this.value); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.intRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitIntLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitIntLiteral(this, arg); |
| |
| @override |
| String toString() { |
| return "IntLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('$value'); |
| } |
| } |
| |
| class DoubleLiteral extends BasicLiteral { |
| @override |
| double value; |
| |
| DoubleLiteral(this.value); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitDoubleLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitDoubleLiteral(this, arg); |
| |
| @override |
| String toString() { |
| return "DoubleLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('$value'); |
| } |
| } |
| |
| class BoolLiteral extends BasicLiteral { |
| @override |
| bool value; |
| |
| BoolLiteral(this.value); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitBoolLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitBoolLiteral(this, arg); |
| |
| @override |
| String toString() { |
| return "BoolLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('$value'); |
| } |
| } |
| |
| class NullLiteral extends BasicLiteral { |
| @override |
| Object? get value => null; |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => const NullType(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitNullLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitNullLiteral(this, arg); |
| |
| @override |
| String toString() { |
| return "NullLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('null'); |
| } |
| } |
| |
| class SymbolLiteral extends Expression { |
| String value; // Everything strictly after the '#'. |
| |
| SymbolLiteral(this.value); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSymbolLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSymbolLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "SymbolLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('#'); |
| printer.write(value); |
| } |
| } |
| |
| class TypeLiteral extends Expression { |
| DartType type; |
| |
| TypeLiteral(this.type); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.typeRawType(context.nonNullable); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitTypeLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitTypeLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| type.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| type = v.visitDartType(type); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| type = v.visitDartType(type, cannotRemoveSentinel); |
| } |
| |
| @override |
| String toString() { |
| return "TypeLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeType(type); |
| } |
| } |
| |
| class ThisExpression extends Expression { |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.thisType!; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitThisExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitThisExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "ThisExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('this'); |
| } |
| } |
| |
| class Rethrow extends Expression { |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.isNonNullableByDefault |
| ? const NeverType.nonNullable() |
| : const NeverType.legacy(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRethrow(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitRethrow(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "Rethrow(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('rethrow'); |
| } |
| } |
| |
| class Throw extends Expression { |
| Expression expression; |
| |
| Throw(this.expression) { |
| expression.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| context.isNonNullableByDefault |
| ? const NeverType.nonNullable() |
| : const NeverType.legacy(); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitThrow(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitThrow(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "Throw(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('throw '); |
| printer.writeExpression(expression); |
| } |
| } |
| |
| class ListLiteral extends Expression { |
| bool isConst; |
| DartType typeArgument; // Not null, defaults to DynamicType. |
| final List<Expression> expressions; |
| |
| ListLiteral(this.expressions, |
| {this.typeArgument = const DynamicType(), this.isConst = false}) { |
| // ignore: unnecessary_null_comparison |
| assert(typeArgument != null); |
| setParents(expressions, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.listType(typeArgument, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitListLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitListLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| visitList(expressions, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| typeArgument = v.visitDartType(typeArgument); |
| v.transformList(expressions, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); |
| v.transformExpressionList(expressions, this); |
| } |
| |
| @override |
| String toString() { |
| return "ListLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isConst) { |
| printer.write('const '); |
| } |
| printer.write('<'); |
| printer.writeType(typeArgument); |
| printer.write('>['); |
| printer.writeExpressions(expressions); |
| printer.write(']'); |
| } |
| } |
| |
| class SetLiteral extends Expression { |
| bool isConst; |
| DartType typeArgument; // Not null, defaults to DynamicType. |
| final List<Expression> expressions; |
| |
| SetLiteral(this.expressions, |
| {this.typeArgument = const DynamicType(), this.isConst = false}) { |
| // ignore: unnecessary_null_comparison |
| assert(typeArgument != null); |
| setParents(expressions, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.setType(typeArgument, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitSetLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitSetLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| visitList(expressions, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| typeArgument = v.visitDartType(typeArgument); |
| v.transformList(expressions, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel); |
| v.transformExpressionList(expressions, this); |
| } |
| |
| @override |
| String toString() { |
| return "SetLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isConst) { |
| printer.write('const '); |
| } |
| printer.write('<'); |
| printer.writeType(typeArgument); |
| printer.write('>{'); |
| printer.writeExpressions(expressions); |
| printer.write('}'); |
| } |
| } |
| |
| class MapLiteral extends Expression { |
| bool isConst; |
| DartType keyType; // Not null, defaults to DynamicType. |
| DartType valueType; // Not null, defaults to DynamicType. |
| final List<MapLiteralEntry> entries; |
| |
| MapLiteral(this.entries, |
| {this.keyType = const DynamicType(), |
| this.valueType = const DynamicType(), |
| this.isConst = false}) { |
| // ignore: unnecessary_null_comparison |
| assert(keyType != null); |
| // ignore: unnecessary_null_comparison |
| assert(valueType != null); |
| setParents(entries, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment |
| .mapType(keyType, valueType, context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitMapLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitMapLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| keyType.accept(v); |
| valueType.accept(v); |
| visitList(entries, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| keyType = v.visitDartType(keyType); |
| valueType = v.visitDartType(valueType); |
| v.transformList(entries, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| keyType = v.visitDartType(keyType, cannotRemoveSentinel); |
| valueType = v.visitDartType(valueType, cannotRemoveSentinel); |
| v.transformMapEntryList(entries, this); |
| } |
| |
| @override |
| String toString() { |
| return "MapLiteral(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isConst) { |
| printer.write('const '); |
| } |
| printer.write('<'); |
| printer.writeType(keyType); |
| printer.write(', '); |
| printer.writeType(valueType); |
| printer.write('>{'); |
| for (int index = 0; index < entries.length; index++) { |
| if (index > 0) { |
| printer.write(', '); |
| } |
| printer.writeMapEntry(entries[index]); |
| } |
| printer.write('}'); |
| } |
| } |
| |
| class MapLiteralEntry extends TreeNode { |
| Expression key; |
| Expression value; |
| |
| MapLiteralEntry(this.key, this.value) { |
| key.parent = this; |
| value.parent = this; |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitMapLiteralEntry(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => |
| v.visitMapLiteralEntry(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| key.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (key != null) { |
| key = v.transform(key); |
| key.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (key != null) { |
| key = v.transform(key); |
| key.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "MapEntry(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| toTextInternal(printer); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(key); |
| printer.write(': '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| class RecordLiteral extends Expression { |
| bool isConst; |
| final List<Expression> positional; |
| final List<NamedExpression> named; |
| RecordType recordType; |
| |
| RecordLiteral(this.positional, this.named, this.recordType, |
| {this.isConst = false}) |
| : assert(positional.length == recordType.positional.length && |
| named.length == recordType.named.length && |
| recordType.named |
| .map((f) => f.name) |
| .toSet() |
| .containsAll(named.map((f) => f.name))), |
| assert(() { |
| // Assert that the named fields are sorted. |
| for (int i = 1; i < named.length; i++) { |
| if (named[i].name.compareTo(named[i - 1].name) < 0) { |
| return false; |
| } |
| } |
| return true; |
| }(), |
| "Named fields of a RecordLiterals aren't sorted lexicographically: " |
| "${named.map((f) => f.name).join(", ")}") { |
| setParents(positional, this); |
| setParents(named, this); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return recordType; |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRecordLiteral(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitRecordLiteral(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(positional, v); |
| visitList(named, v); |
| recordType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(positional, this); |
| v.transformList(named, this); |
| recordType = v.visitDartType(recordType) as RecordType; |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(positional, this); |
| v.transformNamedExpressionList(named, this); |
| recordType = |
| v.visitDartType(recordType, cannotRemoveSentinel) as RecordType; |
| } |
| |
| @override |
| String toString() { |
| return "RecordType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isConst) { |
| printer.write("const "); |
| } |
| printer.write("("); |
| for (int index = 0; index < positional.length; index++) { |
| if (index > 0) { |
| printer.write(", "); |
| } |
| printer.writeExpression(positional[index]); |
| } |
| if (named.isNotEmpty) { |
| if (positional.isNotEmpty) { |
| printer.write(", "); |
| } |
| for (int index = 0; index < named.length; index++) { |
| if (index > 0) { |
| printer.write(", "); |
| } |
| printer.writeNamedExpression(named[index]); |
| } |
| } |
| printer.write(")"); |
| } |
| } |
| |
| /// Expression of form `await x`. |
| class AwaitExpression extends Expression { |
| Expression operand; |
| |
| /// If non-null, the runtime should check whether the value of [operand] is a |
| /// subtype of [runtimeCheckType], and if _not_ so, wrap the value in a call |
| /// to the `Future.value()` constructor. |
| /// |
| /// For instance |
| /// |
| /// FutureOr<Object> future1 = Future<Object?>.value(); |
| /// var x = await future1; // Check against `Future<Object>`. |
| /// |
| /// Object object = Future<Object?>.value(); |
| /// var y = await object; // Check against `Future<Object>`. |
| /// |
| /// Future<Object?> future2 = Future<Object?>.value(); |
| /// var z = await future2; // No check. |
| /// |
| /// This runtime checks is necessary to ensure that we don't evaluate the |
| /// await expression to `null` when the static type of the expression is |
| /// non-nullable. |
| /// |
| /// The [runtimeCheckType] is computed as `Future<T>` where `T = flatten(S)` |
| /// and `S` is the static type of [operand]. To avoid unnecessary runtime |
| /// checks, the [runtimeCheckType] is not set if the static type of the |
| /// [operand] is a subtype of `Future<T>`. |
| /// |
| /// See https://github.com/dart-lang/sdk/issues/49396 for further discussion |
| /// of which the check is needed. |
| DartType? runtimeCheckType; |
| |
| AwaitExpression(this.operand) { |
| operand.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.flatten(operand.getStaticType(context)); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitAwaitExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitAwaitExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| operand.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| if (runtimeCheckType != null) { |
| runtimeCheckType = v.visitDartType(runtimeCheckType!); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (operand != null) { |
| operand = v.transform(operand); |
| operand.parent = this; |
| } |
| if (runtimeCheckType != null) { |
| runtimeCheckType = v.visitDartType(runtimeCheckType!, null); |
| } |
| } |
| |
| @override |
| String toString() { |
| return "AwaitExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('await '); |
| printer.writeExpression(operand); |
| } |
| } |
| |
| /// Common super-interface for [FunctionExpression] and [FunctionDeclaration]. |
| abstract class LocalFunction implements TreeNode { |
| FunctionNode get function; |
| } |
| |
| /// Expression of form `(x,y) => ...` or `(x,y) { ... }` |
| /// |
| /// The arrow-body form `=> e` is desugared into `return e;`. |
| class FunctionExpression extends Expression implements LocalFunction { |
| @override |
| FunctionNode function; |
| |
| FunctionExpression(this.function) { |
| function.parent = this; |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return function.computeFunctionType(context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitFunctionExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| function.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FunctionExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeFunctionNode(function, ''); |
| } |
| } |
| |
| class ConstantExpression extends Expression { |
| Constant constant; |
| DartType type; |
| |
| ConstantExpression(this.constant, [this.type = const DynamicType()]) { |
| // ignore: unnecessary_null_comparison |
| assert(constant != null); |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => type; |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstantExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitConstantExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| constant.acceptReference(v); |
| type.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| constant = v.visitConstant(constant); |
| type = v.visitDartType(type); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| constant = v.visitConstant(constant, cannotRemoveSentinel); |
| type = v.visitDartType(type, cannotRemoveSentinel); |
| } |
| |
| @override |
| String toString() { |
| return "ConstantExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeConstant(constant); |
| } |
| } |
| |
| /// Synthetic expression of form `let v = x in y` |
| class Let extends Expression { |
| VariableDeclaration variable; // Must have an initializer. |
| Expression body; |
| |
| Let(this.variable, this.body) { |
| variable.parent = this; |
| body.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| body.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLet(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitLet(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| variable.accept(v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "Let(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('let '); |
| printer.writeVariableDeclaration(variable); |
| printer.write(' in '); |
| printer.writeExpression(body); |
| } |
| } |
| |
| class BlockExpression extends Expression { |
| Block body; |
| Expression value; |
| |
| BlockExpression(this.body, this.value) { |
| body.parent = this; |
| value.parent = this; |
| } |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) => |
| value.getStaticType(context); |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitBlockExpression(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitBlockExpression(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| body.accept(v); |
| value.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (value != null) { |
| value = v.transform(value); |
| value.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "BlockExpression(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('block '); |
| printer.writeBlock(body.statements); |
| printer.write(' => '); |
| printer.writeExpression(value); |
| } |
| } |
| |
| /// Attempt to load the library referred to by a deferred import. |
| /// |
| /// This instruction is concerned with: |
| /// - keeping track whether the deferred import is marked as 'loaded' |
| /// - keeping track of whether the library code has already been downloaded |
| /// - actually downloading and linking the library |
| /// |
| /// Should return a future. The value in this future will be the same value |
| /// seen by callers of `loadLibrary` functions. |
| /// |
| /// On backends that link the entire program eagerly, this instruction needs |
| /// to mark the deferred import as 'loaded' and return a future. |
| class LoadLibrary extends Expression { |
| /// Reference to a deferred import in the enclosing library. |
| LibraryDependency import; |
| |
| LoadLibrary(this.import); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment |
| .futureType(const DynamicType(), context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitLoadLibrary(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitLoadLibrary(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "LoadLibrary(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(import.name!); |
| printer.write('.loadLibrary()'); |
| } |
| } |
| |
| /// Checks that the given deferred import has been marked as 'loaded'. |
| class CheckLibraryIsLoaded extends Expression { |
| /// Reference to a deferred import in the enclosing library. |
| LibraryDependency import; |
| |
| CheckLibraryIsLoaded(this.import); |
| |
| @override |
| DartType getStaticType(StaticTypeContext context) => |
| getStaticTypeInternal(context); |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return context.typeEnvironment.coreTypes.objectRawType(context.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitCheckLibraryIsLoaded(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitCheckLibraryIsLoaded(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "CheckLibraryIsLoaded(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(import.name!); |
| printer.write('.checkLibraryIsLoaded()'); |
| } |
| } |
| |
| /// Tearing off a constructor of a class. |
| class ConstructorTearOff extends Expression { |
| /// The reference to the constructor being torn off. |
| Reference targetReference; |
| |
| ConstructorTearOff(Member target) |
| : assert( |
| target is Constructor || (target is Procedure && target.isFactory), |
| "Unexpected constructor tear off target: $target"), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| ConstructorTearOff.byReference(this.targetReference); |
| |
| Member get target => targetReference.asMember; |
| |
| FunctionNode get function => target.function!; |
| |
| void set target(Member member) { |
| assert(member is Constructor || |
| (member is Procedure && member.kind == ProcedureKind.Factory)); |
| targetReference = getNonNullableMemberReferenceGetter(member); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return target.function!.computeFunctionType(Nullability.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitConstructorTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "ConstructorTearOff(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| } |
| |
| /// Tearing off a redirecting factory constructor of a class. |
| class RedirectingFactoryTearOff extends Expression { |
| /// The reference to the redirecting factory constructor being torn off. |
| Reference targetReference; |
| |
| RedirectingFactoryTearOff(Procedure target) |
| : assert(target.isRedirectingFactory), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| RedirectingFactoryTearOff.byReference(this.targetReference); |
| |
| Procedure get target => targetReference.asProcedure; |
| |
| void set target(Procedure target) { |
| targetReference = getNonNullableMemberReferenceGetter(target); |
| } |
| |
| FunctionNode get function => target.function; |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| return target.function.computeFunctionType(Nullability.nonNullable); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitRedirectingFactoryTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitRedirectingFactoryTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "RedirectingFactoryTearOff(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| } |
| |
| class TypedefTearOff extends Expression { |
| final List<TypeParameter> typeParameters; |
| Expression expression; |
| final List<DartType> typeArguments; |
| |
| TypedefTearOff(this.typeParameters, this.expression, this.typeArguments) { |
| expression.parent = this; |
| setParents(typeParameters, this); |
| } |
| |
| @override |
| DartType getStaticTypeInternal(StaticTypeContext context) { |
| FreshTypeParameters freshTypeParameters = |
| getFreshTypeParameters(typeParameters); |
| FunctionType type = expression.getStaticType(context) as FunctionType; |
| type = freshTypeParameters.substitute( |
| Substitution.fromPairs(type.typeParameters, typeArguments) |
| .substituteType(type.withoutTypeParameters)) as FunctionType; |
| return new FunctionType( |
| type.positionalParameters, type.returnType, type.declaredNullability, |
| namedParameters: type.namedParameters, |
| typeParameters: freshTypeParameters.freshTypeParameters, |
| requiredParameterCount: type.requiredParameterCount); |
| } |
| |
| @override |
| R accept<R>(ExpressionVisitor<R> v) => v.visitTypedefTearOff(this); |
| |
| @override |
| R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => |
| v.visitTypedefTearOff(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| visitList(typeParameters, v); |
| visitList(typeArguments, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformList(typeParameters, this); |
| v.transformDartTypeList(typeArguments); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformList(typeParameters, this, dummyTypeParameter); |
| v.transformDartTypeList(typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "TypedefTearOff(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypeParameters(typeParameters); |
| printer.write(".("); |
| printer.writeExpression(expression); |
| printer.writeTypeArguments(typeArguments); |
| printer.write(")"); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // STATEMENTS |
| // ------------------------------------------------------------------------ |
| |
| abstract class Statement extends TreeNode { |
| @override |
| R accept<R>(StatementVisitor<R> v); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg); |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeStatement(this); |
| return printer.getText(); |
| } |
| } |
| |
| class ExpressionStatement extends Statement { |
| Expression expression; |
| |
| ExpressionStatement(this.expression) { |
| expression.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitExpressionStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitExpressionStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "ExpressionStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExpression(expression); |
| printer.write(';'); |
| } |
| } |
| |
| class Block extends Statement { |
| final List<Statement> statements; |
| |
| /// End offset in the source file it comes from. Valid values are from 0 and |
| /// up, or -1 ([TreeNode.noOffset]) if the file end offset is not available |
| /// (this is the default if none is specifically set). |
| int fileEndOffset = TreeNode.noOffset; |
| |
| Block(this.statements) { |
| // Ensure statements is mutable. |
| assert(checkListIsMutable(statements, dummyStatement)); |
| setParents(statements, this); |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitBlock(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => v.visitBlock(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(statements, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(statements, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformStatementList(statements, this); |
| } |
| |
| void addStatement(Statement node) { |
| statements.add(node); |
| node.parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "Block(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeBlock(statements); |
| } |
| } |
| |
| /// A block that is only executed when asserts are enabled. |
| /// |
| /// Sometimes arbitrary statements must be guarded by whether asserts are |
| /// enabled. For example, when a subexpression of an assert in async code is |
| /// linearized and named, it can produce such a block of statements. |
| class AssertBlock extends Statement { |
| final List<Statement> statements; |
| |
| AssertBlock(this.statements) { |
| // Ensure statements is mutable. |
| assert(checkListIsMutable(statements, dummyStatement)); |
| setParents(statements, this); |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitAssertBlock(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitAssertBlock(this, arg); |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(statements, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformStatementList(statements, this); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(statements, v); |
| } |
| |
| void addStatement(Statement node) { |
| statements.add(node); |
| node.parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "AssertBlock(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('assert '); |
| printer.writeBlock(statements); |
| } |
| } |
| |
| class EmptyStatement extends Statement { |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitEmptyStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitEmptyStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "EmptyStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(';'); |
| } |
| } |
| |
| class AssertStatement extends Statement { |
| Expression condition; |
| Expression? message; // May be null. |
| |
| /// Character offset in the source where the assertion condition begins. |
| /// |
| /// Note: This is not the offset into the UTF8 encoded `List<int>` source. |
| int conditionStartOffset; |
| |
| /// Character offset in the source where the assertion condition ends. |
| /// |
| /// Note: This is not the offset into the UTF8 encoded `List<int>` source. |
| int conditionEndOffset; |
| |
| AssertStatement(this.condition, |
| {this.message, |
| required this.conditionStartOffset, |
| required this.conditionEndOffset}) { |
| condition.parent = this; |
| message?.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitAssertStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitAssertStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| condition.accept(v); |
| message?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| if (message != null) { |
| message = v.transform(message!); |
| message?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| if (message != null) { |
| message = v.transformOrRemoveExpression(message!); |
| message?.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "AssertStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('assert('); |
| printer.writeExpression(condition); |
| if (message != null) { |
| printer.write(', '); |
| printer.writeExpression(message!); |
| } |
| printer.write(');'); |
| } |
| } |
| |
| /// A target of a [Break] statement. |
| /// |
| /// The label itself has no name; breaks reference the statement directly. |
| /// |
| /// The frontend does not generate labeled statements without uses. |
| class LabeledStatement extends Statement { |
| late Statement body; |
| |
| LabeledStatement(Statement? body) { |
| if (body != null) { |
| this.body = body..parent = this; |
| } |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitLabeledStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitLabeledStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "LabeledStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write(printer.getLabelName(this)); |
| printer.write(':'); |
| printer.newLine(); |
| printer.writeStatement(body); |
| } |
| } |
| |
| /// Breaks out of an enclosing [LabeledStatement]. |
| /// |
| /// Both `break` and `continue` statements are translated into this node. |
| /// |
| /// Example `break` desugaring: |
| /// |
| /// while (x) { |
| /// if (y) break; |
| /// BODY |
| /// } |
| /// |
| /// ==> |
| /// |
| /// L: while (x) { |
| /// if (y) break L; |
| /// BODY |
| /// } |
| /// |
| /// Example `continue` desugaring: |
| /// |
| /// while (x) { |
| /// if (y) continue; |
| /// BODY |
| /// } |
| /// |
| /// ==> |
| /// |
| /// while (x) { |
| /// L: { |
| /// if (y) break L; |
| /// BODY |
| /// } |
| /// } |
| /// |
| /// Note: Compiler-generated [LabeledStatement]s for [WhileStatement]s and |
| /// [ForStatement]s are only generated when needed. If there isn't a `break` or |
| /// `continue` in a loop, the kernel for the loop won't have a generated |
| /// [LabeledStatement]. |
| class BreakStatement extends Statement { |
| LabeledStatement target; |
| |
| BreakStatement(this.target); |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitBreakStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitBreakStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "BreakStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('break '); |
| printer.write(printer.getLabelName(target)); |
| printer.write(';'); |
| } |
| } |
| |
| class WhileStatement extends Statement { |
| Expression condition; |
| Statement body; |
| |
| WhileStatement(this.condition, this.body) { |
| condition.parent = this; |
| body.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitWhileStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitWhileStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| condition.accept(v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "WhileStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('while ('); |
| printer.writeExpression(condition); |
| printer.write(') '); |
| printer.writeStatement(body); |
| } |
| } |
| |
| class DoStatement extends Statement { |
| Statement body; |
| Expression condition; |
| |
| DoStatement(this.body, this.condition) { |
| body.parent = this; |
| condition.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitDoStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitDoStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| body.accept(v); |
| condition.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "DoStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('do '); |
| printer.writeStatement(body); |
| printer.write(' while ('); |
| printer.writeExpression(condition); |
| printer.write(');'); |
| } |
| } |
| |
| class ForStatement extends Statement { |
| final List<VariableDeclaration> variables; // May be empty, but not null. |
| Expression? condition; // May be null. |
| final List<Expression> updates; // May be empty, but not null. |
| Statement body; |
| |
| ForStatement(this.variables, this.condition, this.updates, this.body) { |
| setParents(variables, this); |
| condition?.parent = this; |
| setParents(updates, this); |
| body.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitForStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitForStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(variables, v); |
| condition?.accept(v); |
| visitList(updates, v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(variables, this); |
| if (condition != null) { |
| condition = v.transform(condition!); |
| condition?.parent = this; |
| } |
| v.transformList(updates, this); |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformVariableDeclarationList(variables, this); |
| if (condition != null) { |
| condition = v.transformOrRemoveExpression(condition!); |
| condition?.parent = this; |
| } |
| v.transformExpressionList(updates, this); |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "ForStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('for ('); |
| for (int index = 0; index < variables.length; index++) { |
| if (index > 0) { |
| printer.write(', '); |
| } |
| printer.writeVariableDeclaration(variables[index], |
| includeModifiersAndType: index == 0); |
| } |
| printer.write('; '); |
| if (condition != null) { |
| printer.writeExpression(condition!); |
| } |
| printer.write('; '); |
| printer.writeExpressions(updates); |
| printer.write(') '); |
| printer.writeStatement(body); |
| } |
| } |
| |
| class ForInStatement extends Statement { |
| /// Offset in the source file it comes from. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file |
| /// offset is not available (this is the default if none is specifically set). |
| int bodyOffset = TreeNode.noOffset; |
| |
| VariableDeclaration variable; // Has no initializer. |
| Expression iterable; |
| Statement body; |
| bool isAsync; // True if this is an 'await for' loop. |
| |
| ForInStatement(this.variable, this.iterable, this.body, |
| {this.isAsync = false}) { |
| variable.parent = this; |
| iterable.parent = this; |
| body.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitForInStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitForInStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| variable.accept(v); |
| iterable.accept(v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (iterable != null) { |
| iterable = v.transform(iterable); |
| iterable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (iterable != null) { |
| iterable = v.transform(iterable); |
| iterable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| /// Returns the type of the iterator in this for-in statement. |
| /// |
| /// This calls `StaticTypeContext.getForInIteratorType` which calls |
| /// [getStaticTypeInternal] to compute the type of not already cached in |
| /// [context]. |
| DartType getIteratorType(StaticTypeContext context) => |
| context.getForInIteratorType(this); |
| |
| /// Computes the type of the iterator in this for-in statement. |
| /// |
| /// This is called by `StaticTypeContext.getForInIteratorType` if the iterator |
| /// type of this for-in statement is not already cached in [context]. |
| DartType getIteratorTypeInternal(StaticTypeContext context) { |
| DartType? iteratorType; |
| if (isAsync) { |
| InterfaceType? streamType = iterable.getStaticTypeAsInstanceOf( |
| context.typeEnvironment.coreTypes.streamClass, context); |
| // ignore: unnecessary_null_comparison |
| if (streamType != null) { |
| iteratorType = new InterfaceType( |
| context.typeEnvironment.coreTypes.streamIteratorClass, |
| context.nonNullable, |
| streamType.typeArguments); |
| } |
| } else { |
| InterfaceType iterableType = iterable.getStaticTypeAsInstanceOf( |
| context.typeEnvironment.coreTypes.iterableClass, context); |
| Member? member = context.typeEnvironment.hierarchy |
| .getInterfaceMember(iterableType.classNode, new Name('iterator')); |
| if (member != null) { |
| iteratorType = Substitution.fromInterfaceType(iterableType) |
| .substituteType(member.getterType); |
| } |
| } |
| return iteratorType ?? const DynamicType(); |
| } |
| |
| /// Returns the type of the element in this for-in statement. |
| /// |
| /// This calls `StaticTypeContext.getForInElementType` which calls |
| /// [getStaticTypeInternal] to compute the type of not already cached in |
| /// [context]. |
| DartType getElementType(StaticTypeContext context) => |
| context.getForInElementType(this); |
| |
| /// Computes the type of the element in this for-in statement. |
| /// |
| /// This is called by `StaticTypeContext.getForInElementType` if the element |
| /// type of this for-in statement is not already cached in [context]. |
| DartType getElementTypeInternal(StaticTypeContext context) { |
| DartType iterableType = |
| iterable.getStaticType(context).resolveTypeParameterType; |
| // TODO(johnniwinther): Update this to use the type of |
| // `iterable.iterator.current` if inference is updated accordingly. |
| while (iterableType is TypeParameterType) { |
| TypeParameterType typeParameterType = iterableType; |
| iterableType = typeParameterType.bound; |
| } |
| if (iterableType is NeverType) { |
| return iterableType; |
| } |
| if (iterableType is InvalidType) { |
| return iterableType; |
| } |
| if (iterableType is! InterfaceType) { |
| // TODO(johnniwinther): Change this to an assert once the CFE correctly |
| // inserts casts for all invalid iterable types. |
| return const InvalidType(); |
| } |
| if (isAsync) { |
| List<DartType> typeArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf( |
| iterableType, context.typeEnvironment.coreTypes.streamClass)!; |
| return typeArguments.single; |
| } else { |
| List<DartType> typeArguments = context.typeEnvironment |
| .getTypeArgumentsAsInstanceOf( |
| iterableType, context.typeEnvironment.coreTypes.iterableClass)!; |
| return typeArguments.single; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "ForInStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('for ('); |
| printer.writeVariableDeclaration(variable); |
| |
| printer.write(' in '); |
| printer.writeExpression(iterable); |
| printer.write(') '); |
| printer.writeStatement(body); |
| } |
| } |
| |
| /// Statement of form `switch (e) { case x: ... }`. |
| /// |
| /// Adjacent case clauses have been merged into a single [SwitchCase]. A runtime |
| /// exception must be thrown if one [SwitchCase] falls through to another case. |
| class SwitchStatement extends Statement { |
| Expression expression; |
| final List<SwitchCase> cases; |
| |
| /// For switches without a default clause, whether all possible values are |
| /// covered by a switch case. For switches with a default clause, always |
| /// `false`. |
| /// Initialized during type inference. |
| bool isExplicitlyExhaustive; |
| |
| SwitchStatement(this.expression, this.cases, |
| {this.isExplicitlyExhaustive = false}) { |
| expression.parent = this; |
| setParents(cases, this); |
| } |
| |
| /// Whether the switch has a `default` case. |
| bool get hasDefault { |
| assert(cases.every((c) => c == cases.last || !c.isDefault)); |
| return cases.isNotEmpty && cases.last.isDefault; |
| } |
| |
| /// Whether the switch is guaranteed to hit one of the cases (including the |
| /// default case, if present). |
| bool get isExhaustive => isExplicitlyExhaustive || hasDefault; |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitSwitchStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitSwitchStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| visitList(cases, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformList(cases, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| v.transformSwitchCaseList(cases, this); |
| } |
| |
| @override |
| String toString() { |
| return "SwitchStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('switch ('); |
| printer.writeExpression(expression); |
| printer.write(') {'); |
| printer.incIndentation(); |
| for (SwitchCase switchCase in cases) { |
| printer.newLine(); |
| printer.writeSwitchCase(switchCase); |
| } |
| printer.decIndentation(); |
| printer.newLine(); |
| printer.write('}'); |
| } |
| } |
| |
| /// A group of `case` clauses and/or a `default` clause. |
| /// |
| /// This is a potential target of [ContinueSwitchStatement]. |
| class SwitchCase extends TreeNode { |
| final List<Expression> expressions; |
| final List<int> expressionOffsets; |
| late Statement body; |
| bool isDefault; |
| |
| SwitchCase(this.expressions, this.expressionOffsets, Statement? body, |
| {this.isDefault = false}) { |
| setParents(expressions, this); |
| if (body != null) { |
| this.body = body..parent = this; |
| } |
| } |
| |
| SwitchCase.defaultCase(Statement? body) |
| : isDefault = true, |
| expressions = <Expression>[], |
| expressionOffsets = <int>[] { |
| if (body != null) { |
| this.body = body..parent = this; |
| } |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitSwitchCase(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitSwitchCase(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(expressions, v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(expressions, this); |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(expressions, this); |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "SwitchCase(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| toTextInternal(printer); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| for (int index = 0; index < expressions.length; index++) { |
| if (index > 0) { |
| printer.newLine(); |
| } |
| printer.write('case '); |
| printer.writeExpression(expressions[index]); |
| printer.write(':'); |
| } |
| if (isDefault) { |
| if (expressions.isNotEmpty) { |
| printer.newLine(); |
| } |
| printer.write('default:'); |
| } |
| printer.incIndentation(); |
| Statement? block = body; |
| if (block is Block) { |
| for (Statement statement in block.statements) { |
| printer.newLine(); |
| printer.writeStatement(statement); |
| } |
| } else { |
| printer.write(' '); |
| printer.writeStatement(body); |
| } |
| printer.decIndentation(); |
| } |
| } |
| |
| /// Jump to a case in an enclosing switch. |
| class ContinueSwitchStatement extends Statement { |
| SwitchCase target; |
| |
| ContinueSwitchStatement(this.target); |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitContinueSwitchStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitContinueSwitchStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| void transformChildren(Transformer v) {} |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) {} |
| |
| @override |
| String toString() { |
| return "ContinueSwitchStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('continue '); |
| printer.write(printer.getSwitchCaseName(target)); |
| printer.write(';'); |
| } |
| } |
| |
| class IfStatement extends Statement { |
| Expression condition; |
| Statement then; |
| Statement? otherwise; |
| |
| IfStatement(this.condition, this.then, this.otherwise) { |
| condition.parent = this; |
| then.parent = this; |
| otherwise?.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitIfStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitIfStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| condition.accept(v); |
| then.accept(v); |
| otherwise?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (then != null) { |
| then = v.transform(then); |
| then.parent = this; |
| } |
| if (otherwise != null) { |
| otherwise = v.transform(otherwise!); |
| otherwise?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (condition != null) { |
| condition = v.transform(condition); |
| condition.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (then != null) { |
| then = v.transform(then); |
| then.parent = this; |
| } |
| if (otherwise != null) { |
| otherwise = v.transformOrRemoveStatement(otherwise!); |
| otherwise?.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "IfStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('if ('); |
| printer.writeExpression(condition); |
| printer.write(') '); |
| printer.writeStatement(then); |
| if (otherwise != null) { |
| printer.write(' else '); |
| printer.writeStatement(otherwise!); |
| } |
| } |
| } |
| |
| class ReturnStatement extends Statement { |
| Expression? expression; // May be null. |
| |
| ReturnStatement([this.expression]) { |
| expression?.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitReturnStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitReturnStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| if (expression != null) { |
| expression = v.transform(expression!); |
| expression?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| if (expression != null) { |
| expression = v.transformOrRemoveExpression(expression!); |
| expression?.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "ReturnStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('return'); |
| if (expression != null) { |
| printer.write(' '); |
| printer.writeExpression(expression!); |
| } |
| printer.write(';'); |
| } |
| } |
| |
| class TryCatch extends Statement { |
| Statement body; |
| List<Catch> catches; |
| bool isSynthetic; |
| |
| TryCatch(this.body, this.catches, {this.isSynthetic = false}) { |
| body.parent = this; |
| setParents(catches, this); |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitTryCatch(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitTryCatch(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| body.accept(v); |
| visitList(catches, v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| v.transformList(catches, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| v.transformCatchList(catches, this); |
| } |
| |
| @override |
| String toString() { |
| return "TryCatch(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('try '); |
| printer.writeStatement(body); |
| for (Catch catchClause in catches) { |
| printer.write(' '); |
| printer.writeCatch(catchClause); |
| } |
| } |
| } |
| |
| class Catch extends TreeNode { |
| DartType guard; // Not null, defaults to dynamic. |
| VariableDeclaration? exception; |
| VariableDeclaration? stackTrace; |
| Statement body; |
| |
| Catch(this.exception, this.body, |
| {this.guard = const DynamicType(), this.stackTrace}) { |
| // ignore: unnecessary_null_comparison |
| assert(guard != null); |
| exception?.parent = this; |
| stackTrace?.parent = this; |
| body.parent = this; |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitCatch(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitCatch(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| guard.accept(v); |
| exception?.accept(v); |
| stackTrace?.accept(v); |
| body.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| guard = v.visitDartType(guard); |
| if (exception != null) { |
| exception = v.transform(exception!); |
| exception?.parent = this; |
| } |
| if (stackTrace != null) { |
| stackTrace = v.transform(stackTrace!); |
| stackTrace?.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| guard = v.visitDartType(guard, cannotRemoveSentinel); |
| if (exception != null) { |
| exception = v.transformOrRemoveVariableDeclaration(exception!); |
| exception?.parent = this; |
| } |
| if (stackTrace != null) { |
| stackTrace = v.transformOrRemoveVariableDeclaration(stackTrace!); |
| stackTrace?.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "Catch(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| toTextInternal(printer); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| bool isImplicitType(DartType type) { |
| if (type is DynamicType) { |
| return true; |
| } |
| if (type is InterfaceType && |
| type.className.node != null && |
| type.classNode.name == 'Object') { |
| Uri uri = type.classNode.enclosingLibrary.importUri; |
| return uri.isScheme('dart') && |
| uri.path == 'core' && |
| type.nullability == Nullability.nonNullable; |
| } |
| return false; |
| } |
| |
| if (exception != null) { |
| if (!isImplicitType(guard)) { |
| printer.write('on '); |
| printer.writeType(guard); |
| printer.write(' '); |
| } |
| printer.write('catch ('); |
| printer.writeVariableDeclaration(exception!, |
| includeModifiersAndType: false); |
| if (stackTrace != null) { |
| printer.write(', '); |
| printer.writeVariableDeclaration(stackTrace!, |
| includeModifiersAndType: false); |
| } |
| printer.write(') '); |
| } else { |
| printer.write('on '); |
| printer.writeType(guard); |
| printer.write(' '); |
| } |
| printer.writeStatement(body); |
| } |
| } |
| |
| class TryFinally extends Statement { |
| Statement body; |
| Statement finalizer; |
| |
| TryFinally(this.body, this.finalizer) { |
| body.parent = this; |
| finalizer.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitTryFinally(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitTryFinally(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| body.accept(v); |
| finalizer.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (finalizer != null) { |
| finalizer = v.transform(finalizer); |
| finalizer.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (body != null) { |
| body = v.transform(body); |
| body.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (finalizer != null) { |
| finalizer = v.transform(finalizer); |
| finalizer.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "TryFinally(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (body is! TryCatch) { |
| // This is a `try {} catch (e) {} finally {}`. Avoid repeating `try`. |
| printer.write('try '); |
| } |
| printer.writeStatement(body); |
| printer.write(' finally '); |
| printer.writeStatement(finalizer); |
| } |
| } |
| |
| /// Statement of form `yield x` or `yield* x`. |
| class YieldStatement extends Statement { |
| Expression expression; |
| int flags = 0; |
| |
| YieldStatement(this.expression, {bool isYieldStar = false}) { |
| expression.parent = this; |
| this.isYieldStar = isYieldStar; |
| } |
| |
| static const int FlagYieldStar = 1 << 0; |
| |
| bool get isYieldStar => flags & FlagYieldStar != 0; |
| |
| void set isYieldStar(bool value) { |
| flags = value ? (flags | FlagYieldStar) : (flags & ~FlagYieldStar); |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitYieldStatement(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitYieldStatement(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (expression != null) { |
| expression = v.transform(expression); |
| expression.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "YieldStatement(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('yield'); |
| if (isYieldStar) { |
| printer.write('*'); |
| } |
| printer.write(' '); |
| printer.writeExpression(expression); |
| printer.write(';'); |
| } |
| } |
| |
| /// Declaration of a local variable. |
| /// |
| /// This may occur as a statement, but is also used in several non-statement |
| /// contexts, such as in [ForStatement], [Catch], and [FunctionNode]. |
| /// |
| /// When this occurs as a statement, it must be a direct child of a [Block]. |
| // |
| // DESIGN TODO: Should we remove the 'final' modifier from variables? |
| class VariableDeclaration extends Statement implements Annotatable { |
| /// Offset of the equals sign in the source file it comes from. |
| /// |
| /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) |
| /// if the equals sign offset is not available (e.g. if not initialized) |
| /// (this is the default if none is specifically set). |
| int fileEqualsOffset = TreeNode.noOffset; |
| |
| /// List of metadata annotations on the variable declaration. |
| /// |
| /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| /// annotations if needed. |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| /// The name of the variable or parameter as provided in the source code. |
| /// |
| /// If this variable is synthesized, for instance the variable of a [Let] |
| /// expression, the name can be `null`. |
| String? _name; |
| int flags = 0; |
| DartType type; // Not null, defaults to dynamic. |
| |
| /// Offset of the declaration, set and used when writing the binary. |
| int binaryOffsetNoTag = -1; |
| |
| /// For locals, this is the initial value. |
| /// For parameters, this is the default value. |
| /// |
| /// Should be null in other cases. |
| Expression? initializer; // May be null. |
| |
| VariableDeclaration(this._name, |
| {this.initializer, |
| this.type = const DynamicType(), |
| int flags = -1, |
| bool isFinal = false, |
| bool isConst = false, |
| bool isInitializingFormal = false, |
| bool isCovariantByDeclaration = false, |
| bool isLate = false, |
| bool isRequired = false, |
| bool isLowered = false, |
| bool isSynthesized = false, |
| bool isHoisted = false, |
| bool hasDeclaredInitializer = false}) { |
| // ignore: unnecessary_null_comparison |
| assert(type != null); |
| initializer?.parent = this; |
| if (flags != -1) { |
| this.flags = flags; |
| } else { |
| this.isFinal = isFinal; |
| this.isConst = isConst; |
| this.isInitializingFormal = isInitializingFormal; |
| this.isCovariantByDeclaration = isCovariantByDeclaration; |
| this.isLate = isLate; |
| this.isRequired = isRequired; |
| this.isLowered = isLowered; |
| this.hasDeclaredInitializer = hasDeclaredInitializer; |
| this.isSynthesized = isSynthesized; |
| this.isHoisted = isHoisted; |
| } |
| assert(_name != null || this.isSynthesized, |
| "Only synthesized variables can have no name."); |
| } |
| |
| /// Creates a synthetic variable with the given expression as initializer. |
| VariableDeclaration.forValue(this.initializer, |
| {bool isFinal = true, |
| bool isConst = false, |
| bool isInitializingFormal = false, |
| bool isLate = false, |
| bool isRequired = false, |
| bool isLowered = false, |
| this.type = const DynamicType()}) { |
| // ignore: unnecessary_null_comparison |
| assert(type != null); |
| initializer?.parent = this; |
| this.isFinal = isFinal; |
| this.isConst = isConst; |
| this.isInitializingFormal = isInitializingFormal; |
| this.isLate = isLate; |
| this.isRequired = isRequired; |
| this.isLowered = isLowered; |
| this.hasDeclaredInitializer = true; |
| this.isSynthesized = true; |
| } |
| |
| /// The name of the variable as provided in the source code. |
| /// |
| /// The name of a variable can only be omitted if the variable is synthesized. |
| /// Otherwise, its name is as provided in the source code. |
| String? get name => _name; |
| |
| void set name(String? value) { |
| assert(value != null || isSynthesized, |
| "Only synthesized variables can have no name."); |
| _name = value; |
| } |
| |
| static const int FlagFinal = 1 << 0; // Must match serialized bit positions. |
| static const int FlagConst = 1 << 1; |
| static const int FlagHasDeclaredInitializer = 1 << 2; |
| static const int FlagInitializingFormal = 1 << 3; |
| static const int FlagCovariantByClass = 1 << 4; |
| static const int FlagLate = 1 << 5; |
| static const int FlagRequired = 1 << 6; |
| static const int FlagCovariantByDeclaration = 1 << 7; |
| static const int FlagLowered = 1 << 8; |
| static const int FlagSynthesized = 1 << 9; |
| static const int FlagHoisted = 1 << 10; |
| |
| bool get isFinal => flags & FlagFinal != 0; |
| bool get isConst => flags & FlagConst != 0; |
| |
| /// Whether the parameter is declared with the `covariant` keyword. |
| bool get isCovariantByDeclaration => flags & FlagCovariantByDeclaration != 0; |
| |
| /// Whether the variable is declared as an initializing formal parameter of |
| /// a constructor. |
| @informative |
| bool get isInitializingFormal => flags & FlagInitializingFormal != 0; |
| |
| /// If this [VariableDeclaration] is a parameter of a method, indicates |
| /// whether the method implementation needs to contain a runtime type check to |
| /// deal with generic covariance. |
| /// |
| /// When `true`, runtime checks may need to be performed. |
| bool get isCovariantByClass => flags & FlagCovariantByClass != 0; |
| |
| /// Whether the variable is declared with the `late` keyword. |
| /// |
| /// The `late` modifier is only supported on local variables and not on |
| /// parameters. |
| bool get isLate => flags & FlagLate != 0; |
| |
| /// Whether the parameter is declared with the `required` keyword. |
| /// |
| /// The `required` modifier is only supported on named parameters and not on |
| /// positional parameters and local variables. |
| bool get isRequired => flags & FlagRequired != 0; |
| |
| /// Whether the variable is part of a lowering. |
| /// |
| /// If a variable is part of a lowering its name may be synthesized so that it |
| /// doesn't reflect the name used in the source code and might not have a |
| /// one-to-one correspondence with the variable in the source. |
| /// |
| /// Lowering is used for instance of encoding of 'this' in extension instance |
| /// members and encoding of late locals. |
| bool get isLowered => flags & FlagLowered != 0; |
| |
| /// Whether this variable is synthesized, that is, it is _not_ declared in |
| /// the source code. |
| /// |
| /// The name of a variable can only be omitted if the variable is synthesized. |
| /// Otherwise, its name is as provided in the source code. |
| bool get isSynthesized => flags & FlagSynthesized != 0; |
| |
| /// Whether the declaration of this variable is has been moved to an earlier |
| /// source location. |
| /// |
| /// This is for instance the case for variables declared in a pattern, where |
| /// the lowering requires the variable to be declared before the expression |
| /// that performs that matching in which its initialization occurs. |
| bool get isHoisted => flags & FlagHoisted != 0; |
| |
| /// Whether the variable has an initializer, either by declaration or copied |
| /// from an original declaration. |
| /// |
| /// Note that the variable might have a synthesized initializer expression, |
| /// so `hasDeclaredInitializer == false` doesn't imply `initializer == null`. |
| /// For instance, for duplicate variable names, an invalid expression is set |
| /// as the initializer of the second variable. |
| bool get hasDeclaredInitializer => flags & FlagHasDeclaredInitializer != 0; |
| |
| /// Whether the variable is assignable. |
| /// |
| /// This is `true` if the variable is neither constant nor final, or if it |
| /// is late final without an initializer. |
| bool get isAssignable { |
| if (isConst) return false; |
| if (isFinal) { |
| if (isLate) return initializer == null; |
| return false; |
| } |
| return true; |
| } |
| |
| void set isFinal(bool value) { |
| flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal); |
| } |
| |
| void set isConst(bool value) { |
| flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
| } |
| |
| void set isCovariantByDeclaration(bool value) { |
| flags = value |
| ? (flags | FlagCovariantByDeclaration) |
| : (flags & ~FlagCovariantByDeclaration); |
| } |
| |
| @informative |
| void set isInitializingFormal(bool value) { |
| flags = value |
| ? (flags | FlagInitializingFormal) |
| : (flags & ~FlagInitializingFormal); |
| } |
| |
| void set isCovariantByClass(bool value) { |
| flags = value |
| ? (flags | FlagCovariantByClass) |
| : (flags & ~FlagCovariantByClass); |
| } |
| |
| void set isLate(bool value) { |
| flags = value ? (flags | FlagLate) : (flags & ~FlagLate); |
| } |
| |
| void set isRequired(bool value) { |
| flags = value ? (flags | FlagRequired) : (flags & ~FlagRequired); |
| } |
| |
| void set isLowered(bool value) { |
| flags = value ? (flags | FlagLowered) : (flags & ~FlagLowered); |
| } |
| |
| void set isSynthesized(bool value) { |
| assert( |
| value || _name != null, "Only synthesized variables can have no name."); |
| flags = value ? (flags | FlagSynthesized) : (flags & ~FlagSynthesized); |
| } |
| |
| void set isHoisted(bool value) { |
| flags = value ? (flags | FlagHoisted) : (flags & ~FlagHoisted); |
| } |
| |
| void set hasDeclaredInitializer(bool value) { |
| flags = value |
| ? (flags | FlagHasDeclaredInitializer) |
| : (flags & ~FlagHasDeclaredInitializer); |
| } |
| |
| void clearAnnotations() { |
| annotations = const <Expression>[]; |
| } |
| |
| @override |
| void addAnnotation(Expression annotation) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(annotation..parent = this); |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitVariableDeclaration(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitVariableDeclaration(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| type.accept(v); |
| initializer?.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| type = v.visitDartType(type); |
| if (initializer != null) { |
| initializer = v.transform(initializer!); |
| initializer?.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| type = v.visitDartType(type, cannotRemoveSentinel); |
| if (initializer != null) { |
| initializer = v.transformOrRemoveExpression(initializer!); |
| initializer?.parent = this; |
| } |
| } |
| |
| /// Returns a possibly synthesized name for this variable, consistent with |
| /// the names used across all [toString] calls. |
| @override |
| String toString() { |
| return "VariableDeclaration(${toStringInternal()})"; |
| } |
| |
| @override |
| String toStringInternal() { |
| AstPrinter printer = new AstPrinter(defaultAstTextStrategy); |
| printer.writeVariableDeclaration(this, includeInitializer: false); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeVariableDeclaration(this); |
| printer.write(';'); |
| } |
| } |
| |
| /// Declaration a local function. |
| /// |
| /// The body of the function may use [variable] as its self-reference. |
| class FunctionDeclaration extends Statement implements LocalFunction { |
| VariableDeclaration variable; // Is final and has no initializer. |
| |
| @override |
| FunctionNode function; |
| |
| FunctionDeclaration(this.variable, this.function) |
| // ignore: unnecessary_null_comparison |
| : assert(function != null) { |
| variable.parent = this; |
| function.parent = this; |
| } |
| |
| @override |
| R accept<R>(StatementVisitor<R> v) => v.visitFunctionDeclaration(this); |
| |
| @override |
| R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => |
| v.visitFunctionDeclaration(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| variable.accept(v); |
| function.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| // ignore: unnecessary_null_comparison |
| if (variable != null) { |
| variable = v.transform(variable); |
| variable.parent = this; |
| } |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| function = v.transform(function); |
| function.parent = this; |
| } |
| } |
| |
| @override |
| String toString() { |
| return "FunctionDeclaration(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // ignore: unnecessary_null_comparison |
| if (function != null) { |
| printer.writeFunctionNode(function, printer.getVariableName(variable)); |
| if (function.body is ReturnStatement) { |
| printer.write(';'); |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // NAMES |
| // ------------------------------------------------------------------------ |
| |
| /// A public name, or a private name qualified by a library. |
| /// |
| /// Names are only used for expressions with dynamic dispatch, as all |
| /// statically resolved references are represented in nameless form. |
| /// |
| /// [Name]s are immutable and compare based on structural equality, and they |
| /// are not AST nodes. |
| /// |
| /// The [toString] method returns a human-readable string that includes the |
| /// library name for private names; uniqueness is not guaranteed. |
| abstract class Name extends Node { |
| @override |
| final int hashCode; |
| |
| final String text; |
| Reference? get libraryName; |
| Library? get library; |
| bool get isPrivate; |
| |
| Name._internal(this.hashCode, this.text); |
| |
| factory Name(String text, [Library? library]) => |
| new Name.byReference(text, library?.reference); |
| |
| factory Name.byReference(String text, Reference? libraryName) { |
| /// Use separate subclasses for the public and private case to save memory |
| /// for public names. |
| if (text.startsWith('_')) { |
| assert(libraryName != null); |
| return new _PrivateName(text, libraryName!); |
| } else { |
| return new _PublicName(text); |
| } |
| } |
| |
| @override |
| bool operator ==(other) { |
| return other is Name && text == other.text && library == other.library; |
| } |
| |
| @override |
| R accept<R>(Visitor<R> v) => v.visitName(this); |
| |
| @override |
| R accept1<R, A>(Visitor1<R, A> v, A arg) => v.visitName(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| // DESIGN TODO: Should we visit the library as a library reference? |
| } |
| |
| /// Returns the textual representation of this node for use in debugging. |
| /// |
| /// Note that this adds some nodes to a static map to ensure consistent |
| /// naming, but that it thus also leaks memory. |
| @override |
| String leakingDebugToString() => astToText.debugNodeToString(this); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeName(this); |
| } |
| |
| /// The name of the `call` method on a function. |
| static final Name callName = new _PublicName('call'); |
| |
| /// The name of the `==` operator. |
| static final Name equalsName = new _PublicName('=='); |
| } |
| |
| class _PrivateName extends Name { |
| @override |
| final Reference libraryName; |
| |
| @override |
| bool get isPrivate => true; |
| |
| _PrivateName(String text, Reference libraryName) |
| : this.libraryName = libraryName, |
| super._internal(_computeHashCode(text, libraryName), text); |
| |
| @override |
| String toString() => toStringInternal(); |
| |
| @override |
| String toStringInternal() => |
| // ignore: unnecessary_null_comparison |
| library != null ? '$library::$text' : text; |
| |
| @override |
| Library get library => libraryName.asLibrary; |
| |
| static int _computeHashCode(String name, Reference libraryName) { |
| // TODO(cstefantsova): Factor in [libraryName] in a non-deterministic way |
| // into the result. Note, the previous code here was the following: |
| // return 131 * name.hashCode + 17 * libraryName.asLibrary._libraryId; |
| return name.hashCode; |
| } |
| } |
| |
| class _PublicName extends Name { |
| @override |
| Reference? get libraryName => null; |
| |
| @override |
| Library? get library => null; |
| |
| @override |
| bool get isPrivate => false; |
| |
| _PublicName(String text) : super._internal(text.hashCode, text); |
| |
| @override |
| String toString() => toStringInternal(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // TYPES |
| // ------------------------------------------------------------------------ |
| |
| /// Represents nullability of a type. |
| enum Nullability { |
| /// Non-legacy types not known to be nullable or non-nullable statically. |
| /// |
| /// An example of such type is type T in the example below. Note that both |
| /// int and int? can be passed in for T, so an attempt to assign null to x is |
| /// a compile-time error as well as assigning x to y. |
| /// |
| /// class A<T extends Object?> { |
| /// foo(T x) { |
| /// x = null; // Compile-time error. |
| /// Object y = x; // Compile-time error. |
| /// } |
| /// } |
| undetermined, |
| |
| /// Nullable types are marked with the '?' modifier. |
| /// |
| /// Null, dynamic, and void are nullable by default. |
| nullable, |
| |
| /// Non-nullable types are types that aren't marked with the '?' modifier. |
| /// |
| /// Note that Null, dynamic, and void that are nullable by default. Note also |
| /// that some types denoted by a type parameter without the '?' modifier can |
| /// be something else rather than non-nullable. |
| nonNullable, |
| |
| /// Types in opt-out libraries are 'legacy' types. |
| /// |
| /// They are both subtypes and supertypes of the nullable and non-nullable |
| /// versions of the type. |
| legacy |
| } |
| |
| /// A syntax-independent notion of a type. |
| /// |
| /// [DartType]s are not AST nodes and may be shared between different parents. |
| /// |
| /// [DartType] objects should be treated as unmodifiable objects, although |
| /// immutability is not enforced for List fields, and [TypeParameter]s are |
| /// cyclic structures that are constructed by mutation. |
| /// |
| /// The `==` operator on [DartType]s compare based on type equality, not |
| /// object identity. |
| abstract class DartType extends Node { |
| const DartType(); |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg); |
| |
| @override |
| bool operator ==(Object other); |
| |
| /// The nullability declared on the type. |
| /// |
| /// For example, the declared nullability of `FutureOr<int?>` is |
| /// [Nullability.nonNullable], the declared nullability of `dynamic` is |
| /// [Nullability.nullable], the declared nullability of `int*` is |
| /// [Nullability.legacy], the declared nullability of the promoted type `X & |
| /// int` where `X extends Object?` |
| /// is [Nullability.undetermined]. |
| Nullability get declaredNullability; |
| |
| /// The nullability of the type as the property to contain null. |
| /// |
| /// For example, nullability-as-property of FutureOr<int?> is |
| /// [Nullability.nullable], nullability-as-property of dynamic is |
| /// [Nullability.nullable], nullability-as-property of int* is |
| /// [Nullability.legacy], nullability-as-property of the promoted type `X & |
| /// int` where `X extends Object?` |
| /// is [Nullability.nonNullable]. |
| Nullability get nullability; |
| |
| /// If this is a typedef type, repeatedly unfolds its type definition until |
| /// the root term is not a typedef type, otherwise returns the type itself. |
| /// |
| /// Will never return a typedef type. |
| DartType get unalias => this; |
| |
| /// If this is a typedef type, unfolds its type definition once, otherwise |
| /// returns the type itself. |
| DartType get unaliasOnce => this; |
| |
| /// Creates a copy of the type with the given [declaredNullability]. |
| /// |
| /// Some types have fixed nullabilities, such as `dynamic`, `invalid-type`, |
| /// `void`, or `bottom`. |
| DartType withDeclaredNullability(Nullability declaredNullability); |
| |
| /// Creates the type corresponding to this type without null, if possible. |
| /// |
| /// Note that not all types, for instance `dynamic`, have a corresponding |
| /// non-nullable type. For these, the type itself is returned. |
| /// |
| /// This corresponds to the `NonNull` function of the nnbd specification. |
| DartType toNonNull() => computeNonNull(this); |
| |
| /// Checks if the type is potentially nullable. |
| /// |
| /// A type is potentially nullable if it's nullable or if its nullability is |
| /// undetermined at compile time. |
| bool get isPotentiallyNullable { |
| return nullability == Nullability.nullable || |
| nullability == Nullability.undetermined; |
| } |
| |
| /// Checks if the type is potentially non-nullable. |
| /// |
| /// A type is potentially non-nullable if it's non-nullable or if its |
| /// nullability is undetermined at compile time. |
| bool get isPotentiallyNonNullable { |
| return nullability == Nullability.nonNullable || |
| nullability == Nullability.undetermined; |
| } |
| |
| /// Returns the non-type parameter type bound of this type. |
| DartType get resolveTypeParameterType; |
| |
| bool equals(Object other, Assumptions? assumptions); |
| |
| /// Returns a textual representation of the this type. |
| /// |
| /// If [verbose] is `true`, qualified names will include the library name/uri. |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeType(this); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer); |
| } |
| |
| /// The type arising from invalid type annotations. |
| /// |
| /// Can usually be treated as 'dynamic', but should occasionally be handled |
| /// differently, e.g. `x is ERROR` should evaluate to false. |
| class InvalidType extends DartType { |
| @override |
| final int hashCode = 12345; |
| |
| const InvalidType(); |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitInvalidType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitInvalidType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) => other is InvalidType; |
| |
| @override |
| Nullability get declaredNullability { |
| // TODO(johnniwinther,cstefantsova): Consider implementing |
| // invalidNullability. |
| return Nullability.legacy; |
| } |
| |
| @override |
| Nullability get nullability { |
| // TODO(johnniwinther,cstefantsova): Consider implementing |
| // invalidNullability. |
| return Nullability.legacy; |
| } |
| |
| @override |
| InvalidType withDeclaredNullability(Nullability declaredNullability) => this; |
| |
| @override |
| String toString() { |
| return "InvalidType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("<invalid>"); |
| } |
| } |
| |
| class DynamicType extends DartType { |
| @override |
| final int hashCode = 54321; |
| |
| const DynamicType(); |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitDynamicType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitDynamicType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) => other is DynamicType; |
| |
| @override |
| Nullability get declaredNullability => Nullability.nullable; |
| |
| @override |
| Nullability get nullability => Nullability.nullable; |
| |
| @override |
| DynamicType withDeclaredNullability(Nullability declaredNullability) => this; |
| |
| @override |
| String toString() { |
| return "DynamicType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("dynamic"); |
| } |
| } |
| |
| class VoidType extends DartType { |
| @override |
| final int hashCode = 123121; |
| |
| const VoidType(); |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitVoidType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitVoidType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) => other is VoidType; |
| |
| @override |
| Nullability get declaredNullability => Nullability.nullable; |
| |
| @override |
| Nullability get nullability => Nullability.nullable; |
| |
| @override |
| VoidType withDeclaredNullability(Nullability declaredNullability) => this; |
| |
| @override |
| String toString() { |
| return "VoidType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("void"); |
| } |
| } |
| |
| class NeverType extends DartType { |
| @override |
| final Nullability declaredNullability; |
| |
| const NeverType.nullable() : this.internal(Nullability.nullable); |
| |
| const NeverType.nonNullable() : this.internal(Nullability.nonNullable); |
| |
| const NeverType.legacy() : this.internal(Nullability.legacy); |
| |
| const NeverType.undetermined() : this.internal(Nullability.undetermined); |
| |
| const NeverType.internal(this.declaredNullability); |
| |
| static NeverType fromNullability(Nullability nullability) { |
| switch (nullability) { |
| case Nullability.nullable: |
| return const NeverType.nullable(); |
| case Nullability.nonNullable: |
| return const NeverType.nonNullable(); |
| case Nullability.legacy: |
| return const NeverType.legacy(); |
| case Nullability.undetermined: |
| return const NeverType.undetermined(); |
| } |
| } |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| int get hashCode { |
| return 485786 ^ ((0x33333333 >> nullability.index) ^ 0x33333333); |
| } |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitNeverType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitNeverType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) => |
| other is NeverType && nullability == other.nullability; |
| |
| @override |
| NeverType withDeclaredNullability(Nullability declaredNullability) { |
| return this.declaredNullability == declaredNullability |
| ? this |
| : NeverType.fromNullability(declaredNullability); |
| } |
| |
| @override |
| String toString() { |
| return "NeverType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("Never"); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| class NullType extends DartType { |
| @override |
| final int hashCode = 415324; |
| |
| const NullType(); |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitNullType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) { |
| return v.visitNullType(this, arg); |
| } |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) => other is NullType; |
| |
| @override |
| Nullability get declaredNullability => Nullability.nullable; |
| |
| @override |
| Nullability get nullability => Nullability.nullable; |
| |
| @override |
| DartType withDeclaredNullability(Nullability nullability) => this; |
| |
| @override |
| String toString() { |
| return "NullType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("Null"); |
| } |
| } |
| |
| class InterfaceType extends DartType { |
| final Reference className; |
| |
| @override |
| final Nullability declaredNullability; |
| |
| final List<DartType> typeArguments; |
| |
| /// The [typeArguments] list must not be modified after this call. If the |
| /// list is omitted, 'dynamic' type arguments are filled in. |
| InterfaceType(Class classNode, Nullability declaredNullability, |
| [List<DartType>? typeArguments]) |
| : this.byReference(classNode.reference, declaredNullability, |
| typeArguments ?? _defaultTypeArguments(classNode)); |
| |
| InterfaceType.byReference( |
| this.className, this.declaredNullability, this.typeArguments) |
| // ignore: unnecessary_null_comparison |
| : assert(declaredNullability != null); |
| |
| Class get classNode => className.asClass; |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| static List<DartType> _defaultTypeArguments(Class classNode) { |
| if (classNode.typeParameters.length == 0) { |
| // Avoid allocating a list in this very common case. |
| return const <DartType>[]; |
| } else { |
| return new List<DartType>.filled( |
| classNode.typeParameters.length, const DynamicType()); |
| } |
| } |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitInterfaceType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitInterfaceType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| classNode.acceptReference(v); |
| visitList(typeArguments, v); |
| } |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) return true; |
| if (other is InterfaceType) { |
| if (nullability != other.nullability) return false; |
| if (className != other.className) return false; |
| if (typeArguments.length != other.typeArguments.length) return false; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| if (!typeArguments[i].equals(other.typeArguments[i], assumptions)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x3fffffff & className.hashCode; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| } |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| @override |
| InterfaceType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new InterfaceType.byReference( |
| className, declaredNullability, typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "InterfaceType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeClassName(className, forType: true); |
| printer.writeTypeArguments(typeArguments); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| /// A possibly generic function type. |
| class FunctionType extends DartType { |
| final List<TypeParameter> typeParameters; |
| final int requiredParameterCount; |
| final List<DartType> positionalParameters; |
| final List<NamedType> namedParameters; // Must be sorted. |
| |
| @override |
| final Nullability declaredNullability; |
| |
| final DartType returnType; |
| |
| @override |
| late final int hashCode = _computeHashCode(); |
| |
| FunctionType(List<DartType> positionalParameters, this.returnType, |
| this.declaredNullability, |
| {this.namedParameters = const <NamedType>[], |
| this.typeParameters = const <TypeParameter>[], |
| int? requiredParameterCount}) |
| : this.positionalParameters = positionalParameters, |
| this.requiredParameterCount = |
| requiredParameterCount ?? positionalParameters.length; |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitFunctionType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitFunctionType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(typeParameters, v); |
| visitList(positionalParameters, v); |
| visitList(namedParameters, v); |
| returnType.accept(v); |
| } |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) { |
| return true; |
| } else if (other is FunctionType) { |
| if (nullability != other.nullability) return false; |
| if (typeParameters.length != other.typeParameters.length || |
| requiredParameterCount != other.requiredParameterCount || |
| positionalParameters.length != other.positionalParameters.length || |
| namedParameters.length != other.namedParameters.length) { |
| return false; |
| } |
| if (typeParameters.isNotEmpty) { |
| assumptions ??= new Assumptions(); |
| for (int index = 0; index < typeParameters.length; index++) { |
| assumptions.assume( |
| typeParameters[index], other.typeParameters[index]); |
| } |
| for (int index = 0; index < typeParameters.length; index++) { |
| if (!typeParameters[index] |
| .bound |
| .equals(other.typeParameters[index].bound, assumptions)) { |
| return false; |
| } |
| } |
| } |
| if (!returnType.equals(other.returnType, assumptions)) { |
| return false; |
| } |
| |
| for (int index = 0; index < positionalParameters.length; index++) { |
| if (!positionalParameters[index] |
| .equals(other.positionalParameters[index], assumptions)) { |
| return false; |
| } |
| } |
| for (int index = 0; index < namedParameters.length; index++) { |
| if (!namedParameters[index] |
| .equals(other.namedParameters[index], assumptions)) { |
| return false; |
| } |
| } |
| if (typeParameters.isNotEmpty) { |
| for (int index = 0; index < typeParameters.length; index++) { |
| assumptions! |
| .forget(typeParameters[index], other.typeParameters[index]); |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /// Returns a variant of this function type that does not declare any type |
| /// parameters. |
| /// |
| /// Any uses of its type parameters become free variables in the returned |
| /// type. |
| FunctionType get withoutTypeParameters { |
| if (typeParameters.isEmpty) return this; |
| return new FunctionType(positionalParameters, returnType, nullability, |
| requiredParameterCount: requiredParameterCount, |
| namedParameters: namedParameters); |
| } |
| |
| /// Looks up the type of the named parameter with the given name. |
| /// |
| /// Returns `null` if there is no named parameter with the given name. |
| DartType? getNamedParameter(String name) { |
| int lower = 0; |
| int upper = namedParameters.length - 1; |
| while (lower <= upper) { |
| int pivot = (lower + upper) ~/ 2; |
| NamedType namedParameter = namedParameters[pivot]; |
| int comparison = name.compareTo(namedParameter.name); |
| if (comparison == 0) { |
| return namedParameter.type; |
| } else if (comparison < 0) { |
| upper = pivot - 1; |
| } else { |
| lower = pivot + 1; |
| } |
| } |
| return null; |
| } |
| |
| int _computeHashCode() { |
| int hash = 1237; |
| hash = 0x3fffffff & (hash * 31 + requiredParameterCount); |
| for (int i = 0; i < typeParameters.length; ++i) { |
| TypeParameter parameter = typeParameters[i]; |
| hash = 0x3fffffff & (hash * 31 + parameter.bound.hashCode); |
| } |
| for (int i = 0; i < positionalParameters.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + positionalParameters[i].hashCode); |
| } |
| for (int i = 0; i < namedParameters.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + namedParameters[i].hashCode); |
| } |
| hash = 0x3fffffff & (hash * 31 + returnType.hashCode); |
| hash = 0x3fffffff & (hash * 31 + nullability.index); |
| return hash; |
| } |
| |
| @override |
| FunctionType withDeclaredNullability(Nullability declaredNullability) { |
| if (declaredNullability == this.declaredNullability) return this; |
| FunctionType result = FunctionType( |
| positionalParameters, returnType, declaredNullability, |
| namedParameters: namedParameters, |
| typeParameters: typeParameters, |
| requiredParameterCount: requiredParameterCount); |
| if (typeParameters.isEmpty) return result; |
| return getFreshTypeParameters(typeParameters).applyToFunctionType(result); |
| } |
| |
| @override |
| String toString() { |
| return "FunctionType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeType(returnType); |
| printer.write(" Function"); |
| printer.writeTypeParameters(typeParameters); |
| printer.write("("); |
| for (int i = 0; i < positionalParameters.length; i++) { |
| if (i > 0) { |
| printer.write(", "); |
| } |
| if (i == requiredParameterCount) { |
| printer.write("["); |
| } |
| printer.writeType(positionalParameters[i]); |
| } |
| if (requiredParameterCount < positionalParameters.length) { |
| printer.write("]"); |
| } |
| |
| if (namedParameters.isNotEmpty) { |
| if (positionalParameters.isNotEmpty) { |
| printer.write(", "); |
| } |
| printer.write("{"); |
| for (int i = 0; i < namedParameters.length; i++) { |
| if (i > 0) { |
| printer.write(", "); |
| } |
| printer.writeNamedType(namedParameters[i]); |
| } |
| printer.write("}"); |
| } |
| printer.write(")"); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| /// A use of a [Typedef] as a type. |
| /// |
| /// The underlying type can be extracted using [unalias]. |
| class TypedefType extends DartType { |
| @override |
| final Nullability declaredNullability; |
| final Reference typedefReference; |
| final List<DartType> typeArguments; |
| |
| TypedefType(Typedef typedef, Nullability nullability, |
| [List<DartType>? typeArguments]) |
| : this.byReference(typedef.reference, nullability, |
| typeArguments ?? const <DartType>[]); |
| |
| TypedefType.byReference( |
| this.typedefReference, this.declaredNullability, this.typeArguments); |
| |
| Typedef get typedefNode => typedefReference.asTypedef; |
| |
| // TODO(cstefantsova): Replace with uniteNullabilities(declaredNullability, |
| // typedefNode.type.nullability). |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => unalias.resolveTypeParameterType; |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitTypedefType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitTypedefType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(typeArguments, v); |
| v.visitTypedefReference(typedefNode); |
| } |
| |
| @override |
| DartType get unaliasOnce { |
| DartType result = |
| Substitution.fromTypedefType(this).substituteType(typedefNode.type!); |
| return result.withDeclaredNullability( |
| combineNullabilitiesForSubstitution(result.nullability, nullability)); |
| } |
| |
| @override |
| DartType get unalias { |
| return unaliasOnce.unalias; |
| } |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) { |
| return true; |
| } else if (other is TypedefType) { |
| if (nullability != other.nullability) return false; |
| if (typedefReference != other.typedefReference || |
| typeArguments.length != other.typeArguments.length) { |
| return false; |
| } |
| for (int i = 0; i < typeArguments.length; ++i) { |
| if (!typeArguments[i].equals(other.typeArguments[i], assumptions)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x3fffffff & typedefNode.hashCode; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| } |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| @override |
| TypedefType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new TypedefType.byReference( |
| typedefReference, declaredNullability, typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "TypedefType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypedefName(typedefReference); |
| printer.writeTypeArguments(typeArguments); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| class FutureOrType extends DartType { |
| final DartType typeArgument; |
| |
| @override |
| final Nullability declaredNullability; |
| |
| FutureOrType(this.typeArgument, this.declaredNullability); |
| |
| @override |
| Nullability get nullability { |
| return uniteNullabilities(typeArgument.nullability, declaredNullability); |
| } |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitFutureOrType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) { |
| return v.visitFutureOrType(this, arg); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| } |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) return true; |
| if (other is FutureOrType) { |
| if (declaredNullability != other.declaredNullability) return false; |
| if (!typeArgument.equals(other.typeArgument, assumptions)) { |
| return false; |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x12345678; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArgument.hashCode)); |
| int nullabilityHash = |
| (0x33333333 >> declaredNullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| @override |
| FutureOrType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new FutureOrType(typeArgument, declaredNullability); |
| } |
| |
| @override |
| String toString() { |
| return "FutureOrType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("FutureOr<"); |
| printer.writeType(typeArgument); |
| printer.write(">"); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| class ExtensionType extends DartType { |
| final Reference extensionReference; |
| |
| @override |
| final Nullability declaredNullability; |
| |
| final List<DartType> typeArguments; |
| |
| DartType? _onType; |
| |
| ExtensionType(Extension extensionNode, Nullability declaredNullability, |
| [List<DartType>? typeArguments]) |
| : this.byReference(extensionNode.reference, declaredNullability, |
| typeArguments ?? _defaultTypeArguments(extensionNode)); |
| |
| ExtensionType.byReference( |
| this.extensionReference, this.declaredNullability, this.typeArguments) |
| // ignore: unnecessary_null_comparison |
| : assert(declaredNullability != null); |
| |
| Extension get extension => extensionReference.asExtension; |
| |
| DartType get onType => |
| _onType ??= _computeOnType(extensionReference, typeArguments); |
| |
| @override |
| Nullability get nullability { |
| return uniteNullabilities( |
| declaredNullability, extension.onType.nullability); |
| } |
| |
| @override |
| DartType get resolveTypeParameterType => onType.resolveTypeParameterType; |
| |
| static List<DartType> _defaultTypeArguments(Extension extensionNode) { |
| if (extensionNode.typeParameters.length == 0) { |
| // Avoid allocating a list in this very common case. |
| return const <DartType>[]; |
| } else { |
| return new List<DartType>.filled( |
| extensionNode.typeParameters.length, const DynamicType()); |
| } |
| } |
| |
| static DartType _computeOnType( |
| Reference extensionName, List<DartType> typeArguments) { |
| Extension extensionNode = extensionName.asExtension; |
| if (extensionNode.typeParameters.isEmpty) { |
| return extensionNode.onType; |
| } else { |
| assert(extensionNode.typeParameters.length == typeArguments.length); |
| return Substitution.fromPairs(extensionNode.typeParameters, typeArguments) |
| .substituteType(extensionNode.onType); |
| } |
| } |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) { |
| return v.visitExtensionType(this); |
| } |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) { |
| return v.visitExtensionType(this, arg); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| extension.acceptReference(v); |
| visitList(typeArguments, v); |
| } |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) return true; |
| if (other is ExtensionType) { |
| if (nullability != other.nullability) return false; |
| if (extensionReference != other.extensionReference) return false; |
| if (typeArguments.length != other.typeArguments.length) return false; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| if (!typeArguments[i].equals(other.typeArguments[i], assumptions)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x3fffffff & extensionReference.hashCode; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| } |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| @override |
| ExtensionType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new ExtensionType.byReference( |
| extensionReference, declaredNullability, typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "ExtensionType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeExtensionName(extensionReference); |
| printer.writeTypeArguments(typeArguments); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| class InlineType extends DartType { |
| final Reference inlineClassReference; |
| |
| @override |
| final Nullability declaredNullability; |
| |
| final List<DartType> typeArguments; |
| |
| DartType? _instantiatedRepresentationType; |
| |
| InlineType(InlineClass inlineClass, Nullability declaredNullability, |
| [List<DartType>? typeArguments]) |
| : this.byReference(inlineClass.reference, declaredNullability, |
| typeArguments ?? _defaultTypeArguments(inlineClass)); |
| |
| InlineType.byReference( |
| this.inlineClassReference, this.declaredNullability, this.typeArguments, |
| [this._instantiatedRepresentationType]) |
| // ignore: unnecessary_null_comparison |
| : assert(declaredNullability != null); |
| |
| InlineClass get inlineClass => inlineClassReference.asInlineClass; |
| |
| DartType get instantiatedRepresentationType => |
| _instantiatedRepresentationType ??= _computeRepresentationType( |
| inlineClassReference, typeArguments, declaredNullability); |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => |
| instantiatedRepresentationType.resolveTypeParameterType; |
| |
| static List<DartType> _defaultTypeArguments(InlineClass inlineClass) { |
| if (inlineClass.typeParameters.length == 0) { |
| // Avoid allocating a list in this very common case. |
| return const <DartType>[]; |
| } else { |
| return new List<DartType>.filled( |
| inlineClass.typeParameters.length, const DynamicType()); |
| } |
| } |
| |
| static DartType _computeRepresentationType(Reference inlineClassReference, |
| List<DartType> typeArguments, Nullability declaredNullability) { |
| InlineClass inlineClass = inlineClassReference.asInlineClass; |
| if (inlineClass.typeParameters.isEmpty) { |
| return inlineClass.declaredRepresentationType; |
| } else { |
| assert(inlineClass.typeParameters.length == typeArguments.length); |
| return Substitution.fromPairs(inlineClass.typeParameters, typeArguments) |
| .substituteType(inlineClass.declaredRepresentationType) |
| .withDeclaredNullability(uniteNullabilities(declaredNullability, |
| inlineClass.declaredRepresentationType.nullability)); |
| } |
| } |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) { |
| return v.visitInlineType(this); |
| } |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) { |
| return v.visitInlineType(this, arg); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| inlineClass.acceptReference(v); |
| visitList(typeArguments, v); |
| } |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) return true; |
| if (other is InlineType) { |
| if (nullability != other.nullability) return false; |
| if (inlineClassReference != other.inlineClassReference) return false; |
| if (typeArguments.length != other.typeArguments.length) return false; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| if (!typeArguments[i].equals(other.typeArguments[i], assumptions)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x3fffffff & inlineClassReference.hashCode; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| } |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| @override |
| InlineType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new InlineType.byReference( |
| inlineClassReference, declaredNullability, typeArguments); |
| } |
| |
| @override |
| String toString() { |
| return "InlineType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeInlineClassName(inlineClassReference); |
| printer.writeTypeArguments(typeArguments); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| /// A named parameter in [FunctionType]. |
| class NamedType extends Node implements Comparable<NamedType> { |
| // Flag used for serialization if [isRequired]. |
| static const int FlagRequiredNamedType = 1 << 0; |
| |
| final String name; |
| final DartType type; |
| final bool isRequired; |
| |
| const NamedType(this.name, this.type, {this.isRequired = false}); |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| bool equals(Object other, Assumptions? assumptions) { |
| return other is NamedType && |
| name == other.name && |
| isRequired == other.isRequired && |
| type.equals(other.type, assumptions); |
| } |
| |
| @override |
| int get hashCode { |
| return name.hashCode * 31 + type.hashCode * 37 + isRequired.hashCode * 41; |
| } |
| |
| @override |
| int compareTo(NamedType other) => name.compareTo(other.name); |
| |
| @override |
| R accept<R>(Visitor<R> v) => v.visitNamedType(this); |
| |
| @override |
| R accept1<R, A>(Visitor1<R, A> v, A arg) => v.visitNamedType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| type.accept(v); |
| } |
| |
| @override |
| String toString() { |
| return "NamedType(${toStringInternal()})"; |
| } |
| |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeNamedType(this); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| if (isRequired) { |
| printer.write("required "); |
| } |
| printer.write(name); |
| printer.write(': '); |
| printer.writeType(type); |
| } |
| } |
| |
| class IntersectionType extends DartType { |
| final TypeParameterType left; |
| final DartType right; |
| |
| IntersectionType(this.left, this.right) { |
| // TODO(cstefantsova): Also assert that [rhs] is a subtype of [lhs.bound]. |
| |
| Nullability leftNullability = left.nullability; |
| Nullability rightNullability = right.nullability; |
| assert( |
| (leftNullability == Nullability.nonNullable && |
| rightNullability == Nullability.nonNullable) || |
| (leftNullability == Nullability.nonNullable && |
| rightNullability == Nullability.undetermined) || |
| (leftNullability == Nullability.legacy && |
| rightNullability == Nullability.legacy) || |
| (leftNullability == Nullability.undetermined && |
| rightNullability == Nullability.nonNullable) || |
| (leftNullability == Nullability.undetermined && |
| rightNullability == Nullability.nullable) || |
| (leftNullability == Nullability.undetermined && |
| rightNullability == Nullability.undetermined) |
| // These are observed in real situations: |
| || |
| // pkg/front_end/test/id_tests/type_promotion_test |
| // replicated in nnbd_mixed/type_parameter_nullability |
| (leftNullability == Nullability.nullable && |
| rightNullability == Nullability.nonNullable) || |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/incremental_hello_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/tool/fasta_perf_test |
| // nnbd/issue42089 |
| // replicated in nnbd_mixed/type_parameter_nullability |
| (leftNullability == Nullability.nullable && |
| rightNullability == Nullability.nullable) || |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/test/dill_round_trip_test |
| // pkg/front_end/test/compile_dart2js_with_no_sdk_test |
| // pkg/front_end/test/fasta/types/large_app_benchmark_test |
| // pkg/front_end/test/incremental_dart2js_test |
| // pkg/front_end/test/read_dill_from_binary_md_test |
| // pkg/front_end/test/static_types/static_type_test |
| // pkg/front_end/test/split_dill_test |
| // pkg/front_end/tool/incremental_perf_test |
| // pkg/vm/test/kernel_front_end_test |
| // general/promoted_null_aware_access |
| // inference/constructors_infer_from_arguments_factory |
| // inference/infer_types_on_loop_indices_for_each_loop |
| // inference/infer_types_on_loop_indices_for_each_loop_async |
| // replicated in nnbd_mixed/type_parameter_nullability |
| (leftNullability == Nullability.legacy && |
| rightNullability == Nullability.nonNullable) || |
| // pkg/front_end/test/fasta/incremental_hello_test |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/tool/fasta_perf_test |
| // replicated in nnbd_mixed/type_parameter_nullability |
| (leftNullability == Nullability.nullable && |
| rightNullability == Nullability.undetermined) || |
| // These are only observed in tests and might be artifacts of the |
| // tests rather than real situations: |
| // |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| (leftNullability == Nullability.legacy && |
| rightNullability == Nullability.nullable) || |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| (leftNullability == Nullability.nonNullable && |
| rightNullability == Nullability.nullable) || |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| (leftNullability == Nullability.undetermined && |
| rightNullability == Nullability.legacy) || |
| // pkg/kernel/test/clone_test |
| // The legacy nullability is due to RHS being InvalidType. |
| (leftNullability == Nullability.nonNullable && |
| rightNullability == Nullability.legacy), |
| "Unexpected nullabilities for ${left} & ${right}: " |
| "leftNullability = ${leftNullability}, " |
| "rightNullability = ${rightNullability}."); |
| } |
| |
| @override |
| DartType get resolveTypeParameterType => right.resolveTypeParameterType; |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitIntersectionType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitIntersectionType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| left.accept(v); |
| right.accept(v); |
| } |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) { |
| return true; |
| } else if (other is IntersectionType) { |
| return left.equals(other.left, assumptions) && |
| right.equals(other.right, assumptions); |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| int hash = nullabilityHash; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ left.hashCode)); |
| hash = 0x3fffffff & (hash * 31 + (hash ^ right.hashCode)); |
| return hash; |
| } |
| |
| /// Computes the nullability of [IntersectionType] from its parts. |
| /// |
| /// [nullability] is calculated from [left.nullability] and |
| /// [right.nullability]. |
| /// |
| /// In the following program the nullability of `x` is |
| /// [Nullability.undetermined] because it's copied from that of `bar`. The |
| /// nullability of `y` is [Nullability.nonNullable] because its type is an |
| /// intersection type where the LHS is `T` and the RHS is the promoted type |
| /// `int`. The nullability of the type of `y` is computed from the |
| /// nullabilities of those two types. |
| /// |
| /// class A<T extends Object?> { |
| /// foo(T bar) { |
| /// var x = bar; |
| /// if (bar is int) { |
| /// var y = bar; |
| /// } |
| /// } |
| /// } |
| /// |
| /// The method combines the nullabilities of [left] and [right] to yield the |
| /// nullability of the intersection type. |
| @override |
| Nullability get nullability { |
| // Note that RHS is always a subtype of the bound of the type parameter. |
| |
| // The code below implements the rule for the nullability of an |
| // intersection type as per the following table: |
| // |
| // | LHS \ RHS | ! | ? | * | % | |
| // |-----------|-----|-----|-----|-----| |
| // | ! | ! | + | N/A | ! | |
| // | ? | (!) | (?) | N/A | (%) | |
| // | * | (*) | + | * | N/A | |
| // | % | ! | % | + | % | |
| // |
| // In the table, LHS corresponds to [lhsNullability] in the code below; RHS |
| // corresponds to [rhsNullability]; !, ?, *, and % correspond to |
| // nonNullable, nullable, legacy, and undetermined values of the |
| // Nullability enum. |
| |
| Nullability lhsNullability = left.nullability; |
| Nullability rhsNullability = right.nullability; |
| assert( |
| (lhsNullability == Nullability.nonNullable && |
| rhsNullability == Nullability.nonNullable) || |
| (lhsNullability == Nullability.nonNullable && |
| rhsNullability == Nullability.undetermined) || |
| (lhsNullability == Nullability.legacy && |
| rhsNullability == Nullability.legacy) || |
| (lhsNullability == Nullability.undetermined && |
| rhsNullability == Nullability.nonNullable) || |
| (lhsNullability == Nullability.undetermined && |
| rhsNullability == Nullability.nullable) || |
| (lhsNullability == Nullability.undetermined && |
| rhsNullability == Nullability.undetermined) |
| // Apparently these happens as well: |
| || |
| // pkg/front_end/test/id_tests/type_promotion_test |
| (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.nonNullable) || |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/incremental_hello_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/tool/fasta_perf_test |
| // nnbd/issue42089 |
| (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.nullable) || |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/test/dill_round_trip_test |
| // pkg/front_end/test/compile_dart2js_with_no_sdk_test |
| // pkg/front_end/test/fasta/types/large_app_benchmark_test |
| // pkg/front_end/test/incremental_dart2js_test |
| // pkg/front_end/test/read_dill_from_binary_md_test |
| // pkg/front_end/test/static_types/static_type_test |
| // pkg/front_end/test/split_dill_test |
| // pkg/front_end/tool/incremental_perf_test |
| // pkg/vm/test/kernel_front_end_test |
| // general/promoted_null_aware_access |
| // inference/constructors_infer_from_arguments_factory |
| // inference/infer_types_on_loop_indices_for_each_loop |
| // inference/infer_types_on_loop_indices_for_each_loop_async |
| (lhsNullability == Nullability.legacy && |
| rhsNullability == Nullability.nonNullable) || |
| // pkg/front_end/test/fasta/incremental_hello_test |
| // pkg/front_end/test/explicit_creation_test |
| // pkg/front_end/tool/fasta_perf_test |
| // pkg/front_end/test/fasta/incremental_hello_test |
| (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.undetermined) || |
| |
| // This is created but never observed. |
| // (lhsNullability == Nullability.legacy && |
| // rhsNullability == Nullability.nullable) || |
| |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| (lhsNullability == Nullability.undetermined && |
| rhsNullability == Nullability.legacy) || |
| // pkg/front_end/test/fasta/types/kernel_type_parser_test |
| // pkg/front_end/test/fasta/types/fasta_types_test |
| (lhsNullability == Nullability.nonNullable && |
| rhsNullability == Nullability.nullable), |
| "Unexpected nullabilities for: LHS nullability = $lhsNullability, " |
| "RHS nullability = ${rhsNullability}."); |
| |
| // Whenever there's N/A in the table, it means that the corresponding |
| // combination of the LHS and RHS nullability is not possible when |
| // compiling from Dart source files, so we can define it to be whatever is |
| // faster and more convenient to implement. The verifier should check that |
| // the cases marked as N/A never occur in the output of the CFE. |
| // |
| // The code below uses the following extension of the table function: |
| // |
| // | LHS \ RHS | ! | ? | * | % | |
| // |-----------|-----|-----|-----|-----| |
| // | ! | ! | ! | ! | ! | |
| // | ? | (!) | (?) | * | (%) | |
| // | * | (*) | * | * | % | |
| // | % | ! | % | % | % | |
| |
| if (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.nonNullable) { |
| return Nullability.nonNullable; |
| } |
| |
| if (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.nullable) { |
| return Nullability.nullable; |
| } |
| |
| if (lhsNullability == Nullability.legacy && |
| rhsNullability == Nullability.nonNullable) { |
| return Nullability.legacy; |
| } |
| |
| if (lhsNullability == Nullability.nullable && |
| rhsNullability == Nullability.undetermined) { |
| return Nullability.undetermined; |
| } |
| |
| // Intersection with a non-nullable type always yields a non-nullable type, |
| // as it's the most restrictive kind of types. |
| if (lhsNullability == Nullability.nonNullable || |
| rhsNullability == Nullability.nonNullable) { |
| return Nullability.nonNullable; |
| } |
| |
| // If the nullability of LHS is 'undetermined', the nullability of the |
| // intersection is also 'undetermined' if RHS is 'undetermined' or |
| // nullable. |
| // |
| // Consider the following example: |
| // |
| // class A<X extends Object?, Y extends X> { |
| // foo(X x) { |
| // if (x is Y) { |
| // x = null; // Compile-time error. Consider X = Y = int. |
| // Object a = x; // Compile-time error. Consider X = Y = int?. |
| // } |
| // if (x is int?) { |
| // x = null; // Compile-time error. Consider X = int. |
| // Object b = x; // Compile-time error. Consider X = int?. |
| // } |
| // } |
| // } |
| if (lhsNullability == Nullability.undetermined || |
| rhsNullability == Nullability.undetermined) { |
| return Nullability.undetermined; |
| } |
| |
| return Nullability.legacy; |
| } |
| |
| @override |
| Nullability get declaredNullability => nullability; |
| |
| @override |
| IntersectionType withDeclaredNullability(Nullability declaredNullability) { |
| if (left.declaredNullability == declaredNullability) { |
| return this; |
| } |
| TypeParameterType newLeft = |
| left.withDeclaredNullability(declaredNullability); |
| if (identical(newLeft, left)) { |
| return this; |
| } |
| return new IntersectionType(newLeft, right); |
| } |
| |
| @override |
| String toString() { |
| return "IntersectionType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('('); |
| printer.writeType(left); |
| printer.write(" & "); |
| printer.writeType(right); |
| printer.write(')'); |
| printer.writeNullability(nullability); |
| } |
| } |
| |
| /// Reference to a type variable. |
| /// |
| /// A type variable has an optional bound because type promotion can change the |
| /// bound. A bound of `null` indicates that the bound has not been promoted and |
| /// is the same as the [TypeParameter]'s bound. This allows one to detect |
| /// whether the bound has been promoted. The case of promoted bound can be |
| /// viewed as representing an intersection type between the type-parameter type |
| /// and the promoted bound. |
| class TypeParameterType extends DartType { |
| /// The declared nullability of a type-parameter type. |
| /// |
| /// When a [TypeParameterType] represents an intersection, |
| /// [declaredNullability] is the nullability of the left-hand side. |
| @override |
| Nullability declaredNullability; |
| |
| TypeParameter parameter; |
| |
| TypeParameterType(this.parameter, this.declaredNullability); |
| |
| /// Creates a type-parameter type to be used in alpha-renaming. |
| /// |
| /// The constructed type object is supposed to be used as a value in a |
| /// substitution map created to perform an alpha-renaming from parameter |
| /// [from] to parameter [to] on a generic type. The resulting type-parameter |
| /// type is an occurrence of [to] as a type, but the nullability property is |
| /// derived from the bound of [from]. It allows to assign the bound to [to] |
| /// after the desired alpha-renaming is performed, which is often the case. |
| TypeParameterType.forAlphaRenaming(TypeParameter from, TypeParameter to) |
| : this(to, computeNullabilityFromBound(from)); |
| |
| /// Creates a type-parameter type with default nullability for the library. |
| /// |
| /// The nullability is computed as if the programmer omitted the modifier. It |
| /// means that in the opt-out libraries `Nullability.legacy` will be used, and |
| /// in opt-in libraries either `Nullability.nonNullable` or |
| /// `Nullability.undetermined` will be used, depending on the nullability of |
| /// the bound of [parameter]. |
| TypeParameterType.withDefaultNullabilityForLibrary( |
| this.parameter, Library library) |
| : declaredNullability = library.isNonNullableByDefault |
| ? computeNullabilityFromBound(parameter) |
| : Nullability.legacy; |
| |
| @override |
| DartType get resolveTypeParameterType => bound.resolveTypeParameterType; |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) => v.visitTypeParameterType(this); |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) => |
| v.visitTypeParameterType(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) { |
| return true; |
| } else if (other is TypeParameterType) { |
| if (nullability != other.nullability) return false; |
| if (parameter != other.parameter) { |
| if (parameter.parent == null) { |
| // Function type parameters are also equal by assumption. |
| if (assumptions == null) { |
| return false; |
| } |
| if (!assumptions.isAssumed(parameter, other.parameter)) { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| // TODO(johnniwinther): Since we use a unification strategy for function |
| // type parameter equality, we have to assume they can end up being |
| // equal. Maybe we should change the equality strategy. |
| int hash = parameter.isFunctionTypeTypeParameter ? 0 : parameter.hashCode; |
| int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash)); |
| return hash; |
| } |
| |
| /// A quick access to the bound of the parameter. |
| DartType get bound => parameter.bound; |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| /// Gets a new [TypeParameterType] with given [typeParameterTypeNullability]. |
| /// |
| /// In contrast with other types, [TypeParameterType.withDeclaredNullability] |
| /// doesn't set the overall nullability of the returned type but sets that of |
| /// the left-hand side of the intersection type. In case [promotedBound] is |
| /// null, it is an equivalent of setting the overall nullability. |
| @override |
| TypeParameterType withDeclaredNullability(Nullability declaredNullability) { |
| if (declaredNullability == this.declaredNullability) { |
| return this; |
| } |
| return new TypeParameterType(parameter, declaredNullability); |
| } |
| |
| /// Gets the nullability of a type-parameter type based on the bound. |
| /// |
| /// This is a helper function to be used when the bound of the type parameter |
| /// is changing or is being set for the first time, and the update on some |
| /// type-parameter types is required. |
| static Nullability computeNullabilityFromBound(TypeParameter typeParameter) { |
| // If the bound is nullable or 'undetermined', both nullable and |
| // non-nullable types can be passed in for the type parameter, making the |
| // corresponding type parameter types 'undetermined.' Otherwise, the |
| // nullability matches that of the bound. |
| DartType bound = typeParameter.bound; |
| if (identical(bound, TypeParameter.unsetBoundSentinel)) { |
| throw new StateError("Can't compute nullability from an absent bound."); |
| } |
| |
| // If a type parameter's nullability depends on itself, it is deemed |
| // 'undetermined'. Currently, it's possible if the type parameter has a |
| // possibly nested FutureOr containing that type parameter. If there are |
| // other ways for such a dependency to exist, they should be checked here. |
| bool nullabilityDependsOnItself = false; |
| { |
| DartType type = typeParameter.bound; |
| while (type is FutureOrType) { |
| type = type.typeArgument; |
| } |
| if (type is TypeParameterType && type.parameter == typeParameter) { |
| nullabilityDependsOnItself = true; |
| } |
| } |
| if (nullabilityDependsOnItself) { |
| return Nullability.undetermined; |
| } |
| |
| Nullability boundNullability = |
| bound is InvalidType ? Nullability.undetermined : bound.nullability; |
| return boundNullability == Nullability.nullable || |
| boundNullability == Nullability.undetermined |
| ? Nullability.undetermined |
| : boundNullability; |
| } |
| |
| @override |
| String toString() { |
| return "TypeParameterType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypeParameterName(parameter); |
| printer.writeNullability(declaredNullability); |
| } |
| } |
| |
| class RecordType extends DartType { |
| final List<DartType> positional; |
| final List<NamedType> named; |
| |
| @override |
| final Nullability declaredNullability; |
| |
| RecordType(this.positional, this.named, this.declaredNullability) |
| : /*TODO(johnniwinther): Enabled this assert: |
| assert(named.length == named.map((p) => p.name).toSet().length, |
| "Named field types must have unique names in a RecordType: " |
| "${named}"),*/ |
| assert(() { |
| // Assert that the named field types are sorted. |
| for (int i = 1; i < named.length; i++) { |
| if (named[i].name.compareTo(named[i - 1].name) < 0) { |
| return false; |
| } |
| } |
| return true; |
| }(), |
| "Named field types aren't sorted lexicographically " |
| "in a RecordType: ${named}"); |
| |
| @override |
| Nullability get nullability => declaredNullability; |
| |
| @override |
| DartType get resolveTypeParameterType => this; |
| |
| @override |
| R accept<R>(DartTypeVisitor<R> v) { |
| return v.visitRecordType(this); |
| } |
| |
| @override |
| R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) { |
| return v.visitRecordType(this, arg); |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(positional, v); |
| visitList(named, v); |
| } |
| |
| @override |
| bool operator ==(Object other) => equals(other, null); |
| |
| @override |
| bool equals(Object other, Assumptions? assumptions) { |
| if (identical(this, other)) { |
| return true; |
| } else if (other is RecordType) { |
| if (nullability != other.nullability) return false; |
| if (positional.length != other.positional.length) return false; |
| if (named.length != other.named.length) return false; |
| for (int index = 0; index < positional.length; index++) { |
| if (!positional[index].equals(other.positional[index], assumptions)) { |
| return false; |
| } |
| } |
| for (int index = 0; index < named.length; index++) { |
| if (!named[index].equals(other.named[index], assumptions)) { |
| return false; |
| } |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 1237; |
| for (int i = 0; i < positional.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + positional[i].hashCode); |
| } |
| for (int i = 0; i < named.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + named[i].hashCode); |
| } |
| hash = 0x3fffffff & (hash * 31 + nullability.index); |
| return hash; |
| } |
| |
| @override |
| RecordType withDeclaredNullability(Nullability declaredNullability) { |
| return declaredNullability == this.declaredNullability |
| ? this |
| : new RecordType(this.positional, this.named, declaredNullability); |
| } |
| |
| @override |
| String toString() { |
| return "RecordType(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("("); |
| printer.writeTypes(positional); |
| if (named.isNotEmpty) { |
| if (positional.isNotEmpty) { |
| printer.write(", "); |
| } |
| printer.write("{"); |
| for (int i = 0; i < named.length; i++) { |
| if (i > 0) { |
| printer.write(", "); |
| } |
| printer.writeType(named[i].type); |
| printer.write(' '); |
| printer.write(named[i].name); |
| } |
| printer.write("}"); |
| } |
| printer.write(")"); |
| } |
| } |
| |
| /// Value set for variance of a type parameter X in a type term T. |
| class Variance { |
| /// Used when X does not occur free in T. |
| static const int unrelated = 0; |
| |
| /// Used when X occurs free in T, and U <: V implies [U/X]T <: [V/X]T. |
| static const int covariant = 1; |
| |
| /// Used when X occurs free in T, and U <: V implies [V/X]T <: [U/X]T. |
| static const int contravariant = 2; |
| |
| /// Used when there exists a pair U and V such that U <: V, but [U/X]T and |
| /// [V/X]T are incomparable. |
| static const int invariant = 3; |
| |
| /// Variance values form a lattice where [unrelated] is the top, [invariant] |
| /// is the bottom, and [covariant] and [contravariant] are incomparable. |
| /// [meet] calculates the meet of two elements of such lattice. It can be |
| /// used, for example, to calculate the variance of a typedef type parameter |
| /// if it's encountered on the r.h.s. of the typedef multiple times. |
| static int meet(int a, int b) => a | b; |
| |
| /// Combines variances of X in T and Y in S into variance of X in [Y/T]S. |
| /// |
| /// Consider the following examples: |
| /// |
| /// * variance of X in Function(X) is [contravariant], variance of Y in |
| /// List<Y> is [covariant], so variance of X in List<Function(X)> is |
| /// [contravariant]; |
| /// |
| /// * variance of X in List<X> is [covariant], variance of Y in Function(Y) is |
| /// [contravariant], so variance of X in Function(List<X>) is [contravariant]; |
| /// |
| /// * variance of X in Function(X) is [contravariant], variance of Y in |
| /// Function(Y) is [contravariant], so variance of X in Function(Function(X)) |
| /// is [covariant]; |
| /// |
| /// * let the following be declared: |
| /// |
| /// typedef F<Z> = Function(); |
| /// |
| /// then variance of X in F<X> is [unrelated], variance of Y in List<Y> is |
| /// [covariant], so variance of X in List<F<X>> is [unrelated]; |
| /// |
| /// * let the following be declared: |
| /// |
| /// typedef G<Z> = Z Function(Z); |
| /// |
| /// then variance of X in List<X> is [covariant], variance of Y in G<Y> is |
| /// [invariant], so variance of `X` in `G<List<X>>` is [invariant]. |
| static int combine(int a, int b) { |
| if (a == unrelated || b == unrelated) return unrelated; |
| if (a == invariant || b == invariant) return invariant; |
| return a == b ? covariant : contravariant; |
| } |
| |
| /// Returns true if [a] is greater than (above) [b] in the partial order |
| /// induced by the variance lattice. |
| static bool greaterThan(int a, int b) { |
| return greaterThanOrEqual(a, b) && a != b; |
| } |
| |
| /// Returns true if [a] is greater than (above) or equal to [b] in the |
| /// partial order induced by the variance lattice. |
| static bool greaterThanOrEqual(int a, int b) { |
| return meet(a, b) == b; |
| } |
| |
| /// Returns true if [a] is less than (below) [b] in the partial order |
| /// induced by the variance lattice. |
| static bool lessThan(int a, int b) { |
| return lessThanOrEqual(a, b) && a != b; |
| } |
| |
| /// Returns true if [a] is less than (below) or equal to [b] in the |
| /// partial order induced by the variance lattice. |
| static bool lessThanOrEqual(int a, int b) { |
| return meet(a, b) == a; |
| } |
| |
| static int fromString(String variance) { |
| if (variance == "in") { |
| return contravariant; |
| } else if (variance == "inout") { |
| return invariant; |
| } else if (variance == "out") { |
| return covariant; |
| } else { |
| return unrelated; |
| } |
| } |
| |
| // Returns the keyword lexeme associated with the variance given. |
| static String keywordString(int variance) { |
| switch (variance) { |
| case Variance.contravariant: |
| return 'in'; |
| case Variance.invariant: |
| return 'inout'; |
| case Variance.covariant: |
| default: |
| return 'out'; |
| } |
| } |
| } |
| |
| /// Declaration of a type variable. |
| /// |
| /// Type parameters declared in a [Class] or [FunctionNode] are part of the AST, |
| /// have a parent pointer to its declaring class or function, and will be seen |
| /// by tree visitors. |
| /// |
| /// Type parameters declared by a [FunctionType] are orphans and have a `null` |
| /// parent pointer. [TypeParameter] objects should not be shared between |
| /// different [FunctionType] objects. |
| class TypeParameter extends TreeNode implements Annotatable { |
| int flags = 0; |
| |
| /// List of metadata annotations on the type parameter. |
| /// |
| /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| /// annotations if needed. |
| @override |
| List<Expression> annotations = const <Expression>[]; |
| |
| String? name; // Cosmetic name. |
| |
| /// Sentinel value used for the [bound] that has not yet been computed. This |
| /// is needed to make the [bound] field non-nullable while supporting |
| /// recursive bounds. |
| static final DartType unsetBoundSentinel = new InvalidType(); |
| |
| /// The bound on the type variable. |
| /// |
| /// This is set to [unsetBoundSentinel] temporarily during IR construction. |
| /// This is set to the `Object?` for type parameters without an explicit |
| /// bound. |
| DartType bound; |
| |
| /// Sentinel value used for the [defaultType] that has not yet been computed. |
| /// This is needed to make the [defaultType] field non-nullable while |
| /// supporting recursive bounds for which the default type need to be set |
| /// late. |
| static final DartType unsetDefaultTypeSentinel = new InvalidType(); |
| |
| /// The default value of the type variable. It is used to provide the |
| /// corresponding missing type argument in type annotations and as the |
| /// fall-back type value in type inference at compile time. At run time, |
| /// [defaultType] is used by the backends in place of the missing type |
| /// argument of a dynamic invocation of a generic function. |
| DartType defaultType; |
| |
| /// Describes variance of the type parameter w.r.t. declaration on which it is |
| /// defined. For classes, if variance is not explicitly set, the type |
| /// parameter has legacy covariance defined by [isLegacyCovariant] which |
| /// on the lattice is equivalent to [Variance.covariant]. For typedefs, it's |
| /// the variance of the type parameters in the type term on the r.h.s. of the |
| /// typedef. |
| int? _variance; |
| |
| int get variance => _variance ?? Variance.covariant; |
| |
| void set variance(int? newVariance) => _variance = newVariance; |
| |
| bool get isLegacyCovariant => _variance == null; |
| |
| static const int legacyCovariantSerializationMarker = 4; |
| |
| TypeParameter([this.name, DartType? bound, DartType? defaultType]) |
| : bound = bound ?? unsetBoundSentinel, |
| defaultType = defaultType ?? unsetDefaultTypeSentinel; |
| |
| // Must match serialized bit positions. |
| static const int FlagCovariantByClass = 1 << 0; |
| |
| /// If this [TypeParameter] is a type parameter of a generic method, indicates |
| /// whether the method implementation needs to contain a runtime type check to |
| /// deal with generic covariance. |
| /// |
| /// When `true`, runtime checks may need to be performed. |
| bool get isCovariantByClass => flags & FlagCovariantByClass != 0; |
| |
| void set isCovariantByClass(bool value) { |
| flags = value |
| ? (flags | FlagCovariantByClass) |
| : (flags & ~FlagCovariantByClass); |
| } |
| |
| @override |
| void addAnnotation(Expression annotation) { |
| if (annotations.isEmpty) { |
| annotations = <Expression>[]; |
| } |
| annotations.add(annotation..parent = this); |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitTypeParameter(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => |
| v.visitTypeParameter(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(annotations, v); |
| bound.accept(v); |
| defaultType.accept(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(annotations, this); |
| // ignore: unnecessary_null_comparison |
| if (bound != null) { |
| bound = v.visitDartType(bound); |
| } |
| // ignore: unnecessary_null_comparison |
| if (defaultType != null) { |
| defaultType = v.visitDartType(defaultType); |
| } |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformExpressionList(annotations, this); |
| // ignore: unnecessary_null_comparison |
| if (bound != null) { |
| bound = v.visitDartType(bound, cannotRemoveSentinel); |
| } |
| // ignore: unnecessary_null_comparison |
| if (defaultType != null) { |
| defaultType = v.visitDartType(defaultType, cannotRemoveSentinel); |
| } |
| } |
| |
| /// Returns a possibly synthesized name for this type parameter, consistent |
| /// with the names used across all [toString] calls. |
| @override |
| String toString() { |
| return "TypeParameter(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypeParameterName(this); |
| } |
| |
| bool get isFunctionTypeTypeParameter => parent == null; |
| } |
| |
| class Supertype extends Node { |
| Reference className; |
| final List<DartType> typeArguments; |
| |
| Supertype(Class classNode, List<DartType> typeArguments) |
| : this.byReference(classNode.reference, typeArguments); |
| |
| Supertype.byReference(this.className, this.typeArguments); |
| |
| Class get classNode => className.asClass; |
| |
| @override |
| R accept<R>(Visitor<R> v) => v.visitSupertype(this); |
| |
| @override |
| R accept1<R, A>(Visitor1<R, A> v, A arg) => v.visitSupertype(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| classNode.acceptReference(v); |
| visitList(typeArguments, v); |
| } |
| |
| InterfaceType get asInterfaceType { |
| return new InterfaceType(classNode, Nullability.legacy, typeArguments); |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) return true; |
| if (other is Supertype) { |
| if (className != other.className) return false; |
| if (typeArguments.length != other.typeArguments.length) return false; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| if (typeArguments[i] != other.typeArguments[i]) return false; |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 0x3fffffff & className.hashCode; |
| for (int i = 0; i < typeArguments.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| } |
| return hash; |
| } |
| |
| @override |
| String toString() { |
| return "Supertype(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeClassName(className, forType: true); |
| printer.writeTypeArguments(typeArguments); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // CONSTANTS |
| // ------------------------------------------------------------------------ |
| |
| abstract class Constant extends Node { |
| /// Calls the `visit*ConstantReference()` method on visitor [v] for all |
| /// constants referenced in this constant. |
| /// |
| /// (Note that a constant can be seen as a DAG (directed acyclic graph) and |
| /// not a tree!) |
| @override |
| void visitChildren(Visitor v); |
| |
| /// Calls the `visit*Constant()` method on the visitor [v]. |
| @override |
| R accept<R>(ConstantVisitor<R> v); |
| |
| /// Calls the `visit*Constant()` method on the visitor [v]. |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg); |
| |
| /// Calls the `visit*ConstantReference()` method on the visitor [v]. |
| R acceptReference<R>(Visitor<R> v); |
| |
| /// Calls the `visit*ConstantReference()` method on the visitor [v]. |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg); |
| |
| /// The Kernel AST will reference [Constant]s via [ConstantExpression]s. The |
| /// constants are not required to be canonicalized, but they have to be deeply |
| /// comparable via hashCode/==! |
| @override |
| int get hashCode; |
| |
| @override |
| bool operator ==(Object other); |
| |
| @override |
| String toString() => throw '$runtimeType'; |
| |
| /// Returns a textual representation of the this constant. |
| /// |
| /// If [verbose] is `true`, qualified names will include the library name/uri. |
| @override |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeConstant(this); |
| return printer.getText(); |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer); |
| |
| /// Gets the type of this constant. |
| DartType getType(StaticTypeContext context); |
| } |
| |
| abstract class PrimitiveConstant<T> extends Constant { |
| final T value; |
| |
| PrimitiveConstant(this.value); |
| |
| @override |
| int get hashCode => value.hashCode; |
| |
| @override |
| bool operator ==(Object other) => |
| other is PrimitiveConstant<T> && other.value == value; |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('$value'); |
| } |
| } |
| |
| class NullConstant extends PrimitiveConstant<Null> { |
| NullConstant() : super(null); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitNullConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitNullConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitNullConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitNullConstantReference(this, arg); |
| |
| @override |
| DartType getType(StaticTypeContext context) => const NullType(); |
| |
| @override |
| String toString() => 'NullConstant(${toStringInternal()})'; |
| } |
| |
| class BoolConstant extends PrimitiveConstant<bool> { |
| BoolConstant(bool value) : super(value); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitBoolConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitBoolConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitBoolConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitBoolConstantReference(this, arg); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.boolRawType(context.nonNullable); |
| |
| @override |
| String toString() => 'BoolConstant(${toStringInternal()})'; |
| } |
| |
| /// An integer constant on a non-JS target. |
| class IntConstant extends PrimitiveConstant<int> { |
| IntConstant(int value) : super(value); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitIntConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitIntConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitIntConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitIntConstantReference(this, arg); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.intRawType(context.nonNullable); |
| |
| @override |
| String toString() => 'IntConstant(${toStringInternal()})'; |
| } |
| |
| /// A double constant on a non-JS target or any numeric constant on a JS target. |
| class DoubleConstant extends PrimitiveConstant<double> { |
| DoubleConstant(double value) : super(value); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitDoubleConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitDoubleConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitDoubleConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitDoubleConstantReference(this, arg); |
| |
| @override |
| int get hashCode => value.isNaN ? 199 : super.hashCode; |
| |
| @override |
| bool operator ==(Object other) => |
| other is DoubleConstant && identical(value, other.value); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable); |
| |
| @override |
| String toString() => 'DoubleConstant(${toStringInternal()})'; |
| } |
| |
| class StringConstant extends PrimitiveConstant<String> { |
| StringConstant(String value) : super(value) { |
| // ignore: unnecessary_null_comparison |
| assert(value != null); |
| } |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitStringConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitStringConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitStringConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitStringConstantReference(this, arg); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.stringRawType(context.nonNullable); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('"'); |
| printer.write(escapeString(value)); |
| printer.write('"'); |
| } |
| |
| @override |
| String toString() => 'StringConstant(${toStringInternal()})'; |
| } |
| |
| class SymbolConstant extends Constant { |
| final String name; |
| final Reference? libraryReference; |
| |
| SymbolConstant(this.name, this.libraryReference); |
| |
| @override |
| void visitChildren(Visitor v) {} |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitSymbolConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitSymbolConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitSymbolConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitSymbolConstantReference(this, arg); |
| |
| @override |
| String toString() => 'SymbolConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => _Hash.hash2(name, libraryReference); |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| (other is SymbolConstant && |
| other.name == name && |
| other.libraryReference == libraryReference); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('#'); |
| if (printer.includeAuxiliaryProperties && libraryReference != null) { |
| printer.write(libraryNameToString(libraryReference!.asLibrary)); |
| printer.write('::'); |
| } |
| printer.write(name); |
| } |
| } |
| |
| class MapConstant extends Constant { |
| final DartType keyType; |
| final DartType valueType; |
| final List<ConstantMapEntry> entries; |
| |
| MapConstant(this.keyType, this.valueType, this.entries); |
| |
| @override |
| void visitChildren(Visitor v) { |
| keyType.accept(v); |
| valueType.accept(v); |
| for (final ConstantMapEntry entry in entries) { |
| entry.key.acceptReference(v); |
| entry.value.acceptReference(v); |
| } |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitMapConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitMapConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitMapConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitMapConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('const <'); |
| printer.writeType(keyType); |
| printer.write(', '); |
| printer.writeType(valueType); |
| printer.write('>{'); |
| for (int i = 0; i < entries.length; i++) { |
| if (i > 0) { |
| printer.write(', '); |
| } |
| printer.writeConstantMapEntry(entries[i]); |
| } |
| printer.write('}'); |
| } |
| |
| @override |
| String toString() => 'MapConstant(${toStringInternal()})'; |
| |
| @override |
| late final int hashCode = _Hash.combine2Finish( |
| keyType.hashCode, valueType.hashCode, _Hash.combineListHash(entries)); |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| (other is MapConstant && |
| other.keyType == keyType && |
| other.valueType == valueType && |
| listEquals(other.entries, entries)); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.mapType(keyType, valueType, context.nonNullable); |
| } |
| |
| class ConstantMapEntry { |
| final Constant key; |
| final Constant value; |
| ConstantMapEntry(this.key, this.value); |
| |
| @override |
| String toString() => 'ConstantMapEntry(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => _Hash.hash2(key, value); |
| |
| @override |
| bool operator ==(Object other) => |
| other is ConstantMapEntry && other.key == key && other.value == value; |
| |
| String toStringInternal() => toText(defaultAstTextStrategy); |
| |
| String toText(AstTextStrategy strategy) { |
| AstPrinter printer = new AstPrinter(strategy); |
| printer.writeConstantMapEntry(this); |
| return printer.getText(); |
| } |
| |
| void toTextInternal(AstPrinter printer) { |
| printer.writeConstant(key); |
| printer.write(': '); |
| printer.writeConstant(value); |
| } |
| } |
| |
| class ListConstant extends Constant { |
| final DartType typeArgument; |
| final List<Constant> entries; |
| |
| ListConstant(this.typeArgument, this.entries); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| for (final Constant constant in entries) { |
| constant.acceptReference(v); |
| } |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitListConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitListConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitListConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitListConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('const <'); |
| printer.writeType(typeArgument); |
| printer.write('>['); |
| for (int i = 0; i < entries.length; i++) { |
| if (i > 0) { |
| printer.write(', '); |
| } |
| printer.writeConstant(entries[i]); |
| } |
| printer.write(']'); |
| } |
| |
| @override |
| String toString() => 'ListConstant(${toStringInternal()})'; |
| |
| @override |
| late final int hashCode = _Hash.combineFinish( |
| typeArgument.hashCode, _Hash.combineListHash(entries)); |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| (other is ListConstant && |
| other.typeArgument == typeArgument && |
| listEquals(other.entries, entries)); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.listType(typeArgument, context.nonNullable); |
| } |
| |
| class SetConstant extends Constant { |
| final DartType typeArgument; |
| final List<Constant> entries; |
| |
| SetConstant(this.typeArgument, this.entries); |
| |
| @override |
| void visitChildren(Visitor v) { |
| typeArgument.accept(v); |
| for (final Constant constant in entries) { |
| constant.acceptReference(v); |
| } |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitSetConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitSetConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitSetConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitSetConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('const <'); |
| printer.writeType(typeArgument); |
| printer.write('>{'); |
| for (int i = 0; i < entries.length; i++) { |
| if (i > 0) { |
| printer.write(', '); |
| } |
| printer.writeConstant(entries[i]); |
| } |
| printer.write('}'); |
| } |
| |
| @override |
| String toString() => 'SetConstant(${toStringInternal()})'; |
| |
| @override |
| late final int hashCode = _Hash.combineFinish( |
| typeArgument.hashCode, _Hash.combineListHash(entries)); |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| (other is SetConstant && |
| other.typeArgument == typeArgument && |
| listEquals(other.entries, entries)); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.setType(typeArgument, context.nonNullable); |
| } |
| |
| class RecordConstant extends Constant { |
| /// Positional field values. |
| final List<Constant> positional; |
| |
| /// Named field values, sorted by name. |
| final Map<String, Constant> named; |
| |
| /// The static type of the constant. |
| final RecordType recordType; |
| |
| RecordConstant(this.positional, this.named, this.recordType) |
| : assert(positional.length == recordType.positional.length && |
| named.length == recordType.named.length && |
| recordType.named |
| .map((f) => f.name) |
| .toSet() |
| .containsAll(named.keys)), |
| assert(() { |
| // Assert that the named fields are sorted. |
| String? previous; |
| for (String name in named.keys) { |
| if (previous != null && name.compareTo(previous) < 0) { |
| return false; |
| } |
| previous = name; |
| } |
| return true; |
| }(), |
| "Named fields of a RecordConstant aren't sorted lexicographically: " |
| "${named.keys.join(", ")}"); |
| |
| @override |
| void visitChildren(Visitor v) { |
| recordType.accept(v); |
| for (final Constant entry in positional) { |
| entry.acceptReference(v); |
| } |
| for (final Constant entry in named.values) { |
| entry.acceptReference(v); |
| } |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitRecordConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitRecordConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitRecordConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitRecordConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write("const ("); |
| String comma = ''; |
| for (Constant entry in positional) { |
| printer.write(comma); |
| printer.writeConstant(entry); |
| comma = ', '; |
| } |
| if (named.isNotEmpty) { |
| printer.write(comma); |
| comma = ''; |
| printer.write("{"); |
| for (MapEntry<String, Constant> entry in named.entries) { |
| printer.write(comma); |
| printer.write(entry.key); |
| printer.write(": "); |
| printer.writeConstant(entry.value); |
| comma = ', '; |
| } |
| printer.write("}"); |
| } |
| printer.write(")"); |
| } |
| |
| @override |
| String toString() => "RecordConstant(${toStringInternal()})"; |
| |
| @override |
| late final int hashCode = _Hash.combineFinish(recordType.hashCode, |
| _Hash.combineMapHashUnordered(named, _Hash.combineListHash(positional))); |
| |
| @override |
| bool operator ==(Object other) => |
| identical(this, other) || |
| (other is RecordConstant && |
| other.recordType == recordType && |
| listEquals(other.positional, positional) && |
| mapEquals(other.named, named)); |
| |
| @override |
| DartType getType(StaticTypeContext context) => recordType; |
| } |
| |
| class InstanceConstant extends Constant { |
| final Reference classReference; |
| final List<DartType> typeArguments; |
| final Map<Reference, Constant> fieldValues; |
| |
| InstanceConstant(this.classReference, this.typeArguments, this.fieldValues); |
| |
| Class get classNode => classReference.asClass; |
| |
| @override |
| void visitChildren(Visitor v) { |
| classReference.asClass.acceptReference(v); |
| visitList(typeArguments, v); |
| for (final Reference reference in fieldValues.keys) { |
| reference.asField.acceptReference(v); |
| } |
| for (final Constant constant in fieldValues.values) { |
| constant.acceptReference(v); |
| } |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitInstanceConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitInstanceConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => v.visitInstanceConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitInstanceConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('const '); |
| printer.writeClassName(classReference); |
| printer.writeTypeArguments(typeArguments); |
| printer.write('{'); |
| String comma = ''; |
| fieldValues.forEach((Reference fieldRef, Constant constant) { |
| printer.write(comma); |
| printer.writeMemberName(fieldRef); |
| printer.write(': '); |
| printer.writeConstant(constant); |
| comma = ', '; |
| }); |
| printer.write('}'); |
| } |
| |
| @override |
| String toString() => 'InstanceConstant(${toStringInternal()})'; |
| |
| @override |
| late final int hashCode = _Hash.combine2Finish(classReference.hashCode, |
| listHashCode(typeArguments), _Hash.combineMapHashUnordered(fieldValues)); |
| |
| @override |
| bool operator ==(Object other) { |
| return identical(this, other) || |
| (other is InstanceConstant && |
| other.classReference == classReference && |
| listEquals(other.typeArguments, typeArguments) && |
| mapEquals(other.fieldValues, fieldValues)); |
| } |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| new InterfaceType(classNode, context.nonNullable, typeArguments); |
| } |
| |
| class InstantiationConstant extends Constant { |
| final Constant tearOffConstant; |
| final List<DartType> types; |
| |
| InstantiationConstant(this.tearOffConstant, this.types); |
| |
| @override |
| void visitChildren(Visitor v) { |
| tearOffConstant.acceptReference(v); |
| visitList(types, v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitInstantiationConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitInstantiationConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitInstantiationConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitInstantiationConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeConstant(tearOffConstant); |
| printer.writeTypeArguments(types); |
| } |
| |
| @override |
| String toString() => 'InstantiationConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => _Hash.combineFinish( |
| tearOffConstant.hashCode, _Hash.combineListHash(types)); |
| |
| @override |
| bool operator ==(Object other) { |
| return other is InstantiationConstant && |
| other.tearOffConstant == tearOffConstant && |
| listEquals(other.types, types); |
| } |
| |
| @override |
| DartType getType(StaticTypeContext context) { |
| final FunctionType type = tearOffConstant.getType(context) as FunctionType; |
| final Map<TypeParameter, DartType> mapping = <TypeParameter, DartType>{}; |
| for (final TypeParameter parameter in type.typeParameters) { |
| mapping[parameter] = types[mapping.length]; |
| } |
| return substitute(type.withoutTypeParameters, mapping); |
| } |
| } |
| |
| abstract class TearOffConstant implements Constant { |
| Reference get targetReference; |
| Member get target; |
| FunctionNode get function; |
| } |
| |
| class StaticTearOffConstant extends Constant implements TearOffConstant { |
| @override |
| final Reference targetReference; |
| |
| StaticTearOffConstant(Procedure target) |
| : assert(target.isStatic), |
| assert(target.kind == ProcedureKind.Method, |
| "Unexpected static tear off target: $target"), |
| targetReference = target.reference; |
| |
| StaticTearOffConstant.byReference(this.targetReference); |
| |
| @override |
| Procedure get target => targetReference.asProcedure; |
| |
| @override |
| FunctionNode get function => target.function; |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitStaticTearOffConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitStaticTearOffConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitStaticTearOffConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitStaticTearOffConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| |
| @override |
| String toString() => 'StaticTearOffConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => targetReference.hashCode; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is StaticTearOffConstant && |
| other.targetReference == targetReference; |
| } |
| |
| @override |
| FunctionType getType(StaticTypeContext context) { |
| return target.function.computeFunctionType(context.nonNullable); |
| } |
| } |
| |
| class ConstructorTearOffConstant extends Constant implements TearOffConstant { |
| @override |
| final Reference targetReference; |
| |
| ConstructorTearOffConstant(Member target) |
| : assert( |
| target is Constructor || (target is Procedure && target.isFactory), |
| "Unexpected constructor tear off target: $target"), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| ConstructorTearOffConstant.byReference(this.targetReference); |
| |
| @override |
| Member get target => targetReference.asMember; |
| |
| @override |
| FunctionNode get function => target.function!; |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitConstructorTearOffConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitConstructorTearOffConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitConstructorTearOffConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitConstructorTearOffConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| |
| @override |
| String toString() => 'ConstructorTearOffConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => targetReference.hashCode; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is ConstructorTearOffConstant && |
| other.targetReference == targetReference; |
| } |
| |
| @override |
| FunctionType getType(StaticTypeContext context) { |
| return function.computeFunctionType(context.nonNullable); |
| } |
| } |
| |
| class RedirectingFactoryTearOffConstant extends Constant |
| implements TearOffConstant { |
| @override |
| final Reference targetReference; |
| |
| RedirectingFactoryTearOffConstant(Procedure target) |
| : assert(target.isRedirectingFactory), |
| this.targetReference = getNonNullableMemberReferenceGetter(target); |
| |
| RedirectingFactoryTearOffConstant.byReference(this.targetReference); |
| |
| @override |
| Procedure get target => targetReference.asProcedure; |
| |
| @override |
| FunctionNode get function => target.function; |
| |
| @override |
| void visitChildren(Visitor v) { |
| target.acceptReference(v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => |
| v.visitRedirectingFactoryTearOffConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitRedirectingFactoryTearOffConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitRedirectingFactoryTearOffConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitRedirectingFactoryTearOffConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeMemberName(targetReference); |
| } |
| |
| @override |
| String toString() => |
| 'RedirectingFactoryTearOffConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => targetReference.hashCode; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is RedirectingFactoryTearOffConstant && |
| other.targetReference == targetReference; |
| } |
| |
| @override |
| FunctionType getType(StaticTypeContext context) { |
| return function.computeFunctionType(context.nonNullable); |
| } |
| } |
| |
| class TypedefTearOffConstant extends Constant { |
| final List<TypeParameter> parameters; |
| final TearOffConstant tearOffConstant; |
| final List<DartType> types; |
| |
| @override |
| late final int hashCode = _computeHashCode(); |
| |
| TypedefTearOffConstant(this.parameters, this.tearOffConstant, this.types); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(parameters, v); |
| tearOffConstant.acceptReference(v); |
| visitList(types, v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitTypedefTearOffConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitTypedefTearOffConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitTypedefTearOffConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitTypedefTearOffConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeTypeParameters(parameters); |
| printer.writeConstant(tearOffConstant); |
| printer.writeTypeArguments(types); |
| } |
| |
| @override |
| String toString() => 'TypedefTearOffConstant(${toStringInternal()})'; |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is! TypedefTearOffConstant) return false; |
| if (other.tearOffConstant != tearOffConstant) return false; |
| if (other.parameters.length != parameters.length) return false; |
| if (parameters.isNotEmpty) { |
| Assumptions assumptions = new Assumptions(); |
| for (int index = 0; index < parameters.length; index++) { |
| assumptions.assume(parameters[index], other.parameters[index]); |
| } |
| for (int index = 0; index < parameters.length; index++) { |
| if (!parameters[index] |
| .bound |
| .equals(other.parameters[index].bound, assumptions)) { |
| return false; |
| } |
| } |
| for (int i = 0; i < types.length; ++i) { |
| if (!types[i].equals(other.types[i], assumptions)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| int _computeHashCode() { |
| int hash = 1237; |
| for (int i = 0; i < parameters.length; ++i) { |
| TypeParameter parameter = parameters[i]; |
| hash = 0x3fffffff & (hash * 31 + parameter.bound.hashCode); |
| } |
| for (int i = 0; i < types.length; ++i) { |
| hash = 0x3fffffff & (hash * 31 + types[i].hashCode); |
| } |
| hash = 0x3fffffff & (hash * 31 + tearOffConstant.hashCode); |
| return hash; |
| } |
| |
| @override |
| DartType getType(StaticTypeContext context) { |
| FunctionType type = tearOffConstant.getType(context) as FunctionType; |
| FreshTypeParameters freshTypeParameters = |
| getFreshTypeParameters(parameters); |
| type = freshTypeParameters.substitute( |
| Substitution.fromPairs(type.typeParameters, types) |
| .substituteType(type.withoutTypeParameters)) as FunctionType; |
| return new FunctionType( |
| type.positionalParameters, type.returnType, type.declaredNullability, |
| namedParameters: type.namedParameters, |
| typeParameters: freshTypeParameters.freshTypeParameters, |
| requiredParameterCount: type.requiredParameterCount); |
| } |
| } |
| |
| class TypeLiteralConstant extends Constant { |
| final DartType type; |
| |
| TypeLiteralConstant(this.type); |
| |
| @override |
| void visitChildren(Visitor v) { |
| type.accept(v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitTypeLiteralConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitTypeLiteralConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitTypeLiteralConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitTypeLiteralConstantReference(this, arg); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.writeType(type); |
| } |
| |
| @override |
| String toString() => 'TypeLiteralConstant(${toStringInternal()})'; |
| |
| @override |
| int get hashCode => type.hashCode; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is TypeLiteralConstant && other.type == type; |
| } |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| context.typeEnvironment.coreTypes.typeRawType(context.nonNullable); |
| } |
| |
| class UnevaluatedConstant extends Constant { |
| final Expression expression; |
| |
| UnevaluatedConstant(this.expression) { |
| expression.parent = null; |
| } |
| |
| @override |
| void visitChildren(Visitor v) { |
| expression.accept(v); |
| } |
| |
| @override |
| R accept<R>(ConstantVisitor<R> v) => v.visitUnevaluatedConstant(this); |
| |
| @override |
| R accept1<R, A>(ConstantVisitor1<R, A> v, A arg) => |
| v.visitUnevaluatedConstant(this, arg); |
| |
| @override |
| R acceptReference<R>(Visitor<R> v) => |
| v.visitUnevaluatedConstantReference(this); |
| |
| @override |
| R acceptReference1<R, A>(Visitor1<R, A> v, A arg) => |
| v.visitUnevaluatedConstantReference(this, arg); |
| |
| @override |
| DartType getType(StaticTypeContext context) => |
| expression.getStaticType(context); |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| printer.write('unevaluated{'); |
| printer.writeExpression(expression); |
| printer.write('}'); |
| } |
| |
| @override |
| String toString() { |
| return "UnevaluatedConstant(${toStringInternal()})"; |
| } |
| |
| @override |
| int get hashCode => expression.hashCode; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is UnevaluatedConstant && other.expression == expression; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // COMPONENT |
| // ------------------------------------------------------------------------ |
| |
| /// A way to bundle up libraries in a component. |
| class Component extends TreeNode { |
| final CanonicalName root; |
| |
| /// Problems in this [Component] encoded as json objects. |
| /// |
| /// Note that this field can be null, and by convention should be null if the |
| /// list is empty. |
| List<String>? problemsAsJson; |
| |
| final List<Library> libraries; |
| |
| /// Map from a source file URI to a line-starts table and source code. |
| /// Given a source file URI and a offset in that file one can translate |
| /// it to a line:column position in that file. |
| final Map<Uri, Source> uriToSource; |
| |
| /// Mapping between string tags and [MetadataRepository] corresponding to |
| /// those tags. |
| final Map<String, MetadataRepository<dynamic>> metadata = |
| <String, MetadataRepository<dynamic>>{}; |
| |
| /// Reference to the main method in one of the libraries. |
| Reference? _mainMethodName; |
| Reference? get mainMethodName => _mainMethodName; |
| NonNullableByDefaultCompiledMode? _mode; |
| NonNullableByDefaultCompiledMode get mode { |
| return _mode ?? NonNullableByDefaultCompiledMode.Weak; |
| } |
| |
| NonNullableByDefaultCompiledMode? get modeRaw => _mode; |
| |
| Component( |
| {CanonicalName? nameRoot, |
| List<Library>? libraries, |
| Map<Uri, Source>? uriToSource, |
| NonNullableByDefaultCompiledMode? mode}) |
| : root = nameRoot ?? new CanonicalName.root(), |
| libraries = libraries ?? <Library>[], |
| uriToSource = uriToSource ?? <Uri, Source>{}, |
| _mode = mode { |
| adoptChildren(); |
| } |
| |
| void adoptChildren() { |
| // ignore: unnecessary_null_comparison |
| if (libraries != null) { |
| for (int i = 0; i < libraries.length; ++i) { |
| // The libraries are owned by this component, and so are their canonical |
| // names if they exist. |
| Library library = libraries[i]; |
| library.parent = this; |
| CanonicalName? name = library.reference.canonicalName; |
| if (name != null && name.parent != root) { |
| root.adoptChild(name); |
| } |
| } |
| } |
| } |
| |
| void computeCanonicalNames() { |
| for (int i = 0; i < libraries.length; ++i) { |
| computeCanonicalNamesForLibrary(libraries[i]); |
| } |
| } |
| |
| /// This is an advanced feature. Use of this method should be coordinated |
| /// with the kernel team. |
| /// |
| /// Makes sure all references in named nodes in this component points to said |
| /// named node. |
| /// |
| /// The use case is advanced incremental compilation, where we want to rebuild |
| /// a single library and make all other libraries use the new library and the |
| /// content therein *while* having the option to go back to pointing (be |
| /// "linked") to the old library if the delta is rejected. |
| /// |
| /// Please note that calling this is a potentially dangerous thing to do, |
| /// and that stuff *can* go wrong, and you could end up in a situation where |
| /// you point to several versions of "the same" library. Examples: |
| /// * If you only relink part (e.g. a class) if your component you can wind |
| /// up in an unfortunate situation where if the library (say libA) contains |
| /// class 'B' and class 'C', you only replace 'B' (with one in library |
| /// 'libAPrime'), everything pointing to 'B' via parent pointers talks |
| /// about 'libAPrime', whereas everything pointing to 'C' would still |
| /// ultimately point to 'libA'. |
| /// * If you relink to a library that doesn't have exactly the same members |
| /// as the one you're "linking from" you can wind up in an unfortunate |
| /// situation, e.g. if the thing you relink two is missing a static method, |
| /// any links to that static method will still point to the old static |
| /// method and thus (via parent pointers) to the old library. |
| /// * (probably more). |
| void relink() { |
| for (int i = 0; i < libraries.length; ++i) { |
| libraries[i].relink(); |
| } |
| } |
| |
| void computeCanonicalNamesForLibrary(Library library) { |
| library.ensureCanonicalNames(root); |
| } |
| |
| void unbindCanonicalNames() { |
| // TODO(jensj): Get rid of this. |
| for (int i = 0; i < libraries.length; i++) { |
| Library lib = libraries[i]; |
| for (int j = 0; j < lib.classes.length; j++) { |
| Class c = lib.classes[j]; |
| c.dirty = true; |
| } |
| } |
| root.unbindAll(); |
| } |
| |
| Procedure? get mainMethod => mainMethodName?.asProcedure; |
| |
| void setMainMethodAndMode(Reference? main, bool overwriteMainIfSet, |
| NonNullableByDefaultCompiledMode mode) { |
| if (_mainMethodName == null || overwriteMainIfSet) { |
| _mainMethodName = main; |
| } |
| _mode = mode; |
| } |
| |
| @override |
| R accept<R>(TreeVisitor<R> v) => v.visitComponent(this); |
| |
| @override |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitComponent(this, arg); |
| |
| @override |
| void visitChildren(Visitor v) { |
| visitList(libraries, v); |
| mainMethod?.acceptReference(v); |
| } |
| |
| @override |
| void transformChildren(Transformer v) { |
| v.transformList(libraries, this); |
| } |
| |
| @override |
| void transformOrRemoveChildren(RemovingTransformer v) { |
| v.transformLibraryList(libraries, this); |
| } |
| |
| @override |
| Component get enclosingComponent => this; |
| |
| /// Translates an offset to line and column numbers in the given file. |
| Location? getLocation(Uri file, int offset) { |
| return uriToSource[file]?.getLocation(file, offset); |
| } |
| |
| /// Translates line and column numbers to an offset in the given file. |
| /// |
| /// Returns offset of the line and column in the file, or -1 if the |
| /// source is not available or has no lines. |
| /// Throws [RangeError] if line or calculated offset are out of range. |
| int getOffset(Uri file, int line, int column) { |
| return uriToSource[file]?.getOffset(line, column) ?? -1; |
| } |
| |
| void addMetadataRepository(MetadataRepository repository) { |
| metadata[repository.tag] = repository; |
| } |
| |
| @override |
| String toString() { |
| return "Component(${toStringInternal()})"; |
| } |
| |
| @override |
| void toTextInternal(AstPrinter printer) { |
| // TODO(johnniwinther): Implement this. |
| } |
| |
| @override |
| String leakingDebugToString() => astToText.debugComponentToString(this); |
| } |
| |
| /// A tuple with file, line, and column number, for displaying human-readable |
| /// locations. |
| class Location { |
| final Uri file; |
| final int line; // 1-based. |
| final int column; // 1-based. |
| |
| Location(this.file, this.line, this.column); |
| |
| @override |
| String toString() => '$file:$line:$column'; |
| } |
| |
| abstract class MetadataRepository<T> { |
| /// Unique string tag associated with this repository. |
| String get tag; |
| |
| /// Mutable mapping between nodes and their metadata. |
| Map<Node, T> get mapping; |
| |
| /// Write [metadata] object corresponding to the given [Node] into |
| /// the given [BinarySink]. |
| /// |
| /// Metadata is serialized immediately before serializing [node], |
| /// so implementation of this method can use serialization context of |
| /// [node]'s parents (such as declared type parameters and variables). |
| /// In order to use scope declared by the [node] itself, implementation of |
| /// this method can use [BinarySink.enterScope] and [BinarySink.leaveScope] |
| /// methods. |
| /// |
| /// [metadata] must be an object owned by this repository. |
| void writeToBinary(T metadata, Node node, BinarySink sink); |
| |
| /// Construct a metadata object from its binary payload read from the |
| /// given [BinarySource]. |
| /// |
| /// Metadata is deserialized immediately after deserializing [node], |
| /// so it can use deserialization context of [node]'s parents. |
| /// In order to use scope declared by the [node] itself, implementation of |
| /// this method can use [BinarySource.enterScope] and |
| /// [BinarySource.leaveScope] methods. |
| T readFromBinary(Node node, BinarySource source); |
| |
| /// Method to check whether a node can have metadata attached to it |
| /// or referenced from the metadata payload. |
| /// |
| /// Currently due to binary format specifics Catch and MapEntry nodes |
| /// can't have metadata attached to them. Also, metadata is not saved on |
| /// Block nodes inside BlockExpressions. |
| static bool isSupported(Node node) { |
| return !(node is MapLiteralEntry || |
| node is Catch || |
| (node is Block && node.parent is BlockExpression)); |
| } |
| } |
| |
| abstract class BinarySink { |
| int getBufferOffset(); |
| |
| void writeByte(int byte); |
| void writeBytes(List<int> bytes); |
| void writeUInt32(int value); |
| void writeUInt30(int value); |
| |
| /// Write List<Byte> into the sink. |
| void writeByteList(List<int> bytes); |
| |
| void writeNullAllowedCanonicalNameReference(Reference? reference); |
| void writeStringReference(String str); |
| void writeName(Name node); |
| void writeDartType(DartType type); |
| void writeConstantReference(Constant constant); |
| void writeNode(Node node); |
| |
| void enterScope( |
| {List<TypeParameter> typeParameters, |
| bool memberScope = false, |
| bool variableScope = false}); |
| void leaveScope( |
| {List<TypeParameter> typeParameters, |
| bool memberScope = false, |
| bool variableScope = false}); |
| } |
| |
| abstract class BinarySource { |
| int get currentOffset; |
| List<int> get bytes; |
| |
| int readByte(); |
| List<int> readBytes(int length); |
| int readUInt30(); |
| int readUint32(); |
| |
| /// Read List<Byte> from the source. |
| List<int> readByteList(); |
| |
| CanonicalName? readNullableCanonicalNameReference(); |
| String readStringReference(); |
| Name readName(); |
| DartType readDartType(); |
| Constant readConstantReference(); |
| FunctionNode readFunctionNode(); |
| |
| void enterScope({List<TypeParameter> typeParameters}); |
| void leaveScope({List<TypeParameter> typeParameters}); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // INTERNAL FUNCTIONS |
| // ------------------------------------------------------------------------ |
| |
| void setParents(List<TreeNode> nodes, TreeNode parent) { |
| for (int i = 0; i < nodes.length; ++i) { |
| nodes[i].parent = parent; |
| } |
| } |
| |
| void visitList(List<Node> nodes, Visitor visitor) { |
| for (int i = 0; i < nodes.length; ++i) { |
| nodes[i].accept(visitor); |
| } |
| } |
| |
| void visitIterable(Iterable<Node> nodes, Visitor visitor) { |
| for (Node node in nodes) { |
| node.accept(visitor); |
| } |
| } |
| |
| class _ChildReplacer extends Transformer { |
| final TreeNode child; |
| final TreeNode replacement; |
| |
| _ChildReplacer(this.child, this.replacement); |
| |
| @override |
| TreeNode defaultTreeNode(TreeNode node) { |
| if (node == child) { |
| return replacement; |
| } else { |
| return node; |
| } |
| } |
| } |
| |
| class Source { |
| final List<int>? lineStarts; |
| |
| /// A UTF8 encoding of the original source file. |
| final List<int> source; |
| |
| final Uri? importUri; |
| |
| final Uri? fileUri; |
| |
| Set<Reference>? constantCoverageConstructors; |
| |
| String? cachedText; |
| |
| Source(this.lineStarts, this.source, this.importUri, this.fileUri); |
| |
| /// Return the text corresponding to [line] which is a 1-based line |
| /// number. The returned line contains no line separators. |
| String? getTextLine(int line) { |
| List<int>? lineStarts = this.lineStarts; |
| if (source.isEmpty || lineStarts == null || lineStarts.isEmpty) { |
| return null; |
| } |
| RangeError.checkValueInInterval(line, 1, lineStarts.length, 'line'); |
| |
| String cachedText = |
| this.cachedText ??= utf8.decode(source, allowMalformed: true); |
| // -1 as line numbers start at 1. |
| int index = line - 1; |
| if (index + 1 == lineStarts.length) { |
| // Last line. |
| return cachedText.substring(lineStarts[index]); |
| } else if (index < lineStarts.length) { |
| // We subtract 1 from the next line for two reasons: |
| // 1. If the file isn't terminated by a newline, that index is invalid. |
| // 2. To remove the newline at the end of the line. |
| int endOfLine = lineStarts[index + 1] - 1; |
| if (endOfLine > index && cachedText[endOfLine - 1] == "\r") { |
| --endOfLine; // Windows line endings. |
| } |
| return cachedText.substring(lineStarts[index], endOfLine); |
| } |
| // This shouldn't happen: should have been caught by the range check above. |
| throw "Internal error"; |
| } |
| |
| /// Translates an offset to 1-based line and column numbers in the given file. |
| Location getLocation(Uri file, int offset) { |
| List<int>? lineStarts = this.lineStarts; |
| if (lineStarts == null || lineStarts.isEmpty) { |
| return new Location(file, TreeNode.noOffset, TreeNode.noOffset); |
| } |
| RangeError.checkValueInInterval(offset, 0, lineStarts.last, 'offset'); |
| int low = 0, high = lineStarts.length - 1; |
| while (low < high) { |
| int mid = high - ((high - low) >> 1); // Get middle, rounding up. |
| int pivot = lineStarts[mid]; |
| if (pivot <= offset) { |
| low = mid; |
| } else { |
| high = mid - 1; |
| } |
| } |
| int lineIndex = low; |
| int lineStart = lineStarts[lineIndex]; |
| int lineNumber = 1 + lineIndex; |
| int columnNumber = 1 + offset - lineStart; |
| return new Location(file, lineNumber, columnNumber); |
| } |
| |
| /// Translates 1-based line and column numbers to an offset in the given file |
| /// |
| /// Returns offset of the line and column in the file, or -1 if the source |
| /// has no lines. |
| /// Throws [RangeError] if line or calculated offset are out of range. |
| int getOffset(int line, int column) { |
| List<int>? lineStarts = this.lineStarts; |
| if (lineStarts == null || lineStarts.isEmpty) { |
| return -1; |
| } |
| RangeError.checkValueInInterval(line, 1, lineStarts.length, 'line'); |
| int offset = lineStarts[line - 1] + column - 1; |
| RangeError.checkValueInInterval(offset, 0, lineStarts.last, 'offset'); |
| return offset; |
| } |
| } |
| |
| /// Returns the [Reference] object for the given member based on the |
| /// ProcedureKind. |
| /// |
| /// Returns `null` if the member is `null`. |
| Reference? getMemberReferenceBasedOnProcedureKind( |
| Member? member, ProcedureKind kind) { |
| if (member == null) return null; |
| if (member is Field) { |
| if (kind == ProcedureKind.Setter) return member.setterReference!; |
| return member.getterReference; |
| } |
| return member.reference; |
| } |
| |
| /// Returns the (getter) [Reference] object for the given member. |
| /// |
| /// Returns `null` if the member is `null`. |
| /// TODO(jensj): Should it be called NotSetter instead of Getter? |
| Reference? getMemberReferenceGetter(Member? member) { |
| if (member == null) return null; |
| return getNonNullableMemberReferenceGetter(member); |
| } |
| |
| Reference getNonNullableMemberReferenceGetter(Member member) { |
| if (member is Field) return member.getterReference; |
| return member.reference; |
| } |
| |
| /// Returns the setter [Reference] object for the given member. |
| /// |
| /// Returns `null` if the member is `null`. |
| Reference? getMemberReferenceSetter(Member? member) { |
| if (member == null) return null; |
| return getNonNullableMemberReferenceSetter(member); |
| } |
| |
| Reference getNonNullableMemberReferenceSetter(Member member) { |
| if (member is Field) return member.setterReference!; |
| return member.reference; |
| } |
| |
| /// Murmur-inspired hashing, with a fall-back to Jenkins-inspired hashing when |
| /// compiled to JavaScript. |
| /// |
| /// A hash function should be constructed of several [combine] calls followed by |
| /// a [finish] call. |
| class _Hash { |
| static const int M = 0x9ddfea08eb382000 + 0xd69; |
| static const bool intIs64Bit = (1 << 63) != 0; |
| |
| /// Primitive hash combining step. |
| static int combine(int value, int hash) { |
| if (intIs64Bit) { |
| value *= M; |
| value ^= _shru(value, 47); |
| value *= M; |
| hash ^= value; |
| hash *= M; |
| } else { |
| // Fall back to Jenkins-inspired hashing on JavaScript platforms. |
| hash = 0x1fffffff & (hash + value); |
| hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
| hash = hash ^ (hash >> 6); |
| } |
| return hash; |
| } |
| |
| /// Primitive hash finalization step. |
| static int finish(int hash) { |
| if (intIs64Bit) { |
| hash ^= _shru(hash, 44); |
| hash *= M; |
| hash ^= _shru(hash, 41); |
| } else { |
| // Fall back to Jenkins-inspired hashing on JavaScript platforms. |
| hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| hash = hash ^ (hash >> 11); |
| hash = 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| } |
| return hash; |
| } |
| |
| static int combineFinish(int value, int hash) { |
| return finish(combine(value, hash)); |
| } |
| |
| static int combine2(int value1, int value2, int hash) { |
| return combine(value2, combine(value1, hash)); |
| } |
| |
| static int combine2Finish(int value1, int value2, int hash) { |
| return finish(combine2(value1, value2, hash)); |
| } |
| |
| static int hash2(Object object1, Object? object2) { |
| return combine2Finish(object2.hashCode, object2.hashCode, 0); |
| } |
| |
| static int combineListHash(List<Object> list, [int hash = 1]) { |
| for (Object item in list) { |
| hash = _Hash.combine(item.hashCode, hash); |
| } |
| return hash; |
| } |
| |
| static int combineList(List<int> hashes, int hash) { |
| for (int item in hashes) { |
| hash = combine(item, hash); |
| } |
| return hash; |
| } |
| |
| static int combineMapHashUnordered(Map? map, [int hash = 2]) { |
| if (map == null || map.isEmpty) return hash; |
| List<int> entryHashes = List.filled( |
| map.length, |
| // `-1` is used as a dummy default value. |
| -1); |
| int i = 0; |
| for (MapEntry entry in map.entries) { |
| entryHashes[i++] = combine(entry.key.hashCode, entry.value.hashCode); |
| } |
| entryHashes.sort(); |
| return combineList(entryHashes, hash); |
| } |
| |
| // TODO(sra): Replace with '>>>'. |
| static int _shru(int v, int n) { |
| assert(n >= 1); |
| assert(intIs64Bit); |
| return ((v >> 1) & (0x7fffFFFFffffF000 + 0xFFF)) >> (n - 1); |
| } |
| } |
| |
| int listHashCode(List<Object> list) { |
| return _Hash.finish(_Hash.combineListHash(list)); |
| } |
| |
| int mapHashCode(Map map) { |
| return mapHashCodeUnordered(map); |
| } |
| |
| int mapHashCodeOrdered(Map map, [int hash = 2]) { |
| for (final Object x in map.keys) { |
| hash = _Hash.combine(x.hashCode, hash); |
| } |
| for (final Object x in map.values) { |
| hash = _Hash.combine(x.hashCode, hash); |
| } |
| return _Hash.finish(hash); |
| } |
| |
| int mapHashCodeUnordered(Map map) { |
| return _Hash.finish(_Hash.combineMapHashUnordered(map)); |
| } |
| |
| bool listEquals(List a, List b) { |
| if (a.length != b.length) return false; |
| for (int i = 0; i < a.length; i++) { |
| if (a[i] != b[i]) return false; |
| } |
| return true; |
| } |
| |
| bool mapEquals(Map a, Map b) { |
| if (a.length != b.length) return false; |
| for (final Object key in a.keys) { |
| if (!b.containsKey(key) || a[key] != b[key]) return false; |
| } |
| return true; |
| } |
| |
| /// Annotation describing information which is not part of Dart semantics; in |
| /// other words, if this information (or any information it refers to) changes, |
| /// static analysis and runtime behavior of the library are unaffected. |
| const Null informative = null; |
| |
| Location? _getLocationInComponent( |
| Component? component, Uri fileUri, int offset) { |
| if (component != null) { |
| return component.getLocation(fileUri, offset); |
| } else { |
| return new Location(fileUri, TreeNode.noOffset, TreeNode.noOffset); |
| } |
| } |
| |
| /// Convert the synthetic name of an implicit mixin application class |
| /// into a name suitable for user-faced strings. |
| /// |
| /// For example, when compiling "class A extends S with M1, M2", the |
| /// two synthetic classes will be named "_A&S&M1" and "_A&S&M1&M2". |
| /// This function will return "S with M1" and "S with M1, M2", respectively. |
| String demangleMixinApplicationName(String name) { |
| List<String> nameParts = name.split('&'); |
| if (nameParts.length < 2 || name == "&") return name; |
| String demangledName = nameParts[1]; |
| for (int i = 2; i < nameParts.length; i++) { |
| demangledName += (i == 2 ? " with " : ", ") + nameParts[i]; |
| } |
| return demangledName; |
| } |
| |
| /// Extract from the synthetic name of an implicit mixin application class |
| /// the name of the final subclass of the mixin application. |
| /// |
| /// For example, when compiling "class A extends S with M1, M2", the |
| /// two synthetic classes will be named "_A&S&M1" and "_A&S&M1&M2". |
| /// This function will return "A" for both classes. |
| String demangleMixinApplicationSubclassName(String name) { |
| List<String> nameParts = name.split('&'); |
| if (nameParts.length < 2) return name; |
| assert(nameParts[0].startsWith('_')); |
| return nameParts[0].substring(1); |
| } |
| |
| /// Computes a list of [typeParameters] taken as types. |
| List<DartType> getAsTypeArguments( |
| List<TypeParameter> typeParameters, Library library) { |
| if (typeParameters.isEmpty) return const <DartType>[]; |
| return new List<DartType>.generate( |
| typeParameters.length, |
| (int i) => new TypeParameterType.withDefaultNullabilityForLibrary( |
| typeParameters[i], library), |
| growable: false); |
| } |
| |
| class Version extends Object { |
| final int major; |
| final int minor; |
| |
| const Version(this.major, this.minor) |
| // ignore: unnecessary_null_comparison |
| : assert(major != null), |
| // ignore: unnecessary_null_comparison |
| assert(minor != null); |
| |
| bool operator <(Version other) { |
| if (major < other.major) return true; |
| if (major > other.major) return false; |
| |
| // Major is the same. |
| if (minor < other.minor) return true; |
| return false; |
| } |
| |
| bool operator <=(Version other) { |
| if (major < other.major) return true; |
| if (major > other.major) return false; |
| |
| // Major is the same. |
| if (minor <= other.minor) return true; |
| return false; |
| } |
| |
| bool operator >(Version other) { |
| if (major > other.major) return true; |
| if (major < other.major) return false; |
| |
| // Major is the same. |
| if (minor > other.minor) return true; |
| return false; |
| } |
| |
| bool operator >=(Version other) { |
| if (major > other.major) return true; |
| if (major < other.major) return false; |
| |
| // Major is the same. |
| if (minor >= other.minor) return true; |
| return false; |
| } |
| |
| /// Returns this language version as a 'major.minor' text. |
| String toText() => '${major}.${minor}'; |
| |
| @override |
| int get hashCode { |
| return major.hashCode * 13 + minor.hashCode * 17; |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) return true; |
| return other is Version && major == other.major && minor == other.minor; |
| } |
| |
| @override |
| String toString() { |
| return "Version(major=$major, minor=$minor)"; |
| } |
| } |
| |
| /// Almost const <NamedExpression>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<NamedExpression> emptyListOfNamedExpression = |
| List.filled(0, dummyNamedExpression, growable: false); |
| |
| /// Almost const <VariableDeclaration>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<VariableDeclaration> emptyListOfVariableDeclaration = |
| List.filled(0, dummyVariableDeclaration, growable: false); |
| |
| /// Almost const <Combinator>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Combinator> emptyListOfCombinator = |
| List.filled(0, dummyCombinator, growable: false); |
| |
| /// Almost const <Expression>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Expression> emptyListOfExpression = |
| List.filled(0, dummyExpression, growable: false); |
| |
| /// Almost const <AssertStatement>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<AssertStatement> emptyListOfAssertStatement = |
| List.filled(0, dummyAssertStatement, growable: false); |
| |
| /// Almost const <Statement>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Statement> emptyListOfStatement = |
| List.filled(0, dummyStatement, growable: false); |
| |
| /// Almost const <SwitchCase>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<SwitchCase> emptyListOfSwitchCase = |
| List.filled(0, dummySwitchCase, growable: false); |
| |
| /// Almost const <SwitchExpressionCase>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<SwitchExpressionCase> emptyListOfSwitchExpressionCase = |
| List.filled(0, dummySwitchExpressionCase, growable: false); |
| |
| /// Almost const <PatternSwitchCase>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<PatternSwitchCase> emptyListOfPatternSwitchCase = |
| List.filled(0, dummyPatternSwitchCase, growable: false); |
| |
| /// Almost const <Catch>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Catch> emptyListOfCatch = |
| List.filled(0, dummyCatch, growable: false); |
| |
| /// Almost const <Supertype>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Supertype> emptyListOfSupertype = |
| List.filled(0, dummySupertype, growable: false); |
| |
| /// Almost const <DartType>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<DartType> emptyListOfDartType = |
| List.filled(0, dummyDartType, growable: false); |
| |
| /// Almost const <NamedType>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<NamedType> emptyListOfNamedType = |
| List.filled(0, dummyNamedType, growable: false); |
| |
| /// Almost const <TypeParameter>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<TypeParameter> emptyListOfTypeParameter = |
| List.filled(0, dummyTypeParameter, growable: false); |
| |
| /// Almost const <Constant>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Constant> emptyListOfConstant = |
| List.filled(0, dummyConstant, growable: false); |
| |
| /// Almost const <String>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<String> emptyListOfString = List.filled(0, '', growable: false); |
| |
| /// Almost const <Reference>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Reference> emptyListOfReference = |
| List.filled(0, Reference(), growable: false); |
| |
| /// Almost const <Typedef>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Typedef> emptyListOfTypedef = |
| List.filled(0, dummyTypedef, growable: false); |
| |
| /// Almost const <Extension>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Extension> emptyListOfExtension = |
| List.filled(0, dummyExtension, growable: false); |
| |
| /// Almost const <InlineClass>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<InlineClass> emptyListOfInlineClass = |
| List.filled(0, dummyInlineClass, growable: false); |
| |
| /// Almost const <Field>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Field> emptyListOfField = |
| List.filled(0, dummyField, growable: false); |
| |
| /// Almost const <LibraryPart>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<LibraryPart> emptyListOfLibraryPart = |
| List.filled(0, dummyLibraryPart, growable: false); |
| |
| /// Almost const <LibraryDependency>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<LibraryDependency> emptyListOfLibraryDependency = |
| List.filled(0, dummyLibraryDependency, growable: false); |
| |
| /// Almost const <Procedure>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Procedure> emptyListOfProcedure = |
| List.filled(0, dummyProcedure, growable: false); |
| |
| /// Almost const <MapLiteralEntry>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<MapLiteralEntry> emptyListOfMapLiteralEntry = |
| List.filled(0, dummyMapLiteralEntry, growable: false); |
| |
| /// Almost const <Class>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Class> emptyListOfClass = |
| List.filled(0, dummyClass, growable: false); |
| |
| /// Almost const <ExtensionMemberDescriptor>[], but not const in an attempt to |
| /// avoid polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<ExtensionMemberDescriptor> emptyListOfExtensionMemberDescriptor = |
| List.filled(0, dummyExtensionMemberDescriptor, growable: false); |
| |
| /// Almost const <InlineClassMemberDescriptor>[], but not const in an attempt to |
| /// avoid polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<InlineClassMemberDescriptor> emptyListOfInlineClassMemberDescriptor = |
| List.filled(0, dummyInlineClassMemberDescriptor, growable: false); |
| |
| /// Almost const <InlineType>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<InlineType> emptyListOfInlineType = |
| List.filled(0, dummyInlineType, growable: false); |
| |
| /// Almost const <Constructor>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Constructor> emptyListOfConstructor = |
| List.filled(0, dummyConstructor, growable: false); |
| |
| /// Almost const <RedirectingFactory>[], but not const in an attempt to avoid |
| /// polymorphism. See |
| /// https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<RedirectingFactory> emptyListOfRedirectingFactory = |
| List.filled(0, dummyRedirectingFactory, growable: false); |
| |
| /// Almost const <Initializer>[], but not const in an attempt to avoid |
| /// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Initializer> emptyListOfInitializer = |
| List.filled(0, dummyInitializer, growable: false); |
| |
| /// Non-nullable [DartType] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final DartType dummyDartType = new DynamicType(); |
| |
| /// Non-nullable [Supertype] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Supertype dummySupertype = new Supertype(dummyClass, const []); |
| |
| /// Non-nullable [NamedType] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final NamedType dummyNamedType = |
| new NamedType('', dummyDartType, isRequired: false); |
| |
| /// Non-nullable [Uri] dummy value. |
| final Uri dummyUri = new Uri(scheme: 'dummy'); |
| |
| /// Non-nullable [Name] dummy value. |
| final Name dummyName = new _PublicName(''); |
| |
| /// Non-nullable [Reference] dummy value. |
| final Reference dummyReference = new Reference(); |
| |
| /// Non-nullable [Component] dummy value. |
| /// |
| /// This can be used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Component dummyComponent = new Component(); |
| |
| /// Non-nullable [Library] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Library dummyLibrary = new Library(dummyUri, fileUri: dummyUri); |
| |
| /// Non-nullable [LibraryDependency] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final LibraryDependency dummyLibraryDependency = |
| new LibraryDependency.import(dummyLibrary); |
| |
| /// Non-nullable [Combinator] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Combinator dummyCombinator = new Combinator(false, const []); |
| |
| /// Non-nullable [LibraryPart] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final LibraryPart dummyLibraryPart = new LibraryPart(const [], ''); |
| |
| /// Non-nullable [Class] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Class dummyClass = new Class(name: '', fileUri: dummyUri); |
| |
| /// Non-nullable [Constructor] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Constructor dummyConstructor = |
| new Constructor(dummyFunctionNode, name: dummyName, fileUri: dummyUri); |
| |
| /// Non-nullable [Extension] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Extension dummyExtension = new Extension(name: '', fileUri: dummyUri); |
| |
| /// Non-nullable [ExtensionMemberDescriptor] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final ExtensionMemberDescriptor dummyExtensionMemberDescriptor = |
| new ExtensionMemberDescriptor( |
| name: dummyName, |
| kind: ExtensionMemberKind.Getter, |
| member: dummyReference); |
| |
| /// Non-nullable [InlineClass] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final InlineClass dummyInlineClass = |
| new InlineClass(name: '', fileUri: dummyUri); |
| |
| /// Non-nullable [InlineClassMemberDescriptor] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final InlineClassMemberDescriptor dummyInlineClassMemberDescriptor = |
| new InlineClassMemberDescriptor( |
| name: dummyName, |
| kind: InlineClassMemberKind.Getter, |
| member: dummyReference); |
| |
| /// Non-nullable [InlineType] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final InlineType dummyInlineType = |
| new InlineType(dummyInlineClass, Nullability.nonNullable); |
| |
| /// Non-nullable [Member] dummy value. |
| /// |
| /// This can be used for instance as a dummy initial value for the |
| /// `List.filled` constructor. |
| final Member dummyMember = new Field.mutable(dummyName, fileUri: dummyUri); |
| |
| /// Non-nullable [Procedure] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Procedure dummyProcedure = new Procedure( |
| dummyName, ProcedureKind.Method, dummyFunctionNode, |
| fileUri: dummyUri); |
| |
| /// Non-nullable [Field] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Field dummyField = new Field.mutable(dummyName, fileUri: dummyUri); |
| |
| /// Non-nullable [RedirectingFactory] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final RedirectingFactory dummyRedirectingFactory = new RedirectingFactory(null, |
| name: dummyName, fileUri: dummyUri, function: dummyFunctionNode); |
| |
| /// Non-nullable [Typedef] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Typedef dummyTypedef = new Typedef('', null, fileUri: dummyUri); |
| |
| /// Non-nullable [Initializer] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Initializer dummyInitializer = new InvalidInitializer(); |
| |
| /// Non-nullable [FunctionNode] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final FunctionNode dummyFunctionNode = new FunctionNode(null); |
| |
| /// Non-nullable [Statement] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Statement dummyStatement = new EmptyStatement(); |
| |
| /// Non-nullable [Expression] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Expression dummyExpression = new NullLiteral(); |
| |
| /// Non-nullable [NamedExpression] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final NamedExpression dummyNamedExpression = |
| new NamedExpression('', dummyExpression); |
| |
| /// Almost const <Pattern>[], but not const in an attempt to avoid |
| /// polymorphism. See |
| /// https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<Pattern> emptyListOfPattern = |
| List.filled(0, dummyPattern, growable: false); |
| |
| /// Almost const <NamedPattern>[], but not const in an attempt to avoid |
| /// polymorphism. See |
| /// https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<NamedPattern> emptyListOfNamedPattern = |
| List.filled(0, dummyNamedPattern, growable: false); |
| |
| /// Almost const <MapPatternEntry>[], but not const in an attempt to avoid |
| /// polymorphism. See |
| /// https://dart-review.googlesource.com/c/sdk/+/185828. |
| final List<MapPatternEntry> emptyListOfMapPatternEntry = |
| List.filled(0, dummyMapPatternEntry, growable: false); |
| |
| /// Non-nullable [VariableDeclaration] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final VariableDeclaration dummyVariableDeclaration = |
| new VariableDeclaration(null, isSynthesized: true); |
| |
| /// Non-nullable [TypeParameter] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final TypeParameter dummyTypeParameter = new TypeParameter(); |
| |
| /// Non-nullable [MapLiteralEntry] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final MapLiteralEntry dummyMapLiteralEntry = |
| new MapLiteralEntry(dummyExpression, dummyExpression); |
| |
| /// Non-nullable [Arguments] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Arguments dummyArguments = new Arguments(const []); |
| |
| /// Non-nullable [AssertStatement] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final AssertStatement dummyAssertStatement = new AssertStatement( |
| dummyExpression, |
| conditionStartOffset: TreeNode.noOffset, |
| conditionEndOffset: TreeNode.noOffset); |
| |
| /// Non-nullable [SwitchCase] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final SwitchCase dummySwitchCase = new SwitchCase.defaultCase(dummyStatement); |
| |
| /// Non-nullable [Catch] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Catch dummyCatch = new Catch(null, dummyStatement); |
| |
| /// Non-nullable [Constant] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final Constant dummyConstant = new NullConstant(); |
| |
| /// Non-nullable [LabeledStatement] dummy value. |
| /// |
| /// This is used as the removal sentinel in [RemovingTransformer] and can be |
| /// used for instance as a dummy initial value for the `List.filled` |
| /// constructor. |
| final LabeledStatement dummyLabeledStatement = new LabeledStatement(null); |
| |
| /// Of the dummy nodes, some are tree nodes. `TreeNode`s has a parent pointer |
| /// and that can be set when the dummy is used. This means that we can leak |
| /// through them. This list will (at least as a stopgap) allow us to null-out |
| /// the parent pointer when/if needed. |
| /// |
| /// This should manually be kept up to date. |
| final List<TreeNode> dummyTreeNodes = [ |
| dummyComponent, |
| dummyLibrary, |
| dummyLibraryDependency, |
| dummyCombinator, |
| dummyLibraryPart, |
| dummyClass, |
| dummyConstructor, |
| dummyExtension, |
| dummyMember, |
| dummyProcedure, |
| dummyField, |
| dummyRedirectingFactory, |
| dummyTypedef, |
| dummyInitializer, |
| dummyFunctionNode, |
| dummyStatement, |
| dummyExpression, |
| dummyNamedExpression, |
| dummyVariableDeclaration, |
| dummyTypeParameter, |
| dummyMapLiteralEntry, |
| dummyArguments, |
| dummyAssertStatement, |
| dummySwitchCase, |
| dummyCatch, |
| dummyLabeledStatement, |
| ]; |
| |
| void clearDummyTreeNodesParentPointer() { |
| for (TreeNode treeNode in dummyTreeNodes) { |
| treeNode.parent = null; |
| } |
| } |
| |
| /// Sentinel value used to signal that a node cannot be removed through the |
| /// [RemovingTransformer]. |
| const Null cannotRemoveSentinel = null; |
| |
| /// Helper that can be used in asserts to check that [list] is mutable by |
| /// adding and removing [dummyElement]. |
| bool checkListIsMutable<E>(List<E> list, E dummyElement) { |
| list |
| ..add(dummyElement) |
| ..removeLast(); |
| return true; |
| } |