| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library fasta.scope; | 5 library fasta.scope; |
| 6 | 6 |
| 7 import 'builder/builder.dart' show Builder, MixedAccessor; | 7 import 'builder/builder.dart' show Builder, TypeVariableBuilder; |
| 8 | 8 |
| 9 import 'errors.dart' show internalError; | 9 import 'errors.dart' show internalError; |
| 10 | 10 |
| 11 class Scope { | 11 class MutableScope { |
| 12 /// Names declared in this scope. | 12 /// Names declared in this scope. |
| 13 final Map<String, Builder> local; | 13 Map<String, Builder> local; |
| 14 | 14 |
| 15 /// Setters declared in this scope. | 15 /// Setters declared in this scope. |
| 16 final Map<String, Builder> setters; | 16 Map<String, Builder> setters; |
| 17 | 17 |
| 18 /// The scope that this scope is nested within, or `null` if this is the top | 18 /// The scope that this scope is nested within, or `null` if this is the top |
| 19 /// level scope. | 19 /// level scope. |
| 20 final Scope parent; | 20 Scope parent; |
| 21 | 21 |
| 22 MutableScope(this.local, this.setters, this.parent); |
| 23 } |
| 24 |
| 25 class Scope extends MutableScope { |
| 22 /// Indicates whether an attempt to declare new names in this scope should | 26 /// Indicates whether an attempt to declare new names in this scope should |
| 23 /// succeed. | 27 /// succeed. |
| 24 final bool isModifiable; | 28 final bool isModifiable; |
| 25 | 29 |
| 26 Map<String, Builder> labels; | 30 Map<String, Builder> labels; |
| 27 | 31 |
| 28 Map<String, Builder> forwardDeclaredLabels; | 32 Map<String, Builder> forwardDeclaredLabels; |
| 29 | 33 |
| 30 Scope(this.local, Map<String, Builder> setters, this.parent, | 34 Scope(Map<String, Builder> local, Map<String, Builder> setters, Scope parent, |
| 31 {this.isModifiable: true}) | 35 {this.isModifiable: true}) |
| 32 : setters = setters ?? const <String, Builder>{}; | 36 : super(local, setters = setters ?? const <String, Builder>{}, parent); |
| 33 | 37 |
| 34 Scope.top({bool isModifiable: false}) | 38 Scope.top({bool isModifiable: false}) |
| 35 : this(<String, Builder>{}, <String, Builder>{}, null, | 39 : this(<String, Builder>{}, <String, Builder>{}, null, |
| 36 isModifiable: isModifiable); | 40 isModifiable: isModifiable); |
| 37 | 41 |
| 38 Scope.immutable() | 42 Scope.immutable() |
| 39 : this(const <String, Builder>{}, const <String, Builder>{}, null, | 43 : this(const <String, Builder>{}, const <String, Builder>{}, null, |
| 40 isModifiable: false); | 44 isModifiable: false); |
| 41 | 45 |
| 42 Scope.nested(Scope parent, {bool isModifiable: true}) | 46 Scope.nested(Scope parent, {bool isModifiable: true}) |
| 43 : this(<String, Builder>{}, null, parent, isModifiable: isModifiable); | 47 : this(<String, Builder>{}, null, parent, isModifiable: isModifiable); |
| 44 | 48 |
| 49 /// Don't use this. Use [becomePartOf] instead. |
| 50 void set local(_) => internalError("Unsupported operation."); |
| 51 |
| 52 /// Don't use this. Use [becomePartOf] instead. |
| 53 void set setters(_) => internalError("Unsupported operation."); |
| 54 |
| 55 /// Don't use this. Use [becomePartOf] instead. |
| 56 void set parent(_) => internalError("Unsupported operation."); |
| 57 |
| 58 /// This scope becomes equivalent to [scope]. This is used for parts to |
| 59 /// become part of their library's scope. |
| 60 void becomePartOf(Scope scope) { |
| 61 assert(parent.parent == null); |
| 62 assert(scope.parent.parent == null); |
| 63 super.local = scope.local; |
| 64 super.setters = scope.setters; |
| 65 super.parent = scope.parent; |
| 66 } |
| 67 |
| 45 Scope createNestedScope({bool isModifiable: true}) { | 68 Scope createNestedScope({bool isModifiable: true}) { |
| 46 return new Scope.nested(this, isModifiable: isModifiable); | 69 return new Scope.nested(this, isModifiable: isModifiable); |
| 47 } | 70 } |
| 48 | 71 |
| 72 Scope withTypeVariables(List<TypeVariableBuilder> typeVariables) { |
| 73 if (typeVariables == null) return this; |
| 74 Scope newScope = new Scope.nested(this, isModifiable: false); |
| 75 for (TypeVariableBuilder t in typeVariables) { |
| 76 newScope.local[t.name] = t; |
| 77 } |
| 78 return newScope; |
| 79 } |
| 80 |
| 49 /// Create a special scope for use by labeled staments. This scope doesn't | 81 /// Create a special scope for use by labeled staments. This scope doesn't |
| 50 /// introduce a new scope for local variables, only for labels. This deals | 82 /// introduce a new scope for local variables, only for labels. This deals |
| 51 /// with corner cases like this: | 83 /// with corner cases like this: |
| 52 /// | 84 /// |
| 53 /// L: var x; | 85 /// L: var x; |
| 54 /// x = 42; | 86 /// x = 42; |
| 55 /// print("The answer is $x."); | 87 /// print("The answer is $x."); |
| 56 Scope createNestedLabelScope() { | 88 Scope createNestedLabelScope() { |
| 57 return new Scope(local, setters, parent, isModifiable: true); | 89 return new Scope(local, setters, parent, isModifiable: true); |
| 58 } | 90 } |
| 59 | 91 |
| 92 Builder lookupIn(String name, int charOffset, Uri fileUri, |
| 93 Map<String, Builder> map, bool isInstanceScope) { |
| 94 Builder builder = map[name]; |
| 95 if (builder == null) return null; |
| 96 if (builder.next != null) { |
| 97 return new AmbiguousBuilder(name, builder, charOffset, fileUri); |
| 98 } else if (!isInstanceScope && builder.isInstanceMember) { |
| 99 return null; |
| 100 } else { |
| 101 return builder; |
| 102 } |
| 103 } |
| 104 |
| 60 Builder lookup(String name, int charOffset, Uri fileUri, | 105 Builder lookup(String name, int charOffset, Uri fileUri, |
| 61 {bool isInstanceScope: true}) { | 106 {bool isInstanceScope: true}) { |
| 62 Builder builder = local[name]; | 107 Builder builder = |
| 63 if (builder != null) { | 108 lookupIn(name, charOffset, fileUri, local, isInstanceScope); |
| 64 if (builder.next != null) { | 109 if (builder != null) return builder; |
| 65 return lookupAmbiguous(name, builder, false, charOffset, fileUri); | 110 builder = lookupIn(name, charOffset, fileUri, setters, isInstanceScope); |
| 66 } | 111 if (builder != null && !builder.hasProblem) { |
| 67 return builder.isSetter | 112 return new AccessErrorBuilder(name, builder, charOffset, fileUri); |
| 68 ? new AccessErrorBuilder(name, builder, charOffset, fileUri) | |
| 69 : builder; | |
| 70 } else { | |
| 71 return parent?.lookup(name, charOffset, fileUri); | |
| 72 } | 113 } |
| 114 if (!isInstanceScope) { |
| 115 // For static lookup, do not seach the parent scope. |
| 116 return builder; |
| 117 } |
| 118 return builder ?? parent?.lookup(name, charOffset, fileUri); |
| 73 } | 119 } |
| 74 | 120 |
| 75 Builder lookupSetter(String name, int charOffset, Uri fileUri, | 121 Builder lookupSetter(String name, int charOffset, Uri fileUri, |
| 76 {bool isInstanceScope: true}) { | 122 {bool isInstanceScope: true}) { |
| 77 Builder builder = local[name]; | 123 Builder builder = |
| 78 if (builder != null) { | 124 lookupIn(name, charOffset, fileUri, setters, isInstanceScope); |
| 79 if (builder.next != null) { | 125 if (builder != null) return builder; |
| 80 return lookupAmbiguous(name, builder, true, charOffset, fileUri); | 126 builder = lookupIn(name, charOffset, fileUri, local, isInstanceScope); |
| 81 } | 127 if (builder != null && !builder.hasProblem) { |
| 82 if (builder.isField) { | 128 return new AccessErrorBuilder(name, builder, charOffset, fileUri); |
| 83 if (builder.isFinal) { | |
| 84 return new AccessErrorBuilder(name, builder, charOffset, fileUri); | |
| 85 } else { | |
| 86 return builder; | |
| 87 } | |
| 88 } else if (builder.isSetter) { | |
| 89 return builder; | |
| 90 } else { | |
| 91 return new AccessErrorBuilder(name, builder, charOffset, fileUri); | |
| 92 } | |
| 93 } else { | |
| 94 return parent?.lookupSetter(name, charOffset, fileUri); | |
| 95 } | 129 } |
| 96 } | 130 if (!isInstanceScope) { |
| 97 | 131 // For static lookup, do not seach the parent scope. |
| 98 Builder lookupAmbiguous( | 132 return builder; |
| 99 String name, Builder builder, bool setter, int charOffset, Uri fileUri) { | |
| 100 assert(builder.next != null); | |
| 101 if (builder is MixedAccessor) { | |
| 102 return setter ? builder.setter : builder.getter; | |
| 103 } | 133 } |
| 104 Builder setterBuilder; | 134 return builder ?? parent?.lookupSetter(name, charOffset, fileUri); |
| 105 Builder getterBuilder; | |
| 106 Builder current = builder; | |
| 107 while (current != null) { | |
| 108 if (current.isGetter && getterBuilder == null) { | |
| 109 getterBuilder = current; | |
| 110 } else if (current.isSetter && setterBuilder == null) { | |
| 111 setterBuilder = current; | |
| 112 } else { | |
| 113 return new AmbiguousBuilder(name, builder, charOffset, fileUri); | |
| 114 } | |
| 115 current = current.next; | |
| 116 } | |
| 117 assert(getterBuilder != null); | |
| 118 assert(setterBuilder != null); | |
| 119 return setter ? setterBuilder : getterBuilder; | |
| 120 } | 135 } |
| 121 | 136 |
| 122 bool hasLocalLabel(String name) => labels != null && labels.containsKey(name); | 137 bool hasLocalLabel(String name) => labels != null && labels.containsKey(name); |
| 123 | 138 |
| 124 void declareLabel(String name, Builder target) { | 139 void declareLabel(String name, Builder target) { |
| 125 if (isModifiable) { | 140 if (isModifiable) { |
| 126 labels ??= <String, Builder>{}; | 141 labels ??= <String, Builder>{}; |
| 127 labels[name] = target; | 142 labels[name] = target; |
| 128 } else { | 143 } else { |
| 129 internalError("Can't extend an unmodifiable scope."); | 144 internalError("Can't extend an unmodifiable scope."); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 154 | 169 |
| 155 // TODO(ahe): Rename to extend or something. | 170 // TODO(ahe): Rename to extend or something. |
| 156 void operator []=(String name, Builder member) { | 171 void operator []=(String name, Builder member) { |
| 157 if (isModifiable) { | 172 if (isModifiable) { |
| 158 local[name] = member; | 173 local[name] = member; |
| 159 } else { | 174 } else { |
| 160 internalError("Can't extend an unmodifiable scope."); | 175 internalError("Can't extend an unmodifiable scope."); |
| 161 } | 176 } |
| 162 } | 177 } |
| 163 | 178 |
| 179 void merge(Scope scope, |
| 180 buildAmbiguousBuilder(String name, Builder existing, Builder member)) { |
| 181 Map<String, Builder> map = local; |
| 182 |
| 183 void mergeMember(String name, Builder member) { |
| 184 Builder existing = map[name]; |
| 185 if (existing != null) { |
| 186 if (existing != member) { |
| 187 member = buildAmbiguousBuilder(name, existing, member); |
| 188 } |
| 189 } |
| 190 map[name] = member; |
| 191 } |
| 192 |
| 193 scope.local.forEach(mergeMember); |
| 194 map = setters; |
| 195 scope.setters.forEach(mergeMember); |
| 196 } |
| 197 |
| 164 void forEach(f(String name, Builder member)) { | 198 void forEach(f(String name, Builder member)) { |
| 165 local.forEach(f); | 199 local.forEach(f); |
| 200 setters.forEach(f); |
| 166 } | 201 } |
| 167 | 202 |
| 168 String get debugString { | 203 String get debugString { |
| 169 StringBuffer buffer = new StringBuffer(); | 204 StringBuffer buffer = new StringBuffer(); |
| 170 int nestingLevel = writeOn(buffer); | 205 int nestingLevel = writeOn(buffer); |
| 171 for (int i = nestingLevel; i >= 0; i--) { | 206 for (int i = nestingLevel; i >= 0; i--) { |
| 172 buffer.writeln("${' ' * i}}"); | 207 buffer.writeln("${' ' * i}}"); |
| 173 } | 208 } |
| 174 return "$buffer"; | 209 return "$buffer"; |
| 175 } | 210 } |
| 176 | 211 |
| 177 int writeOn(StringSink sink) { | 212 int writeOn(StringSink sink) { |
| 178 int nestingLevel = (parent?.writeOn(sink) ?? -1) + 1; | 213 int nestingLevel = (parent?.writeOn(sink) ?? -1) + 1; |
| 179 String indent = " " * nestingLevel; | 214 String indent = " " * nestingLevel; |
| 180 sink.writeln("$indent{"); | 215 sink.writeln("$indent{"); |
| 181 local.forEach((String name, Builder member) { | 216 local.forEach((String name, Builder member) { |
| 182 sink.writeln("$indent $name"); | 217 sink.writeln("$indent $name"); |
| 183 }); | 218 }); |
| 184 setters.forEach((String name, Builder member) { | 219 setters.forEach((String name, Builder member) { |
| 185 sink.writeln("$indent $name="); | 220 sink.writeln("$indent $name="); |
| 186 }); | 221 }); |
| 187 return nestingLevel; | 222 return nestingLevel; |
| 188 } | 223 } |
| 189 } | 224 } |
| 190 | 225 |
| 226 class ScopeBuilder { |
| 227 final Scope scope; |
| 228 |
| 229 ScopeBuilder(this.scope); |
| 230 |
| 231 void addMember(String name, Builder builder) { |
| 232 scope.local[name] = builder; |
| 233 } |
| 234 |
| 235 void addSetter(String name, Builder builder) { |
| 236 scope.setters[name] = builder; |
| 237 } |
| 238 |
| 239 Builder operator [](String name) => scope.local[name]; |
| 240 } |
| 241 |
| 191 abstract class ProblemBuilder extends Builder { | 242 abstract class ProblemBuilder extends Builder { |
| 192 final String name; | 243 final String name; |
| 193 | 244 |
| 194 final Builder builder; | 245 final Builder builder; |
| 195 | 246 |
| 196 ProblemBuilder(this.name, this.builder, int charOffset, Uri fileUri) | 247 ProblemBuilder(this.name, this.builder, int charOffset, Uri fileUri) |
| 197 : super(null, charOffset, fileUri); | 248 : super(null, charOffset, fileUri); |
| 198 | 249 |
| 199 get target => null; | 250 get target => null; |
| 200 | 251 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 | 287 |
| 237 String get message => "Access error: '$name'."; | 288 String get message => "Access error: '$name'."; |
| 238 } | 289 } |
| 239 | 290 |
| 240 class AmbiguousBuilder extends ProblemBuilder { | 291 class AmbiguousBuilder extends ProblemBuilder { |
| 241 AmbiguousBuilder(String name, Builder builder, int charOffset, Uri fileUri) | 292 AmbiguousBuilder(String name, Builder builder, int charOffset, Uri fileUri) |
| 242 : super(name, builder, charOffset, fileUri); | 293 : super(name, builder, charOffset, fileUri); |
| 243 | 294 |
| 244 String get message => "Duplicated named: '$name'."; | 295 String get message => "Duplicated named: '$name'."; |
| 245 } | 296 } |
| OLD | NEW |