OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 part of dart.core; | |
6 | |
7 /** | |
8 * Error objects thrown in the case of a program failure. | |
9 * | |
10 * An `Error` object represents a program failure that the programmer | |
11 * should have avoided. | |
12 * | |
13 * Examples include calling a function with invalid arguments, | |
14 * or even with the wrong number of arguments, | |
15 * or calling it at a time when it is not allowed. | |
16 * | |
17 * These are not errors that a caller should expect or catch - | |
18 * if they occur, the program is erroneous, | |
19 * and terminating the program may be the safest response. | |
20 * | |
21 * When deciding that a function throws an error, | |
22 * the conditions where it happens should be clearly described, | |
23 * and they should be detectable and predictable, | |
24 * so the programmer using the function can avoid triggering the error. | |
25 * | |
26 * Such descriptions often uses words like | |
27 * "must" or "must not" to describe the condition, | |
28 * and if you see words like that in a function's documentation, | |
29 * then not satisfying the requirement | |
30 * is very likely to cause an error to be thrown. | |
31 * | |
32 * Example (from [String.contains]): | |
33 * | |
34 * `startIndex` must not be negative or greater than `length`. | |
35 * | |
36 * In this case, an error will be thrown if `startIndex` is negative | |
37 * or too large. | |
38 * | |
39 * If the conditions are not detectable before calling a function, | |
40 * the called function should not throw an `Error`. | |
41 * It may still throw a value, | |
42 * but the caller will have to catch the thrown value, | |
43 * effectively making it an alternative result rather than an error. | |
44 * The thrown object can choose to implement [Exception] | |
45 * to document that it represents an exceptional, but not erroneous, occurrence, | |
46 * but it has no other effect than documentation. | |
47 * | |
48 * All non-`null` values can be thrown in Dart. | |
49 * Objects extending `Error` are handled specially: | |
50 * The first time they are thrown, | |
51 * the stack trace at the throw point is recorded | |
52 * and stored in the error object. | |
53 * It can be retrieved using the [stackTrace] getter. | |
54 * An error object that merely implements `Error`, and doesn't extend it, | |
55 * will not store the stack trace automatically. | |
56 * | |
57 * Error objects are also used for system wide failures | |
58 * like stack overflow or an out-of-memory situation. | |
59 * | |
60 * Since errors are not created to be caught, | |
61 * there is no need for subclasses to distinguish the errors. | |
62 * Instead subclasses have been created in order to make groups | |
63 * of related errors easy to create with consistent error messages. | |
64 * For example, the [String.contains] method will use a [RangeError] | |
65 * if its `startIndex` isn't in the range `0..length`, | |
66 * which is easily created by `new RangeError.range(startIndex, 0, length)`. | |
67 */ | |
68 class Error { | |
69 Error(); // Prevent use as mixin. | |
70 | |
71 /** | |
72 * Safely convert a value to a [String] description. | |
73 * | |
74 * The conversion is guaranteed to not throw, so it won't use the object's | |
75 * toString method. | |
76 */ | |
77 static String safeToString(Object object) { | |
78 if (object is num || object is bool || null == object) { | |
79 return object.toString(); | |
80 } | |
81 if (object is String) { | |
82 return _stringToSafeString(object); | |
83 } | |
84 return _objectToString(object); | |
85 } | |
86 | |
87 /** Convert string to a valid string literal with no control characters. */ | |
88 external static String _stringToSafeString(String string); | |
89 | |
90 external static String _objectToString(Object object); | |
91 | |
92 external StackTrace get stackTrace; | |
93 } | |
94 | |
95 /** | |
96 * Error thrown by the runtime system when an assert statement fails. | |
97 */ | |
98 class AssertionError extends Error { | |
99 AssertionError(); | |
100 String toString() => "Assertion failed"; | |
101 } | |
102 | |
103 /** | |
104 * Error thrown by the runtime system when a type assertion fails. | |
105 */ | |
106 class TypeError extends AssertionError { | |
107 } | |
108 | |
109 /** | |
110 * Error thrown by the runtime system when a cast operation fails. | |
111 */ | |
112 class CastError extends Error { | |
113 } | |
114 | |
115 /** | |
116 * Error thrown when attempting to throw [:null:]. | |
117 */ | |
118 class NullThrownError extends Error { | |
119 String toString() => "Throw of null."; | |
120 } | |
121 | |
122 /** | |
123 * Error thrown when a function is passed an unacceptable argument. | |
124 */ | |
125 class ArgumentError extends Error { | |
126 /** Whether value was provided. */ | |
127 final bool _hasValue; | |
128 /** The invalid value. */ | |
129 final invalidValue; | |
130 /** Name of the invalid argument, if available. */ | |
131 final String name; | |
132 /** Message describing the problem. */ | |
133 final message; | |
134 | |
135 /** | |
136 * The [message] describes the erroneous argument. | |
137 * | |
138 * Existing code may be using `message` to hold the invalid value. | |
139 * If the `message` is not a [String], it is assumed to be a value instead | |
140 * of a message. | |
141 */ | |
142 ArgumentError([this.message]) | |
143 : invalidValue = null, | |
144 _hasValue = false, | |
145 name = null; | |
146 | |
147 /** | |
148 * Creates error containing the invalid [value]. | |
149 * | |
150 * A message is built by suffixing the [message] argument with | |
151 * the [name] argument (if provided) and the value. Example | |
152 * | |
153 * "Invalid argument (foo): null" | |
154 * | |
155 * The `name` should match the argument name of the function, but if | |
156 * the function is a method implementing an interface, and its argument | |
157 * names differ from the interface, it might be more useful to use the | |
158 * interface method's argument name (or just rename arguments to match). | |
159 */ | |
160 ArgumentError.value(value, | |
161 [String this.name, | |
162 String this.message]) | |
163 : invalidValue = value, | |
164 _hasValue = true; | |
165 | |
166 /** | |
167 * Create an argument error for a `null` argument that must not be `null`. | |
168 */ | |
169 ArgumentError.notNull([this.name]) | |
170 : _hasValue = false, | |
171 message = "Must not be null", | |
172 invalidValue = null; | |
173 | |
174 // Helper functions for toString overridden in subclasses. | |
175 String get _errorName => "Invalid argument${!_hasValue ? "(s)" : ""}"; | |
176 String get _errorExplanation => ""; | |
177 | |
178 String toString() { | |
179 String nameString = ""; | |
180 if (name != null) { | |
181 nameString = " ($name)"; | |
182 } | |
183 var message = (this.message == null) ? "" : ": ${this.message}"; | |
184 String prefix = "$_errorName$nameString$message"; | |
185 if (!_hasValue) return prefix; | |
186 // If we know the invalid value, we can try to describe the problem. | |
187 String explanation = _errorExplanation; | |
188 String errorValue = Error.safeToString(invalidValue); | |
189 return "$prefix$explanation: $errorValue"; | |
190 } | |
191 } | |
192 | |
193 /** | |
194 * Error thrown due to an index being outside a valid range. | |
195 */ | |
196 class RangeError extends ArgumentError { | |
197 /** The minimum value that [value] is allowed to assume. */ | |
198 final num start; | |
199 /** The maximum value that [value] is allowed to assume. */ | |
200 final num end; | |
201 | |
202 // TODO(lrn): This constructor should be called only with string values. | |
203 // It currently isn't in all cases. | |
204 /** | |
205 * Create a new [RangeError] with the given [message]. | |
206 */ | |
207 RangeError(var message) | |
208 : start = null, end = null, super(message); | |
209 | |
210 /** | |
211 * Create a new [RangeError] with a message for the given [value]. | |
212 * | |
213 * An optional [name] can specify the argument name that has the | |
214 * invalid value, and the [message] can override the default error | |
215 * description. | |
216 */ | |
217 RangeError.value(num value, [String name, String message]) | |
218 : start = null, end = null, | |
219 super.value(value, name, | |
220 (message != null) ? message : "Value not in range"); | |
221 | |
222 /** | |
223 * Create a new [RangeError] with for an invalid value being outside a range. | |
224 * | |
225 * The allowed range is from [minValue] to [maxValue], inclusive. | |
226 * If `minValue` or `maxValue` are `null`, the range is infinite in | |
227 * that direction. | |
228 * | |
229 * For a range from 0 to the length of something, end exclusive, use | |
230 * [RangeError.index]. | |
231 * | |
232 * An optional [name] can specify the argument name that has the | |
233 * invalid value, and the [message] can override the default error | |
234 * description. | |
235 */ | |
236 RangeError.range(num invalidValue, int minValue, int maxValue, | |
237 [String name, String message]) | |
238 : start = minValue, | |
239 end = maxValue, | |
240 super.value(invalidValue, name, | |
241 (message != null) ? message : "Invalid value"); | |
242 | |
243 /** | |
244 * Creates a new [RangeError] stating that [index] is not a valid index | |
245 * into [indexable]. | |
246 * | |
247 * An optional [name] can specify the argument name that has the | |
248 * invalid value, and the [message] can override the default error | |
249 * description. | |
250 * | |
251 * The [length] is the length of [indexable] at the time of the error. | |
252 * If `length` is omitted, it defaults to `indexable.length`. | |
253 */ | |
254 factory RangeError.index(int index, indexable, | |
255 [String name, | |
256 String message, | |
257 int length]) = IndexError; | |
258 | |
259 /** | |
260 * Check that a [value] lies in a specific interval. | |
261 * | |
262 * Throws if [value] is not in the interval. | |
263 * The interval is from [minValue] to [maxValue], both inclusive. | |
264 */ | |
265 static void checkValueInInterval(int value, int minValue, int maxValue, | |
266 [String name, String message]) { | |
267 if (value < minValue || value > maxValue) { | |
268 throw new RangeError.range(value, minValue, maxValue, name, message); | |
269 } | |
270 } | |
271 | |
272 /** | |
273 * Check that a value is a valid index into an indexable object. | |
274 * | |
275 * Throws if [index] is not a valid index into [indexable]. | |
276 * | |
277 * An indexable object is one that has a `length` and a and index-operator | |
278 * `[]` that accepts an index if `0 <= index < length`. | |
279 * | |
280 * If [length] is provided, it is used as the length of the indexable object, | |
281 * otherwise the length is found as `indexable.length`. | |
282 */ | |
283 static void checkValidIndex(int index, var indexable, | |
284 [String name, int length, String message]) { | |
285 if (length == null) length = indexable.length; | |
286 // Comparing with `0` as receiver produces better dart2js type inference. | |
287 if (0 > index || index >= length) { | |
288 if (name == null) name = "index"; | |
289 throw new RangeError.index(index, indexable, name, message, length); | |
290 } | |
291 } | |
292 | |
293 /** | |
294 * Check that a range represents a slice of an indexable object. | |
295 * | |
296 * Throws if the range is not valid for an indexable object with | |
297 * the given [length]. | |
298 * A range is valid for an indexable object with a given [length] | |
299 * | |
300 * if `0 <= [start] <= [end] <= [length]`. | |
301 * An `end` of `null` is considered equivalent to `length`. | |
302 * | |
303 * The [startName] and [endName] defaults to `"start"` and `"end"`, | |
304 * respectively. | |
305 * | |
306 * Returns the actual `end` value, which is `length` if `end` is `null`, | |
307 * and `end` otherwise. | |
308 */ | |
309 static int checkValidRange(int start, int end, int length, | |
310 [String startName, String endName, | |
311 String message]) { | |
312 // Comparing with `0` as receiver produces better dart2js type inference. | |
313 // Ditto `start > end` below. | |
314 if (0 > start || start > length) { | |
315 if (startName == null) startName = "start"; | |
316 throw new RangeError.range(start, 0, length, startName, message); | |
317 } | |
318 if (end != null) { | |
319 if (start > end || end > length) { | |
320 if (endName == null) endName = "end"; | |
321 throw new RangeError.range(end, start, length, endName, message); | |
322 } | |
323 return end; | |
324 } | |
325 return length; | |
326 } | |
327 | |
328 /** | |
329 * Check that an integer value isn't negative. | |
330 * | |
331 * Throws if the value is negative. | |
332 */ | |
333 static void checkNotNegative(int value, [String name, String message]) { | |
334 if (value < 0) throw new RangeError.range(value, 0, null, name, message); | |
335 } | |
336 | |
337 String get _errorName => "RangeError"; | |
338 String get _errorExplanation { | |
339 assert(_hasValue); | |
340 String explanation = ""; | |
341 if (start == null) { | |
342 if (end != null) { | |
343 explanation = ": Not less than or equal to $end"; | |
344 } | |
345 // If both are null, we don't add a description of the limits. | |
346 } else if (end == null) { | |
347 explanation = ": Not greater than or equal to $start"; | |
348 } else if (end > start) { | |
349 explanation = ": Not in range $start..$end, inclusive"; | |
350 } else if (end < start) { | |
351 explanation = ": Valid value range is empty"; | |
352 } else { | |
353 // end == start. | |
354 explanation = ": Only valid value is $start"; | |
355 } | |
356 return explanation; | |
357 } | |
358 } | |
359 | |
360 /** | |
361 * A specialized [RangeError] used when an index is not in the range | |
362 * `0..indexable.length-1`. | |
363 * | |
364 * Also contains the indexable object, its length at the time of the error, | |
365 * and the invalid index itself. | |
366 */ | |
367 class IndexError extends ArgumentError implements RangeError { | |
368 /** The indexable object that [index] was not a valid index into. */ | |
369 final indexable; | |
370 /** The length of [indexable] at the time of the error. */ | |
371 final int length; | |
372 | |
373 /** | |
374 * Creates a new [IndexError] stating that [invalidValue] is not a valid index | |
375 * into [indexable]. | |
376 * | |
377 * The [length] is the length of [indexable] at the time of the error. | |
378 * If `length` is omitted, it defaults to `indexable.length`. | |
379 * | |
380 * The message is used as part of the string representation of the error. | |
381 */ | |
382 IndexError(int invalidValue, indexable, | |
383 [String name, String message, int length]) | |
384 : this.indexable = indexable, | |
385 this.length = (length != null) ? length : indexable.length, | |
386 super.value(invalidValue, name, | |
387 (message != null) ? message : "Index out of range"); | |
388 | |
389 // Getters inherited from RangeError. | |
390 int get start => 0; | |
391 int get end => length - 1; | |
392 | |
393 String get _errorName => "RangeError"; | |
394 String get _errorExplanation { | |
395 assert(_hasValue); | |
396 if (invalidValue < 0) { | |
397 return ": index must not be negative"; | |
398 } | |
399 if (length == 0) { | |
400 return ": no indices are valid"; | |
401 } | |
402 return ": index should be less than $length"; | |
403 } | |
404 } | |
405 | |
406 | |
407 /** | |
408 * Error thrown when control reaches the end of a switch case. | |
409 * | |
410 * The Dart specification requires this error to be thrown when | |
411 * control reaches the end of a switch case (except the last case | |
412 * of a switch) without meeting a break or similar end of the control | |
413 * flow. | |
414 */ | |
415 class FallThroughError extends Error { | |
416 FallThroughError(); | |
417 } | |
418 | |
419 /** | |
420 * Error thrown when trying to instantiate an abstract class. | |
421 */ | |
422 class AbstractClassInstantiationError extends Error { | |
423 final String _className; | |
424 AbstractClassInstantiationError(String this._className); | |
425 String toString() => "Cannot instantiate abstract class: '$_className'"; | |
426 } | |
427 | |
428 | |
429 /** | |
430 * Error thrown by the default implementation of [:noSuchMethod:] on [Object]. | |
431 */ | |
432 class NoSuchMethodError extends Error { | |
433 final Object _receiver; | |
434 final Symbol _memberName; | |
435 final List _arguments; | |
436 final Map<Symbol, dynamic> _namedArguments; | |
437 final List _existingArgumentNames; | |
438 | |
439 /** | |
440 * Create a [NoSuchMethodError] corresponding to a failed method call. | |
441 * | |
442 * The [receiver] is the receiver of the method call. | |
443 * That is, the object on which the method was attempted called. | |
444 * If the receiver is `null`, it is interpreted as a call to a top-level | |
445 * function of a library. | |
446 * | |
447 * The [memberName] is a [Symbol] representing the name of the called method | |
448 * or accessor. It should not be `null`. | |
449 * | |
450 * The [positionalArguments] is a list of the positional arguments that the | |
451 * method was called with. If `null`, it is considered equivalent to the | |
452 * empty list. | |
453 * | |
454 * The [namedArguments] is a map from [Symbol]s to the values of named | |
455 * arguments that the method was called with. | |
456 * | |
457 * The optional [existingArgumentNames] is the expected parameters of a | |
458 * method with the same name on the receiver, if available. This is | |
459 * the signature of the method that would have been called if the parameters | |
460 * had matched. | |
461 */ | |
462 NoSuchMethodError(Object receiver, | |
463 Symbol memberName, | |
464 List positionalArguments, | |
465 Map<Symbol ,dynamic> namedArguments, | |
466 [List existingArgumentNames = null]) | |
467 : _receiver = receiver, | |
468 _memberName = memberName, | |
469 _arguments = positionalArguments, | |
470 _namedArguments = namedArguments, | |
471 _existingArgumentNames = existingArgumentNames; | |
472 | |
473 external String toString(); | |
474 } | |
475 | |
476 | |
477 /** | |
478 * The operation was not allowed by the object. | |
479 * | |
480 * This [Error] is thrown when an instance cannot implement one of the methods | |
481 * in its signature. | |
482 */ | |
483 class UnsupportedError extends Error { | |
484 final String message; | |
485 UnsupportedError(this.message); | |
486 String toString() => "Unsupported operation: $message"; | |
487 } | |
488 | |
489 | |
490 /** | |
491 * Thrown by operations that have not been implemented yet. | |
492 * | |
493 * This [Error] is thrown by unfinished code that hasn't yet implemented | |
494 * all the features it needs. | |
495 * | |
496 * If a class is not intending to implement the feature, it should throw | |
497 * an [UnsupportedError] instead. This error is only intended for | |
498 * use during development. | |
499 */ | |
500 class UnimplementedError extends Error implements UnsupportedError { | |
501 final String message; | |
502 UnimplementedError([String this.message]); | |
503 String toString() => (this.message != null | |
504 ? "UnimplementedError: $message" | |
505 : "UnimplementedError"); | |
506 } | |
507 | |
508 | |
509 /** | |
510 * The operation was not allowed by the current state of the object. | |
511 * | |
512 * This is a generic error used for a variety of different erroneous | |
513 * actions. The message should be descriptive. | |
514 */ | |
515 class StateError extends Error { | |
516 final String message; | |
517 StateError(this.message); | |
518 String toString() => "Bad state: $message"; | |
519 } | |
520 | |
521 | |
522 /** | |
523 * Error occurring when a collection is modified during iteration. | |
524 * | |
525 * Some modifications may be allowed for some collections, so each collection | |
526 * ([Iterable] or similar collection of values) should declare which operations | |
527 * are allowed during an iteration. | |
528 */ | |
529 class ConcurrentModificationError extends Error { | |
530 /** The object that was modified in an incompatible way. */ | |
531 final Object modifiedObject; | |
532 | |
533 ConcurrentModificationError([this.modifiedObject]); | |
534 | |
535 String toString() { | |
536 if (modifiedObject == null) { | |
537 return "Concurrent modification during iteration."; | |
538 } | |
539 return "Concurrent modification during iteration: " | |
540 "${Error.safeToString(modifiedObject)}."; | |
541 } | |
542 } | |
543 | |
544 | |
545 class OutOfMemoryError implements Error { | |
546 const OutOfMemoryError(); | |
547 String toString() => "Out of Memory"; | |
548 | |
549 StackTrace get stackTrace => null; | |
550 } | |
551 | |
552 | |
553 class StackOverflowError implements Error { | |
554 const StackOverflowError(); | |
555 String toString() => "Stack Overflow"; | |
556 | |
557 StackTrace get stackTrace => null; | |
558 } | |
559 | |
560 /** | |
561 * Error thrown when a lazily initialized variable cannot be initialized. | |
562 * | |
563 * A static/library variable with an initializer expression is initialized | |
564 * the first time it is read. If evaluating the initializer expression causes | |
565 * another read of the variable, this error is thrown. | |
566 */ | |
567 class CyclicInitializationError extends Error { | |
568 final String variableName; | |
569 CyclicInitializationError([this.variableName]); | |
570 String toString() => variableName == null | |
571 ? "Reading static variable during its initialization" | |
572 : "Reading static variable '$variableName' during its initialization"; | |
573 } | |
OLD | NEW |