| // 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 should throw 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]): |
| /// ```plaintext |
| /// `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, |
| /// but the caller will have to catch the thrown value, |
| /// effectively making it an alternative result rather than an error. |
| /// If so, we consider the thrown object an *exception* rather than an error. |
| /// The thrown object can choose to implement [Exception] |
| /// to document that it represents an exceptional, but not erroneous, |
| /// occurrence, but being an [Exception] has no other effect |
| /// than documentation. |
| /// |
| /// All non-`null` values can be thrown in Dart. |
| /// Objects *extending* the `Error` class 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, |
| /// which the user is also not expected to catch or handle. |
| /// |
| /// 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 `RangeError.range(startIndex, 0, length)`. |
| /// Catching specific subclasses of [Error] is not intended, |
| /// and shouldn't happen outside of testing your own code. |
| @pragma('flutter:keep-to-string-in-subtypes') |
| 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 except for specific known and trusted types. |
| 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); |
| |
| /// The stack trace at the point where this error was first thrown. |
| /// |
| /// Classes which *extend* `Error` will automatically have a stack |
| /// trace filled in the first time they are thrown by a `throw` |
| /// expression. |
| external StackTrace? get stackTrace; |
| |
| /// Throws [error] with associated stack trace [stackTrace]. |
| /// |
| /// Behaves like `throw error` would |
| /// if the [current stack trace][StackTrace.current] was [stackTrace] |
| /// at the time of the `throw`. |
| /// |
| /// Like for a `throw`, if [error] extends [Error], and it has not been |
| /// thrown before, its [Error.stackTrace] property will be set to |
| /// the [stackTrace]. |
| /// |
| /// This function does not guarantee to preserve the identity of [stackTrace]. |
| /// The [StackTrace] object that is caught by a `try`/`catch` of |
| /// this error, or which is set as the [Error.stackTrace] of an [error], |
| /// may not be the same [stackTrace] object provided as argument, |
| /// but it will have the same contents according to [StackTrace.toString]. |
| @Since("2.16") |
| static Never throwWithStackTrace(Object error, StackTrace stackTrace) { |
| checkNotNullable(error, "error"); |
| checkNotNullable(stackTrace, "stackTrace"); |
| _throw(error, stackTrace); |
| } |
| |
| @pragma("wasm:entry-point") |
| external static Never _throw(Object error, StackTrace stackTrace); |
| } |
| |
| /// Error thrown by the runtime system when an assert statement fails. |
| class AssertionError extends Error { |
| /// Message describing the assertion error. |
| final Object? message; |
| |
| /// Creates an assertion error with the provided [message]. |
| AssertionError([this.message]); |
| |
| String toString() { |
| if (message != null) { |
| return "Assertion failed: ${Error.safeToString(message)}"; |
| } |
| return "Assertion failed"; |
| } |
| } |
| |
| /// Error thrown by the runtime system when a dynamic type error happens. |
| class TypeError extends Error {} |
| |
| /// Error thrown when a function is passed an unacceptable argument. |
| /// |
| /// The method should document restrictions on the arguments it accepts, |
| /// for example if an integer argument must be non-nullable, |
| /// a string argument must be non-empty, |
| /// or a `dynamic`-typed argument must actually have one of a few accepted |
| /// types. |
| /// |
| /// The user should be able to predict which arguments will cause an |
| /// error to be throw, and avoid calling with those. |
| /// |
| /// It's almost always a good idea to provide the unacceptable value |
| /// as part of the error, to help the user figure out what went wrong, |
| /// so the [ArgumentError.value] constructor is the preferred constructor. |
| /// Use [ArgumentError.new] only when the value cannot be provided for some |
| /// reason. |
| @pragma("vm:entry-point") |
| class ArgumentError extends Error { |
| /// Whether value was provided. |
| final bool _hasValue; |
| |
| /// The invalid value. |
| final dynamic invalidValue; |
| |
| /// Name of the invalid argument, if available. |
| final String? name; |
| |
| /// Message describing the problem. |
| final dynamic message; |
| |
| /// Creates an error with [message] describing the problem with an 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. |
| /// |
| /// If [name] is provided, it should be the name of the parameter |
| /// which received an invalid argument. |
| /// |
| /// Prefer using [ArgumentError.value] instead to retain and document the |
| /// invalid value as well. |
| @pragma("vm:entry-point") |
| ArgumentError([this.message, @Since("2.14") this.name]) |
| : invalidValue = null, |
| _hasValue = false; |
| |
| /// 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: |
| /// ```plaintext |
| /// 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). |
| @pragma("vm:entry-point") |
| ArgumentError.value(value, [this.name, this.message]) |
| : invalidValue = value, |
| _hasValue = true; |
| |
| /// Creates an argument error for a `null` argument that must not be `null`. |
| ArgumentError.notNull([this.name]) |
| : _hasValue = false, |
| message = "Must not be null", |
| invalidValue = null; |
| |
| /// Throws if [argument] is `null`. |
| /// |
| /// If [name] is supplied, it is used as the parameter name |
| /// in the error message. |
| /// |
| /// Returns the [argument] if it is not null. |
| static T checkNotNull<T>(T? argument, [String? name]) => |
| argument ?? (throw ArgumentError.notNull(name)); |
| |
| // Helper functions for toString overridden in subclasses. |
| String get _errorName => "Invalid argument${!_hasValue ? "(s)" : ""}"; |
| String get _errorExplanation => ""; |
| |
| String toString() { |
| String? name = this.name; |
| String nameString = (name == null) ? "" : " ($name)"; |
| Object? message = this.message; |
| var messageString = (message == null) ? "" : ": ${message}"; |
| String prefix = "$_errorName$nameString$messageString"; |
| 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 argument value being outside an accepted 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; |
| |
| num? get invalidValue => super.invalidValue; |
| |
| // 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]. |
| @pragma("vm:entry-point") |
| 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 ?? "Value not in range"); |
| |
| /// Create a new [RangeError] for a value being outside the valid 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. |
| @pragma("vm:entry-point") |
| RangeError.range( |
| num invalidValue, |
| int? minValue, |
| int? maxValue, [ |
| String? name, |
| String? message, |
| ]) : start = minValue, |
| end = maxValue, |
| super.value(invalidValue, name, 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, |
| dynamic indexable, [ |
| String? name, |
| String? message, |
| int? length, |
| ]) = IndexError; |
| |
| /// Check that an integer [value] lies in a specific interval. |
| /// |
| /// Throws if [value] is not in the interval. |
| /// The interval is from [minValue] to [maxValue], both inclusive. |
| /// |
| /// If [name] or [message] are provided, they are used as the parameter |
| /// name and message text of the thrown error. |
| /// |
| /// Returns [value] if it is in the interval. |
| static int checkValueInInterval( |
| int value, |
| int minValue, |
| int maxValue, [ |
| String? name, |
| String? message, |
| ]) { |
| if (value < minValue || value > maxValue) { |
| throw RangeError.range(value, minValue, maxValue, name, message); |
| } |
| return value; |
| } |
| |
| /// Check that [index] 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 an index-operator |
| /// `[]` that accepts an index if `0 <= index < length`. |
| /// |
| /// If [name] or [message] are provided, they are used as the parameter |
| /// name and message text of the thrown error. If [name] is omitted, it |
| /// defaults to `"index"`. |
| /// |
| /// If [length] is provided, it is used as the length of the indexable object, |
| /// otherwise the length is found as `indexable.length`. |
| /// |
| /// Returns [index] if it is a valid index. |
| static int checkValidIndex( |
| int index, |
| dynamic indexable, [ |
| String? name, |
| int? length, |
| String? message, |
| ]) { |
| length ??= (indexable.length as int); |
| return IndexError.check( |
| index, |
| length, |
| indexable: indexable, |
| name: name, |
| message: message, |
| ); |
| } |
| |
| /// 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) { |
| startName ??= "start"; |
| throw RangeError.range(start, 0, length, startName, message); |
| } |
| if (end != null) { |
| if (start > end || end > length) { |
| endName ??= "end"; |
| throw RangeError.range(end, start, length, endName, message); |
| } |
| return end; |
| } |
| return length; |
| } |
| |
| /// Check that an integer value is non-negative. |
| /// |
| /// Throws if the value is negative. |
| /// |
| /// If [name] or [message] are provided, they are used as the parameter |
| /// name and message text of the thrown error. If [name] is omitted, it |
| /// defaults to `index`. |
| /// |
| /// Returns [value] if it is not negative. |
| static int checkNotNegative(int value, [String? name, String? message]) { |
| if (value < 0) { |
| throw RangeError.range(value, 0, null, name ?? "index", message); |
| } |
| return value; |
| } |
| |
| String get _errorName => "RangeError"; |
| String get _errorExplanation { |
| assert(_hasValue); |
| String explanation = ""; |
| num? start = this.start; |
| num? end = this.end; |
| 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 inclusive range $start..$end"; |
| } 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 [invalidValue] was not a valid index into. |
| /// |
| /// Can be, for example, a [List] or [String], |
| /// which both have index based operations. |
| final Object? indexable; |
| |
| /// The length of [indexable] at the time of the error. |
| final int length; |
| |
| int get invalidValue => super.invalidValue; |
| |
| /// 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. |
| @Deprecated("Use IndexError.withLength instead.") |
| IndexError( |
| int invalidValue, |
| dynamic indexable, [ |
| String? name, |
| String? message, |
| int? length, |
| ]) : this.indexable = indexable, |
| // ignore: avoid_dynamic_calls |
| this.length = length ?? indexable.length, |
| super.value(invalidValue, name, message ?? "Index out of range"); |
| |
| /// 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. |
| /// |
| /// The message is used as part of the string representation of the error. |
| @Since("2.19") |
| IndexError.withLength( |
| int invalidValue, |
| this.length, { |
| this.indexable, |
| String? name, |
| String? message, |
| }) : super.value(invalidValue, name, message ?? "Index out of range"); |
| |
| /// Check that [index] is a valid index into an indexable object. |
| /// |
| /// Throws if [index] is not a valid index. |
| /// |
| /// An indexable object is one that has a `length` and an index-operator |
| /// `[]` that accepts an index if `0 <= index < length`. |
| /// |
| /// The [length] is the length of the indexable object. |
| /// |
| /// The [indexable], if provided, is the indexable object. |
| /// |
| /// The [name] is the parameter name of the index value. Defaults to "index", |
| /// and can be set to null to omit a name from the error string, |
| /// if the invalid index was not a parameter. |
| /// |
| /// The [message], if provided, is included in the error string. |
| /// |
| /// Returns [index] if it is a valid index. |
| @Since("2.19") |
| static int check( |
| int index, |
| int length, { |
| Object? indexable, |
| String? name, |
| String? message, |
| }) { |
| // Comparing with `0` as receiver produces better dart2js type inference. |
| if (0 > index || index >= length) { |
| name ??= "index"; |
| throw IndexError.withLength( |
| index, |
| length, |
| indexable: indexable, |
| name: name, |
| message: message, |
| ); |
| } |
| return index; |
| } |
| |
| // Getters inherited from RangeError. |
| int get start => 0; |
| int get end => length - 1; |
| |
| String get _errorName => "RangeError"; |
| String get _errorExplanation { |
| assert(_hasValue); |
| int invalidValue = this.invalidValue; |
| 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 on an invalid function or method invocation. |
| /// |
| /// Thrown when a dynamic function or method call provides an invalid |
| /// type argument or argument list to the function being called. |
| /// For non-dynamic invocations, static type checking prevents |
| /// such invalid arguments. |
| /// |
| /// Also thrown by the default implementation of [Object.noSuchMethod]. |
| class NoSuchMethodError extends Error { |
| /// Creates 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. |
| /// |
| /// The [invocation] represents the method call that failed. It |
| /// should not be `null`. |
| external factory NoSuchMethodError.withInvocation( |
| Object? receiver, |
| Invocation invocation, |
| ); |
| |
| 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. |
| /// For example, it's used by unmodifiable versions of collections, |
| /// when someone calls a modifying method. |
| @pragma("vm:entry-point") |
| class UnsupportedError extends Error { |
| final String? message; |
| @pragma("vm:entry-point") |
| UnsupportedError(String 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 the class does not intend 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([this.message]); |
| String toString() { |
| var message = this.message; |
| return (message != null) |
| ? "UnimplementedError: $message" |
| : "UnimplementedError"; |
| } |
| } |
| |
| /// The operation was not allowed by the current state of the object. |
| /// |
| /// Should be used when this particular object is currently in a state |
| /// which doesn't support the requested operation, but other similar |
| /// objects might, or the object itself can later change its state |
| /// to one which supports the operation. |
| /// |
| /// Example: Asking for `list.first` on a currently empty list. |
| /// If the operation is never supported by this object or class, |
| /// consider using [UnsupportedError] instead. |
| /// |
| /// 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; |
| @pragma("vm:entry-point") |
| 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)}."; |
| } |
| } |
| |
| /// Error that the platform can use in case of memory shortage. |
| final class OutOfMemoryError implements Error { |
| @pragma("vm:entry-point") |
| const OutOfMemoryError(); |
| String toString() => "Out of Memory"; |
| |
| StackTrace? get stackTrace => null; |
| } |
| |
| /// Error that the platform can use in case of stack overflow. |
| final class StackOverflowError implements Error { |
| @pragma("vm:entry-point") |
| const StackOverflowError(); |
| String toString() => "Stack Overflow"; |
| |
| StackTrace? get stackTrace => null; |
| } |