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 /// ----------------------------------------------------------------------- | 5 /// ----------------------------------------------------------------------- |
6 /// ERROR HANDLING | 6 /// ERROR HANDLING |
7 /// ----------------------------------------------------------------------- | 7 /// ----------------------------------------------------------------------- |
8 /// | 8 /// |
9 /// As a rule of thumb, errors that can be detected statically are handled by | 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 | 10 /// the frontend, typically by translating the erroneous code into a 'throw' or |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
43 /// | 43 /// |
44 /// Use [Transformer] for bulk transformations that are likely to transform lots | 44 /// Use [Transformer] for bulk transformations that are likely to transform lots |
45 /// of nodes, and [TreeNode.replaceWith] for sparse transformations that mutate | 45 /// of nodes, and [TreeNode.replaceWith] for sparse transformations that mutate |
46 /// relatively few nodes. Or use whichever is more convenient. | 46 /// relatively few nodes. Or use whichever is more convenient. |
47 /// | 47 /// |
48 /// The AST can also be mutated by direct field manipulation, but the user then | 48 /// The AST can also be mutated by direct field manipulation, but the user then |
49 /// has to update parent pointers manually. | 49 /// has to update parent pointers manually. |
50 /// | 50 /// |
51 library kernel.ast; | 51 library kernel.ast; |
52 | 52 |
53 import 'dart:convert' show UTF8; | |
54 | |
53 import 'visitor.dart'; | 55 import 'visitor.dart'; |
54 export 'visitor.dart'; | 56 export 'visitor.dart'; |
55 | 57 |
56 import 'canonical_name.dart' show CanonicalName; | 58 import 'canonical_name.dart' show CanonicalName; |
57 export 'canonical_name.dart' show CanonicalName; | 59 export 'canonical_name.dart' show CanonicalName; |
58 | 60 |
59 import 'transformations/flags.dart'; | 61 import 'transformations/flags.dart'; |
60 import 'text/ast_to_text.dart'; | 62 import 'text/ast_to_text.dart'; |
61 import 'type_algebra.dart'; | 63 import 'type_algebra.dart'; |
62 import 'type_environment.dart'; | 64 import 'type_environment.dart'; |
(...skipping 4037 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4100 } | 4102 } |
4101 | 4103 |
4102 transformChildren(Transformer v) { | 4104 transformChildren(Transformer v) { |
4103 transformList(libraries, v, this); | 4105 transformList(libraries, v, this); |
4104 } | 4106 } |
4105 | 4107 |
4106 Program get enclosingProgram => this; | 4108 Program get enclosingProgram => this; |
4107 | 4109 |
4108 /// Translates an offset to line and column numbers in the given file. | 4110 /// Translates an offset to line and column numbers in the given file. |
4109 Location getLocation(String file, int offset) { | 4111 Location getLocation(String file, int offset) { |
4110 List<int> lines = uriToSource[file].lineStarts; | 4112 return uriToSource[file]?.getLocation(file, offset); |
4111 int low = 0, high = lines.length - 1; | |
4112 while (low < high) { | |
4113 int mid = high - ((high - low) >> 1); // Get middle, rounding up. | |
4114 int pivot = lines[mid]; | |
4115 if (pivot <= offset) { | |
4116 low = mid; | |
4117 } else { | |
4118 high = mid - 1; | |
4119 } | |
4120 } | |
4121 int lineIndex = low; | |
4122 int lineStart = lines[lineIndex]; | |
4123 int lineNumber = 1 + lineIndex; | |
4124 int columnNumber = 1 + offset - lineStart; | |
4125 return new Location(file, lineNumber, columnNumber); | |
4126 } | 4113 } |
4127 } | 4114 } |
4128 | 4115 |
4129 /// A tuple with file, line, and column number, for displaying human-readable | 4116 /// A tuple with file, line, and column number, for displaying human-readable |
4130 /// locations. | 4117 /// locations. |
4131 class Location { | 4118 class Location { |
4132 final String file; | 4119 final String file; |
4133 final int line; // 1-based. | 4120 final int line; // 1-based. |
4134 final int column; // 1-based. | 4121 final int column; // 1-based. |
4135 | 4122 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4221 if (node == child) { | 4208 if (node == child) { |
4222 return replacement; | 4209 return replacement; |
4223 } else { | 4210 } else { |
4224 return node; | 4211 return node; |
4225 } | 4212 } |
4226 } | 4213 } |
4227 } | 4214 } |
4228 | 4215 |
4229 class Source { | 4216 class Source { |
4230 final List<int> lineStarts; | 4217 final List<int> lineStarts; |
4218 | |
4231 final List<int> source; | 4219 final List<int> source; |
4232 | 4220 |
4221 String cachedText; | |
4222 | |
4233 Source(this.lineStarts, this.source); | 4223 Source(this.lineStarts, this.source); |
4224 | |
4225 /// Return the text corresponding to [line] which is a 1-based line | |
4226 /// number. The returned line contains no line separators. | |
4227 String getTextLine(int line) { | |
4228 RangeError.checkValueInInterval( | |
4229 line, | |
4230 1, | |
4231 lineStarts.length, | |
4232 "line", | |
4233 "The value of 'line' ($line) must be between 1 and " | |
4234 "${lineStarts.length}."); | |
4235 if (source == null) return null; | |
4236 | |
4237 cachedText ??= UTF8.decode(source, allowMalformed: true); | |
4238 // -1 as line numbers start at 1. | |
4239 int index = line - 1; | |
4240 if (index + 1 == lineStarts.length) { | |
4241 // Last line. | |
4242 return cachedText.substring(lineStarts[index]); | |
4243 } else if (index < lineStarts.length) { | |
4244 // We subtract 1 from the next line for two reasons: | |
4245 // 1. If the file isn't terminated by a newline, that index is invalid. | |
4246 // 2. To remove the newline at the end of the line. | |
4247 int nextLine = lineStarts[index + 1] - 1; | |
4248 String result = cachedText.substring(lineStarts[index], nextLine); | |
4249 if (result.endsWith("\r")) { | |
4250 // Support for Windows text files. | |
4251 result = result.substring(0, result.length - 1); | |
asgerf
2017/04/03 09:55:25
The 'nextLine' variable does not actually refer to
ahe
2017/04/03 14:57:08
Done.
| |
4252 } | |
4253 return result; | |
4254 } | |
4255 // This shouldn't happen: should have been caught by the range check above. | |
4256 throw "Internal error"; | |
4257 } | |
4258 | |
4259 /// Translates an offset to line and column numbers in the given file. | |
4260 Location getLocation(String file, int offset) { | |
4261 RangeError.checkValueInInterval( | |
4262 offset, | |
4263 0, | |
4264 lineStarts.last, | |
4265 "offset", | |
4266 "The value of 'offset' ($offset) must be between 1 and " | |
asgerf
2017/04/03 09:55:25
between 1 and -> between 0 and
ahe
2017/04/03 14:57:08
Done.
| |
4267 "${lineStarts.length}."); | |
4268 | |
4269 int low = 0, high = lineStarts.length - 1; | |
4270 while (low < high) { | |
4271 int mid = high - ((high - low) >> 1); // Get middle, rounding up. | |
4272 int pivot = lineStarts[mid]; | |
4273 if (pivot <= offset) { | |
4274 low = mid; | |
4275 } else { | |
4276 high = mid - 1; | |
4277 } | |
4278 } | |
4279 int lineIndex = low; | |
4280 int lineStart = lineStarts[lineIndex]; | |
4281 int lineNumber = 1 + lineIndex; | |
4282 int columnNumber = 1 + offset - lineStart; | |
4283 return new Location(file, lineNumber, columnNumber); | |
4284 } | |
4234 } | 4285 } |
4235 | 4286 |
4236 /// Returns the [Reference] object for the given member. | 4287 /// Returns the [Reference] object for the given member. |
4237 /// | 4288 /// |
4238 /// Returns `null` if the member is `null`. | 4289 /// Returns `null` if the member is `null`. |
4239 Reference getMemberReference(Member member) { | 4290 Reference getMemberReference(Member member) { |
4240 return member?.reference; | 4291 return member?.reference; |
4241 } | 4292 } |
4242 | 4293 |
4243 /// Returns the [Reference] object for the given class. | 4294 /// Returns the [Reference] object for the given class. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4275 /// library has not been assigned a canonical name yet. | 4326 /// library has not been assigned a canonical name yet. |
4276 /// | 4327 /// |
4277 /// Returns `null` if the library is `null`. | 4328 /// Returns `null` if the library is `null`. |
4278 CanonicalName getCanonicalNameOfLibrary(Library library) { | 4329 CanonicalName getCanonicalNameOfLibrary(Library library) { |
4279 if (library == null) return null; | 4330 if (library == null) return null; |
4280 if (library.canonicalName == null) { | 4331 if (library.canonicalName == null) { |
4281 throw '$library has no canonical name'; | 4332 throw '$library has no canonical name'; |
4282 } | 4333 } |
4283 return library.canonicalName; | 4334 return library.canonicalName; |
4284 } | 4335 } |
OLD | NEW |