| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of dart.core; |
| |
| /** |
| * Error objects thrown in the case of a program failure. |
| * |
| * An `Error` object represents a program failure that the programmer |
| * should have avoided. |
| * |
| * Examples include calling a function with invalid arguments, |
| * or even with the wrong number of arguments, |
| * or calling it at a time when it is not allowed. |
| * |
| * These are not errors that a caller should expect or catch - |
| * if they occur, the program is erroneous, |
| * and terminating the program may be the safest response. |
| * |
| * When deciding that a function throws an error, |
| * the conditions where it happens should be clearly described, |
| * and they should be detectable and predictable, |
| * so the programmer using the function can avoid triggering the error. |
| * |
| * Such descriptions often uses words like |
| * "must" or "must not" to describe the condition, |
| * and if you see words like that in a function's documentation, |
| * then not satisfying the requirement |
| * is very likely to cause an error to be thrown. |
| * |
| * Example (from [String.contains]): |
| * |
| * `startIndex` must not be negative or greater than `length`. |
| * |
| * In this case, an error will be thrown if `startIndex` is negative |
| * or too large. |
| * |
| * If the conditions are not detectable before calling a function, |
| * the called function should not throw an `Error`. |
| * It may still throw a value, |
| * but the caller will have to catch the thrown value, |
| * effectively making it an alternative result rather than an error. |
| * The thrown object can choose to implement [Exception] |
| * to document that it represents an exceptional, but not erroneous, occurrence, |
| * but it has no other effect than documentation. |
| * |
| * All non-`null` values can be thrown in Dart. |
| * Objects extending `Error` are handled specially: |
| * The first time they are thrown, |
| * the stack trace at the throw point is recorded |
| * and stored in the error object. |
| * It can be retrieved using the [stackTrace] getter. |
| * An error object that merely implements `Error`, and doesn't extend it, |
| * will not store the stack trace automatically. |
| * |
| * Error objects are also used for system wide failures |
| * like stack overflow or an out-of-memory situation. |
| * |
| * Since errors are not created to be caught, |
| * there is no need for subclasses to distinguish the errors. |
| * Instead subclasses have been created in order to make groups |
| * of related errors easy to create with consistent error messages. |
| * For example, the [String.contains] method will use a [RangeError] |
| * if its `startIndex` isn't in the range `0..length`, |
| * which is easily created by `new RangeError.range(startIndex, 0, length)`. |
| */ |
| class Error { |
| Error(); // Prevent use as mixin. |
| |
| /** |
| * Safely convert a value to a [String] description. |
| * |
| * The conversion is guaranteed to not throw, so it won't use the object's |
| * toString method. |
| */ |
| static String safeToString(Object object) { |
| if (object is num || object is bool || null == object) { |
| return object.toString(); |
| } |
| if (object is String) { |
| return _stringToSafeString(object); |
| } |
| return _objectToString(object); |
| } |
| |
| /** Convert string to a valid string literal with no control characters. */ |
| external static String _stringToSafeString(String string); |
| |
| external static String _objectToString(Object object); |
| |
| external StackTrace get stackTrace; |
| } |
| |
| /** |
| * Error thrown by the runtime system when an assert statement fails. |
| */ |
| class AssertionError extends Error { |
| AssertionError(); |
| String toString() => "Assertion failed"; |
| } |
| |
| /** |
| * Error thrown by the runtime system when a type assertion fails. |
| */ |
| class TypeError extends AssertionError { |
| } |
| |
| /** |
| * Error thrown by the runtime system when a cast operation fails. |
| */ |
| class CastError extends Error { |
| } |
| |
| /** |
| * Error thrown when attempting to throw [:null:]. |
| */ |
| class NullThrownError extends Error { |
| String toString() => "Throw of null."; |
| } |
| |
| /** |
| * Error thrown when a function is passed an unacceptable argument. |
| */ |
| class ArgumentError extends Error { |
| /** Whether value was provided. */ |
| final bool _hasValue; |
| /** The invalid value. */ |
| final invalidValue; |
| /** Name of the invalid argument, if available. */ |
| final String name; |
| /** Message describing the problem. */ |
| final message; |
| |
| /** |
| * The [message] describes the erroneous argument. |
| * |
| * Existing code may be using `message` to hold the invalid value. |
| * If the `message` is not a [String], it is assumed to be a value instead |
| * of a message. |
| */ |
| ArgumentError([this.message]) |
| : invalidValue = null, |
| _hasValue = false, |
| name = null; |
| |
| /** |
| * Creates error containing the invalid [value]. |
| * |
| * A message is built by suffixing the [message] argument with |
| * the [name] argument (if provided) and the value. Example |
| * |
| * "Invalid argument (foo): null" |
| * |
| * The `name` should match the argument name of the function, but if |
| * the function is a method implementing an interface, and its argument |
| * names differ from the interface, it might be more useful to use the |
| * interface method's argument name (or just rename arguments to match). |
| */ |
| ArgumentError.value(value, |
| [String this.name, |
| String this.message]) |
| : invalidValue = value, |
| _hasValue = true; |
| |
| /** |
| * Create an argument error for a `null` argument that must not be `null`. |
| * |
| * Shorthand for calling [ArgumentError.value] with a `null` value and a |
| * message of `"Must not be null"`. |
| */ |
| ArgumentError.notNull([String name]) |
| : this.value(null, name, "Must not be null"); |
| |
| // Helper functions for toString overridden in subclasses. |
| String get _errorName => "Invalid argument${!_hasValue ? "(s)" : ""}"; |
| String get _errorExplanation => ""; |
| |
| String toString() { |
| String nameString = ""; |
| if (name != null) { |
| nameString = " ($name)"; |
| } |
| var message = (this.message == null) ? "" : ": ${this.message}"; |
| String prefix = "$_errorName$nameString$message"; |
| if (!_hasValue) return prefix; |
| // If we know the invalid value, we can try to describe the problem. |
| String explanation = _errorExplanation; |
| String errorValue = Error.safeToString(invalidValue); |
| return "$prefix$explanation: $errorValue"; |
| } |
| } |
| |
| /** |
| * Error thrown due to an index being outside a valid range. |
| */ |
| class RangeError extends ArgumentError { |
| /** The minimum value that [value] is allowed to assume. */ |
| final num start; |
| /** The maximum value that [value] is allowed to assume. */ |
| final num end; |
| |
| // TODO(lrn): This constructor should be called only with string values. |
| // It currently isn't in all cases. |
| /** |
| * Create a new [RangeError] with the given [message]. |
| */ |
| RangeError(var message) |
| : start = null, end = null, super(message); |
| |
| /** |
| * Create a new [RangeError] with a message for the given [value]. |
| * |
| * An optional [name] can specify the argument name that has the |
| * invalid value, and the [message] can override the default error |
| * description. |
| */ |
| RangeError.value(num value, [String name, String message]) |
| : start = null, end = null, |
| super.value(value, name, |
| (message != null) ? message : "Value not in range"); |
| |
| /** |
| * Create a new [RangeError] with for an invalid value being outside a range. |
| * |
| * The allowed range is from [minValue] to [maxValue], inclusive. |
| * If `minValue` or `maxValue` are `null`, the range is infinite in |
| * that direction. |
| * |
| * For a range from 0 to the length of something, end exclusive, use |
| * [RangeError.index]. |
| * |
| * An optional [name] can specify the argument name that has the |
| * invalid value, and the [message] can override the default error |
| * description. |
| */ |
| RangeError.range(num invalidValue, int minValue, int maxValue, |
| [String name, String message]) |
| : start = minValue, |
| end = maxValue, |
| super.value(invalidValue, name, |
| (message != null) ? message : "Invalid value"); |
| |
| /** |
| * Creates a new [RangeError] stating that [index] is not a valid index |
| * into [indexable]. |
| * |
| * An optional [name] can specify the argument name that has the |
| * invalid value, and the [message] can override the default error |
| * description. |
| * |
| * The [length] is the length of [indexable] at the time of the error. |
| * If `length` is omitted, it defaults to `indexable.length`. |
| */ |
| factory RangeError.index(int index, indexable, |
| [String name, |
| String message, |
| int length]) = IndexError; |
| |
| /** |
| * Check that a [value] lies in a specific interval. |
| * |
| * Throws if [value] is not in the interval. |
| * The interval is from [minValue] to [maxValue], both inclusive. |
| */ |
| static void checkValueInInterval(int value, int minValue, int maxValue, |
| [String name, String message]) { |
| if (value < minValue || value > maxValue) { |
| throw new RangeError.range(value, minValue, maxValue, name, message); |
| } |
| } |
| |
| /** |
| * Check that a value is a valid index into an indexable object. |
| * |
| * Throws if [index] is not a valid index into [indexable]. |
| * |
| * An indexable object is one that has a `length` and a and index-operator |
| * `[]` that accepts an index if `0 <= index < length`. |
| * |
| * If [length] is provided, it is used as the length of the indexable object, |
| * otherwise the length is found as `indexable.length`. |
| */ |
| static void checkValidIndex(int index, var indexable, |
| [String name, int length, String message]) { |
| if (length == null) length = indexable.length; |
| // Comparing with `0` as receiver produces better dart2js type inference. |
| if (0 > index || index >= length) { |
| if (name == null) name = "index"; |
| throw new RangeError.index(index, indexable, name, message, length); |
| } |
| } |
| |
| /** |
| * Check that a range represents a slice of an indexable object. |
| * |
| * Throws if the range is not valid for an indexable object with |
| * the given [length]. |
| * A range is valid for an indexable object with a given [length] |
| * |
| * if `0 <= [start] <= [end] <= [length]`. |
| * An `end` of `null` is considered equivalent to `length`. |
| * |
| * The [startName] and [endName] defaults to `"start"` and `"end"`, |
| * respectively. |
| * |
| * Returns the actual `end` value, which is `length` if `end` is `null`, |
| * and `end` otherwise. |
| */ |
| static int checkValidRange(int start, int end, int length, |
| [String startName, String endName, |
| String message]) { |
| // Comparing with `0` as receiver produces better dart2js type inference. |
| // Ditto `start > end` below. |
| if (0 > start || start > length) { |
| if (startName == null) startName = "start"; |
| throw new RangeError.range(start, 0, length, startName, message); |
| } |
| if (end != null) { |
| if (start > end || end > length) { |
| if (endName == null) endName = "end"; |
| throw new RangeError.range(end, start, length, endName, message); |
| } |
| return end; |
| } |
| return length; |
| } |
| |
| /** |
| * Check that an integer value isn't negative. |
| * |
| * Throws if the value is negative. |
| */ |
| static void checkNotNegative(int value, [String name, String message]) { |
| if (value < 0) throw new RangeError.range(value, 0, null, name, message); |
| } |
| |
| String get _errorName => "RangeError"; |
| String get _errorExplanation { |
| assert(_hasValue); |
| String explanation = ""; |
| if (start == null) { |
| if (end != null) { |
| explanation = ": Not less than or equal to $end"; |
| } |
| // If both are null, we don't add a description of the limits. |
| } else if (end == null) { |
| explanation = ": Not greater than or equal to $start"; |
| } else if (end > start) { |
| explanation = ": Not in range $start..$end, inclusive"; |
| } else if (end < start) { |
| explanation = ": Valid value range is empty"; |
| } else { |
| // end == start. |
| explanation = ": Only valid value is $start"; |
| } |
| return explanation; |
| } |
| } |
| |
| /** |
| * A specialized [RangeError] used when an index is not in the range |
| * `0..indexable.length-1`. |
| * |
| * Also contains the indexable object, its length at the time of the error, |
| * and the invalid index itself. |
| */ |
| class IndexError extends ArgumentError implements RangeError { |
| /** The indexable object that [index] was not a valid index into. */ |
| final indexable; |
| /** The length of [indexable] at the time of the error. */ |
| final int length; |
| |
| /** |
| * Creates a new [IndexError] stating that [invalidValue] is not a valid index |
| * into [indexable]. |
| * |
| * The [length] is the length of [indexable] at the time of the error. |
| * If `length` is omitted, it defaults to `indexable.length`. |
| * |
| * The message is used as part of the string representation of the error. |
| */ |
| IndexError(int invalidValue, indexable, |
| [String name, String message, int length]) |
| : this.indexable = indexable, |
| this.length = (length != null) ? length : indexable.length, |
| super.value(invalidValue, name, |
| (message != null) ? message : "Index out of range"); |
| |
| // Getters inherited from RangeError. |
| int get start => 0; |
| int get end => length - 1; |
| |
| String get _errorName => "RangeError"; |
| String get _errorExplanation { |
| assert(_hasValue); |
| if (invalidValue < 0) { |
| return ": index must not be negative"; |
| } |
| if (length == 0) { |
| return ": no indices are valid"; |
| } |
| return ": index should be less than $length"; |
| } |
| } |
| |
| |
| /** |
| * Error thrown when control reaches the end of a switch case. |
| * |
| * The Dart specification requires this error to be thrown when |
| * control reaches the end of a switch case (except the last case |
| * of a switch) without meeting a break or similar end of the control |
| * flow. |
| */ |
| class FallThroughError extends Error { |
| FallThroughError(); |
| } |
| |
| /** |
| * Error thrown when trying to instantiate an abstract class. |
| */ |
| class AbstractClassInstantiationError extends Error { |
| final String _className; |
| AbstractClassInstantiationError(String this._className); |
| String toString() => "Cannot instantiate abstract class: '$_className'"; |
| } |
| |
| |
| /** |
| * Error thrown by the default implementation of [:noSuchMethod:] on [Object]. |
| */ |
| class NoSuchMethodError extends Error { |
| final Object _receiver; |
| final Symbol _memberName; |
| final List _arguments; |
| final Map<Symbol, dynamic> _namedArguments; |
| final List _existingArgumentNames; |
| |
| /** |
| * Create a [NoSuchMethodError] corresponding to a failed method call. |
| * |
| * The [receiver] is the receiver of the method call. |
| * That is, the object on which the method was attempted called. |
| * If the receiver is `null`, it is interpreted as a call to a top-level |
| * function of a library. |
| * |
| * The [memberName] is a [Symbol] representing the name of the called method |
| * or accessor. It should not be `null`. |
| * |
| * The [positionalArguments] is a list of the positional arguments that the |
| * method was called with. If `null`, it is considered equivalent to the |
| * empty list. |
| * |
| * The [namedArguments] is a map from [Symbol]s to the values of named |
| * arguments that the method was called with. |
| * |
| * The optional [exisitingArgumentNames] is the expected parameters of a |
| * method with the same name on the receiver, if available. This is |
| * the signature of the method that would have been called if the parameters |
| * had matched. |
| */ |
| NoSuchMethodError(Object receiver, |
| Symbol memberName, |
| List positionalArguments, |
| Map<Symbol ,dynamic> namedArguments, |
| [List existingArgumentNames = null]) |
| : _receiver = receiver, |
| _memberName = memberName, |
| _arguments = positionalArguments, |
| _namedArguments = namedArguments, |
| _existingArgumentNames = existingArgumentNames; |
| |
| external String toString(); |
| } |
| |
| |
| /** |
| * The operation was not allowed by the object. |
| * |
| * This [Error] is thrown when an instance cannot implement one of the methods |
| * in its signature. |
| */ |
| class UnsupportedError extends Error { |
| final String message; |
| UnsupportedError(this.message); |
| String toString() => "Unsupported operation: $message"; |
| } |
| |
| |
| /** |
| * Thrown by operations that have not been implemented yet. |
| * |
| * This [Error] is thrown by unfinished code that hasn't yet implemented |
| * all the features it needs. |
| * |
| * If a class is not intending to implement the feature, it should throw |
| * an [UnsupportedError] instead. This error is only intended for |
| * use during development. |
| */ |
| class UnimplementedError extends Error implements UnsupportedError { |
| final String message; |
| UnimplementedError([String this.message]); |
| String toString() => (this.message != null |
| ? "UnimplementedError: $message" |
| : "UnimplementedError"); |
| } |
| |
| |
| /** |
| * The operation was not allowed by the current state of the object. |
| * |
| * This is a generic error used for a variety of different erroneous |
| * actions. The message should be descriptive. |
| */ |
| class StateError extends Error { |
| final String message; |
| StateError(this.message); |
| String toString() => "Bad state: $message"; |
| } |
| |
| |
| /** |
| * Error occurring when a collection is modified during iteration. |
| * |
| * Some modifications may be allowed for some collections, so each collection |
| * ([Iterable] or similar collection of values) should declare which operations |
| * are allowed during an iteration. |
| */ |
| class ConcurrentModificationError extends Error { |
| /** The object that was modified in an incompatible way. */ |
| final Object modifiedObject; |
| |
| ConcurrentModificationError([this.modifiedObject]); |
| |
| String toString() { |
| if (modifiedObject == null) { |
| return "Concurrent modification during iteration."; |
| } |
| return "Concurrent modification during iteration: " |
| "${Error.safeToString(modifiedObject)}."; |
| } |
| } |
| |
| |
| class OutOfMemoryError implements Error { |
| const OutOfMemoryError(); |
| String toString() => "Out of Memory"; |
| |
| StackTrace get stackTrace => null; |
| } |
| |
| |
| class StackOverflowError implements Error { |
| const StackOverflowError(); |
| String toString() => "Stack Overflow"; |
| |
| StackTrace get stackTrace => null; |
| } |
| |
| /** |
| * Error thrown when a lazily initialized variable cannot be initialized. |
| * |
| * A static/library variable with an initializer expression is initialized |
| * the first time it is read. If evaluating the initializer expression causes |
| * another read of the variable, this error is thrown. |
| */ |
| class CyclicInitializationError extends Error { |
| final String variableName; |
| CyclicInitializationError([this.variableName]); |
| String toString() => variableName == null |
| ? "Reading static variable during its initialization" |
| : "Reading static variable '$variableName' during its initialization"; |
| } |