Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1 | // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | /// ----------------------------------------------------------------------- |
| 6 | /// ERROR HANDLING |
| 7 | /// ----------------------------------------------------------------------- |
| 8 | /// |
Asger Feldthaus | 0d9ab90 | 2016-08-17 11:51:33 | [diff] [blame] | 9 | /// As a rule of thumb, errors that can be detected statically are handled by |
| 10 | /// the frontend, typically by translating the erroneous code into a 'throw' or |
| 11 | /// a call to 'noSuchMethod'. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 12 | /// |
Asger Feldthaus | 0d9ab90 | 2016-08-17 11:51:33 | [diff] [blame] | 13 | /// For example, there are no arity mismatches in static invocations, and |
| 14 | /// there are no direct invocations of a constructor on a abstract class. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 15 | /// |
| 16 | /// ----------------------------------------------------------------------- |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 17 | /// STATIC vs TOP-LEVEL |
| 18 | /// ----------------------------------------------------------------------- |
| 19 | /// |
| 20 | /// The term `static` includes both static class members and top-level members. |
| 21 | /// |
| 22 | /// "Static class member" is the preferred term for non-top level statics. |
| 23 | /// |
| 24 | /// Static class members are not lifted to the library level because mirrors |
| 25 | /// and stack traces can observe that they are class members. |
| 26 | /// |
| 27 | /// ----------------------------------------------------------------------- |
| 28 | /// PROCEDURES |
| 29 | /// ----------------------------------------------------------------------- |
| 30 | /// |
| 31 | /// "Procedure" is an umbrella term for method, getter, setter, index-getter, |
| 32 | /// index-setter, operator overloader, and factory constructor. |
| 33 | /// |
| 34 | /// Generative constructors, field initializers, local functions are NOT |
| 35 | /// procedures. |
| 36 | /// |
| 37 | /// ----------------------------------------------------------------------- |
| 38 | /// TRANSFORMATIONS |
| 39 | /// ----------------------------------------------------------------------- |
| 40 | /// |
| 41 | /// AST transformations can be performed using [TreeNode.replaceWith] or the |
| 42 | /// [Transformer] visitor class. |
| 43 | /// |
| 44 | /// Use [Transformer] for bulk transformations that are likely to transform lots |
| 45 | /// of nodes, and [TreeNode.replaceWith] for sparse transformations that mutate |
| 46 | /// relatively few nodes. Or use whichever is more convenient. |
| 47 | /// |
| 48 | /// The AST can also be mutated by direct field manipulation, but the user then |
| 49 | /// has to update parent pointers manually. |
| 50 | /// |
| 51 | library kernel.ast; |
| 52 | |
| 53 | import 'visitor.dart'; |
| 54 | export 'visitor.dart'; |
| 55 | |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 56 | import 'type_propagation/type_propagation.dart'; |
| 57 | export 'type_propagation/type_propagation.dart'; |
| 58 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 59 | import 'transformations/flags.dart'; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 60 | import 'text/ast_to_text.dart'; |
| 61 | import 'type_algebra.dart'; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 62 | import 'type_environment.dart'; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 63 | |
| 64 | /// Any type of node in the IR. |
| 65 | abstract class Node { |
| 66 | const Node(); |
| 67 | |
| 68 | accept(Visitor v); |
| 69 | visitChildren(Visitor v); |
| 70 | |
| 71 | /// Returns the textual representation of this node for use in debugging. |
| 72 | /// |
| 73 | /// [toString] should only be used for debugging and short-running test tools |
| 74 | /// as it can cause serious memory leaks. |
| 75 | /// |
| 76 | /// Synthetic names are cached globally to retain consistency across different |
| 77 | /// [toString] calls (hence the memory leak). |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 78 | /// |
| 79 | /// Nodes that are named, such as [Class] and [Member], return their |
| 80 | /// (possibly synthesized) name, whereas other AST nodes return the complete |
| 81 | /// textual representation of their subtree. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 82 | String toString() => debugNodeToString(this); |
| 83 | } |
| 84 | |
| 85 | /// A mutable AST node with a parent pointer. |
| 86 | /// |
| 87 | /// This is anything other than [Name] and [DartType] nodes. |
| 88 | abstract class TreeNode extends Node { |
| 89 | static int _hashCounter = 0; |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 90 | final int hashCode = _hashCounter = (_hashCounter + 1) & 0x3fffffff; |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 91 | static const int noOffset = -1; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 92 | |
| 93 | TreeNode parent; |
| 94 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 95 | /// Offset in the source file it comes from. Valid values are from 0 and up, |
| 96 | /// or -1 ([noOffset]) if the file offset is not available |
| 97 | /// (this is the default if none is specifically set). |
| 98 | int fileOffset = noOffset; |
| 99 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 100 | accept(TreeVisitor v); |
| 101 | visitChildren(Visitor v); |
| 102 | transformChildren(Transformer v); |
| 103 | |
| 104 | /// Replaces [child] with [replacement]. |
| 105 | /// |
| 106 | /// The caller is responsible for ensuring that the AST remains a tree. In |
| 107 | /// particular, [replacement] should be an orphan or be part of an orphaned |
| 108 | /// subtree. |
| 109 | /// |
| 110 | /// Has no effect if [child] is not actually a child of this node. |
| 111 | /// |
| 112 | /// If [replacement] is `null`, this will [remove] the [child] node. |
| 113 | void replaceChild(TreeNode child, TreeNode replacement) { |
| 114 | transformChildren(new _ChildReplacer(child, replacement)); |
| 115 | } |
| 116 | |
| 117 | /// Inserts another node in place of this one. |
| 118 | /// |
| 119 | /// The caller is responsible for ensuring that the AST remains a tree. In |
| 120 | /// particular, [replacement] should be an orphan or be part of an orphaned |
| 121 | /// subtree. |
| 122 | /// |
| 123 | /// If [replacement] is `null`, this will [remove] the node. |
| 124 | void replaceWith(TreeNode replacement) { |
| 125 | parent.replaceChild(this, replacement); |
| 126 | parent = null; |
| 127 | } |
| 128 | |
| 129 | /// Removes this node from the [List] it is currently stored in, or assigns |
| 130 | /// `null` to the field on the parent currently pointing to the node. |
| 131 | /// |
| 132 | /// Has no effect if the node is orphaned or if the parent pointer is stale. |
| 133 | void remove() { |
| 134 | parent?.replaceChild(this, null); |
| 135 | parent = null; |
| 136 | } |
Asger Feldthaus | 5a40636 | 2016-10-31 10:29:37 | [diff] [blame] | 137 | |
| 138 | Program get enclosingProgram => parent?.enclosingProgram; |
| 139 | |
| 140 | /// Returns the best known source location of the given AST node, or `null` if |
| 141 | /// the node is orphaned. |
| 142 | /// |
| 143 | /// This getter is intended for diagnostics and debugging, and should be |
| 144 | /// avoided in production code. |
| 145 | Location get location { |
| 146 | if (fileOffset == noOffset) return parent?.location; |
| 147 | return _getLocationInEnclosingFile(fileOffset); |
| 148 | } |
| 149 | |
| 150 | Location _getLocationInEnclosingFile(int offset) { |
| 151 | return parent?._getLocationInEnclosingFile(offset); |
| 152 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | // ------------------------------------------------------------------------ |
| 156 | // LIBRARIES and CLASSES |
| 157 | // ------------------------------------------------------------------------ |
| 158 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 159 | class Library extends TreeNode implements Comparable<Library> { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 160 | /// An absolute import path to this library. |
| 161 | /// |
| 162 | /// The [Uri] should have the `dart`, `package`, or `file` scheme. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 163 | Uri importUri; |
| 164 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 165 | /// The uri of the source file this library was loaded from. |
| 166 | String fileUri; |
| 167 | |
Asger Feldthaus | cd94dc0 | 2016-10-04 15:26:10 | [diff] [blame] | 168 | /// If true, the library is part of another build unit and its contents |
| 169 | /// are only partially loaded. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 170 | /// |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 171 | /// Classes of an external library are loaded at one of the [ClassLevel]s |
| 172 | /// other than [ClassLevel.Body]. Members in an external library have no |
| 173 | /// body, but have their typed interface present. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 174 | /// |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 175 | /// If the libary is non-external, then its classes are at [ClassLevel.Body] |
| 176 | /// and all members are loaded. |
| 177 | bool isExternal; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 178 | |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 179 | String name; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 180 | final List<Class> classes; |
| 181 | final List<Procedure> procedures; |
| 182 | final List<Field> fields; |
| 183 | |
| 184 | Library(this.importUri, |
| 185 | {this.name, |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 186 | this.isExternal: false, |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 187 | List<Class> classes, |
| 188 | List<Procedure> procedures, |
| 189 | List<Field> fields}) |
| 190 | : this.classes = classes ?? <Class>[], |
| 191 | this.procedures = procedures ?? <Procedure>[], |
| 192 | this.fields = fields ?? <Field>[] { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 193 | setParents(this.classes, this); |
| 194 | setParents(this.procedures, this); |
| 195 | setParents(this.fields, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 196 | } |
| 197 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 198 | /// Returns the top-level fields and procedures defined in this library. |
| 199 | /// |
| 200 | /// This getter is for convenience, not efficiency. Consider manually |
| 201 | /// iterating the members to speed up code in production. |
| 202 | Iterable<Member> get members => |
| 203 | <Iterable<Member>>[fields, procedures].expand((x) => x); |
| 204 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 205 | void addMember(Member member) { |
| 206 | member.parent = this; |
| 207 | if (member is Procedure) { |
| 208 | procedures.add(member); |
| 209 | } else if (member is Field) { |
| 210 | fields.add(member); |
| 211 | } else { |
| 212 | throw new ArgumentError(member); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | void addClass(Class class_) { |
| 217 | class_.parent = this; |
| 218 | classes.add(class_); |
| 219 | } |
| 220 | |
| 221 | accept(TreeVisitor v) => v.visitLibrary(this); |
| 222 | |
| 223 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 224 | visitList(classes, v); |
| 225 | visitList(procedures, v); |
| 226 | visitList(fields, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 227 | } |
| 228 | |
| 229 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 230 | transformList(classes, v, this); |
| 231 | transformList(procedures, v, this); |
| 232 | transformList(fields, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 233 | } |
| 234 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 235 | static int _libraryIdCounter = 0; |
| 236 | int _libraryId = ++_libraryIdCounter; |
| 237 | |
| 238 | int compareTo(Library other) => _libraryId - other._libraryId; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 239 | |
| 240 | /// Returns a possibly synthesized name for this library, consistent with |
| 241 | /// the names across all [toString] calls. |
| 242 | String toString() => debugLibraryName(this); |
Asger Feldthaus | 5a40636 | 2016-10-31 10:29:37 | [diff] [blame] | 243 | |
| 244 | Location _getLocationInEnclosingFile(int offset) { |
| 245 | return enclosingProgram.getLocation(fileUri, offset); |
| 246 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 247 | } |
| 248 | |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 249 | /// The degree to which the contents of a class have been loaded into memory. |
| 250 | /// |
| 251 | /// Each level imply the requirements of the previous ones. |
| 252 | enum ClassLevel { |
| 253 | /// Temporary loading level for internal use by IR producers. Consumers of |
| 254 | /// kernel code should not expect to see classes at this level. |
| 255 | Temporary, |
| 256 | |
| 257 | /// The class may be used as a type, and it may contain members that are |
| 258 | /// referenced from this build unit. |
| 259 | /// |
| 260 | /// The type parameters and their bounds are present. |
| 261 | /// |
| 262 | /// There is no guarantee that all members are present. |
| 263 | /// |
| 264 | /// All supertypes of this class are at [Type] level or higher. |
| 265 | Type, |
| 266 | |
| 267 | /// All instance members of the class are present. |
| 268 | /// |
| 269 | /// All supertypes of this class are at [Hierarchy] level or higher. |
| 270 | /// |
| 271 | /// This level exists so supertypes of a fully loaded class contain all the |
| 272 | /// members needed to detect override constraints. |
| 273 | Hierarchy, |
| 274 | |
| 275 | /// All members of the class are fully loaded and are in the correct order. |
| 276 | /// |
| 277 | /// Annotations are present on classes and members. |
| 278 | /// |
| 279 | /// All supertypes of this class are at [Hierarchy] level or higher, |
| 280 | /// not necessarily at [Body] level. |
| 281 | Body, |
| 282 | } |
| 283 | |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 284 | /// Declaration of a regular class or a mixin application. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 285 | /// |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 286 | /// Mixin applications may not contain fields or procedures, as they implicitly |
| 287 | /// use those from its mixed-in type. However, the IR does not enforce this |
| 288 | /// rule directly, as doing so can obstruct transformations. It is possible to |
| 289 | /// transform a mixin application to become a regular class, and vice versa. |
| 290 | class Class extends TreeNode { |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 291 | /// The degree to which the contents of the class have been loaded. |
| 292 | ClassLevel level = ClassLevel.Body; |
| 293 | |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 294 | /// List of metadata annotations on the class. |
| 295 | /// |
| 296 | /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| 297 | /// annotations if needed. |
| 298 | List<Expression> annotations = const <Expression>[]; |
| 299 | |
Asger Feldthaus | 97111b8 | 2016-08-31 07:42:55 | [diff] [blame] | 300 | /// Name of the class. |
| 301 | /// |
| 302 | /// Must be non-null and must be unique within the library. |
| 303 | /// |
| 304 | /// The name may contain characters that are not valid in a Dart identifier, |
| 305 | /// in particular, the symbol '&' is used in class names generated for mixin |
| 306 | /// applications. |
| 307 | String name; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 308 | bool isAbstract; |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 309 | |
| 310 | /// The uri of the source file this class was loaded from. |
| 311 | String fileUri; |
| 312 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 313 | final List<TypeParameter> typeParameters; |
| 314 | |
| 315 | /// The immediate super type, or `null` if this is the root class. |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 316 | Supertype supertype; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 317 | |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 318 | /// The mixed-in type if this is a mixin application, otherwise `null`. |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 319 | Supertype mixedInType; |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 320 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 321 | /// The types from the `implements` clause. |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 322 | final List<Supertype> implementedTypes; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 323 | |
| 324 | /// Fields declared in the class. |
| 325 | /// |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 326 | /// For mixin applications this should be empty. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 327 | final List<Field> fields; |
| 328 | |
| 329 | /// Constructors declared in the class. |
| 330 | final List<Constructor> constructors; |
| 331 | |
| 332 | /// Procedures declared in the class. |
| 333 | /// |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 334 | /// For mixin applications this should be empty. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 335 | final List<Procedure> procedures; |
| 336 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 337 | Class( |
| 338 | {this.name, |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 339 | this.isAbstract: false, |
| 340 | this.supertype, |
| 341 | this.mixedInType, |
| 342 | List<TypeParameter> typeParameters, |
| 343 | List<InterfaceType> implementedTypes, |
| 344 | List<Constructor> constructors, |
| 345 | List<Procedure> procedures, |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 346 | List<Field> fields, |
| 347 | this.fileUri}) |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 348 | : this.typeParameters = typeParameters ?? <TypeParameter>[], |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 349 | this.implementedTypes = implementedTypes ?? <Supertype>[], |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 350 | this.fields = fields ?? <Field>[], |
| 351 | this.constructors = constructors ?? <Constructor>[], |
| 352 | this.procedures = procedures ?? <Procedure>[] { |
| 353 | setParents(this.typeParameters, this); |
| 354 | setParents(this.constructors, this); |
| 355 | setParents(this.procedures, this); |
| 356 | setParents(this.fields, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 357 | } |
| 358 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 359 | /// The immediate super class, or `null` if this is the root class. |
| 360 | Class get superclass => supertype?.classNode; |
| 361 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 362 | /// The mixed-in class if this is a mixin application, otherwise `null`. |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 363 | /// |
| 364 | /// Note that this may itself be a mixin application. Use [mixin] to get the |
| 365 | /// class that has the fields and procedures. |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 366 | Class get mixedInClass => mixedInType?.classNode; |
| 367 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 368 | /// The class that declares the field and procedures of this class. |
| 369 | Class get mixin => mixedInClass?.mixin ?? this; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 370 | |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 371 | bool get isMixinApplication => mixedInType != null; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 372 | |
| 373 | /// Members declared in this class. |
| 374 | /// |
| 375 | /// This getter is for convenience, not efficiency. Consider manually |
| 376 | /// iterating the members to speed up code in production. |
| 377 | Iterable<Member> get members => |
| 378 | <Iterable<Member>>[fields, constructors, procedures].expand((x) => x); |
| 379 | |
| 380 | /// The immediately extended, mixed-in, and implemented types. |
| 381 | /// |
| 382 | /// This getter is for convenience, not efficiency. Consider manually |
| 383 | /// iterating the super types to speed up code in production. |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 384 | Iterable<Supertype> get supers => <Iterable<Supertype>>[ |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 385 | supertype == null ? const [] : [supertype], |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 386 | mixedInType == null ? const [] : [mixedInType], |
| 387 | implementedTypes |
| 388 | ].expand((x) => x); |
| 389 | |
| 390 | /// The library containing this class. |
| 391 | Library get enclosingLibrary => parent; |
| 392 | |
| 393 | /// Adds a member to this class. |
| 394 | /// |
| 395 | /// Throws an error if attempting to add a field or procedure to a mixin |
| 396 | /// application. |
| 397 | void addMember(Member member) { |
| 398 | member.parent = this; |
| 399 | if (member is Constructor) { |
| 400 | constructors.add(member); |
| 401 | } else if (member is Procedure) { |
| 402 | procedures.add(member); |
| 403 | } else if (member is Field) { |
| 404 | fields.add(member); |
| 405 | } else { |
| 406 | throw new ArgumentError(member); |
| 407 | } |
| 408 | } |
| 409 | |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 410 | void addAnnotation(Expression node) { |
| 411 | if (annotations.isEmpty) { |
| 412 | annotations = <Expression>[]; |
| 413 | } |
| 414 | annotations.add(node); |
| 415 | node.parent = this; |
| 416 | } |
| 417 | |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 418 | accept(TreeVisitor v) => v.visitClass(this); |
| 419 | acceptReference(Visitor v) => v.visitClassReference(this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 420 | |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 421 | /// If true, the class is part of an external library, that is, it is defined |
| 422 | /// in another build unit. Only a subset of its members are present. |
| 423 | /// |
| 424 | /// These classes should be loaded at either [ClassLevel.Type] or |
| 425 | /// [ClassLevel.Hierarchy] level. |
| 426 | bool get isInExternalLibrary => enclosingLibrary.isExternal; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 427 | |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 428 | Supertype get asRawSupertype { |
| 429 | return new Supertype(this, |
| 430 | new List<DartType>.filled(typeParameters.length, const DynamicType())); |
| 431 | } |
| 432 | |
| 433 | Supertype get asThisSupertype { |
| 434 | return new Supertype(this, _getAsTypeArguments(typeParameters)); |
| 435 | } |
| 436 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 437 | InterfaceType _rawType; |
| 438 | InterfaceType get rawType => _rawType ??= new InterfaceType(this); |
| 439 | |
| 440 | InterfaceType _thisType; |
| 441 | InterfaceType get thisType { |
| 442 | return _thisType ??= |
| 443 | new InterfaceType(this, _getAsTypeArguments(typeParameters)); |
| 444 | } |
| 445 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 446 | InterfaceType _bottomType; |
| 447 | InterfaceType get bottomType { |
| 448 | return _bottomType ??= new InterfaceType(this, |
| 449 | new List<DartType>.filled(typeParameters.length, const BottomType())); |
| 450 | } |
| 451 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 452 | /// Returns a possibly synthesized name for this class, consistent with |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 453 | /// the names used across all [toString] calls. |
| 454 | String toString() => debugQualifiedClassName(this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 455 | |
| 456 | visitChildren(Visitor v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 457 | visitList(annotations, v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 458 | visitList(typeParameters, v); |
| 459 | supertype?.accept(v); |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 460 | mixedInType?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 461 | visitList(implementedTypes, v); |
| 462 | visitList(constructors, v); |
| 463 | visitList(procedures, v); |
| 464 | visitList(fields, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | transformChildren(Transformer v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 468 | transformList(annotations, v, this); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 469 | transformList(typeParameters, v, this); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 470 | if (supertype != null) { |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 471 | supertype = v.visitSupertype(supertype); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 472 | } |
| 473 | if (mixedInType != null) { |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 474 | mixedInType = v.visitSupertype(mixedInType); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 475 | } |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 476 | transformSupertypeList(implementedTypes, v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 477 | transformList(constructors, v, this); |
| 478 | transformList(procedures, v, this); |
| 479 | transformList(fields, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 480 | } |
Asger Feldthaus | 5a40636 | 2016-10-31 10:29:37 | [diff] [blame] | 481 | |
| 482 | Location _getLocationInEnclosingFile(int offset) { |
| 483 | return enclosingProgram.getLocation(fileUri, offset); |
| 484 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 485 | } |
| 486 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 487 | // ------------------------------------------------------------------------ |
| 488 | // MEMBERS |
| 489 | // ------------------------------------------------------------------------ |
| 490 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 491 | /// A indirect reference to a member, which can be updated to point at another |
| 492 | /// member at a later time. |
| 493 | class _MemberAccessor { |
| 494 | Member target; |
| 495 | _MemberAccessor(this.target); |
| 496 | } |
| 497 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 498 | abstract class Member extends TreeNode { |
Harry Terkelsen | 9509d28 | 2016-08-22 16:56:05 | [diff] [blame] | 499 | /// List of metadata annotations on the member. |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 500 | /// |
| 501 | /// This defaults to an immutable empty list. Use [addAnnotation] to add |
| 502 | /// annotations if needed. |
| 503 | List<Expression> annotations = const <Expression>[]; |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 504 | Name name; |
| 505 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 506 | /// Flags summarizing the kinds of AST nodes contained in this member, for |
| 507 | /// speeding up transformations that only affect certain types of nodes. |
| 508 | /// |
| 509 | /// See [TransformerFlag] for the meaning of each bit. |
| 510 | /// |
| 511 | /// These should not be used for any purpose other than skipping certain |
| 512 | /// members if it can be determined that no work is needed in there. |
| 513 | /// |
| 514 | /// It is valid for these flags to be false positives in rare cases, so |
| 515 | /// transformers must tolerate the case where a flag is spuriously set. |
| 516 | /// |
| 517 | /// This value is not serialized; it is populated by the frontend and the |
| 518 | /// deserializer. |
| 519 | // |
| 520 | // TODO(asgerf): It might be worthwhile to put this on classes as well. |
| 521 | int transformerFlags = 0; |
| 522 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 523 | Member(this.name); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 524 | |
| 525 | Class get enclosingClass => parent is Class ? parent : null; |
| 526 | Library get enclosingLibrary => parent is Class ? parent.parent : parent; |
| 527 | |
| 528 | accept(MemberVisitor v); |
| 529 | acceptReference(MemberReferenceVisitor v); |
| 530 | |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 531 | /// If true, the member is part of an external library, that is, it is defined |
| 532 | /// in another build unit. Such members have no body or initializer present |
| 533 | /// in the IR. |
| 534 | bool get isInExternalLibrary => enclosingLibrary.isExternal; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 535 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 536 | /// Returns true if this is an abstract procedure. |
| 537 | bool get isAbstract => false; |
| 538 | |
Asger Feldthaus | e3f2bae | 2016-09-13 10:40:11 | [diff] [blame] | 539 | /// True if this is a field or non-setter procedure. |
| 540 | /// |
| 541 | /// Note that operators and factories return `true`, even though there are |
| 542 | /// normally no calls to their getter. |
| 543 | bool get hasGetter; |
| 544 | |
| 545 | /// True if this is a setter or a mutable field. |
| 546 | bool get hasSetter; |
| 547 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 548 | /// True if this is a non-static field or procedure. |
| 549 | bool get isInstanceMember; |
| 550 | |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 551 | /// True if the member has the `external` modifier, implying that the |
| 552 | /// implementation is provided by the backend, and is not necessarily written |
| 553 | /// in Dart. |
| 554 | /// |
| 555 | /// Members can have this modifier independently of whether the enclosing |
| 556 | /// library is external. |
Asger Feldthaus | 3781ba1 | 2016-08-16 11:18:46 | [diff] [blame] | 557 | bool get isExternal; |
| 558 | void set isExternal(bool value); |
| 559 | |
Asger Feldthaus | 8a3e753 | 2016-08-02 16:22:54 | [diff] [blame] | 560 | /// The body of the procedure or constructor, or `null` if this is a field. |
| 561 | FunctionNode get function => null; |
| 562 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 563 | /// Returns a possibly synthesized name for this member, consistent with |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 564 | /// the names used across all [toString] calls. |
| 565 | String toString() => debugQualifiedMemberName(this); |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 566 | |
| 567 | void addAnnotation(Expression node) { |
| 568 | if (annotations.isEmpty) { |
| 569 | annotations = <Expression>[]; |
| 570 | } |
| 571 | annotations.add(node); |
| 572 | node.parent = this; |
| 573 | } |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 574 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 575 | DartType get getterType; |
| 576 | DartType get setterType; |
| 577 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 578 | bool get containsSuperCalls { |
| 579 | return transformerFlags & TransformerFlag.superCalls != 0; |
| 580 | } |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 581 | |
| 582 | _MemberAccessor get _getterInterface; |
| 583 | _MemberAccessor get _setterInterface; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 584 | } |
| 585 | |
| 586 | /// A field declaration. |
| 587 | /// |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 588 | /// The implied getter and setter for the field are not represented explicitly, |
| 589 | /// but can be made explicit if needed. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 590 | class Field extends Member { |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 591 | _MemberAccessor _getterInterface, _setterInterface; |
| 592 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 593 | DartType type; // Not null. Defaults to DynamicType. |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 594 | InferredValue inferredValue; // May be null. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 595 | int flags = 0; |
| 596 | Expression initializer; // May be null. |
| 597 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 598 | /// The uri of the source file this field was loaded from. |
| 599 | String fileUri; |
| 600 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 601 | Field(Name name, |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 602 | {this.type: const DynamicType(), |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 603 | this.inferredValue, |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 604 | this.initializer, |
| 605 | bool isFinal: false, |
| 606 | bool isConst: false, |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 607 | bool isStatic: false, |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 608 | bool hasImplicitGetter, |
| 609 | bool hasImplicitSetter, |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 610 | int transformerFlags: 0, |
| 611 | this.fileUri}) |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 612 | : super(name) { |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 613 | _getterInterface = new _MemberAccessor(this); |
| 614 | _setterInterface = new _MemberAccessor(this); |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 615 | assert(type != null); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 616 | initializer?.parent = this; |
| 617 | this.isFinal = isFinal; |
| 618 | this.isConst = isConst; |
| 619 | this.isStatic = isStatic; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 620 | this.hasImplicitGetter = hasImplicitGetter ?? !isStatic; |
| 621 | this.hasImplicitSetter = hasImplicitSetter ?? (!isStatic && !isFinal); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 622 | this.transformerFlags = transformerFlags; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 623 | } |
| 624 | |
| 625 | static const int FlagFinal = 1 << 0; // Must match serialized bit positions. |
| 626 | static const int FlagConst = 1 << 1; |
| 627 | static const int FlagStatic = 1 << 2; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 628 | static const int FlagHasImplicitGetter = 1 << 3; |
| 629 | static const int FlagHasImplicitSetter = 1 << 4; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 630 | |
| 631 | bool get isFinal => flags & FlagFinal != 0; |
| 632 | bool get isConst => flags & FlagConst != 0; |
| 633 | bool get isStatic => flags & FlagStatic != 0; |
| 634 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 635 | /// If true, a getter should be generated for this field. |
| 636 | /// |
| 637 | /// If false, there may or may not exist an explicit getter in the same class |
| 638 | /// with the same name as the field. |
| 639 | /// |
| 640 | /// By default, all non-static fields have implicit getters. |
| 641 | bool get hasImplicitGetter => flags & FlagHasImplicitGetter != 0; |
| 642 | |
| 643 | /// If true, a setter should be generated for this field. |
| 644 | /// |
| 645 | /// If false, there may or may not exist an explicit setter in the same class |
| 646 | /// with the same name as the field. |
| 647 | /// |
| 648 | /// Final fields never have implicit setters, but a field without an implicit |
| 649 | /// setter is not necessarily final, as it may be mutated by direct field |
| 650 | /// access. |
| 651 | /// |
| 652 | /// By default, all non-static, non-final fields have implicit getters. |
| 653 | bool get hasImplicitSetter => flags & FlagHasImplicitSetter != 0; |
| 654 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 655 | void set isFinal(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 656 | flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 657 | } |
| 658 | |
| 659 | void set isConst(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 660 | flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 661 | } |
| 662 | |
| 663 | void set isStatic(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 664 | flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 665 | } |
| 666 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 667 | void set hasImplicitGetter(bool value) { |
| 668 | flags = value |
| 669 | ? (flags | FlagHasImplicitGetter) |
| 670 | : (flags & ~FlagHasImplicitGetter); |
| 671 | } |
| 672 | |
| 673 | void set hasImplicitSetter(bool value) { |
| 674 | flags = value |
| 675 | ? (flags | FlagHasImplicitSetter) |
| 676 | : (flags & ~FlagHasImplicitSetter); |
| 677 | } |
| 678 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 679 | /// True if the field is neither final nor const. |
Harry Terkelsen | 9509d28 | 2016-08-22 16:56:05 | [diff] [blame] | 680 | bool get isMutable => flags & (FlagFinal | FlagConst) == 0; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 681 | bool get isInstanceMember => !isStatic; |
Asger Feldthaus | e3f2bae | 2016-09-13 10:40:11 | [diff] [blame] | 682 | bool get hasGetter => true; |
| 683 | bool get hasSetter => isMutable; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 684 | |
Asger Feldthaus | 3781ba1 | 2016-08-16 11:18:46 | [diff] [blame] | 685 | bool get isExternal => false; |
| 686 | void set isExternal(bool value) { |
| 687 | if (value) throw 'Fields cannot be external'; |
| 688 | } |
| 689 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 690 | accept(MemberVisitor v) => v.visitField(this); |
| 691 | |
| 692 | acceptReference(MemberReferenceVisitor v) => v.visitFieldReference(this); |
| 693 | |
| 694 | visitChildren(Visitor v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 695 | visitList(annotations, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 696 | type?.accept(v); |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 697 | inferredValue?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 698 | name?.accept(v); |
| 699 | initializer?.accept(v); |
| 700 | } |
| 701 | |
| 702 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 703 | type = v.visitDartType(type); |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 704 | transformList(annotations, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 705 | if (initializer != null) { |
| 706 | initializer = initializer.accept(v); |
| 707 | initializer?.parent = this; |
| 708 | } |
| 709 | } |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 710 | |
| 711 | DartType get getterType => type; |
| 712 | DartType get setterType => isMutable ? type : const BottomType(); |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 713 | |
| 714 | /// Makes all [PropertyGet]s that have this field as its interface target |
| 715 | /// use [getter] as its interface target instead. |
| 716 | /// |
| 717 | /// That can be used to introduce an explicit getter for a field instead of |
| 718 | /// its implicit getter. |
| 719 | /// |
| 720 | /// This method only updates the stored interface target -- the caller must |
| 721 | /// ensure that [getter] actually becomes the target for dispatches that |
| 722 | /// would previously hit the implicit field getter. |
| 723 | /// |
| 724 | /// [DirectPropertyGet]s are not affected, and will continue to access the |
| 725 | /// field directly. [PropertyGet] nodes created after the call will not be |
| 726 | /// affected until the method is called again. |
| 727 | /// |
| 728 | /// Existing [ClassHierarchy] instances are not affected by this call. |
| 729 | void replaceGetterInterfaceWith(Procedure getter) { |
| 730 | _getterInterface.target = getter; |
| 731 | _getterInterface = new _MemberAccessor(this); |
| 732 | } |
| 733 | |
| 734 | /// Makes all [PropertySet]s that have this field as its interface target |
| 735 | /// use [setter] as its interface target instead. |
| 736 | /// |
| 737 | /// That can be used to introduce an explicit setter for a field instead of |
| 738 | /// its implicit setter. |
| 739 | /// |
| 740 | /// This method only updates the stored interface target -- the caller must |
| 741 | /// ensure that [setter] actually becomes the target for dispatches that |
| 742 | /// would previously hit the implicit field setter. |
| 743 | /// |
| 744 | /// [DirectPropertySet] and [FieldInitializer]s are not affected, and will |
| 745 | /// continue to access the field directly. [PropertySet] nodes created after |
| 746 | /// the call will not be affected until the method is called again. |
| 747 | /// |
| 748 | /// Existing [ClassHierarchy] instances are not affected by this call. |
| 749 | void replaceSetterInterfaceWith(Procedure setter) { |
| 750 | _setterInterface.target = setter; |
| 751 | _setterInterface = new _MemberAccessor(this); |
| 752 | } |
Asger Feldthaus | 5a40636 | 2016-10-31 10:29:37 | [diff] [blame] | 753 | |
| 754 | Location _getLocationInEnclosingFile(int offset) { |
| 755 | return enclosingProgram.getLocation(fileUri, offset); |
| 756 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 757 | } |
| 758 | |
| 759 | /// A generative constructor, possibly redirecting. |
| 760 | /// |
| 761 | /// Note that factory constructors are treated as [Procedure]s. |
| 762 | /// |
| 763 | /// Constructors do not take type parameters. Type arguments from a constructor |
| 764 | /// invocation should be matched with the type parameters declared in the class. |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 765 | /// |
Asger Feldthaus | 3ac36ac | 2016-10-04 11:30:46 | [diff] [blame] | 766 | /// For unnamed constructors, the name is an empty string (in a [Name]). |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 767 | class Constructor extends Member { |
| 768 | int flags = 0; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 769 | FunctionNode function; |
| 770 | List<Initializer> initializers; |
| 771 | |
| 772 | Constructor(this.function, |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 773 | {Name name, |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 774 | bool isConst: false, |
| 775 | bool isExternal: false, |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 776 | List<Initializer> initializers, |
| 777 | int transformerFlags: 0}) |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 778 | : this.initializers = initializers ?? <Initializer>[], |
| 779 | super(name) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 780 | function?.parent = this; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 781 | setParents(this.initializers, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 782 | this.isConst = isConst; |
| 783 | this.isExternal = isExternal; |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 784 | this.transformerFlags = transformerFlags; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 785 | } |
| 786 | |
| 787 | static const int FlagConst = 1 << 0; // Must match serialized bit positions. |
| 788 | static const int FlagExternal = 1 << 1; |
| 789 | |
| 790 | bool get isConst => flags & FlagConst != 0; |
| 791 | bool get isExternal => flags & FlagExternal != 0; |
| 792 | |
| 793 | void set isConst(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 794 | flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 795 | } |
| 796 | |
| 797 | void set isExternal(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 798 | flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 799 | } |
| 800 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 801 | bool get isInstanceMember => false; |
Asger Feldthaus | e3f2bae | 2016-09-13 10:40:11 | [diff] [blame] | 802 | bool get hasGetter => false; |
| 803 | bool get hasSetter => false; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 804 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 805 | accept(MemberVisitor v) => v.visitConstructor(this); |
| 806 | |
| 807 | acceptReference(MemberReferenceVisitor v) => |
| 808 | v.visitConstructorReference(this); |
| 809 | |
| 810 | visitChildren(Visitor v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 811 | visitList(annotations, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 812 | name?.accept(v); |
| 813 | function?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 814 | visitList(initializers, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 815 | } |
| 816 | |
| 817 | transformChildren(Transformer v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 818 | transformList(annotations, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 819 | if (function != null) { |
| 820 | function = function.accept(v); |
| 821 | function?.parent = this; |
| 822 | } |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 823 | transformList(initializers, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 824 | } |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 825 | |
| 826 | DartType get getterType => const BottomType(); |
| 827 | DartType get setterType => const BottomType(); |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 828 | |
| 829 | _MemberAccessor get _getterInterface { |
| 830 | throw 'Constructors cannot be used as getters'; |
| 831 | } |
| 832 | |
| 833 | _MemberAccessor get _setterInterface { |
| 834 | throw 'Constructors cannot be used as setters'; |
| 835 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 836 | } |
| 837 | |
| 838 | /// A method, getter, setter, index-getter, index-setter, operator overloader, |
| 839 | /// or factory. |
| 840 | /// |
| 841 | /// Procedures can have the static, abstract, and/or external modifier, although |
| 842 | /// only the static and external modifiers may be used together. |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 843 | /// |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 844 | /// For non-static procedures the name is required for dynamic dispatch. |
| 845 | /// For external procedures the name is required for identifying the external |
| 846 | /// implementation. |
| 847 | /// |
| 848 | /// For methods, getters, and setters the name is just as it was declared. |
| 849 | /// For setters this does not include a trailing `=`. |
| 850 | /// For index-getters/setters, this is `[]` and `[]=`. |
| 851 | /// For operators, this is the token for the operator, e.g. `+` or `==`, |
| 852 | /// except for the unary minus operator, whose name is `unary-`. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 853 | class Procedure extends Member { |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 854 | _MemberAccessor _reference; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 855 | ProcedureKind kind; |
| 856 | int flags = 0; |
Asger Feldthaus | 133498e | 2016-04-27 14:44:25 | [diff] [blame] | 857 | FunctionNode function; // Body is null if and only if abstract or external. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 858 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 859 | /// The uri of the source file this procedure was loaded from. |
| 860 | String fileUri; |
| 861 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 862 | Procedure(Name name, this.kind, this.function, |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 863 | {bool isAbstract: false, |
| 864 | bool isStatic: false, |
| 865 | bool isExternal: false, |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 866 | bool isConst: false, |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 867 | int transformerFlags: 0, |
| 868 | this.fileUri}) |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 869 | : super(name) { |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 870 | _reference = new _MemberAccessor(this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 871 | function?.parent = this; |
| 872 | this.isAbstract = isAbstract; |
| 873 | this.isStatic = isStatic; |
| 874 | this.isExternal = isExternal; |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 875 | this.isConst = isConst; |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 876 | this.transformerFlags = transformerFlags; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 877 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 878 | |
| 879 | static const int FlagStatic = 1 << 0; // Must match serialized bit positions. |
| 880 | static const int FlagAbstract = 1 << 1; |
| 881 | static const int FlagExternal = 1 << 2; |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 882 | static const int FlagConst = 1 << 3; // Only for external const factories. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 883 | |
| 884 | bool get isStatic => flags & FlagStatic != 0; |
| 885 | bool get isAbstract => flags & FlagAbstract != 0; |
| 886 | bool get isExternal => flags & FlagExternal != 0; |
| 887 | |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 888 | /// True if this has the `const` modifier. This is only possible for external |
| 889 | /// constant factories, such as `String.fromEnvironment`. |
| 890 | bool get isConst => flags & FlagConst != 0; |
| 891 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 892 | void set isStatic(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 893 | flags = value ? (flags | FlagStatic) : (flags & ~FlagStatic); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 894 | } |
| 895 | |
| 896 | void set isAbstract(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 897 | flags = value ? (flags | FlagAbstract) : (flags & ~FlagAbstract); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 898 | } |
| 899 | |
| 900 | void set isExternal(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 901 | flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 902 | } |
| 903 | |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 904 | void set isConst(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 905 | flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 906 | } |
| 907 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 908 | bool get isInstanceMember => !isStatic; |
| 909 | bool get isGetter => kind == ProcedureKind.Getter; |
| 910 | bool get isSetter => kind == ProcedureKind.Setter; |
| 911 | bool get isAccessor => isGetter || isSetter; |
Asger Feldthaus | e3f2bae | 2016-09-13 10:40:11 | [diff] [blame] | 912 | bool get hasGetter => kind != ProcedureKind.Setter; |
| 913 | bool get hasSetter => kind == ProcedureKind.Setter; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 914 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 915 | accept(MemberVisitor v) => v.visitProcedure(this); |
| 916 | |
| 917 | acceptReference(MemberReferenceVisitor v) => v.visitProcedureReference(this); |
| 918 | |
| 919 | visitChildren(Visitor v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 920 | visitList(annotations, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 921 | name?.accept(v); |
| 922 | function?.accept(v); |
| 923 | } |
| 924 | |
| 925 | transformChildren(Transformer v) { |
Asger Feldthaus | fd6064c | 2016-08-09 14:32:23 | [diff] [blame] | 926 | transformList(annotations, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 927 | if (function != null) { |
| 928 | function = function.accept(v); |
| 929 | function?.parent = this; |
| 930 | } |
| 931 | } |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 932 | |
| 933 | DartType get getterType { |
| 934 | return isGetter ? function.returnType : function.functionType; |
| 935 | } |
| 936 | |
| 937 | DartType get setterType { |
| 938 | return isSetter |
| 939 | ? function.positionalParameters[0].type |
| 940 | : const BottomType(); |
| 941 | } |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 942 | |
| 943 | _MemberAccessor get _getterInterface => _reference; |
| 944 | _MemberAccessor get _setterInterface => _reference; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 945 | } |
| 946 | |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 947 | enum ProcedureKind { |
| 948 | Method, |
| 949 | Getter, |
| 950 | Setter, |
| 951 | Operator, |
| 952 | Factory, |
| 953 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 954 | |
| 955 | // ------------------------------------------------------------------------ |
| 956 | // CONSTRUCTOR INITIALIZERS |
| 957 | // ------------------------------------------------------------------------ |
| 958 | |
| 959 | /// Part of an initializer list in a constructor. |
| 960 | abstract class Initializer extends TreeNode { |
| 961 | accept(InitializerVisitor v); |
| 962 | } |
| 963 | |
| 964 | /// An initializer with a compile-time error. |
| 965 | /// |
| 966 | /// Should throw an exception at runtime. |
| 967 | // |
| 968 | // DESIGN TODO: The frontend should use this in a lot more cases to catch |
| 969 | // invalid cases. |
| 970 | class InvalidInitializer extends Initializer { |
| 971 | accept(InitializerVisitor v) => v.visitInvalidInitializer(this); |
| 972 | |
| 973 | visitChildren(Visitor v) {} |
| 974 | transformChildren(Transformer v) {} |
| 975 | } |
| 976 | |
Harry Terkelsen | 9509d28 | 2016-08-22 16:56:05 | [diff] [blame] | 977 | /// A field assignment `field = value` occurring in the initializer list of |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 978 | /// a constructor. |
| 979 | /// |
| 980 | /// This node has nothing to do with declaration-site field initializers; those |
| 981 | /// are [Expression]s stored in [Field.initializer]. |
| 982 | // |
| 983 | // TODO: The frontend should check that all final fields are initialized |
| 984 | // exactly once, and that no fields are assigned twice in the initializer list. |
| 985 | class FieldInitializer extends Initializer { |
| 986 | /// Reference to the field being initialized. Not null. |
| 987 | Field field; |
| 988 | Expression value; |
| 989 | |
| 990 | FieldInitializer(this.field, this.value) { |
| 991 | value?.parent = this; |
| 992 | } |
| 993 | |
| 994 | accept(InitializerVisitor v) => v.visitFieldInitializer(this); |
| 995 | |
| 996 | visitChildren(Visitor v) { |
| 997 | field?.acceptReference(v); |
| 998 | value?.accept(v); |
| 999 | } |
| 1000 | |
| 1001 | transformChildren(Transformer v) { |
| 1002 | if (value != null) { |
| 1003 | value = value.accept(v); |
| 1004 | value?.parent = this; |
| 1005 | } |
| 1006 | } |
| 1007 | } |
| 1008 | |
Harry Terkelsen | 9509d28 | 2016-08-22 16:56:05 | [diff] [blame] | 1009 | /// A super call `super(x,y)` occurring in the initializer list of a |
| 1010 | /// constructor. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1011 | /// |
| 1012 | /// There are no type arguments on this call. |
| 1013 | // |
| 1014 | // TODO: The frontend should check that there is no more than one super call. |
| 1015 | // |
| 1016 | // DESIGN TODO: Consider if the frontend should insert type arguments derived |
| 1017 | // from the extends clause. |
| 1018 | class SuperInitializer extends Initializer { |
| 1019 | /// Reference to the constructor being invoked in the super class. Not null. |
| 1020 | Constructor target; |
| 1021 | Arguments arguments; |
| 1022 | |
| 1023 | SuperInitializer(this.target, this.arguments) { |
| 1024 | arguments?.parent = this; |
| 1025 | } |
| 1026 | |
| 1027 | accept(InitializerVisitor v) => v.visitSuperInitializer(this); |
| 1028 | |
| 1029 | visitChildren(Visitor v) { |
| 1030 | target?.acceptReference(v); |
| 1031 | arguments?.accept(v); |
| 1032 | } |
| 1033 | |
| 1034 | transformChildren(Transformer v) { |
| 1035 | if (arguments != null) { |
| 1036 | arguments = arguments.accept(v); |
| 1037 | arguments?.parent = this; |
| 1038 | } |
| 1039 | } |
| 1040 | } |
| 1041 | |
Harry Terkelsen | 9509d28 | 2016-08-22 16:56:05 | [diff] [blame] | 1042 | /// A redirecting call `this(x,y)` occurring in the initializer list of |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1043 | /// a constructor. |
| 1044 | // |
| 1045 | // TODO: The frontend should check that this is the only initializer and if the |
| 1046 | // constructor has a body or if there is a cycle in the initializer calls. |
| 1047 | class RedirectingInitializer extends Initializer { |
| 1048 | /// Reference to the constructor being invoked in the same class. Not null. |
| 1049 | Constructor target; |
| 1050 | Arguments arguments; |
| 1051 | |
| 1052 | RedirectingInitializer(this.target, this.arguments) { |
| 1053 | arguments?.parent = this; |
| 1054 | } |
| 1055 | |
| 1056 | accept(InitializerVisitor v) => v.visitRedirectingInitializer(this); |
| 1057 | |
| 1058 | visitChildren(Visitor v) { |
| 1059 | target?.acceptReference(v); |
| 1060 | arguments?.accept(v); |
| 1061 | } |
| 1062 | |
| 1063 | transformChildren(Transformer v) { |
| 1064 | if (arguments != null) { |
| 1065 | arguments = arguments.accept(v); |
| 1066 | arguments?.parent = this; |
| 1067 | } |
| 1068 | } |
| 1069 | } |
| 1070 | |
Asger Feldthaus | ed443ef | 2016-06-28 12:27:01 | [diff] [blame] | 1071 | /// Binding of a temporary variable in the initializer list of a constructor. |
| 1072 | /// |
| 1073 | /// The variable is in scope for the remainder of the initializer list, but is |
| 1074 | /// not in scope in the constructor body. |
| 1075 | class LocalInitializer extends Initializer { |
| 1076 | VariableDeclaration variable; |
| 1077 | |
| 1078 | LocalInitializer(this.variable) { |
| 1079 | variable?.parent = this; |
| 1080 | } |
| 1081 | |
| 1082 | accept(InitializerVisitor v) => v.visitLocalInitializer(this); |
| 1083 | |
| 1084 | visitChildren(Visitor v) { |
| 1085 | variable?.accept(v); |
| 1086 | } |
| 1087 | |
| 1088 | transformChildren(Transformer v) { |
| 1089 | if (variable != null) { |
| 1090 | variable = variable.accept(v); |
| 1091 | variable?.parent = this; |
| 1092 | } |
| 1093 | } |
| 1094 | } |
| 1095 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1096 | // ------------------------------------------------------------------------ |
| 1097 | // FUNCTIONS |
| 1098 | // ------------------------------------------------------------------------ |
| 1099 | |
| 1100 | /// A function declares parameters and has a body. |
| 1101 | /// |
| 1102 | /// This may occur in a procedure, constructor, function expression, or local |
| 1103 | /// function declaration. |
| 1104 | class FunctionNode extends TreeNode { |
| 1105 | AsyncMarker asyncMarker; |
| 1106 | List<TypeParameter> typeParameters; |
| 1107 | int requiredParameterCount; |
| 1108 | List<VariableDeclaration> positionalParameters; |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 1109 | List<VariableDeclaration> namedParameters; // Must be sorted. |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 1110 | InferredValue inferredReturnValue; // May be null. |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1111 | DartType returnType; // Not null. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1112 | Statement body; |
| 1113 | |
| 1114 | FunctionNode(this.body, |
| 1115 | {List<TypeParameter> typeParameters, |
| 1116 | List<VariableDeclaration> positionalParameters, |
| 1117 | List<VariableDeclaration> namedParameters, |
| 1118 | int requiredParameterCount, |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 1119 | this.returnType: const DynamicType(), |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 1120 | this.inferredReturnValue, |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1121 | this.asyncMarker: AsyncMarker.Sync}) |
| 1122 | : this.positionalParameters = |
| 1123 | positionalParameters ?? <VariableDeclaration>[], |
| 1124 | this.requiredParameterCount = |
| 1125 | requiredParameterCount ?? positionalParameters?.length ?? 0, |
| 1126 | this.namedParameters = namedParameters ?? <VariableDeclaration>[], |
| 1127 | this.typeParameters = typeParameters ?? <TypeParameter>[] { |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 1128 | assert(returnType != null); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1129 | setParents(this.typeParameters, this); |
| 1130 | setParents(this.positionalParameters, this); |
| 1131 | setParents(this.namedParameters, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1132 | body?.parent = this; |
| 1133 | } |
| 1134 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1135 | static DartType _getTypeOfVariable(VariableDeclaration node) => node.type; |
| 1136 | |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 1137 | static NamedType _getNamedTypeOfVariable(VariableDeclaration node) { |
| 1138 | return new NamedType(node.name, node.type); |
| 1139 | } |
| 1140 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1141 | FunctionType get functionType { |
| 1142 | Map<String, DartType> named = const <String, DartType>{}; |
| 1143 | if (namedParameters.isNotEmpty) { |
| 1144 | named = <String, DartType>{}; |
| 1145 | for (var node in namedParameters) { |
| 1146 | named[node.name] = node.type; |
| 1147 | } |
| 1148 | } |
| 1149 | TreeNode parent = this.parent; |
| 1150 | return new FunctionType( |
| 1151 | positionalParameters.map(_getTypeOfVariable).toList(), returnType, |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 1152 | namedParameters: namedParameters.map(_getNamedTypeOfVariable).toList(), |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1153 | typeParameters: parent is Constructor |
| 1154 | ? parent.enclosingClass.typeParameters |
| 1155 | : typeParameters, |
| 1156 | requiredParameterCount: requiredParameterCount); |
| 1157 | } |
| 1158 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1159 | accept(TreeVisitor v) => v.visitFunctionNode(this); |
| 1160 | |
| 1161 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1162 | visitList(typeParameters, v); |
| 1163 | visitList(positionalParameters, v); |
| 1164 | visitList(namedParameters, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1165 | returnType?.accept(v); |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 1166 | inferredReturnValue?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1167 | body?.accept(v); |
| 1168 | } |
| 1169 | |
| 1170 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1171 | transformList(typeParameters, v, this); |
| 1172 | transformList(positionalParameters, v, this); |
| 1173 | transformList(namedParameters, v, this); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1174 | returnType = v.visitDartType(returnType); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1175 | if (body != null) { |
| 1176 | body = body.accept(v); |
| 1177 | body?.parent = this; |
| 1178 | } |
| 1179 | } |
| 1180 | } |
| 1181 | |
| 1182 | enum AsyncMarker { |
| 1183 | // Do not change the order of these, the frontends depend on it. |
| 1184 | Sync, |
| 1185 | SyncStar, |
| 1186 | Async, |
Martin Kustermann | 4f5ae27 | 2016-07-08 11:09:29 | [diff] [blame] | 1187 | AsyncStar, |
Vyacheslav Egorov | ffb724a | 2016-08-23 08:27:27 | [diff] [blame] | 1188 | |
| 1189 | // `SyncYielding` is a marker that tells Dart VM that this function is an |
| 1190 | // artificial closure introduced by an async transformer which desugared all |
| 1191 | // async syntax into a combination of native yields and helper method calls. |
| 1192 | // |
| 1193 | // Native yields (formatted as `[yield]`) are semantically close to |
| 1194 | // `yield x` statement: they denote a yield/resume point within a function |
| 1195 | // but are completely decoupled from the notion of iterators. When |
| 1196 | // execution of the closure reaches `[yield] x` it stops and return the |
| 1197 | // value of `x` to the caller. If closure is called again it continues |
| 1198 | // to the next statement after this yield as if it was suspended and resumed. |
| 1199 | // |
| 1200 | // Consider this example: |
| 1201 | // |
| 1202 | // g() { |
| 1203 | // var :await_jump_var = 0; |
| 1204 | // var :await_ctx_var; |
| 1205 | // |
| 1206 | // f(x) yielding { |
| 1207 | // [yield] '${x}:0'; |
| 1208 | // [yield] '${x}:1'; |
| 1209 | // [yield] '${x}:2'; |
| 1210 | // } |
| 1211 | // |
| 1212 | // return f; |
| 1213 | // } |
| 1214 | // |
| 1215 | // print(f('a')); /* prints 'a:0', :await_jump_var = 1 */ |
| 1216 | // print(f('b')); /* prints 'b:1', :await_jump_var = 2 */ |
| 1217 | // print(f('c')); /* prints 'c:2', :await_jump_var = 3 */ |
| 1218 | // |
| 1219 | // Note: currently Dart VM implicitly relies on async transformer to |
| 1220 | // inject certain artificial variables into g (like `:await_jump_var`). |
| 1221 | // As such SyncYielding and native yield are not intended to be used on their |
| 1222 | // own, but are rather an implementation artifact of the async transformer |
| 1223 | // itself. |
Martin Kustermann | 4f5ae27 | 2016-07-08 11:09:29 | [diff] [blame] | 1224 | SyncYielding, |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1225 | } |
| 1226 | |
| 1227 | // ------------------------------------------------------------------------ |
| 1228 | // EXPRESSIONS |
| 1229 | // ------------------------------------------------------------------------ |
| 1230 | |
| 1231 | abstract class Expression extends TreeNode { |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1232 | /// Returns the static type of the expression. |
| 1233 | /// |
| 1234 | /// Should only be used on code compiled in strong mode, as this method |
| 1235 | /// assumes the IR is strongly typed. |
| 1236 | DartType getStaticType(TypeEnvironment types); |
| 1237 | |
| 1238 | /// Returns the static type of the expression as an instantiation of |
| 1239 | /// [superclass]. |
| 1240 | /// |
| 1241 | /// Should only be used on code compiled in strong mode, as this method |
| 1242 | /// assumes the IR is strongly typed. |
| 1243 | /// |
| 1244 | /// This method futhermore assumes that the type of the expression actually |
| 1245 | /// is a subtype of (some instantiation of) the given [superclass]. |
| 1246 | /// If this is not the case, either an exception is thrown or the raw type of |
| 1247 | /// [superclass] is returned. |
| 1248 | InterfaceType getStaticTypeAsInstanceOf( |
| 1249 | Class superclass, TypeEnvironment types) { |
| 1250 | // This method assumes the program is correctly typed, so if the superclass |
| 1251 | // is not generic, we can just return its raw type without computing the |
| 1252 | // type of this expression. It also ensures that all types are considered |
| 1253 | // subtypes of Object (not just interface types), and function types are |
| 1254 | // considered subtypes of Function. |
| 1255 | if (superclass.typeParameters.isEmpty) { |
| 1256 | return superclass.rawType; |
| 1257 | } |
| 1258 | var type = getStaticType(types); |
| 1259 | while (type is TypeParameterType) { |
Asger Feldthaus | 7346348 | 2016-11-14 09:23:37 | [diff] [blame] | 1260 | type = (type as TypeParameterType).parameter.bound; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1261 | } |
| 1262 | if (type is InterfaceType) { |
| 1263 | var upcastType = types.hierarchy.getTypeAsInstanceOf(type, superclass); |
| 1264 | if (upcastType != null) return upcastType; |
| 1265 | } else if (type is BottomType) { |
| 1266 | return superclass.bottomType; |
| 1267 | } |
| 1268 | types.typeError(this, '$type is not a subtype of $superclass'); |
| 1269 | return superclass.rawType; |
| 1270 | } |
| 1271 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1272 | accept(ExpressionVisitor v); |
| 1273 | } |
| 1274 | |
| 1275 | /// An expression containing compile-time errors. |
| 1276 | /// |
| 1277 | /// Should throw a runtime error when evaluated. |
| 1278 | class InvalidExpression extends Expression { |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1279 | DartType getStaticType(TypeEnvironment types) => const BottomType(); |
| 1280 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1281 | accept(ExpressionVisitor v) => v.visitInvalidExpression(this); |
| 1282 | |
| 1283 | visitChildren(Visitor v) {} |
| 1284 | transformChildren(Transformer v) {} |
| 1285 | } |
| 1286 | |
| 1287 | /// Read a local variable, a local function, or a function parameter. |
| 1288 | class VariableGet extends Expression { |
| 1289 | VariableDeclaration variable; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1290 | DartType promotedType; // Null if not promoted. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1291 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1292 | VariableGet(this.variable, [this.promotedType]); |
| 1293 | |
| 1294 | DartType getStaticType(TypeEnvironment types) { |
| 1295 | return promotedType ?? variable.type; |
| 1296 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1297 | |
| 1298 | accept(ExpressionVisitor v) => v.visitVariableGet(this); |
| 1299 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1300 | visitChildren(Visitor v) { |
| 1301 | promotedType?.accept(v); |
| 1302 | } |
| 1303 | |
Asger Feldthaus | f9851b3 | 2016-10-04 15:28:45 | [diff] [blame] | 1304 | transformChildren(Transformer v) { |
| 1305 | if (promotedType != null) { |
| 1306 | promotedType = v.visitDartType(promotedType); |
| 1307 | } |
| 1308 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1309 | } |
| 1310 | |
| 1311 | /// Assign a local variable or function parameter. |
| 1312 | class VariableSet extends Expression { |
| 1313 | VariableDeclaration variable; |
| 1314 | Expression value; |
| 1315 | |
| 1316 | VariableSet(this.variable, this.value) { |
| 1317 | value?.parent = this; |
| 1318 | } |
| 1319 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1320 | DartType getStaticType(TypeEnvironment types) => value.getStaticType(types); |
| 1321 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1322 | accept(ExpressionVisitor v) => v.visitVariableSet(this); |
| 1323 | |
| 1324 | visitChildren(Visitor v) { |
| 1325 | value?.accept(v); |
| 1326 | } |
| 1327 | |
| 1328 | transformChildren(Transformer v) { |
| 1329 | if (value != null) { |
| 1330 | value = value.accept(v); |
| 1331 | value?.parent = this; |
| 1332 | } |
| 1333 | } |
| 1334 | } |
| 1335 | |
| 1336 | /// Expression of form `x.field`. |
| 1337 | /// |
| 1338 | /// This may invoke a getter, read a field, or tear off a method. |
| 1339 | class PropertyGet extends Expression { |
| 1340 | Expression receiver; |
| 1341 | Name name; |
| 1342 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1343 | _MemberAccessor _interfaceTargetReference; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1344 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1345 | PropertyGet(this.receiver, this.name, [Member interfaceTarget]) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1346 | receiver?.parent = this; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1347 | this.interfaceTarget = interfaceTarget; |
| 1348 | } |
| 1349 | |
| 1350 | Member get interfaceTarget => _interfaceTargetReference?.target; |
| 1351 | |
| 1352 | void set interfaceTarget(Member newTarget) { |
| 1353 | _interfaceTargetReference = newTarget?._getterInterface; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1354 | } |
| 1355 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1356 | DartType getStaticType(TypeEnvironment types) { |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1357 | var interfaceTarget = this.interfaceTarget; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1358 | if (interfaceTarget != null) { |
| 1359 | Class superclass = interfaceTarget.enclosingClass; |
| 1360 | var receiverType = receiver.getStaticTypeAsInstanceOf(superclass, types); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1361 | return Substitution |
| 1362 | .fromInterfaceType(receiverType) |
| 1363 | .substituteType(interfaceTarget.getterType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1364 | } |
| 1365 | // Treat the properties of Object specially. |
| 1366 | String nameString = name.name; |
| 1367 | if (nameString == 'hashCode') { |
| 1368 | return types.intType; |
| 1369 | } else if (nameString == 'runtimeType') { |
| 1370 | return types.typeType; |
| 1371 | } |
| 1372 | return const DynamicType(); |
| 1373 | } |
| 1374 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1375 | accept(ExpressionVisitor v) => v.visitPropertyGet(this); |
| 1376 | |
| 1377 | visitChildren(Visitor v) { |
| 1378 | receiver?.accept(v); |
| 1379 | name?.accept(v); |
| 1380 | } |
| 1381 | |
| 1382 | transformChildren(Transformer v) { |
| 1383 | if (receiver != null) { |
| 1384 | receiver = receiver.accept(v); |
| 1385 | receiver?.parent = this; |
| 1386 | } |
| 1387 | } |
| 1388 | } |
| 1389 | |
| 1390 | /// Expression of form `x.field = value`. |
| 1391 | /// |
| 1392 | /// This may invoke a setter or assign a field. |
| 1393 | class PropertySet extends Expression { |
| 1394 | Expression receiver; |
| 1395 | Name name; |
| 1396 | Expression value; |
| 1397 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1398 | _MemberAccessor _interfaceTargetReference; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1399 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1400 | PropertySet(this.receiver, this.name, this.value, [Member interfaceTarget]) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1401 | receiver?.parent = this; |
| 1402 | value?.parent = this; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1403 | this.interfaceTarget = interfaceTarget; |
| 1404 | } |
| 1405 | |
| 1406 | Member get interfaceTarget => _interfaceTargetReference?.target; |
| 1407 | |
| 1408 | void set interfaceTarget(Member newTarget) { |
| 1409 | _interfaceTargetReference = newTarget?._setterInterface; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1410 | } |
| 1411 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1412 | DartType getStaticType(TypeEnvironment types) => value.getStaticType(types); |
| 1413 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1414 | accept(ExpressionVisitor v) => v.visitPropertySet(this); |
| 1415 | |
| 1416 | visitChildren(Visitor v) { |
| 1417 | receiver?.accept(v); |
| 1418 | name?.accept(v); |
| 1419 | value?.accept(v); |
| 1420 | } |
| 1421 | |
| 1422 | transformChildren(Transformer v) { |
| 1423 | if (receiver != null) { |
| 1424 | receiver = receiver.accept(v); |
| 1425 | receiver?.parent = this; |
| 1426 | } |
| 1427 | if (value != null) { |
| 1428 | value = value.accept(v); |
| 1429 | value?.parent = this; |
| 1430 | } |
| 1431 | } |
| 1432 | } |
| 1433 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1434 | /// Directly read a field, call a getter, or tear off a method. |
| 1435 | class DirectPropertyGet extends Expression { |
| 1436 | Expression receiver; |
| 1437 | Member target; |
| 1438 | |
| 1439 | DirectPropertyGet(this.receiver, this.target) { |
| 1440 | receiver?.parent = this; |
| 1441 | } |
| 1442 | |
| 1443 | visitChildren(Visitor v) { |
| 1444 | receiver?.accept(v); |
| 1445 | target?.acceptReference(v); |
| 1446 | } |
| 1447 | |
| 1448 | transformChildren(Transformer v) { |
| 1449 | if (receiver != null) { |
| 1450 | receiver = receiver.accept(v); |
| 1451 | receiver?.parent = this; |
| 1452 | } |
| 1453 | } |
| 1454 | |
| 1455 | accept(ExpressionVisitor v) => v.visitDirectPropertyGet(this); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1456 | |
| 1457 | DartType getStaticType(TypeEnvironment types) { |
| 1458 | Class superclass = target.enclosingClass; |
| 1459 | var receiverType = receiver.getStaticTypeAsInstanceOf(superclass, types); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1460 | return Substitution |
| 1461 | .fromInterfaceType(receiverType) |
| 1462 | .substituteType(target.getterType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1463 | } |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1464 | } |
| 1465 | |
| 1466 | /// Directly assign a field, or call a setter. |
| 1467 | class DirectPropertySet extends Expression { |
| 1468 | Expression receiver; |
| 1469 | Member target; |
| 1470 | Expression value; |
| 1471 | |
| 1472 | DirectPropertySet(this.receiver, this.target, this.value) { |
| 1473 | receiver?.parent = this; |
| 1474 | value?.parent = this; |
| 1475 | } |
| 1476 | |
| 1477 | visitChildren(Visitor v) { |
| 1478 | receiver?.accept(v); |
| 1479 | target?.acceptReference(v); |
| 1480 | value?.accept(v); |
| 1481 | } |
| 1482 | |
| 1483 | transformChildren(Transformer v) { |
| 1484 | if (receiver != null) { |
| 1485 | receiver = receiver.accept(v); |
| 1486 | receiver?.parent = this; |
| 1487 | } |
| 1488 | if (value != null) { |
| 1489 | value = value.accept(v); |
| 1490 | value?.parent = this; |
| 1491 | } |
| 1492 | } |
| 1493 | |
| 1494 | accept(ExpressionVisitor v) => v.visitDirectPropertySet(this); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1495 | |
| 1496 | DartType getStaticType(TypeEnvironment types) => value.getStaticType(types); |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1497 | } |
| 1498 | |
| 1499 | /// Directly call an instance method, bypassing ordinary dispatch. |
| 1500 | class DirectMethodInvocation extends Expression { |
| 1501 | Expression receiver; |
| 1502 | Procedure target; |
| 1503 | Arguments arguments; |
| 1504 | |
| 1505 | DirectMethodInvocation(this.receiver, this.target, this.arguments) { |
| 1506 | receiver?.parent = this; |
| 1507 | arguments?.parent = this; |
| 1508 | } |
| 1509 | |
| 1510 | visitChildren(Visitor v) { |
| 1511 | receiver?.accept(v); |
| 1512 | target?.acceptReference(v); |
| 1513 | arguments?.accept(v); |
| 1514 | } |
| 1515 | |
| 1516 | transformChildren(Transformer v) { |
| 1517 | if (receiver != null) { |
| 1518 | receiver = receiver.accept(v); |
| 1519 | receiver?.parent = this; |
| 1520 | } |
| 1521 | if (arguments != null) { |
| 1522 | arguments = arguments.accept(v); |
| 1523 | arguments?.parent = this; |
| 1524 | } |
| 1525 | } |
| 1526 | |
| 1527 | accept(ExpressionVisitor v) => v.visitDirectMethodInvocation(this); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1528 | |
| 1529 | DartType getStaticType(TypeEnvironment types) { |
| 1530 | if (types.isOverloadedArithmeticOperator(target)) { |
| 1531 | return types.getTypeOfOverloadedArithmetic(receiver.getStaticType(types), |
| 1532 | arguments.positional[0].getStaticType(types)); |
| 1533 | } |
| 1534 | Class superclass = target.enclosingClass; |
| 1535 | var receiverType = receiver.getStaticTypeAsInstanceOf(superclass, types); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1536 | var returnType = Substitution |
| 1537 | .fromInterfaceType(receiverType) |
| 1538 | .substituteType(target.function.returnType); |
| 1539 | return Substitution |
| 1540 | .fromPairs(target.function.typeParameters, arguments.types) |
| 1541 | .substituteType(returnType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1542 | } |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1543 | } |
| 1544 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1545 | /// Expression of form `super.field`. |
| 1546 | /// |
| 1547 | /// This may invoke a getter, read a field, or tear off a method. |
| 1548 | class SuperPropertyGet extends Expression { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1549 | Name name; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1550 | _MemberAccessor _interfaceTargetReference; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1551 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1552 | SuperPropertyGet(this.name, [Member interfaceTarget]) { |
| 1553 | _interfaceTargetReference = interfaceTarget?._getterInterface; |
| 1554 | } |
| 1555 | |
| 1556 | Member get interfaceTarget => _interfaceTargetReference?.target; |
| 1557 | |
| 1558 | void set interfaceTarget(Member newTarget) { |
| 1559 | _interfaceTargetReference = newTarget?._getterInterface; |
| 1560 | } |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1561 | |
| 1562 | DartType getStaticType(TypeEnvironment types) { |
| 1563 | Class declaringClass = interfaceTarget.enclosingClass; |
| 1564 | if (declaringClass.typeParameters.isEmpty) { |
| 1565 | return interfaceTarget.getterType; |
| 1566 | } |
| 1567 | var receiver = |
| 1568 | types.hierarchy.getTypeAsInstanceOf(types.thisType, declaringClass); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1569 | return Substitution |
| 1570 | .fromInterfaceType(receiver) |
| 1571 | .substituteType(interfaceTarget.getterType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1572 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1573 | |
| 1574 | accept(ExpressionVisitor v) => v.visitSuperPropertyGet(this); |
| 1575 | |
Peter von der Ahé | 184dda5 | 2016-09-05 14:25:34 | [diff] [blame] | 1576 | visitChildren(Visitor v) { |
| 1577 | name?.accept(v); |
| 1578 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1579 | |
| 1580 | transformChildren(Transformer v) {} |
| 1581 | } |
| 1582 | |
| 1583 | /// Expression of form `super.field = value`. |
| 1584 | /// |
| 1585 | /// This may invoke a setter or assign a field. |
| 1586 | class SuperPropertySet extends Expression { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1587 | Name name; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1588 | Expression value; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1589 | _MemberAccessor _interfaceTargetReference; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1590 | |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1591 | SuperPropertySet(this.name, this.value, [Member interfaceTarget]) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1592 | value?.parent = this; |
Asger Feldthaus | 2efc48c | 2016-09-23 10:34:06 | [diff] [blame] | 1593 | _interfaceTargetReference = interfaceTarget?._setterInterface; |
| 1594 | } |
| 1595 | |
| 1596 | Member get interfaceTarget => _interfaceTargetReference?.target; |
| 1597 | |
| 1598 | void set interfaceTarget(Member newTarget) { |
| 1599 | _interfaceTargetReference = newTarget?._setterInterface; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1600 | } |
| 1601 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1602 | DartType getStaticType(TypeEnvironment types) => value.getStaticType(types); |
| 1603 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1604 | accept(ExpressionVisitor v) => v.visitSuperPropertySet(this); |
| 1605 | |
| 1606 | visitChildren(Visitor v) { |
Peter von der Ahé | 184dda5 | 2016-09-05 14:25:34 | [diff] [blame] | 1607 | name?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1608 | value?.accept(v); |
| 1609 | } |
| 1610 | |
| 1611 | transformChildren(Transformer v) { |
| 1612 | if (value != null) { |
| 1613 | value = value.accept(v); |
| 1614 | value?.parent = this; |
| 1615 | } |
| 1616 | } |
| 1617 | } |
| 1618 | |
| 1619 | /// Read a static field, call a static getter, or tear off a static method. |
| 1620 | class StaticGet extends Expression { |
| 1621 | /// A static field, getter, or method (for tear-off). |
| 1622 | Member target; |
| 1623 | |
| 1624 | StaticGet(this.target); |
| 1625 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1626 | DartType getStaticType(TypeEnvironment types) => target.getterType; |
| 1627 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1628 | accept(ExpressionVisitor v) => v.visitStaticGet(this); |
| 1629 | |
| 1630 | visitChildren(Visitor v) { |
| 1631 | target?.acceptReference(v); |
| 1632 | } |
| 1633 | |
| 1634 | transformChildren(Transformer v) {} |
| 1635 | } |
| 1636 | |
| 1637 | /// Assign a static field or call a static setter. |
| 1638 | class StaticSet extends Expression { |
| 1639 | /// A mutable static field or a static setter. |
| 1640 | Member target; |
| 1641 | Expression value; |
| 1642 | |
| 1643 | StaticSet(this.target, this.value) { |
| 1644 | value?.parent = this; |
| 1645 | } |
| 1646 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1647 | DartType getStaticType(TypeEnvironment types) => value.getStaticType(types); |
| 1648 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1649 | accept(ExpressionVisitor v) => v.visitStaticSet(this); |
| 1650 | |
| 1651 | visitChildren(Visitor v) { |
| 1652 | target?.acceptReference(v); |
| 1653 | value?.accept(v); |
| 1654 | } |
| 1655 | |
| 1656 | transformChildren(Transformer v) { |
| 1657 | if (value != null) { |
| 1658 | value = value.accept(v); |
| 1659 | value?.parent = this; |
| 1660 | } |
| 1661 | } |
| 1662 | } |
| 1663 | |
| 1664 | /// The arguments to a function call, divided into type arguments, |
| 1665 | /// positional arguments, and named arguments. |
| 1666 | class Arguments extends TreeNode { |
| 1667 | final List<DartType> types; |
| 1668 | final List<Expression> positional; |
| 1669 | final List<NamedExpression> named; |
| 1670 | |
| 1671 | Arguments(this.positional, |
| 1672 | {List<DartType> types, List<NamedExpression> named}) |
| 1673 | : this.types = types ?? <DartType>[], |
| 1674 | this.named = named ?? <NamedExpression>[] { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1675 | setParents(this.positional, this); |
| 1676 | setParents(this.named, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1677 | } |
| 1678 | |
| 1679 | Arguments.empty() |
| 1680 | : types = <DartType>[], |
| 1681 | positional = <Expression>[], |
| 1682 | named = <NamedExpression>[]; |
| 1683 | |
| 1684 | accept(TreeVisitor v) => v.visitArguments(this); |
| 1685 | |
| 1686 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1687 | visitList(types, v); |
| 1688 | visitList(positional, v); |
| 1689 | visitList(named, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1690 | } |
| 1691 | |
| 1692 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1693 | transformTypeList(types, v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1694 | transformList(positional, v, this); |
| 1695 | transformList(named, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1696 | } |
| 1697 | } |
| 1698 | |
| 1699 | /// A named argument, `name: value`. |
| 1700 | class NamedExpression extends TreeNode { |
| 1701 | String name; |
| 1702 | Expression value; |
| 1703 | |
| 1704 | NamedExpression(this.name, this.value) { |
| 1705 | value?.parent = this; |
| 1706 | } |
| 1707 | |
| 1708 | accept(TreeVisitor v) => v.visitNamedExpression(this); |
| 1709 | |
| 1710 | visitChildren(Visitor v) { |
| 1711 | value?.accept(v); |
| 1712 | } |
| 1713 | |
| 1714 | transformChildren(Transformer v) { |
| 1715 | if (value != null) { |
| 1716 | value = value.accept(v); |
| 1717 | value?.parent = this; |
| 1718 | } |
| 1719 | } |
| 1720 | } |
| 1721 | |
| 1722 | /// Common super class for [MethodInvocation], [SuperMethodInvocation], |
| 1723 | /// [StaticInvocation], and [ConstructorInvocation]. |
| 1724 | abstract class InvocationExpression extends Expression { |
| 1725 | Arguments get arguments; |
| 1726 | set arguments(Arguments value); |
| 1727 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1728 | /// Name of the invoked method. |
| 1729 | /// |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 1730 | /// May be `null` if the target is a synthetic static member without a name. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1731 | Name get name; |
| 1732 | } |
| 1733 | |
| 1734 | /// Expression of form `x.foo(y)`. |
| 1735 | class MethodInvocation extends InvocationExpression { |
| 1736 | Expression receiver; |
| 1737 | Name name; |
| 1738 | Arguments arguments; |
| 1739 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1740 | Procedure interfaceTarget; |
| 1741 | |
| 1742 | MethodInvocation(this.receiver, this.name, this.arguments, |
| 1743 | [this.interfaceTarget]) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1744 | receiver?.parent = this; |
| 1745 | arguments?.parent = this; |
| 1746 | } |
| 1747 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1748 | DartType getStaticType(TypeEnvironment types) { |
| 1749 | if (interfaceTarget != null) { |
| 1750 | if (types.isOverloadedArithmeticOperator(interfaceTarget)) { |
| 1751 | return types.getTypeOfOverloadedArithmetic( |
| 1752 | receiver.getStaticType(types), |
| 1753 | arguments.positional[0].getStaticType(types)); |
| 1754 | } |
| 1755 | Class superclass = interfaceTarget.enclosingClass; |
| 1756 | var receiverType = receiver.getStaticTypeAsInstanceOf(superclass, types); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1757 | var returnType = Substitution |
| 1758 | .fromInterfaceType(receiverType) |
| 1759 | .substituteType(interfaceTarget.function.returnType); |
| 1760 | return Substitution |
| 1761 | .fromPairs(interfaceTarget.function.typeParameters, arguments.types) |
| 1762 | .substituteType(returnType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1763 | } |
| 1764 | if (name.name == 'call') { |
| 1765 | var receiverType = receiver.getStaticType(types); |
| 1766 | if (receiverType is FunctionType) { |
| 1767 | if (receiverType.typeParameters.length != arguments.types.length) { |
| 1768 | return const BottomType(); |
| 1769 | } |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1770 | return Substitution |
| 1771 | .fromPairs(receiverType.typeParameters, arguments.types) |
| 1772 | .substituteType(receiverType.returnType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1773 | } |
| 1774 | } |
| 1775 | if (name.name == '==') { |
| 1776 | // We use this special case to simplify generation of '==' checks. |
| 1777 | return types.boolType; |
| 1778 | } |
| 1779 | return const DynamicType(); |
| 1780 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1781 | |
| 1782 | accept(ExpressionVisitor v) => v.visitMethodInvocation(this); |
| 1783 | |
| 1784 | visitChildren(Visitor v) { |
| 1785 | receiver?.accept(v); |
| 1786 | name?.accept(v); |
| 1787 | arguments?.accept(v); |
| 1788 | } |
| 1789 | |
| 1790 | transformChildren(Transformer v) { |
| 1791 | if (receiver != null) { |
| 1792 | receiver = receiver.accept(v); |
| 1793 | receiver?.parent = this; |
| 1794 | } |
| 1795 | if (arguments != null) { |
| 1796 | arguments = arguments.accept(v); |
| 1797 | arguments?.parent = this; |
| 1798 | } |
| 1799 | } |
| 1800 | } |
| 1801 | |
| 1802 | /// Expression of form `super.foo(x)`. |
| 1803 | /// |
| 1804 | /// The provided arguments might not match the parameters of the target. |
| 1805 | class SuperMethodInvocation extends InvocationExpression { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1806 | Name name; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1807 | Arguments arguments; |
| 1808 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1809 | Member interfaceTarget; |
| 1810 | |
| 1811 | SuperMethodInvocation(this.name, this.arguments, this.interfaceTarget) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1812 | arguments?.parent = this; |
| 1813 | } |
| 1814 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1815 | DartType getStaticType(TypeEnvironment types) { |
| 1816 | if (interfaceTarget == null) return const DynamicType(); |
| 1817 | Class superclass = interfaceTarget.enclosingClass; |
| 1818 | var receiverType = |
| 1819 | types.hierarchy.getTypeAsInstanceOf(types.thisType, superclass); |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1820 | var returnType = Substitution |
| 1821 | .fromInterfaceType(receiverType) |
| 1822 | .substituteType(interfaceTarget.function.returnType); |
| 1823 | return Substitution |
| 1824 | .fromPairs(interfaceTarget.function.typeParameters, arguments.types) |
| 1825 | .substituteType(returnType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1826 | } |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 1827 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1828 | accept(ExpressionVisitor v) => v.visitSuperMethodInvocation(this); |
| 1829 | |
| 1830 | visitChildren(Visitor v) { |
Peter von der Ahé | 184dda5 | 2016-09-05 14:25:34 | [diff] [blame] | 1831 | name?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1832 | arguments?.accept(v); |
| 1833 | } |
| 1834 | |
| 1835 | transformChildren(Transformer v) { |
| 1836 | if (arguments != null) { |
| 1837 | arguments = arguments.accept(v); |
| 1838 | arguments?.parent = this; |
| 1839 | } |
| 1840 | } |
| 1841 | } |
| 1842 | |
Asger Feldthaus | be5b7d3 | 2016-04-28 10:58:42 | [diff] [blame] | 1843 | /// Expression of form `foo(x)`, or `const foo(x)` if the target is an |
| 1844 | /// external constant factory. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1845 | /// |
| 1846 | /// The provided arguments might not match the parameters of the target. |
| 1847 | class StaticInvocation extends InvocationExpression { |
Asger Feldthaus | 7f12034 | 2016-04-28 10:28:32 | [diff] [blame] | 1848 | Procedure target; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1849 | Arguments arguments; |
| 1850 | |
Asger Feldthaus | be5b7d3 | 2016-04-28 10:58:42 | [diff] [blame] | 1851 | /// True if this is a constant call to an external constant factory. |
| 1852 | bool isConst; |
| 1853 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1854 | Name get name => target?.name; |
| 1855 | |
Asger Feldthaus | be5b7d3 | 2016-04-28 10:58:42 | [diff] [blame] | 1856 | StaticInvocation(this.target, this.arguments, {this.isConst: false}) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1857 | arguments?.parent = this; |
| 1858 | } |
| 1859 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1860 | DartType getStaticType(TypeEnvironment types) { |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 1861 | return Substitution |
| 1862 | .fromPairs(target.function.typeParameters, arguments.types) |
| 1863 | .substituteType(target.function.returnType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1864 | } |
| 1865 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1866 | accept(ExpressionVisitor v) => v.visitStaticInvocation(this); |
| 1867 | |
| 1868 | visitChildren(Visitor v) { |
| 1869 | target?.acceptReference(v); |
| 1870 | arguments?.accept(v); |
| 1871 | } |
| 1872 | |
| 1873 | transformChildren(Transformer v) { |
| 1874 | if (arguments != null) { |
| 1875 | arguments = arguments.accept(v); |
| 1876 | arguments?.parent = this; |
| 1877 | } |
| 1878 | } |
| 1879 | } |
| 1880 | |
| 1881 | /// Expression of form `new Foo(x)` or `const Foo(x)`. |
| 1882 | /// |
| 1883 | /// The provided arguments might not match the parameters of the target. |
| 1884 | // |
| 1885 | // DESIGN TODO: Should we pass type arguments in a separate field |
| 1886 | // `classTypeArguments`? They are quite different from type arguments to |
| 1887 | // generic functions. |
Peter von der Ahé | e56467e | 2016-06-28 11:11:25 | [diff] [blame] | 1888 | class ConstructorInvocation extends InvocationExpression { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1889 | Constructor target; |
| 1890 | Arguments arguments; |
| 1891 | bool isConst; |
| 1892 | |
Peter von der Ahé | e56467e | 2016-06-28 11:11:25 | [diff] [blame] | 1893 | Name get name => target?.name; |
| 1894 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1895 | ConstructorInvocation(this.target, this.arguments, {this.isConst: false}) { |
| 1896 | arguments?.parent = this; |
| 1897 | } |
| 1898 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1899 | DartType getStaticType(TypeEnvironment types) { |
| 1900 | return arguments.types.isEmpty |
| 1901 | ? target.enclosingClass.rawType |
| 1902 | : new InterfaceType(target.enclosingClass, arguments.types); |
| 1903 | } |
| 1904 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1905 | accept(ExpressionVisitor v) => v.visitConstructorInvocation(this); |
| 1906 | |
| 1907 | visitChildren(Visitor v) { |
| 1908 | target?.acceptReference(v); |
| 1909 | arguments?.accept(v); |
| 1910 | } |
| 1911 | |
| 1912 | transformChildren(Transformer v) { |
| 1913 | if (arguments != null) { |
| 1914 | arguments = arguments.accept(v); |
| 1915 | arguments?.parent = this; |
| 1916 | } |
| 1917 | } |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 1918 | |
| 1919 | InterfaceType get constructedType { |
| 1920 | return arguments.types.isEmpty |
| 1921 | ? target.enclosingClass.rawType |
| 1922 | : new InterfaceType(target.enclosingClass, arguments.types); |
| 1923 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1924 | } |
| 1925 | |
| 1926 | /// Expression of form `!x`. |
| 1927 | /// |
| 1928 | /// The `is!` and `!=` operators are desugared into [Not] nodes with `is` and |
| 1929 | /// `==` expressions inside, respectively. |
| 1930 | class Not extends Expression { |
| 1931 | Expression operand; |
| 1932 | |
| 1933 | Not(this.operand) { |
| 1934 | operand?.parent = this; |
| 1935 | } |
| 1936 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1937 | DartType getStaticType(TypeEnvironment types) => types.boolType; |
| 1938 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1939 | accept(ExpressionVisitor v) => v.visitNot(this); |
| 1940 | |
| 1941 | visitChildren(Visitor v) { |
| 1942 | operand?.accept(v); |
| 1943 | } |
| 1944 | |
| 1945 | transformChildren(Transformer v) { |
| 1946 | if (operand != null) { |
| 1947 | operand = operand.accept(v); |
| 1948 | operand?.parent = this; |
| 1949 | } |
| 1950 | } |
| 1951 | } |
| 1952 | |
Asger Feldthaus | d9b0056 | 2016-09-23 10:52:00 | [diff] [blame] | 1953 | /// Expression of form `x && y` or `x || y` |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1954 | class LogicalExpression extends Expression { |
| 1955 | Expression left; |
| 1956 | String operator; // && or || or ?? |
| 1957 | Expression right; |
| 1958 | |
Asger Feldthaus | d9b0056 | 2016-09-23 10:52:00 | [diff] [blame] | 1959 | LogicalExpression(this.left, this.operator, this.right) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1960 | left?.parent = this; |
| 1961 | right?.parent = this; |
| 1962 | } |
| 1963 | |
Asger Feldthaus | d9b0056 | 2016-09-23 10:52:00 | [diff] [blame] | 1964 | DartType getStaticType(TypeEnvironment types) => types.boolType; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1965 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1966 | accept(ExpressionVisitor v) => v.visitLogicalExpression(this); |
| 1967 | |
| 1968 | visitChildren(Visitor v) { |
| 1969 | left?.accept(v); |
| 1970 | right?.accept(v); |
| 1971 | } |
| 1972 | |
| 1973 | transformChildren(Transformer v) { |
| 1974 | if (left != null) { |
| 1975 | left = left.accept(v); |
| 1976 | left?.parent = this; |
| 1977 | } |
| 1978 | if (right != null) { |
| 1979 | right = right.accept(v); |
| 1980 | right?.parent = this; |
| 1981 | } |
| 1982 | } |
| 1983 | } |
| 1984 | |
| 1985 | /// Expression of form `x ? y : z`. |
| 1986 | class ConditionalExpression extends Expression { |
| 1987 | Expression condition; |
| 1988 | Expression then; |
| 1989 | Expression otherwise; |
| 1990 | |
Asger Feldthaus | c227e78 | 2016-11-01 15:04:10 | [diff] [blame] | 1991 | /// The static type of the expression. Should not be `null`. |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 1992 | DartType staticType; |
| 1993 | |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 1994 | ConditionalExpression( |
| 1995 | this.condition, this.then, this.otherwise, this.staticType) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 1996 | condition?.parent = this; |
| 1997 | then?.parent = this; |
| 1998 | otherwise?.parent = this; |
| 1999 | } |
| 2000 | |
Asger Feldthaus | c227e78 | 2016-11-01 15:04:10 | [diff] [blame] | 2001 | DartType getStaticType(TypeEnvironment types) => staticType; |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2002 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2003 | accept(ExpressionVisitor v) => v.visitConditionalExpression(this); |
| 2004 | |
| 2005 | visitChildren(Visitor v) { |
| 2006 | condition?.accept(v); |
| 2007 | then?.accept(v); |
| 2008 | otherwise?.accept(v); |
Asger Feldthaus | f9851b3 | 2016-10-04 15:28:45 | [diff] [blame] | 2009 | staticType?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2010 | } |
| 2011 | |
| 2012 | transformChildren(Transformer v) { |
| 2013 | if (condition != null) { |
| 2014 | condition = condition.accept(v); |
| 2015 | condition?.parent = this; |
| 2016 | } |
| 2017 | if (then != null) { |
| 2018 | then = then.accept(v); |
| 2019 | then?.parent = this; |
| 2020 | } |
| 2021 | if (otherwise != null) { |
| 2022 | otherwise = otherwise.accept(v); |
| 2023 | otherwise?.parent = this; |
| 2024 | } |
Asger Feldthaus | f9851b3 | 2016-10-04 15:28:45 | [diff] [blame] | 2025 | if (staticType != null) { |
| 2026 | staticType = v.visitDartType(staticType); |
| 2027 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2028 | } |
| 2029 | } |
| 2030 | |
| 2031 | /// Convert expressions to strings and concatenate them. Semantically, calls |
| 2032 | /// `toString` on every argument, checks that a string is returned, and returns |
| 2033 | /// the concatenation of all the strings. |
| 2034 | /// |
| 2035 | /// If [expressions] is empty then an empty string is returned. |
| 2036 | /// |
| 2037 | /// These arise from string interpolations and adjacent string literals. |
| 2038 | class StringConcatenation extends Expression { |
| 2039 | final List<Expression> expressions; |
| 2040 | |
| 2041 | StringConcatenation(this.expressions) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2042 | setParents(expressions, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2043 | } |
| 2044 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2045 | DartType getStaticType(TypeEnvironment types) => types.stringType; |
| 2046 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2047 | accept(ExpressionVisitor v) => v.visitStringConcatenation(this); |
| 2048 | |
| 2049 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2050 | visitList(expressions, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2051 | } |
| 2052 | |
| 2053 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2054 | transformList(expressions, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2055 | } |
| 2056 | } |
| 2057 | |
| 2058 | /// Expression of form `x is T`. |
| 2059 | class IsExpression extends Expression { |
| 2060 | Expression operand; |
| 2061 | DartType type; |
| 2062 | |
| 2063 | IsExpression(this.operand, this.type) { |
| 2064 | operand?.parent = this; |
| 2065 | } |
| 2066 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2067 | DartType getStaticType(TypeEnvironment types) => types.boolType; |
| 2068 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2069 | accept(ExpressionVisitor v) => v.visitIsExpression(this); |
| 2070 | |
| 2071 | visitChildren(Visitor v) { |
| 2072 | operand?.accept(v); |
| 2073 | type?.accept(v); |
| 2074 | } |
| 2075 | |
| 2076 | transformChildren(Transformer v) { |
| 2077 | if (operand != null) { |
| 2078 | operand = operand.accept(v); |
| 2079 | operand?.parent = this; |
| 2080 | } |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2081 | type = v.visitDartType(type); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2082 | } |
| 2083 | } |
| 2084 | |
| 2085 | /// Expression of form `x as T`. |
| 2086 | class AsExpression extends Expression { |
| 2087 | Expression operand; |
| 2088 | DartType type; |
| 2089 | |
| 2090 | AsExpression(this.operand, this.type) { |
| 2091 | operand?.parent = this; |
| 2092 | } |
| 2093 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2094 | DartType getStaticType(TypeEnvironment types) => type; |
| 2095 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2096 | accept(ExpressionVisitor v) => v.visitAsExpression(this); |
| 2097 | |
| 2098 | visitChildren(Visitor v) { |
| 2099 | operand?.accept(v); |
| 2100 | type?.accept(v); |
| 2101 | } |
| 2102 | |
| 2103 | transformChildren(Transformer v) { |
| 2104 | if (operand != null) { |
| 2105 | operand = operand.accept(v); |
| 2106 | operand?.parent = this; |
| 2107 | } |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 2108 | type = v.visitDartType(type); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2109 | } |
| 2110 | } |
| 2111 | |
| 2112 | /// An integer, double, boolean, string, or null constant. |
| 2113 | abstract class BasicLiteral extends Expression { |
| 2114 | Object get value; |
| 2115 | |
| 2116 | visitChildren(Visitor v) {} |
| 2117 | transformChildren(Transformer v) {} |
| 2118 | } |
| 2119 | |
| 2120 | class StringLiteral extends BasicLiteral { |
| 2121 | String value; |
| 2122 | |
| 2123 | StringLiteral(this.value); |
| 2124 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2125 | DartType getStaticType(TypeEnvironment types) => types.stringType; |
| 2126 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2127 | accept(ExpressionVisitor v) => v.visitStringLiteral(this); |
| 2128 | } |
| 2129 | |
| 2130 | class IntLiteral extends BasicLiteral { |
| 2131 | int value; |
| 2132 | |
| 2133 | IntLiteral(this.value); |
| 2134 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2135 | DartType getStaticType(TypeEnvironment types) => types.intType; |
| 2136 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2137 | accept(ExpressionVisitor v) => v.visitIntLiteral(this); |
| 2138 | } |
| 2139 | |
| 2140 | class DoubleLiteral extends BasicLiteral { |
| 2141 | double value; |
| 2142 | |
| 2143 | DoubleLiteral(this.value); |
| 2144 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2145 | DartType getStaticType(TypeEnvironment types) => types.doubleType; |
| 2146 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2147 | accept(ExpressionVisitor v) => v.visitDoubleLiteral(this); |
| 2148 | } |
| 2149 | |
| 2150 | class BoolLiteral extends BasicLiteral { |
| 2151 | bool value; |
| 2152 | |
| 2153 | BoolLiteral(this.value); |
| 2154 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2155 | DartType getStaticType(TypeEnvironment types) => types.boolType; |
| 2156 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2157 | accept(ExpressionVisitor v) => v.visitBoolLiteral(this); |
| 2158 | } |
| 2159 | |
| 2160 | class NullLiteral extends BasicLiteral { |
| 2161 | Object get value => null; |
| 2162 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2163 | DartType getStaticType(TypeEnvironment types) => const BottomType(); |
| 2164 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2165 | accept(ExpressionVisitor v) => v.visitNullLiteral(this); |
| 2166 | } |
| 2167 | |
| 2168 | class SymbolLiteral extends Expression { |
| 2169 | String value; // Everything strictly after the '#'. |
| 2170 | |
| 2171 | SymbolLiteral(this.value); |
| 2172 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2173 | DartType getStaticType(TypeEnvironment types) => types.symbolType; |
| 2174 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2175 | accept(ExpressionVisitor v) => v.visitSymbolLiteral(this); |
| 2176 | |
| 2177 | visitChildren(Visitor v) {} |
| 2178 | transformChildren(Transformer v) {} |
| 2179 | } |
| 2180 | |
| 2181 | class TypeLiteral extends Expression { |
| 2182 | DartType type; |
| 2183 | |
| 2184 | TypeLiteral(this.type); |
| 2185 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2186 | DartType getStaticType(TypeEnvironment types) => types.typeType; |
| 2187 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2188 | accept(ExpressionVisitor v) => v.visitTypeLiteral(this); |
| 2189 | |
| 2190 | visitChildren(Visitor v) { |
| 2191 | type?.accept(v); |
| 2192 | } |
| 2193 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 2194 | transformChildren(Transformer v) { |
| 2195 | type = v.visitDartType(type); |
| 2196 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2197 | } |
| 2198 | |
| 2199 | class ThisExpression extends Expression { |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2200 | DartType getStaticType(TypeEnvironment types) => types.thisType; |
| 2201 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2202 | accept(ExpressionVisitor v) => v.visitThisExpression(this); |
| 2203 | |
| 2204 | visitChildren(Visitor v) {} |
| 2205 | transformChildren(Transformer v) {} |
| 2206 | } |
| 2207 | |
| 2208 | class Rethrow extends Expression { |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2209 | DartType getStaticType(TypeEnvironment types) => const BottomType(); |
| 2210 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2211 | accept(ExpressionVisitor v) => v.visitRethrow(this); |
| 2212 | |
| 2213 | visitChildren(Visitor v) {} |
| 2214 | transformChildren(Transformer v) {} |
| 2215 | } |
| 2216 | |
| 2217 | class Throw extends Expression { |
| 2218 | Expression expression; |
| 2219 | |
| 2220 | Throw(this.expression) { |
| 2221 | expression?.parent = this; |
| 2222 | } |
| 2223 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2224 | DartType getStaticType(TypeEnvironment types) => const BottomType(); |
| 2225 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2226 | accept(ExpressionVisitor v) => v.visitThrow(this); |
| 2227 | |
| 2228 | visitChildren(Visitor v) { |
| 2229 | expression?.accept(v); |
| 2230 | } |
| 2231 | |
| 2232 | transformChildren(Transformer v) { |
| 2233 | if (expression != null) { |
| 2234 | expression = expression.accept(v); |
| 2235 | expression?.parent = this; |
| 2236 | } |
| 2237 | } |
| 2238 | } |
| 2239 | |
| 2240 | class ListLiteral extends Expression { |
| 2241 | bool isConst; |
| 2242 | DartType typeArgument; // Not null, defaults to DynamicType. |
| 2243 | final List<Expression> expressions; |
| 2244 | |
| 2245 | ListLiteral(this.expressions, |
| 2246 | {this.typeArgument: const DynamicType(), this.isConst: false}) { |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2247 | assert(typeArgument != null); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2248 | setParents(expressions, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2249 | } |
| 2250 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2251 | DartType getStaticType(TypeEnvironment types) { |
Asger Feldthaus | 3baff07 | 2016-11-01 14:43:31 | [diff] [blame] | 2252 | return types.literalListType(typeArgument); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2253 | } |
| 2254 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2255 | accept(ExpressionVisitor v) => v.visitListLiteral(this); |
| 2256 | |
| 2257 | visitChildren(Visitor v) { |
| 2258 | typeArgument?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2259 | visitList(expressions, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2260 | } |
| 2261 | |
| 2262 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 2263 | typeArgument = v.visitDartType(typeArgument); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2264 | transformList(expressions, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2265 | } |
| 2266 | } |
| 2267 | |
| 2268 | class MapLiteral extends Expression { |
| 2269 | bool isConst; |
| 2270 | DartType keyType; // Not null, defaults to DynamicType. |
| 2271 | DartType valueType; // Not null, defaults to DynamicType. |
| 2272 | final List<MapEntry> entries; |
| 2273 | |
| 2274 | MapLiteral(this.entries, |
| 2275 | {this.keyType: const DynamicType(), |
| 2276 | this.valueType: const DynamicType(), |
| 2277 | this.isConst: false}) { |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2278 | assert(keyType != null); |
| 2279 | assert(valueType != null); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2280 | setParents(entries, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2281 | } |
| 2282 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2283 | DartType getStaticType(TypeEnvironment types) { |
Asger Feldthaus | 3baff07 | 2016-11-01 14:43:31 | [diff] [blame] | 2284 | return types.literalMapType(keyType, valueType); |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2285 | } |
| 2286 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2287 | accept(ExpressionVisitor v) => v.visitMapLiteral(this); |
| 2288 | |
| 2289 | visitChildren(Visitor v) { |
| 2290 | keyType?.accept(v); |
| 2291 | valueType?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2292 | visitList(entries, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2293 | } |
| 2294 | |
| 2295 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 2296 | keyType = v.visitDartType(keyType); |
| 2297 | valueType = v.visitDartType(valueType); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2298 | transformList(entries, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2299 | } |
| 2300 | } |
| 2301 | |
| 2302 | class MapEntry extends TreeNode { |
| 2303 | Expression key; |
| 2304 | Expression value; |
| 2305 | |
| 2306 | MapEntry(this.key, this.value) { |
| 2307 | key?.parent = this; |
| 2308 | value?.parent = this; |
| 2309 | } |
| 2310 | |
| 2311 | accept(TreeVisitor v) => v.visitMapEntry(this); |
| 2312 | |
| 2313 | visitChildren(Visitor v) { |
| 2314 | key?.accept(v); |
| 2315 | value?.accept(v); |
| 2316 | } |
| 2317 | |
| 2318 | transformChildren(Transformer v) { |
| 2319 | if (key != null) { |
| 2320 | key = key.accept(v); |
| 2321 | key?.parent = this; |
| 2322 | } |
| 2323 | if (value != null) { |
| 2324 | value = value.accept(v); |
| 2325 | value?.parent = this; |
| 2326 | } |
| 2327 | } |
| 2328 | } |
| 2329 | |
| 2330 | /// Expression of form `await x`. |
| 2331 | class AwaitExpression extends Expression { |
| 2332 | Expression operand; |
| 2333 | |
| 2334 | AwaitExpression(this.operand) { |
| 2335 | operand?.parent = this; |
| 2336 | } |
| 2337 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2338 | DartType getStaticType(TypeEnvironment types) { |
| 2339 | return types.unfutureType(operand.getStaticType(types)); |
| 2340 | } |
| 2341 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2342 | accept(ExpressionVisitor v) => v.visitAwaitExpression(this); |
| 2343 | |
| 2344 | visitChildren(Visitor v) { |
| 2345 | operand?.accept(v); |
| 2346 | } |
| 2347 | |
| 2348 | transformChildren(Transformer v) { |
| 2349 | if (operand != null) { |
| 2350 | operand = operand.accept(v); |
| 2351 | operand?.parent = this; |
| 2352 | } |
| 2353 | } |
| 2354 | } |
| 2355 | |
| 2356 | /// Expression of form `(x,y) => ...` or `(x,y) { ... }` |
| 2357 | /// |
| 2358 | /// The arrow-body form `=> e` is desugared into `return e;`. |
| 2359 | class FunctionExpression extends Expression { |
| 2360 | FunctionNode function; |
| 2361 | |
| 2362 | FunctionExpression(this.function) { |
| 2363 | function?.parent = this; |
| 2364 | } |
| 2365 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2366 | DartType getStaticType(TypeEnvironment types) => function.functionType; |
| 2367 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2368 | accept(ExpressionVisitor v) => v.visitFunctionExpression(this); |
| 2369 | |
| 2370 | visitChildren(Visitor v) { |
| 2371 | function?.accept(v); |
| 2372 | } |
| 2373 | |
| 2374 | transformChildren(Transformer v) { |
| 2375 | if (function != null) { |
| 2376 | function = function.accept(v); |
| 2377 | function?.parent = this; |
| 2378 | } |
| 2379 | } |
| 2380 | } |
| 2381 | |
| 2382 | /// Synthetic expression of form `let v = x in y` |
| 2383 | class Let extends Expression { |
| 2384 | VariableDeclaration variable; // Must have an initializer. |
| 2385 | Expression body; |
| 2386 | |
| 2387 | Let(this.variable, this.body) { |
| 2388 | variable?.parent = this; |
| 2389 | body?.parent = this; |
| 2390 | } |
| 2391 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 2392 | DartType getStaticType(TypeEnvironment types) => body.getStaticType(types); |
| 2393 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2394 | accept(ExpressionVisitor v) => v.visitLet(this); |
| 2395 | |
| 2396 | visitChildren(Visitor v) { |
| 2397 | variable?.accept(v); |
| 2398 | body?.accept(v); |
| 2399 | } |
| 2400 | |
| 2401 | transformChildren(Transformer v) { |
| 2402 | if (variable != null) { |
| 2403 | variable = variable.accept(v); |
| 2404 | variable?.parent = this; |
| 2405 | } |
| 2406 | if (body != null) { |
| 2407 | body = body.accept(v); |
| 2408 | body?.parent = this; |
| 2409 | } |
| 2410 | } |
| 2411 | } |
| 2412 | |
| 2413 | // ------------------------------------------------------------------------ |
| 2414 | // STATEMENTS |
| 2415 | // ------------------------------------------------------------------------ |
| 2416 | |
| 2417 | abstract class Statement extends TreeNode { |
| 2418 | accept(StatementVisitor v); |
| 2419 | } |
| 2420 | |
| 2421 | /// A statement with a compile-time error. |
| 2422 | /// |
| 2423 | /// Should throw an exception at runtime. |
| 2424 | class InvalidStatement extends Statement { |
| 2425 | accept(StatementVisitor v) => v.visitInvalidStatement(this); |
| 2426 | |
| 2427 | visitChildren(Visitor v) {} |
| 2428 | transformChildren(Transformer v) {} |
| 2429 | } |
| 2430 | |
| 2431 | class ExpressionStatement extends Statement { |
| 2432 | Expression expression; |
| 2433 | |
| 2434 | ExpressionStatement(this.expression) { |
| 2435 | expression?.parent = this; |
| 2436 | } |
| 2437 | |
| 2438 | accept(StatementVisitor v) => v.visitExpressionStatement(this); |
| 2439 | |
| 2440 | visitChildren(Visitor v) { |
| 2441 | expression?.accept(v); |
| 2442 | } |
| 2443 | |
| 2444 | transformChildren(Transformer v) { |
| 2445 | if (expression != null) { |
| 2446 | expression = expression.accept(v); |
| 2447 | expression?.parent = this; |
| 2448 | } |
| 2449 | } |
| 2450 | } |
| 2451 | |
| 2452 | class Block extends Statement { |
| 2453 | final List<Statement> statements; |
| 2454 | |
| 2455 | Block(this.statements) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2456 | setParents(statements, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2457 | } |
| 2458 | |
| 2459 | accept(StatementVisitor v) => v.visitBlock(this); |
| 2460 | |
| 2461 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2462 | visitList(statements, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2463 | } |
| 2464 | |
| 2465 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2466 | transformList(statements, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2467 | } |
Asger Feldthaus | e60caa2 | 2016-11-15 13:27:22 | [diff] [blame] | 2468 | |
| 2469 | void addStatement(Statement node) { |
| 2470 | statements.add(node); |
| 2471 | node.parent = this; |
| 2472 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2473 | } |
| 2474 | |
| 2475 | class EmptyStatement extends Statement { |
| 2476 | accept(StatementVisitor v) => v.visitEmptyStatement(this); |
| 2477 | |
| 2478 | visitChildren(Visitor v) {} |
| 2479 | transformChildren(Transformer v) {} |
| 2480 | } |
| 2481 | |
| 2482 | class AssertStatement extends Statement { |
| 2483 | Expression condition; |
| 2484 | Expression message; // May be null. |
| 2485 | |
| 2486 | AssertStatement(this.condition, [this.message]) { |
| 2487 | condition?.parent = this; |
| 2488 | message?.parent = this; |
| 2489 | } |
| 2490 | |
| 2491 | accept(StatementVisitor v) => v.visitAssertStatement(this); |
| 2492 | |
| 2493 | visitChildren(Visitor v) { |
| 2494 | condition?.accept(v); |
| 2495 | message?.accept(v); |
| 2496 | } |
| 2497 | |
| 2498 | transformChildren(Transformer v) { |
| 2499 | if (condition != null) { |
| 2500 | condition = condition.accept(v); |
| 2501 | condition?.parent = this; |
| 2502 | } |
| 2503 | if (message != null) { |
| 2504 | message = message.accept(v); |
| 2505 | message?.parent = this; |
| 2506 | } |
| 2507 | } |
| 2508 | } |
| 2509 | |
| 2510 | /// A target of a [Break] statement. |
| 2511 | /// |
| 2512 | /// The label itself has no name; breaks reference the statement directly. |
| 2513 | /// |
| 2514 | /// The frontend does not generate labeled statements without uses. |
| 2515 | class LabeledStatement extends Statement { |
| 2516 | Statement body; |
| 2517 | |
| 2518 | LabeledStatement(this.body) { |
| 2519 | body?.parent = this; |
| 2520 | } |
| 2521 | |
| 2522 | accept(StatementVisitor v) => v.visitLabeledStatement(this); |
| 2523 | |
| 2524 | visitChildren(Visitor v) { |
| 2525 | body?.accept(v); |
| 2526 | } |
| 2527 | |
| 2528 | transformChildren(Transformer v) { |
| 2529 | if (body != null) { |
| 2530 | body = body.accept(v); |
| 2531 | body?.parent = this; |
| 2532 | } |
| 2533 | } |
| 2534 | } |
| 2535 | |
| 2536 | /// Breaks out of an enclosing [LabeledStatement]. |
| 2537 | /// |
| 2538 | /// Both `break` and loop `continue` statements are translated into this node. |
| 2539 | /// |
| 2540 | /// For example, the following loop with a `continue` will be desugared: |
| 2541 | /// |
| 2542 | /// while(x) { |
| 2543 | /// if (y) continue; |
| 2544 | /// BODY' |
| 2545 | /// } |
| 2546 | /// |
| 2547 | /// ==> |
| 2548 | /// |
| 2549 | /// while(x) { |
| 2550 | /// L: { |
| 2551 | /// if (y) break L; |
| 2552 | /// BODY' |
| 2553 | /// } |
| 2554 | /// } |
| 2555 | // |
| 2556 | class BreakStatement extends Statement { |
| 2557 | LabeledStatement target; |
| 2558 | |
| 2559 | BreakStatement(this.target); |
| 2560 | |
| 2561 | accept(StatementVisitor v) => v.visitBreakStatement(this); |
| 2562 | |
| 2563 | visitChildren(Visitor v) {} |
| 2564 | transformChildren(Transformer v) {} |
| 2565 | } |
| 2566 | |
| 2567 | class WhileStatement extends Statement { |
| 2568 | Expression condition; |
| 2569 | Statement body; |
| 2570 | |
| 2571 | WhileStatement(this.condition, this.body) { |
| 2572 | condition?.parent = this; |
| 2573 | body?.parent = this; |
| 2574 | } |
| 2575 | |
| 2576 | accept(StatementVisitor v) => v.visitWhileStatement(this); |
| 2577 | |
| 2578 | visitChildren(Visitor v) { |
| 2579 | condition?.accept(v); |
| 2580 | body?.accept(v); |
| 2581 | } |
| 2582 | |
| 2583 | transformChildren(Transformer v) { |
| 2584 | if (condition != null) { |
| 2585 | condition = condition.accept(v); |
| 2586 | condition?.parent = this; |
| 2587 | } |
| 2588 | if (body != null) { |
| 2589 | body = body.accept(v); |
| 2590 | body?.parent = this; |
| 2591 | } |
| 2592 | } |
| 2593 | } |
| 2594 | |
| 2595 | class DoStatement extends Statement { |
| 2596 | Statement body; |
| 2597 | Expression condition; |
| 2598 | |
| 2599 | DoStatement(this.body, this.condition) { |
| 2600 | body?.parent = this; |
| 2601 | condition?.parent = this; |
| 2602 | } |
| 2603 | |
| 2604 | accept(StatementVisitor v) => v.visitDoStatement(this); |
| 2605 | |
| 2606 | visitChildren(Visitor v) { |
| 2607 | body?.accept(v); |
| 2608 | condition?.accept(v); |
| 2609 | } |
| 2610 | |
| 2611 | transformChildren(Transformer v) { |
| 2612 | if (body != null) { |
| 2613 | body = body.accept(v); |
| 2614 | body?.parent = this; |
| 2615 | } |
| 2616 | if (condition != null) { |
| 2617 | condition = condition.accept(v); |
| 2618 | condition?.parent = this; |
| 2619 | } |
| 2620 | } |
| 2621 | } |
| 2622 | |
| 2623 | class ForStatement extends Statement { |
| 2624 | final List<VariableDeclaration> variables; // May be empty, but not null. |
| 2625 | Expression condition; // May be null. |
| 2626 | final List<Expression> updates; // May be empty, but not null. |
| 2627 | Statement body; |
| 2628 | |
| 2629 | ForStatement(this.variables, this.condition, this.updates, this.body) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2630 | setParents(variables, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2631 | condition?.parent = this; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2632 | setParents(updates, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2633 | body?.parent = this; |
| 2634 | } |
| 2635 | |
| 2636 | accept(StatementVisitor v) => v.visitForStatement(this); |
| 2637 | |
| 2638 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2639 | visitList(variables, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2640 | condition?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2641 | visitList(updates, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2642 | body?.accept(v); |
| 2643 | } |
| 2644 | |
| 2645 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2646 | transformList(variables, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2647 | if (condition != null) { |
| 2648 | condition = condition.accept(v); |
| 2649 | condition?.parent = this; |
| 2650 | } |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2651 | transformList(updates, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2652 | if (body != null) { |
| 2653 | body = body.accept(v); |
| 2654 | body?.parent = this; |
| 2655 | } |
| 2656 | } |
| 2657 | } |
| 2658 | |
| 2659 | class ForInStatement extends Statement { |
| 2660 | VariableDeclaration variable; // Has no initializer. |
| 2661 | Expression iterable; |
| 2662 | Statement body; |
| 2663 | bool isAsync; // True if this is an 'await for' loop. |
| 2664 | |
| 2665 | ForInStatement(this.variable, this.iterable, this.body, |
| 2666 | {this.isAsync: false}) { |
| 2667 | variable?.parent = this; |
| 2668 | iterable?.parent = this; |
| 2669 | body?.parent = this; |
| 2670 | } |
| 2671 | |
| 2672 | accept(StatementVisitor v) => v.visitForInStatement(this); |
| 2673 | |
| 2674 | visitChildren(Visitor v) { |
| 2675 | variable?.accept(v); |
| 2676 | iterable?.accept(v); |
| 2677 | body?.accept(v); |
| 2678 | } |
| 2679 | |
| 2680 | transformChildren(Transformer v) { |
| 2681 | if (variable != null) { |
| 2682 | variable = variable.accept(v); |
| 2683 | variable?.parent = this; |
| 2684 | } |
| 2685 | if (iterable != null) { |
| 2686 | iterable = iterable.accept(v); |
| 2687 | iterable?.parent = this; |
| 2688 | } |
| 2689 | if (body != null) { |
| 2690 | body = body.accept(v); |
| 2691 | body?.parent = this; |
| 2692 | } |
| 2693 | } |
| 2694 | } |
| 2695 | |
| 2696 | /// Statement of form `switch (e) { case x: ... }`. |
| 2697 | /// |
| 2698 | /// Adjacent case clauses have been merged into a single [SwitchCase]. A runtime |
| 2699 | /// exception must be thrown if one [SwitchCase] falls through to another case. |
| 2700 | class SwitchStatement extends Statement { |
| 2701 | Expression expression; |
| 2702 | final List<SwitchCase> cases; |
| 2703 | |
| 2704 | SwitchStatement(this.expression, this.cases) { |
| 2705 | expression?.parent = this; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2706 | setParents(cases, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2707 | } |
| 2708 | |
| 2709 | accept(StatementVisitor v) => v.visitSwitchStatement(this); |
| 2710 | |
| 2711 | visitChildren(Visitor v) { |
| 2712 | expression?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2713 | visitList(cases, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2714 | } |
| 2715 | |
| 2716 | transformChildren(Transformer v) { |
| 2717 | if (expression != null) { |
| 2718 | expression = expression.accept(v); |
| 2719 | expression?.parent = this; |
| 2720 | } |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2721 | transformList(cases, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2722 | } |
| 2723 | } |
| 2724 | |
| 2725 | /// A group of `case` clauses and/or a `default` clause. |
| 2726 | /// |
| 2727 | /// This is a potential target of [ContinueSwitchStatement]. |
| 2728 | class SwitchCase extends TreeNode { |
| 2729 | final List<Expression> expressions; |
| 2730 | Statement body; |
| 2731 | bool isDefault; |
| 2732 | |
| 2733 | SwitchCase(this.expressions, this.body, {this.isDefault: false}) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2734 | setParents(expressions, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2735 | body?.parent = this; |
| 2736 | } |
| 2737 | |
| 2738 | SwitchCase.defaultCase(this.body) |
| 2739 | : isDefault = true, |
| 2740 | expressions = <Expression>[] { |
| 2741 | body?.parent = this; |
| 2742 | } |
| 2743 | |
| 2744 | SwitchCase.empty() |
| 2745 | : expressions = <Expression>[], |
| 2746 | body = null, |
| 2747 | isDefault = false; |
| 2748 | |
| 2749 | accept(TreeVisitor v) => v.visitSwitchCase(this); |
| 2750 | |
| 2751 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2752 | visitList(expressions, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2753 | body?.accept(v); |
| 2754 | } |
| 2755 | |
| 2756 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2757 | transformList(expressions, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2758 | if (body != null) { |
| 2759 | body = body.accept(v); |
| 2760 | body?.parent = this; |
| 2761 | } |
| 2762 | } |
| 2763 | } |
| 2764 | |
| 2765 | /// Jump to a case in an enclosing switch. |
| 2766 | class ContinueSwitchStatement extends Statement { |
| 2767 | SwitchCase target; |
| 2768 | |
| 2769 | ContinueSwitchStatement(this.target); |
| 2770 | |
| 2771 | accept(StatementVisitor v) => v.visitContinueSwitchStatement(this); |
| 2772 | |
| 2773 | visitChildren(Visitor v) {} |
| 2774 | transformChildren(Transformer v) {} |
| 2775 | } |
| 2776 | |
| 2777 | class IfStatement extends Statement { |
| 2778 | Expression condition; |
| 2779 | Statement then; |
| 2780 | Statement otherwise; |
| 2781 | |
| 2782 | IfStatement(this.condition, this.then, this.otherwise) { |
| 2783 | condition?.parent = this; |
| 2784 | then?.parent = this; |
| 2785 | otherwise?.parent = this; |
| 2786 | } |
| 2787 | |
| 2788 | accept(StatementVisitor v) => v.visitIfStatement(this); |
| 2789 | |
| 2790 | visitChildren(Visitor v) { |
| 2791 | condition?.accept(v); |
| 2792 | then?.accept(v); |
| 2793 | otherwise?.accept(v); |
| 2794 | } |
| 2795 | |
| 2796 | transformChildren(Transformer v) { |
| 2797 | if (condition != null) { |
| 2798 | condition = condition.accept(v); |
| 2799 | condition?.parent = this; |
| 2800 | } |
| 2801 | if (then != null) { |
| 2802 | then = then.accept(v); |
| 2803 | then?.parent = this; |
| 2804 | } |
| 2805 | if (otherwise != null) { |
| 2806 | otherwise = otherwise.accept(v); |
| 2807 | otherwise?.parent = this; |
| 2808 | } |
| 2809 | } |
| 2810 | } |
| 2811 | |
| 2812 | class ReturnStatement extends Statement { |
| 2813 | Expression expression; // May be null. |
| 2814 | |
| 2815 | ReturnStatement([this.expression]) { |
| 2816 | expression?.parent = this; |
| 2817 | } |
| 2818 | |
| 2819 | accept(StatementVisitor v) => v.visitReturnStatement(this); |
| 2820 | |
| 2821 | visitChildren(Visitor v) { |
| 2822 | expression?.accept(v); |
| 2823 | } |
| 2824 | |
| 2825 | transformChildren(Transformer v) { |
| 2826 | if (expression != null) { |
| 2827 | expression = expression.accept(v); |
| 2828 | expression?.parent = this; |
| 2829 | } |
| 2830 | } |
| 2831 | } |
| 2832 | |
| 2833 | class TryCatch extends Statement { |
| 2834 | Statement body; |
| 2835 | List<Catch> catches; |
| 2836 | |
| 2837 | TryCatch(this.body, this.catches) { |
| 2838 | body?.parent = this; |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2839 | setParents(catches, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2840 | } |
| 2841 | |
| 2842 | accept(StatementVisitor v) => v.visitTryCatch(this); |
| 2843 | |
| 2844 | visitChildren(Visitor v) { |
| 2845 | body?.accept(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2846 | visitList(catches, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2847 | } |
| 2848 | |
| 2849 | transformChildren(Transformer v) { |
| 2850 | if (body != null) { |
| 2851 | body = body.accept(v); |
| 2852 | body?.parent = this; |
| 2853 | } |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2854 | transformList(catches, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2855 | } |
| 2856 | } |
| 2857 | |
| 2858 | class Catch extends TreeNode { |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2859 | DartType guard; // Not null, defaults to dynamic. |
| 2860 | VariableDeclaration exception; // May be null. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2861 | VariableDeclaration stackTrace; // May be null. |
| 2862 | Statement body; |
| 2863 | |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2864 | Catch(this.exception, this.body, |
| 2865 | {this.guard: const DynamicType(), this.stackTrace}) { |
| 2866 | assert(guard != null); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2867 | exception?.parent = this; |
| 2868 | stackTrace?.parent = this; |
| 2869 | body?.parent = this; |
| 2870 | } |
| 2871 | |
| 2872 | accept(TreeVisitor v) => v.visitCatch(this); |
| 2873 | |
| 2874 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 2875 | guard?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2876 | exception?.accept(v); |
| 2877 | stackTrace?.accept(v); |
| 2878 | body?.accept(v); |
| 2879 | } |
| 2880 | |
| 2881 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 2882 | guard = v.visitDartType(guard); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2883 | if (exception != null) { |
| 2884 | exception = exception.accept(v); |
| 2885 | exception?.parent = this; |
| 2886 | } |
| 2887 | if (stackTrace != null) { |
| 2888 | stackTrace = stackTrace.accept(v); |
| 2889 | stackTrace?.parent = this; |
| 2890 | } |
| 2891 | if (body != null) { |
| 2892 | body = body.accept(v); |
| 2893 | body?.parent = this; |
| 2894 | } |
| 2895 | } |
| 2896 | } |
| 2897 | |
| 2898 | class TryFinally extends Statement { |
| 2899 | Statement body; |
| 2900 | Statement finalizer; |
| 2901 | |
| 2902 | TryFinally(this.body, this.finalizer) { |
| 2903 | body?.parent = this; |
| 2904 | finalizer?.parent = this; |
| 2905 | } |
| 2906 | |
| 2907 | accept(StatementVisitor v) => v.visitTryFinally(this); |
| 2908 | |
| 2909 | visitChildren(Visitor v) { |
| 2910 | body?.accept(v); |
| 2911 | finalizer?.accept(v); |
| 2912 | } |
| 2913 | |
| 2914 | transformChildren(Transformer v) { |
| 2915 | if (body != null) { |
| 2916 | body = body.accept(v); |
| 2917 | body?.parent = this; |
| 2918 | } |
| 2919 | if (finalizer != null) { |
| 2920 | finalizer = finalizer.accept(v); |
| 2921 | finalizer?.parent = this; |
| 2922 | } |
| 2923 | } |
| 2924 | } |
| 2925 | |
| 2926 | /// Statement of form `yield x` or `yield* x`. |
Vyacheslav Egorov | ffb724a | 2016-08-23 08:27:27 | [diff] [blame] | 2927 | /// |
| 2928 | /// For native yield semantics see `AsyncMarker.SyncYielding`. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2929 | class YieldStatement extends Statement { |
| 2930 | Expression expression; |
Martin Kustermann | 1febd48 | 2016-07-07 19:24:10 | [diff] [blame] | 2931 | int flags = 0; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2932 | |
Vyacheslav Egorov | 8af4a2e | 2016-07-07 18:05:06 | [diff] [blame] | 2933 | YieldStatement(this.expression, |
Asger Feldthaus | d1f4702 | 2016-08-22 10:35:18 | [diff] [blame] | 2934 | {bool isYieldStar: false, bool isNative: false}) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2935 | expression?.parent = this; |
Martin Kustermann | 1febd48 | 2016-07-07 19:24:10 | [diff] [blame] | 2936 | this.isYieldStar = isYieldStar; |
| 2937 | this.isNative = isNative; |
| 2938 | } |
| 2939 | |
| 2940 | static const int FlagYieldStar = 1 << 0; |
| 2941 | static const int FlagNative = 1 << 1; |
| 2942 | |
| 2943 | bool get isYieldStar => flags & FlagYieldStar != 0; |
| 2944 | bool get isNative => flags & FlagNative != 0; |
| 2945 | |
| 2946 | void set isYieldStar(bool value) { |
| 2947 | flags = value ? (flags | FlagYieldStar) : (flags & ~FlagYieldStar); |
| 2948 | } |
| 2949 | |
| 2950 | void set isNative(bool value) { |
| 2951 | flags = value ? (flags | FlagNative) : (flags & ~FlagNative); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2952 | } |
| 2953 | |
| 2954 | accept(StatementVisitor v) => v.visitYieldStatement(this); |
| 2955 | |
| 2956 | visitChildren(Visitor v) { |
| 2957 | expression?.accept(v); |
| 2958 | } |
| 2959 | |
| 2960 | transformChildren(Transformer v) { |
| 2961 | if (expression != null) { |
| 2962 | expression = expression.accept(v); |
| 2963 | expression?.parent = this; |
| 2964 | } |
| 2965 | } |
| 2966 | } |
| 2967 | |
| 2968 | /// Declaration of a local variable. |
| 2969 | /// |
| 2970 | /// This may occur as a statement, but is also used in several non-statement |
| 2971 | /// contexts, such as in [ForStatement], [Catch], and [FunctionNode]. |
Asger Feldthaus | b3a0d20 | 2016-05-25 14:49:39 | [diff] [blame] | 2972 | /// |
Asger Feldthaus | 0642e6b | 2016-06-27 10:23:02 | [diff] [blame] | 2973 | /// When this occurs as a statement, it must be a direct child of a [Block]. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2974 | // |
| 2975 | // DESIGN TODO: Should we remove the 'final' modifier from variables? |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 2976 | class VariableDeclaration extends Statement |
| 2977 | implements Comparable<VariableDeclaration> { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2978 | /// For named parameters, this is the name of the parameter. No two named |
| 2979 | /// parameters (in the same parameter list) can have the same name. |
| 2980 | /// |
| 2981 | /// In all other cases, the name is cosmetic, may be empty or null, |
| 2982 | /// and is not necessarily unique. |
| 2983 | String name; |
| 2984 | int flags = 0; |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2985 | DartType type; // Not null, defaults to dynamic. |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 2986 | InferredValue inferredValue; // May be null. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 2987 | |
| 2988 | /// For locals, this is the initial value. |
| 2989 | /// For parameters, this is the default value. |
| 2990 | /// |
| 2991 | /// Should be null in other cases. |
| 2992 | Expression initializer; // May be null. |
| 2993 | |
| 2994 | VariableDeclaration(this.name, |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2995 | {this.initializer, |
| 2996 | this.type: const DynamicType(), |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 2997 | this.inferredValue, |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 2998 | bool isFinal: false, |
| 2999 | bool isConst: false}) { |
| 3000 | assert(type != null); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3001 | initializer?.parent = this; |
| 3002 | this.isFinal = isFinal; |
| 3003 | this.isConst = isConst; |
| 3004 | } |
| 3005 | |
| 3006 | /// Creates a synthetic variable with the given expression as initializer. |
| 3007 | VariableDeclaration.forValue(this.initializer, |
Asger Feldthaus | 243ea89 | 2016-06-06 13:01:51 | [diff] [blame] | 3008 | {bool isFinal: true, |
| 3009 | bool isConst: false, |
| 3010 | this.type: const DynamicType()}) { |
| 3011 | assert(type != null); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3012 | initializer?.parent = this; |
| 3013 | this.isFinal = isFinal; |
| 3014 | this.isConst = isConst; |
| 3015 | } |
| 3016 | |
| 3017 | static const int FlagFinal = 1 << 0; // Must match serialized bit positions. |
| 3018 | static const int FlagConst = 1 << 1; |
| 3019 | |
| 3020 | bool get isFinal => flags & FlagFinal != 0; |
| 3021 | bool get isConst => flags & FlagConst != 0; |
| 3022 | |
| 3023 | void set isFinal(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 3024 | flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3025 | } |
| 3026 | |
| 3027 | void set isConst(bool value) { |
Martin Kustermann | 1eb5f62 | 2016-07-08 07:22:36 | [diff] [blame] | 3028 | flags = value ? (flags | FlagConst) : (flags & ~FlagConst); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3029 | } |
| 3030 | |
| 3031 | accept(StatementVisitor v) => v.visitVariableDeclaration(this); |
| 3032 | |
| 3033 | visitChildren(Visitor v) { |
| 3034 | type?.accept(v); |
Martin Kustermann | 1392dcb | 2016-08-09 10:53:42 | [diff] [blame] | 3035 | inferredValue?.accept(v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3036 | initializer?.accept(v); |
| 3037 | } |
| 3038 | |
| 3039 | transformChildren(Transformer v) { |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 3040 | type = v.visitDartType(type); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3041 | if (initializer != null) { |
| 3042 | initializer = initializer.accept(v); |
| 3043 | initializer?.parent = this; |
| 3044 | } |
| 3045 | } |
| 3046 | |
| 3047 | /// Returns a possibly synthesized name for this variable, consistent with |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3048 | /// the names used across all [toString] calls. |
| 3049 | String toString() => debugVariableDeclarationName(this); |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3050 | |
| 3051 | int compareTo(VariableDeclaration other) { |
| 3052 | return name.compareTo(other.name); |
| 3053 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3054 | } |
| 3055 | |
| 3056 | /// Declaration a local function. |
| 3057 | /// |
| 3058 | /// The body of the function may use [variable] as its self-reference. |
| 3059 | class FunctionDeclaration extends Statement { |
| 3060 | VariableDeclaration variable; // Is final and has no initializer. |
| 3061 | FunctionNode function; |
| 3062 | |
| 3063 | FunctionDeclaration(this.variable, this.function) { |
| 3064 | variable?.parent = this; |
| 3065 | function?.parent = this; |
| 3066 | } |
| 3067 | |
| 3068 | accept(StatementVisitor v) => v.visitFunctionDeclaration(this); |
| 3069 | |
| 3070 | visitChildren(Visitor v) { |
| 3071 | variable?.accept(v); |
| 3072 | function?.accept(v); |
| 3073 | } |
| 3074 | |
| 3075 | transformChildren(Transformer v) { |
| 3076 | if (variable != null) { |
| 3077 | variable = variable.accept(v); |
| 3078 | variable?.parent = this; |
| 3079 | } |
| 3080 | if (function != null) { |
| 3081 | function = function.accept(v); |
| 3082 | function?.parent = this; |
| 3083 | } |
| 3084 | } |
| 3085 | } |
| 3086 | |
| 3087 | // ------------------------------------------------------------------------ |
| 3088 | // NAMES |
| 3089 | // ------------------------------------------------------------------------ |
| 3090 | |
| 3091 | /// A public name, or a private name qualified by a library. |
| 3092 | /// |
| 3093 | /// Names are only used for expressions with dynamic dispatch, as all |
| 3094 | /// statically resolved references are represented in nameless form. |
| 3095 | /// |
| 3096 | /// [Name]s are immutable and compare based on structural equality, and they |
| 3097 | /// are not AST nodes. |
| 3098 | /// |
| 3099 | /// The [toString] method returns a human-readable string that includes the |
| 3100 | /// library name for private names; uniqueness is not guaranteed. |
| 3101 | abstract class Name implements Node { |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 3102 | final int hashCode; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3103 | final String name; |
| 3104 | Library get library; |
| 3105 | bool get isPrivate; |
| 3106 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 3107 | Name._internal(this.hashCode, this.name); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3108 | |
| 3109 | factory Name(String name, [Library library]) { |
| 3110 | /// Use separate subclasses for the public and private case to save memory |
| 3111 | /// for public names. |
| 3112 | if (name.startsWith('_')) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3113 | assert(library != null); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3114 | return new _PrivateName(name, library); |
| 3115 | } else { |
| 3116 | return new _PublicName(name); |
| 3117 | } |
| 3118 | } |
| 3119 | |
| 3120 | bool operator ==(other) { |
| 3121 | return other is Name && name == other.name && library == other.library; |
| 3122 | } |
| 3123 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3124 | accept(Visitor v) => v.visitName(this); |
| 3125 | |
| 3126 | visitChildren(Visitor v) { |
| 3127 | // DESIGN TODO: Should we visit the library as a library reference? |
| 3128 | } |
| 3129 | } |
| 3130 | |
| 3131 | class _PrivateName extends Name { |
| 3132 | final Library library; |
| 3133 | bool get isPrivate => true; |
| 3134 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 3135 | _PrivateName(String name, Library library) |
| 3136 | : this.library = library, |
| 3137 | super._internal(_computeHashCode(name, library), name); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3138 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3139 | String toString() => library != null ? '$library::$name' : name; |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 3140 | |
| 3141 | static int _computeHashCode(String name, Library library) { |
| 3142 | return 131 * name.hashCode + 17 * library.hashCode; |
| 3143 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3144 | } |
| 3145 | |
| 3146 | class _PublicName extends Name { |
| 3147 | Library get library => null; |
| 3148 | bool get isPrivate => false; |
| 3149 | |
Asger Feldthaus | 7ad92c2 | 2016-05-09 12:12:40 | [diff] [blame] | 3150 | _PublicName(String name) : super._internal(name.hashCode, name); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3151 | |
| 3152 | String toString() => name; |
| 3153 | } |
| 3154 | |
| 3155 | // ------------------------------------------------------------------------ |
| 3156 | // TYPES |
| 3157 | // ------------------------------------------------------------------------ |
| 3158 | |
| 3159 | /// A syntax-independent notion of a type. |
| 3160 | /// |
| 3161 | /// [DartType]s are not AST nodes and may be shared between different parents. |
| 3162 | /// |
| 3163 | /// [DartType] objects should be treated as unmodifiable objects, although |
| 3164 | /// immutability is not enforced for List fields, and [TypeParameter]s are |
| 3165 | /// cyclic structures that are constructed by mutation. |
| 3166 | /// |
| 3167 | /// The `==` operator on [DartType]s compare based on type equality, not |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3168 | /// object identity. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3169 | abstract class DartType extends Node { |
| 3170 | const DartType(); |
| 3171 | |
| 3172 | accept(DartTypeVisitor v); |
| 3173 | |
| 3174 | bool operator ==(Object other); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3175 | } |
| 3176 | |
| 3177 | /// The type arising from invalid type annotations. |
| 3178 | /// |
| 3179 | /// Can usually be treated as 'dynamic', but should occasionally be handled |
| 3180 | /// differently, e.g. `x is ERROR` should evaluate to false. |
| 3181 | class InvalidType extends DartType { |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3182 | final int hashCode = 12345; |
| 3183 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3184 | const InvalidType(); |
| 3185 | |
| 3186 | accept(DartTypeVisitor v) => v.visitInvalidType(this); |
| 3187 | visitChildren(Visitor v) {} |
| 3188 | |
| 3189 | bool operator ==(Object other) => other is InvalidType; |
| 3190 | } |
| 3191 | |
| 3192 | class DynamicType extends DartType { |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3193 | final int hashCode = 54321; |
| 3194 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3195 | const DynamicType(); |
| 3196 | |
| 3197 | accept(DartTypeVisitor v) => v.visitDynamicType(this); |
| 3198 | visitChildren(Visitor v) {} |
| 3199 | |
| 3200 | bool operator ==(Object other) => other is DynamicType; |
| 3201 | } |
| 3202 | |
| 3203 | class VoidType extends DartType { |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3204 | final int hashCode = 123121; |
| 3205 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3206 | const VoidType(); |
| 3207 | |
| 3208 | accept(DartTypeVisitor v) => v.visitVoidType(this); |
| 3209 | visitChildren(Visitor v) {} |
| 3210 | |
| 3211 | bool operator ==(Object other) => other is VoidType; |
| 3212 | } |
| 3213 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 3214 | class BottomType extends DartType { |
| 3215 | final int hashCode = 514213; |
| 3216 | |
| 3217 | const BottomType(); |
| 3218 | |
| 3219 | accept(DartTypeVisitor v) => v.visitBottomType(this); |
| 3220 | visitChildren(Visitor v) {} |
| 3221 | |
| 3222 | bool operator ==(Object other) => other is BottomType; |
| 3223 | } |
| 3224 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3225 | class InterfaceType extends DartType { |
| 3226 | final Class classNode; |
| 3227 | final List<DartType> typeArguments; |
| 3228 | |
| 3229 | /// The [typeArguments] list must not be modified after this call. If the |
| 3230 | /// list is omitted, 'dynamic' type arguments are filled in. |
| 3231 | InterfaceType(Class classNode, [List<DartType> typeArguments]) |
| 3232 | : this.classNode = classNode, |
| 3233 | this.typeArguments = typeArguments ?? _defaultTypeArguments(classNode); |
| 3234 | |
| 3235 | static List<DartType> _defaultTypeArguments(Class classNode) { |
| 3236 | if (classNode.typeParameters.length == 0) { |
| 3237 | // Avoid allocating a list in this very common case. |
| 3238 | return const <DartType>[]; |
| 3239 | } else { |
| 3240 | return new List<DartType>.filled( |
| 3241 | classNode.typeParameters.length, const DynamicType()); |
| 3242 | } |
| 3243 | } |
| 3244 | |
| 3245 | accept(DartTypeVisitor v) => v.visitInterfaceType(this); |
| 3246 | |
| 3247 | visitChildren(Visitor v) { |
| 3248 | classNode.acceptReference(v); |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3249 | visitList(typeArguments, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3250 | } |
| 3251 | |
| 3252 | bool operator ==(Object other) { |
| 3253 | if (identical(this, other)) return true; |
| 3254 | if (other is InterfaceType) { |
| 3255 | if (classNode != other.classNode) return false; |
| 3256 | if (typeArguments.length != other.typeArguments.length) return false; |
| 3257 | for (int i = 0; i < typeArguments.length; ++i) { |
| 3258 | if (typeArguments[i] != other.typeArguments[i]) return false; |
| 3259 | } |
| 3260 | return true; |
| 3261 | } else { |
| 3262 | return false; |
| 3263 | } |
| 3264 | } |
Peter von der Ahé | 30372f0 | 2016-05-19 11:40:48 | [diff] [blame] | 3265 | |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3266 | int get hashCode { |
| 3267 | int hash = 0x3fffffff & classNode.hashCode; |
| 3268 | for (int i = 0; i < typeArguments.length; ++i) { |
| 3269 | hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| 3270 | } |
| 3271 | return hash; |
| 3272 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3273 | } |
| 3274 | |
| 3275 | /// A possibly generic function type. |
| 3276 | class FunctionType extends DartType { |
| 3277 | final List<TypeParameter> typeParameters; |
| 3278 | final int requiredParameterCount; |
| 3279 | final List<DartType> positionalParameters; |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3280 | final List<NamedType> namedParameters; // Must be sorted. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3281 | final DartType returnType; |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3282 | int _hashCode; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3283 | |
| 3284 | FunctionType(List<DartType> positionalParameters, this.returnType, |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3285 | {this.namedParameters: const <NamedType>[], |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3286 | this.typeParameters: const <TypeParameter>[], |
| 3287 | int requiredParameterCount}) |
| 3288 | : this.positionalParameters = positionalParameters, |
| 3289 | this.requiredParameterCount = |
| 3290 | requiredParameterCount ?? positionalParameters.length; |
| 3291 | |
| 3292 | accept(DartTypeVisitor v) => v.visitFunctionType(this); |
| 3293 | |
| 3294 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3295 | visitList(typeParameters, v); |
| 3296 | visitList(positionalParameters, v); |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3297 | visitList(namedParameters, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3298 | returnType.accept(v); |
| 3299 | } |
| 3300 | |
| 3301 | bool operator ==(Object other) { |
| 3302 | if (identical(this, other)) return true; |
| 3303 | if (other is FunctionType) { |
| 3304 | if (typeParameters.length != other.typeParameters.length || |
| 3305 | requiredParameterCount != other.requiredParameterCount || |
| 3306 | positionalParameters.length != other.positionalParameters.length || |
| 3307 | namedParameters.length != other.namedParameters.length) { |
| 3308 | return false; |
| 3309 | } |
| 3310 | if (typeParameters.isEmpty) { |
| 3311 | for (int i = 0; i < positionalParameters.length; ++i) { |
| 3312 | if (positionalParameters[i] != other.positionalParameters[i]) { |
| 3313 | return false; |
| 3314 | } |
| 3315 | } |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3316 | for (int i = 0; i < namedParameters.length; ++i) { |
| 3317 | if (namedParameters[i] != other.namedParameters[i]) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3318 | return false; |
| 3319 | } |
| 3320 | } |
| 3321 | return returnType == other.returnType; |
| 3322 | } else { |
| 3323 | // Structural equality does not tell us if two generic function types |
| 3324 | // are the same type. If they are unifiable without substituting any |
| 3325 | // type variables, they are equal. |
| 3326 | return unifyTypes(this, other, new Set<TypeParameter>()) != null; |
| 3327 | } |
| 3328 | } else { |
| 3329 | return false; |
| 3330 | } |
| 3331 | } |
| 3332 | |
| 3333 | /// Returns a variant of this function type that does not declare any type |
| 3334 | /// parameters. |
| 3335 | /// |
| 3336 | /// Any uses of its type parameters become free variables in the returned |
| 3337 | /// type. |
| 3338 | FunctionType get withoutTypeParameters { |
| 3339 | if (typeParameters.isEmpty) return this; |
| 3340 | return new FunctionType(positionalParameters, returnType, |
| 3341 | requiredParameterCount: requiredParameterCount, |
| 3342 | namedParameters: namedParameters); |
| 3343 | } |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3344 | |
| 3345 | int get hashCode => _hashCode ??= _computeHashCode(); |
| 3346 | |
| 3347 | int _computeHashCode() { |
| 3348 | int hash = 1237; |
| 3349 | hash = 0x3fffffff & (hash * 31 + requiredParameterCount); |
| 3350 | for (int i = 0; i < typeParameters.length; ++i) { |
| 3351 | TypeParameter parameter = typeParameters[i]; |
| 3352 | _temporaryHashCodeTable[parameter] = _temporaryHashCodeTable.length; |
| 3353 | hash = 0x3fffffff & (hash * 31 + parameter.bound.hashCode); |
| 3354 | } |
| 3355 | for (int i = 0; i < positionalParameters.length; ++i) { |
| 3356 | hash = 0x3fffffff & (hash * 31 + positionalParameters[i].hashCode); |
| 3357 | } |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3358 | for (int i = 0; i < namedParameters.length; ++i) { |
| 3359 | hash = 0x3fffffff & (hash * 31 + namedParameters[i].hashCode); |
| 3360 | } |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3361 | hash = 0x3fffffff & (hash * 31 + returnType.hashCode); |
| 3362 | for (int i = 0; i < typeParameters.length; ++i) { |
| 3363 | // Remove the type parameters from the scope again. |
| 3364 | _temporaryHashCodeTable.remove(typeParameters[i]); |
| 3365 | } |
| 3366 | return hash; |
| 3367 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3368 | } |
| 3369 | |
Asger Feldthaus | 0defa95 | 2016-11-16 14:16:24 | [diff] [blame] | 3370 | /// A named parameter in [FunctionType]. |
| 3371 | class NamedType extends Node implements Comparable<NamedType> { |
| 3372 | final String name; |
| 3373 | final DartType type; |
| 3374 | |
| 3375 | NamedType(this.name, this.type); |
| 3376 | |
| 3377 | bool operator ==(Object other) { |
| 3378 | return other is NamedType && name == other.name && type == other.type; |
| 3379 | } |
| 3380 | |
| 3381 | int get hashCode { |
| 3382 | return name.hashCode * 31 + type.hashCode * 37; |
| 3383 | } |
| 3384 | |
| 3385 | int compareTo(NamedType other) => name.compareTo(other.name); |
| 3386 | |
| 3387 | accept(Visitor v) => v.visitNamedType(this); |
| 3388 | |
| 3389 | void visitChildren(Visitor v) { |
| 3390 | type.accept(v); |
| 3391 | } |
| 3392 | } |
| 3393 | |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3394 | /// Stores the hash code of function type parameters while computing the hash |
| 3395 | /// code of a [FunctionType] object. |
| 3396 | /// |
| 3397 | /// This ensures that distinct [FunctionType] objects get the same hash code |
| 3398 | /// if they represent the same type, even though their type parameters are |
| 3399 | /// represented by different objects. |
| 3400 | final Map<TypeParameter, int> _temporaryHashCodeTable = <TypeParameter, int>{}; |
| 3401 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3402 | /// Reference to a type variable. |
| 3403 | class TypeParameterType extends DartType { |
| 3404 | TypeParameter parameter; |
| 3405 | |
| 3406 | TypeParameterType(this.parameter); |
| 3407 | |
| 3408 | accept(DartTypeVisitor v) => v.visitTypeParameterType(this); |
| 3409 | |
| 3410 | visitChildren(Visitor v) {} |
| 3411 | |
| 3412 | bool operator ==(Object other) { |
| 3413 | return other is TypeParameterType && parameter == other.parameter; |
| 3414 | } |
Peter von der Ahé | 30372f0 | 2016-05-19 11:40:48 | [diff] [blame] | 3415 | |
Asger Feldthaus | 885be10 | 2016-05-25 11:09:59 | [diff] [blame] | 3416 | int get hashCode => _temporaryHashCodeTable[parameter] ?? parameter.hashCode; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3417 | } |
| 3418 | |
| 3419 | /// Declaration of a type variable. |
| 3420 | /// |
| 3421 | /// Type parameters declared in a [Class] or [FunctionNode] are part of the AST, |
| 3422 | /// have a parent pointer to its declaring class or function, and will be seen |
| 3423 | /// by tree visitors. |
| 3424 | /// |
| 3425 | /// Type parameters declared by a [FunctionType] are orphans and have a `null` |
| 3426 | /// parent pointer. [TypeParameter] objects should not be shared between |
| 3427 | /// different [FunctionType] objects. |
| 3428 | class TypeParameter extends TreeNode { |
| 3429 | String name; // Cosmetic name. |
| 3430 | |
Asger Feldthaus | 98a6a2c | 2016-10-14 11:29:36 | [diff] [blame] | 3431 | /// The bound on the type variable. |
| 3432 | /// |
| 3433 | /// Should not be null except temporarily during IR construction. Should |
| 3434 | /// be set to the root class for type parameters without an explicit bound. |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3435 | DartType bound; |
| 3436 | |
Asger Feldthaus | 90ebc0b | 2016-10-11 13:21:48 | [diff] [blame] | 3437 | TypeParameter([this.name, this.bound]); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3438 | |
| 3439 | accept(TreeVisitor v) => v.visitTypeParameter(this); |
| 3440 | |
| 3441 | visitChildren(Visitor v) { |
| 3442 | bound.accept(v); |
| 3443 | } |
| 3444 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 3445 | transformChildren(Transformer v) { |
| 3446 | bound = v.visitDartType(bound); |
| 3447 | } |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3448 | |
| 3449 | /// Returns a possibly synthesized name for this type parameter, consistent |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3450 | /// with the names used across all [toString] calls. |
| 3451 | String toString() => debugQualifiedTypeParameterName(this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3452 | } |
| 3453 | |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 3454 | class Supertype extends Node { |
| 3455 | final Class classNode; |
| 3456 | final List<DartType> typeArguments; |
| 3457 | |
| 3458 | Supertype(this.classNode, this.typeArguments); |
| 3459 | |
| 3460 | accept(Visitor v) => v.visitSupertype(this); |
| 3461 | |
| 3462 | visitChildren(Visitor v) { |
| 3463 | classNode.acceptReference(v); |
| 3464 | visitList(typeArguments, v); |
| 3465 | } |
| 3466 | |
| 3467 | InterfaceType get asInterfaceType { |
| 3468 | return new InterfaceType(classNode, typeArguments); |
| 3469 | } |
| 3470 | |
| 3471 | bool operator ==(Object other) { |
| 3472 | if (identical(this, other)) return true; |
| 3473 | if (other is Supertype) { |
| 3474 | if (classNode != other.classNode) return false; |
| 3475 | if (typeArguments.length != other.typeArguments.length) return false; |
| 3476 | for (int i = 0; i < typeArguments.length; ++i) { |
| 3477 | if (typeArguments[i] != other.typeArguments[i]) return false; |
| 3478 | } |
| 3479 | return true; |
| 3480 | } else { |
| 3481 | return false; |
| 3482 | } |
| 3483 | } |
| 3484 | |
| 3485 | int get hashCode { |
| 3486 | int hash = 0x3fffffff & classNode.hashCode; |
| 3487 | for (int i = 0; i < typeArguments.length; ++i) { |
| 3488 | hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode)); |
| 3489 | } |
| 3490 | return hash; |
| 3491 | } |
| 3492 | } |
| 3493 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3494 | // ------------------------------------------------------------------------ |
| 3495 | // PROGRAM |
| 3496 | // ------------------------------------------------------------------------ |
| 3497 | |
| 3498 | /// A way to bundle up all the libraries in a program. |
| 3499 | class Program extends TreeNode { |
| 3500 | final List<Library> libraries; |
| 3501 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 3502 | /// Map from a source file uri to a line-starts table. |
| 3503 | /// Given a source file uri and a offset in that file one can translate |
| 3504 | /// it to a line:column position in that file. |
| 3505 | final Map<String, List<int>> uriToLineStarts; |
| 3506 | |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3507 | /// Reference to the main method in one of the libraries. |
| 3508 | Procedure mainMethod; |
| 3509 | |
Jens Johansen | a85aabb | 2016-09-29 09:26:40 | [diff] [blame] | 3510 | Program([List<Library> libraries, Map<String, List<int>> uriToLineStarts]) |
| 3511 | : libraries = libraries ?? <Library>[], |
| 3512 | uriToLineStarts = uriToLineStarts ?? {} { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3513 | setParents(libraries, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3514 | } |
| 3515 | |
| 3516 | accept(TreeVisitor v) => v.visitProgram(this); |
| 3517 | |
| 3518 | visitChildren(Visitor v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3519 | visitList(libraries, v); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3520 | mainMethod?.acceptReference(v); |
| 3521 | } |
| 3522 | |
| 3523 | transformChildren(Transformer v) { |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3524 | transformList(libraries, v, this); |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3525 | } |
Asger Feldthaus | 5a40636 | 2016-10-31 10:29:37 | [diff] [blame] | 3526 | |
| 3527 | Program get enclosingProgram => this; |
| 3528 | |
| 3529 | /// Translates an offset to line and column numbers in the given file. |
| 3530 | Location getLocation(String file, int offset) { |
| 3531 | List<int> lines = uriToLineStarts[file]; |
| 3532 | int low = 0, high = lines.length - 1; |
| 3533 | while (low < high) { |
| 3534 | int mid = high - ((high - low) >> 1); // Get middle, rounding up. |
| 3535 | int pivot = lines[mid]; |
| 3536 | if (pivot <= offset) { |
| 3537 | low = mid; |
| 3538 | } else { |
| 3539 | high = mid - 1; |
| 3540 | } |
| 3541 | } |
| 3542 | int lineIndex = low; |
| 3543 | int lineStart = lines[lineIndex]; |
| 3544 | int lineNumber = 1 + lineIndex; |
| 3545 | int columnNumber = offset - lineStart; |
| 3546 | return new Location(file, lineNumber, columnNumber); |
| 3547 | } |
| 3548 | } |
| 3549 | |
| 3550 | /// A tuple with file, line, and column number, for displaying human-readable |
| 3551 | /// locations. |
| 3552 | class Location { |
| 3553 | final String file; |
| 3554 | final int line; // 1-based. |
| 3555 | final int column; // 1-based. |
| 3556 | |
| 3557 | Location(this.file, this.line, this.column); |
| 3558 | |
| 3559 | String toString() => '$file:$line:$column'; |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3560 | } |
| 3561 | |
| 3562 | // ------------------------------------------------------------------------ |
| 3563 | // INTERNAL FUNCTIONS |
| 3564 | // ------------------------------------------------------------------------ |
| 3565 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3566 | void setParents(List<TreeNode> nodes, TreeNode parent) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3567 | for (int i = 0; i < nodes.length; ++i) { |
| 3568 | nodes[i].parent = parent; |
| 3569 | } |
| 3570 | } |
| 3571 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3572 | void visitList(List<Node> nodes, Visitor visitor) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3573 | for (int i = 0; i < nodes.length; ++i) { |
| 3574 | nodes[i].accept(visitor); |
| 3575 | } |
| 3576 | } |
| 3577 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3578 | void visitIterable(Iterable<Node> nodes, Visitor visitor) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3579 | for (var node in nodes) { |
| 3580 | node.accept(visitor); |
| 3581 | } |
| 3582 | } |
| 3583 | |
Asger Feldthaus | 760da8e | 2016-08-24 14:46:50 | [diff] [blame] | 3584 | void transformTypeList(List<DartType> nodes, Transformer visitor) { |
| 3585 | int storeIndex = 0; |
| 3586 | for (int i = 0; i < nodes.length; ++i) { |
| 3587 | var result = visitor.visitDartType(nodes[i]); |
| 3588 | if (result != null) { |
| 3589 | nodes[storeIndex] = result; |
| 3590 | ++storeIndex; |
| 3591 | } |
| 3592 | } |
| 3593 | if (storeIndex < nodes.length) { |
| 3594 | nodes.length = storeIndex; |
| 3595 | } |
| 3596 | } |
| 3597 | |
Asger Feldthaus | e1c26f8 | 2016-10-24 14:21:52 | [diff] [blame] | 3598 | void transformSupertypeList(List<Supertype> nodes, Transformer visitor) { |
| 3599 | int storeIndex = 0; |
| 3600 | for (int i = 0; i < nodes.length; ++i) { |
| 3601 | var result = visitor.visitSupertype(nodes[i]); |
| 3602 | if (result != null) { |
| 3603 | nodes[storeIndex] = result; |
| 3604 | ++storeIndex; |
| 3605 | } |
| 3606 | } |
| 3607 | if (storeIndex < nodes.length) { |
| 3608 | nodes.length = storeIndex; |
| 3609 | } |
| 3610 | } |
| 3611 | |
Asger Feldthaus | 246f6c1 | 2016-05-25 11:56:43 | [diff] [blame] | 3612 | void transformList(List<TreeNode> nodes, Transformer visitor, TreeNode parent) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3613 | int storeIndex = 0; |
| 3614 | for (int i = 0; i < nodes.length; ++i) { |
| 3615 | var result = nodes[i].accept(visitor); |
| 3616 | if (result != null) { |
| 3617 | nodes[storeIndex] = result; |
| 3618 | result.parent = parent; |
| 3619 | ++storeIndex; |
| 3620 | } |
| 3621 | } |
| 3622 | if (storeIndex < nodes.length) { |
| 3623 | nodes.length = storeIndex; |
| 3624 | } |
| 3625 | } |
| 3626 | |
| 3627 | List<DartType> _getAsTypeArguments(List<TypeParameter> typeParameters) { |
| 3628 | if (typeParameters.isEmpty) return const <DartType>[]; |
| 3629 | return new List<DartType>.generate( |
| 3630 | typeParameters.length, (i) => new TypeParameterType(typeParameters[i]), |
| 3631 | growable: false); |
| 3632 | } |
| 3633 | |
| 3634 | class _ChildReplacer extends Transformer { |
| 3635 | final TreeNode child; |
| 3636 | final TreeNode replacement; |
| 3637 | |
| 3638 | _ChildReplacer(this.child, this.replacement); |
| 3639 | |
Asger Feldthaus | 2eaa884 | 2016-09-06 09:40:13 | [diff] [blame] | 3640 | @override |
| 3641 | defaultTreeNode(TreeNode node) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3642 | if (node == child) { |
Asger Feldthaus | 02604e9 | 2016-04-26 08:39:15 | [diff] [blame] | 3643 | return replacement; |
| 3644 | } else { |
| 3645 | return node; |
| 3646 | } |
| 3647 | } |
| 3648 | } |