ation/package_config/latest/) /// to read and parse it. @Since('3.2') external static Uri? get packageConfigSync; /// Resolves a `package:` URI to its actual location. /// /// Returns the actual location of the file or directory specified by the /// [packageUri] `package:` URI. /// /// If the [packageUri] is not a `package:` URI, it's returned as-is. /// /// Returns `null` if [packageUri] is a `package:` URI, but either /// the current package configuration does not have a configuration /// for the package name of the URI, or /// the URI is not valid (doesn't start with `package:valid_package_name/`), /// /// A `package:` URI is resolved to its actual location based on /// a package resolution configuration (see [packageConfig]) /// which specifies how to find the actual location of the file or directory /// that the `package:` URI points to. /// /// The actual location corresponding to a `package:` URI is always a /// non-`package:` URI, typically a `file:` or possibly `http:` URI. /// /// A program may be run in a way where source files are not available, /// and if so, the returned URI may not correspond to the actual file or /// directory or be `null`. external static Future resolvePackageUri(Uri packageUri); /// Resolves a `package:` URI to its actual location. /// /// Returns the actual location of the file or directory specified by the /// [packageUri] `package:` URI. /// /// If the [packageUri] is not a `package:` URI, it's returned as-is. /// /// Returns `null` if [packageUri] is a `package:` URI, but either /// the current package configuration does not have a configuration /// for the package name of the URI, or /// the URI is not valid (doesn't start with `package:valid_package_name/`), /// /// A `package:` URI is resolved to its actual location based on /// a package resolution configuration (see [packageConfig]) /// which specifies how to find the actual location of the file or directory /// that the `package:` URI points to. /// /// The actual location corresponding to a `package:` URI is always a /// non-`package:` URI, typically a `file:` or possibly `http:` URI. /// /// A program may be run in a way where source files are not available, /// and if so, the returned URI may not correspond to the actual file or /// directory or be `null`. @Since('3.2') external static Uri? resolvePackageUriSync(Uri packageUri); /// Spawns an isolate that shares the same code as the current isolate. /// /// The argument [entryPoint] specifies the initial function to call /// in the spawned isolate. /// The entry-point function is invoked in the new isolate with [message] /// as the only argument. /// /// The [entryPoint] function must be able to be called with a single /// argument, that is, a function which accepts at least one positional /// parameter and has at most one required positional parameter. /// The function may accept any number of optional parameters, /// as long as it *can* be called with just a single argument. If /// [entryPoint] is a closure then it may implicitly send unexpected state /// to the isolate due to limitations in the Dart implementation. This can /// cause performance issues, increased memory usage /// (see http://dartbug.com/36983) or, if the state includes objects that /// can't be spent between isolates, a runtime failure. See [run] for an /// example. /// /// [message] must be sendable between isolates. Objects that cannot be sent /// include open files and sockets (see [SendPort.send] for details). Usually /// the initial [message] contains a [SendPort] so that the spawner and /// spawnee can communicate with each other. /// /// If the [paused] parameter is set to `true`, /// the isolate will start up in a paused state, /// just before calling the [entryPoint] function with the [message], /// as if by an initial call of `isolate.pause(isolate.pauseCapability)`. /// To resume the isolate, call `isolate.resume(isolate.pauseCapability)`. /// /// If the [errorsAreFatal], [onExit] and/or [onError] parameters are provided, /// the isolate will act as if, respectively, [setErrorsFatal], /// [addOnExitListener] and [addErrorListener] were called with the /// corresponding parameter and was processed before the isolate starts /// running. /// /// If [debugName] is provided, the spawned [Isolate] will be identifiable by /// this name in debuggers and logging. /// /// If [errorsAreFatal] is omitted, the platform may choose a default behavior /// or inherit the current isolate's behavior. /// /// You can also call the [setErrorsFatal], [addOnExitListener] and /// [addErrorListener] methods on the returned isolate, but unless the /// isolate was started as [paused], it may already have terminated /// before those methods can complete. /// /// Returns a future which will complete with an [Isolate] instance if the /// spawning succeeded. It will complete with an error otherwise. /// /// One can expect the base memory overhead of an isolate to be in the order /// of 30 kb. external static Future spawn( void entryPoint(T message), T message, { bool paused = false, bool errorsAreFatal = true, SendPort? onExit, SendPort? onError, String? debugName, }); /// Spawns an isolate running the script file specified by [uri]. /// /// Creates and spawns a new isolate that runs the Dart program which has /// the [uri] file as the entry point that provides the `main` method. /// The new isolate belongs to a new [isolate group][], different from /// the isolate group of the spawning isolate. /// /// [isolate group]: https://dart.dev/language/concurrency#performance-and-isolate-groups /// /// The isolate starts executing the top-level `main` function of the library /// with the given URI. /// /// The target `main` must be callable with zero, one or two arguments. /// Examples: /// /// * `main()` /// * `main(args)` /// * `main(args, message)` /// /// When present, the parameter `args` is set to the provided [args] list. /// When present, the parameter `message` is set to the initial [message]. A /// runtime error occurs if the [message] argument's runtime type cannot be /// assigned to the second parameter of the `main` method. /// /// If the [paused] parameter is set to `true`, /// the isolate will start up in a paused state, /// as if by an initial call of `isolate.pause(isolate.pauseCapability)`. /// To resume the isolate, call `isolate.resume(isolate.pauseCapability)`. /// /// If the [errorsAreFatal], [onExit] and/or [onError] parameters are provided, /// the isolate will act as if, respectively, [setErrorsFatal], /// [addOnExitListener] and [addErrorListener] were called with the /// corresponding parameter and was processed before the isolate starts /// running. /// /// You can also call the [setErrorsFatal], [addOnExitListener] and /// [addErrorListener] methods on the returned isolate, but unless the /// isolate was started as [paused], it may already have terminated /// before those methods can complete. /// /// The [checked] parameter controls whether asserts are enabled /// in the created isolate. /// In a production-mode program, the parameter is ignored, /// and assertions are always disabled in all isolates. /// Otherwise if a `true` or `false` value is provided, /// assertions are enabled or disabled, respectively, in the /// created isolate. /// If `null` or no argument is provided, asserts are enabled /// in the created isolate if and only if they are enabled /// in the spawning isolate. /// /// It may not always be possible to honor the `checked` parameter. /// If the isolate code was pre-compiled, it may not be possible to change /// the checked mode setting dynamically. /// In that case, the `checked` parameter is ignored. /// /// If the [packageConfig] parameter is provided, then it is used to find the /// location of a package resolution configuration file for the spawned /// isolate. /// /// If the [automaticPackageResolution] parameter is provided, then the /// location of the package sources in the spawned isolate is automatically /// determined. /// /// The [environment] is a mapping from strings to strings which the /// spawned isolate uses when looking up [String.fromEnvironment] values. /// The system may add its own entries to environment as well. /// If `environment` is omitted, the spawned isolate has the same environment /// declarations as the spawning isolate. /// /// WARNING: The [environment] parameter is not implemented on all /// platforms yet. /// /// If [debugName] is provided, the spawned [Isolate] will be identifiable by /// this name in debuggers and logging. /// /// Returns a future that will complete with an [Isolate] instance if the /// spawning succeeded. It will complete with an error otherwise. external static Future spawnUri( Uri uri, List args, message, { bool paused = false, SendPort? onExit, SendPort? onError, bool errorsAreFatal = true, bool? checked, Map? environment, @Deprecated('The packages/ dir is not supported in Dart 2') Uri? packageRoot, Uri? packageConfig, bool automaticPackageResolution = false, String? debugName, }); /// Requests the isolate to pause. /// /// When the isolate receives the pause command, it stops /// processing events from the event loop queue. /// It may still add new events to the queue in response to, e.g., timers /// or receive-port messages. When the isolate is resumed, /// it starts handling the already enqueued events. /// /// The pause request is sent through the isolate's command port, /// which bypasses the receiving isolate's event loop. /// The pause takes effect when it is received, pausing the event loop /// as it is at that time. /// /// The [resumeCapability] is used to identity the pause, /// and must be used again to end the pause using [resume]. /// If [resumeCapability] is omitted, a new capability object is created /// and used instead. /// /// If an isolate is paused more than once using the same capability, /// only one resume with that capability is needed to end the pause. /// /// If an isolate is paused using more than one capability, /// each pause must be individually ended before the isolate resumes. /// /// Returns the capability that must be used to end the pause. /// This is either [resumeCapability], or a new capability when /// [resumeCapability] is omitted. /// /// If [pauseCapability] is `null`, or it's not the pause capability /// of the isolate identified by [controlPort], /// the pause request is ignored by the receiving isolate. Capability pause([Capability? resumeCapability]) { resumeCapability ??= Capability(); _pause(resumeCapability); return resumeCapability; } /// Internal implementation of [pause]. external void _pause(Capability resumeCapability); /// Resumes a paused isolate. /// /// Sends a message to an isolate requesting that it ends a pause /// that was previously requested. /// /// When all active pause requests have been cancelled, the isolate /// will continue processing events and handling normal messages. /// /// If the [resumeCapability] is not one that has previously been used /// to pause the isolate, or it has already been used to resume from /// that pause, the resume call has no effect. external void resume(Capability resumeCapability); /// Requests an exit message on [responsePort] when the isolate terminates. /// /// The isolate will send [response] as a message on [responsePort] as the last /// thing before it terminates. It will run no further code after the message /// has been sent. /// /// Adding the same port more than once will only cause it to receive one exit /// message, using the last response value that was added, /// and it only needs to be removed once using [removeOnExitListener]. /// /// If the isolate has terminated before it can receive this request, /// no exit message will be sent. /// /// The [response] object must follow the same restrictions as enforced by /// [SendPort.send] when sending to an isolate from another isolate group; /// only simple values that can be sent to all isolates, like `null`, /// booleans, numbers or strings, are allowed. /// /// Since isolates run concurrently, it's possible for it to exit before the /// exit listener is established, and in that case no response will be /// sent on [responsePort]. /// To avoid this, either use the corresponding parameter to the spawn /// function, or start the isolate paused, add the listener and /// then resume the isolate. /* TODO(lrn): Can we do better? Can the system recognize this message and * send a reply if the receiving isolate is dead? */ external void addOnExitListener(SendPort responsePort, {Object? response}); /// Stops listening for exit messages from the isolate. /// /// Requests for the isolate to not send exit messages on [responsePort]. /// If the isolate isn't expecting to send exit messages on [responsePort], /// because the port hasn't been added using [addOnExitListener], /// or because it has already been removed, the request is ignored. /// /// If the same port has been passed via [addOnExitListener] more than once, /// only one call to `removeOnExitListener` is needed to stop it from receiving /// exit messages. /// /// Closing the receive port that is associated with the [responsePort] does /// not stop the isolate from sending uncaught errors, they are just going to /// be lost. /// /// An exit message may still be sent if the isolate terminates /// before this request is received and processed. external void removeOnExitListener(SendPort responsePort); /// Sets whether uncaught errors will terminate the isolate. /// /// If errors are fatal, any uncaught error will terminate the isolate /// event loop and shut down the isolate. /// /// This call requires the [terminateCapability] for the isolate. /// If the capability is absent or incorrect, no change is made. /// /// Since isolates run concurrently, it's possible for the receiving isolate /// to exit due to an error, before a request, using this method, has been /// received and processed. /// To avoid this, either use the corresponding parameter to the spawn /// function, or start the isolate paused, set errors non-fatal and /// then resume the isolate. external void setErrorsFatal(bool errorsAreFatal); /// Requests the isolate to shut down. /// /// The isolate is requested to terminate itself. /// The [priority] argument specifies when this must happen. /// /// The [priority], when provided, must be one of [immediate] or /// [beforeNextEvent] (the default). /// The shutdown is performed at different times depending on the priority: /// /// * `immediate`: The isolate shuts down as soon as possible. /// Control messages are handled in order, so all previously sent control /// events from this isolate will all have been processed. /// The shutdown should happen no later than if sent with /// `beforeNextEvent`. /// It may happen earlier if the system has a way to shut down cleanly /// at an earlier time, even during the execution of another event. /// * `beforeNextEvent`: The shutdown is scheduled for the next time /// control returns to the event loop of the receiving isolate, /// after the current event, and any already scheduled control events, /// are completed. /// /// If [terminateCapability] is `null`, or it's not the terminate capability /// of the isolate identified by [controlPort], /// the kill request is ignored by the receiving isolate. external void kill({int priority = beforeNextEvent}); /// Requests that the isolate send [response] on the [responsePort]. /// /// The [response] object must follow the same restrictions as enforced by /// [SendPort.send] when sending to an isolate from another isolate group; /// only simple values that can be sent to all isolates, like `null`, /// booleans, numbers or strings, are allowed. /// /// If the isolate is alive, it will eventually send `response` /// (defaulting to `null`) on the response port. /// /// The [priority] must be one of [immediate] or [beforeNextEvent]. /// The response is sent at different times depending on the ping type: /// /// * `immediate`: The isolate responds as soon as it receives the /// control message. This is after any previous control message /// from the same isolate has been received and processed, /// but may be during execution of another event. /// * `beforeNextEvent`: The response is scheduled for the next time /// control returns to the event loop of the receiving isolate, /// after the current event, and any already scheduled control events, /// are completed. external void ping( SendPort responsePort, { Object? response, int priority = immediate, }); /// Requests that uncaught errors of the isolate are sent back to [port]. /// /// The errors are sent back as two-element lists. /// The first element is a `String` representation of the error, usually /// created by calling `toString` on the error. /// The second element is a `String` representation of an accompanying /// stack trace, or `null` if no stack trace was provided. /// To convert this back to a [StackTrace] object, use [StackTrace.fromString]. /// /// Listening using the same port more than once does nothing. /// A port will only receive each error once, /// and will only need to be removed once using [removeErrorListener]. /// /// Closing the receive port that is associated with the port does not stop /// the isolate from sending uncaught errors, they are just going to be lost. /// Instead use [removeErrorListener] to stop receiving errors on [port]. /// /// Since isolates run concurrently, it's possible for it to exit before the /// error listener is established. To avoid this, start the isolate paused, /// add the listener and then resume the isolate. external void addErrorListener(SendPort port); /// Stops listening for uncaught errors from the isolate. /// /// Requests for the isolate to not send uncaught errors on [port]. /// If the isolate isn't expecting to send uncaught errors on [port], /// because the port hasn't been added using [addErrorListener], /// or because it has already been removed, the request is ignored. /// /// If the same port has been passed via [addErrorListener] more than once, /// only one call to `removeErrorListener` is needed to stop it from receiving /// uncaught errors. /// /// Uncaught errors message may still be sent by the isolate /// until this request is received and processed. external void removeErrorListener(SendPort port); /// Returns a broadcast stream of uncaught errors from the isolate. /// /// Each error is provided as an error event on the stream. /// /// The actual error object and stackTraces will not necessarily /// be the same object types as in the actual isolate, but they will /// always have the same [Object.toString] result. /// /// This stream is based on [addErrorListener] and [removeErrorListener]. Stream get errors { StreamController controller = StreamController.broadcast(sync: true); RawReceivePort? port; void handleError(Object? message) { var listMessage = message as List; var errorDescription = listMessage[0] as String; var stackDescription = listMessage[1] as String; var error = RemoteError(errorDescription, stackDescription); controller.addError(error, error.stackTrace); } controller.onListen = () { RawReceivePort receivePort = RawReceivePort(handleError); port = receivePort; this.addErrorListener(receivePort.sendPort); }; controller.onCancel = () { var listenPort = port!; port = null; this.removeErrorListener(listenPort.sendPort); listenPort.close(); }; return controller.stream; } /// Terminates the current isolate synchronously. /// /// This operation is potentially dangerous and should be used judiciously. /// The isolate stops operating *immediately*. It throws if the optional /// [message] does not adhere to the limitations on what can be sent from one /// isolate to another (see [SendPort.send] for more details). It also throws /// if a [finalMessagePort] is associated with an isolate spawned outside of /// current isolate group, spawned via [spawnUri]. Please refer to /// [isolate group](https://dart.dev/language/concurrency#performance-and-isolate-groups) /// for more details about isolate groups. /// /// If successful, a call to this method does not return. Pending `finally` /// blocks are not executed, control flow will not go back to the event loop, /// scheduled asynchronous asks will never run, and even pending isolate /// control commands may be ignored. (The isolate will send messages to ports /// already registered using [Isolate.addOnExitListener], but no further Dart /// code will run in the isolate.) /// /// If [finalMessagePort] is provided, and the [message] can be sent through /// it (see [SendPort.send] for more details), then the message is sent /// through that port as the final operation of the current isolate. The /// isolate terminates immediately after that [SendPort.send] call returns. /// /// If the port is a native port -- one provided by [ReceivePort.sendPort] or /// [RawReceivePort.sendPort] -- the system may be able to send this final /// message more efficiently than normal port communication between live /// isolates. In these cases this final message object graph will be /// reassigned to the receiving isolate without copying. Further, the /// receiving isolate will in most cases be able to receive the message /// in constant time. external static Never exit([SendPort? finalMessagePort, Object? message]); } /// Sends messages to its [ReceivePort]s. /// /// [SendPort]s are created from [ReceivePort]s. Any message sent through /// a [SendPort] is delivered to its corresponding [ReceivePort]. There might be /// many [SendPort]s for the same [ReceivePort]. /// /// [SendPort]s can be transmitted to other isolates, and they preserve equality /// when sent. @pragma("vm:entry-point") abstract interface class SendPort implements Capability { /// Sends an asynchronous [message] through this send port, to its /// corresponding [ReceivePort]. /// /// If the sending and receiving isolates do not share the same code /// (an isolate created using [Isolate.spawnUri] does not share the code /// of the isolate that spawned it), the transitive object graph of [message] /// can **only** contain the following kinds of objects: /// /// - `null` /// - `true` and `false` /// - Instances of [int], [double], [String] /// - Instances created through list, map and set literals /// - Instances created by constructors of: /// - [List], [Map], [LinkedHashMap], [Set] and [LinkedHashSet] /// - [TransferableTypedData] /// - [Capability] /// - [SendPort] instances from [ReceivePort.sendPort] or /// [RawReceivePort.sendPort] where the receive ports are created /// using those classes' constructors. /// - Instances of [Type] representing one of the types mentioned above, /// `Object`, `dynamic`, `void` and `Never` as well as nullable variants /// of all these types. For generic types type arguments must be sendable /// types for the whole type to be sendable. /// /// If the sender and receiver isolate share the same code (e.g. isolates /// created via [Isolate.spawn]), the transitive object graph of [message] can /// contain any object, with the following exceptions: /// /// - Objects with native resources (subclasses of e.g. /// `NativeFieldWrapperClass1`). A [Socket] object for example refers /// internally to objects that have native resources attached and can /// therefore not be sent. /// - [ReceivePort] /// - [DynamicLibrary] /// - [Finalizable] /// - [Finalizer] /// - [NativeFinalizer] /// - [UserTag] /// - `MirrorReference` /// /// Instances of classes that either themselves are marked with /// `@pragma('vm:isolate-unsendable')`, extend or implement such classes /// cannot be sent through the ports. /// /// Apart from those exceptions any object can be sent. Objects that are /// identified as immutable (e.g. strings) will be shared whereas all other /// objects will be copied. /// /// The send happens immediately and may have a linear time cost to copy the /// transitive object graph. The send itself doesn't block (i.e. doesn't wait /// until the receiver has received the message). The corresponding receive /// port can receive the message as soon as its isolate's event loop is ready /// to deliver it, independently of what the sending isolate is doing. /// /// Note: Due to an implementation choice the Dart VM made for how closures /// represent captured state, closures can currently capture more state than /// they need, which can cause the transitive closure to be larger than /// needed. Open bug to address this: http://dartbug.com/36983 void send(Object? message); /// Tests whether [other] is a [SendPort] pointing to the same /// [ReceivePort] as this one. bool operator ==(other); /// A hash code for this send port that is consistent with the == operator. int get hashCode; } /// Together with [SendPort], the only means of communication between isolates. /// /// [ReceivePort]s have a `sendPort` getter which returns a [SendPort]. /// Any message that is sent through this [SendPort] /// is delivered to the [ReceivePort] it has been created from. There, the /// message is dispatched to the `ReceivePort`'s listener. /// /// A [ReceivePort] is a non-broadcast stream. This means that it buffers /// incoming messages until a listener is registered. Only one listener can /// receive messages. See [Stream.asBroadcastStream] for transforming the port /// to a broadcast stream. /// /// A [ReceivePort] may have many [SendPort]s. abstract interface class ReceivePort implements Stream { /// Opens a long-lived port for receiving messages. /// /// A [ReceivePort] is a non-broadcast stream. This means that it buffers /// incoming messages until a listener is registered. Only one listener can /// receive messages. See [Stream.asBroadcastStream] for transforming the port /// to a broadcast stream. /// /// The optional `debugName` parameter can be set to associate a name with /// this port that can be displayed in tooling. /// /// A receive port is closed by canceling its subscription. external factory ReceivePort([String debugName = '']); /// Creates a [ReceivePort] from a [RawReceivePort]. /// /// The handler of the given [rawPort] is overwritten during the construction /// of the result. external factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort); /// Listen for events from [Stream]. /// /// See [Stream.listen]. /// /// Note that [onError] and [cancelOnError] are ignored since a [ReceivePort] /// will never receive an error. /// /// The [onDone] handler will be called when the stream closes. /// The stream closes when [close] is called. StreamSubscription listen( void onData(message)?, { Function? onError, void onDone()?, bool? cancelOnError, }); /// Closes the receive port. /// /// No further events will be received by the receive port, /// or emitted as stream events. /// /// If [listen] has been called and the [StreamSubscription] has not /// been canceled yet, the subscription will be closed with a "done" /// event. /// /// If the stream has already been canceled this method has no effect. void close(); /// A [SendPort] which sends messages to this receive port. SendPort get sendPort; } /// A low-level asynchronous message receiver. /// /// A [RawReceivePort] is low level feature, and is not [Zone] aware. /// The [handler] will always be invoked in the [Zone.root] zone. /// /// The port cannot be paused. The data-handler must be set before the first /// message is received, otherwise the message is lost. /// /// Messages can be sent to this port using [sendPort]. abstract interface class RawReceivePort { /// Opens a long-lived port for receiving messages. /// /// A [RawReceivePort] is low level and does not work with [Zone]s. It /// cannot be paused. The data-handler must be set before the first /// message is received, otherwise the message is lost. /// /// If [handler] is provided, it's set as the [RawReceivePort.handler]. /// /// The optional `debugName` parameter can be set to associate a name with /// this port that can be displayed in tooling. external factory RawReceivePort([Function? handler, String debugName = '']); /// Sets the handler that is invoked for every incoming message. /// /// The handler is invoked in the [Zone.root] zone. /// If the handler should be invoked in the current zone, do: /// ```dart import:async /// rawPort.handler = Zone.current.bindCallback(actualHandler); /// ``` /// /// The handler must be a function which can accept one argument /// of the type of the messages sent to this port. /// This means that if it is known that messages will all be [String]s, /// a handler of type `void Function(String)` can be used. /// The function is invoked dynamically with the actual messages, /// and if this invocation fails, /// the error becomes a top-level uncaught error in the [Zone.root] zone. // TODO(44659): Change parameter type to `void Function(Never)` to only // accept functions which can be called with one argument. void set handler(Function? newHandler); /// Closes the port. /// /// After a call to this method, any incoming message is silently dropped. /// The [handler] will never be called again. void close(); /// Returns a [SendPort] that sends messages to this raw receive port. SendPort get sendPort; /// Whether this [RawReceivePort] keeps its [Isolate] alive. /// /// By default, receive ports keep the [Isolate] that created them alive until /// [close] is called. If [keepIsolateAlive] is set to `false`, the isolate /// may close while the port is still open. The port is closed when the /// isolate closes, and further messages sent by the [sendPort] are ignored. abstract bool keepIsolateAlive; } /// Description of an error from another isolate. /// /// This error has the same `toString()` and `stackTrace.toString()` behavior /// as the original error, but has no other features of the original error. final class RemoteError implements Error { final String _description; final StackTrace stackTrace; RemoteError(String description, String stackDescription) : _description = description, stackTrace = StackTrace.fromString(stackDescription); String toString() => _description; } /// An efficiently transferable sequence of byte values. /// /// A [TransferableTypedData] is created from a number of bytes. /// This will take time proportional to the number of bytes. /// /// The [TransferableTypedData] can be moved between isolates, so /// sending it through a send port will only take constant time. /// /// When sent this way, the local transferable can no longer be materialized, /// and the received object is now the only way to materialize the data. abstract final class TransferableTypedData { /// Creates a new [TransferableTypedData] containing the bytes of [list]. /// /// It must be possible to create a single [Uint8List] containing the /// bytes, so if there are more bytes than what the platform allows in /// a single [Uint8List], then creation fails. external factory TransferableTypedData.fromList(List list); /// Creates a new [ByteBuffer] containing the bytes stored in this [TransferableTypedData]. /// /// The [TransferableTypedData] is a cross-isolate single-use resource. /// This method must not be called more than once on the same underlying /// transferable bytes, even if the calls occur in different isolates. ByteBuffer materialize(); } /// Parameter object used by [Isolate.run]. /// /// The [_remoteExecute] function is run in a new isolate with a /// [_RemoteRunner] object as argument. final class _RemoteRunner { /// User computation to run. final FutureOr Function() computation; /// Port to send isolate computation result on. /// /// Only one object is ever sent on this port. /// If the value is `null`, it is sent by the isolate's "on-exit" handler /// when the isolate terminates without otherwise sending value. /// If the value is a list with one element, /// then it is the result value of the computation. /// Otherwise it is a list with two elements representing an error. /// If the error is sent by the isolate's "on-error" uncaught error handler, /// then the list contains two strings. This also terminates the isolate. /// If sent manually by this class, after capturing the error, /// the list contains one non-`null` [Object] and one [StackTrace]. final SendPort resultPort; _RemoteRunner(this.computation, this.resultPort); /// Run in a new isolate to get the result of [computation]. /// /// The result is sent back on [resultPort] as a single-element list. /// A two-element list sent on the same port is an error result. /// When sent by this function, it's always an object and a [StackTrace]. /// (The same port listens on uncaught errors from the isolate, which /// sends two-element lists containing [String]s instead). static void _remoteExecute(_RemoteRunner runner) { runner._run(); } void _run() async { R result; try { var potentiallyAsyncResult = computation(); if (potentiallyAsyncResult is Future) { result = await potentiallyAsyncResult; } else { result = potentiallyAsyncResult; } } catch (e, s) { // If sending fails, the error becomes an uncaught error. Isolate.exit(resultPort, _list2(e, s)); } Isolate.exit(resultPort, _list1(result)); } /// Helper function to create a one-element non-growable list. static List _list1(Object? value) => List.filled(1, value); /// Helper function to create a two-element non-growable list. static List _list2(Object? value1, Object? value2) => List.filled(2, value1)..[1] = value2; } „qNN<-4%J@& $@1*..35':(MN:MBLKR!HCMNE J&EAJ7ON6?"K(A/: MJ7<.P21JM%@BK=7)>QP2QN"LF I)PNNL+@( IJIOB838 DG=8'0 F B&*:- *%-K OI2ECJ;KO;L:'7>"H-B/"" .  N4'7>"H+/"" .  JL!'&N? ,##);JGLHJCADMM7LKOPJ/23HLMRBFJP*Q1FIF)MDO,  DLII1\PJMON=23LMRBFJFIF)C>::?@=FM,8PJJNGLAP,EP*LD,  &@-%<3L=6D9I<>KHG>HAB%G2=5'*5 D%FDIG15NRPQ=IH$MMH1OIIBL4N:LNDFOROPB5=?I,DCOMIF5)4?C'NAPA@MJGFMO2<8GMMH1B3FJEFA8GFML5K2I=RA0INPLON41<FHCFNQ?44F>CG5LJ(277C4@354NKPPOE\-NPKPP%OJKNPMKGHJM*JQ1Q:E#GKP;1?0F$>H-MOP3LQ997PK'P#B0&>#GG I>/FCM88*6IF:JM2OC6@B C5J=D$LJ=*M0I?QNJO"2NL+;"<%9A=BANI-LHI1I^JKI,A(,21LC/6FOLAF4?HCLH=> 21/)@..AGAA, dart:isolateÀeÀh³œœXorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/isolate_patch.dartÀQÊ// 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. import "dart:_internal" show ClassID, VMLibraryHooks, patch; import "dart:async" show Completer, Future, Stream, StreamController, StreamSubscription, Timer; import "dart:collection" show HashMap; import 'dart:developer' show postEvent; import "dart:typed_data" show ByteBuffer, TypedData, Uint8List; /// These are the additional parts of this patch library: part "timer_impl.dart"; @patch class ReceivePort { @patch factory ReceivePort([String debugName = '']) => _ReceivePortImpl(debugName); @patch factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) { return _ReceivePortImpl.fromRawReceivePort(rawPort); } } @patch @pragma("vm:entry-point") class Capability { @patch factory Capability() => _Capability(); } @pragma("vm:entry-point") class _Capability implements Capability { @pragma("vm:external-name", "Capability_factory") external factory _Capability(); bool operator ==(Object other) { return (other is _Capability) && _equals(other); } int get hashCode { return _get_hashcode(); } @pragma("vm:external-name", "Capability_equals") external bool _equals(Object other); @pragma("vm:external-name", "Capability_get_hashcode") external int _get_hashcode(); } @patch class RawReceivePort { /** * Opens a long-lived port for receiving messages. * * A [RawReceivePort] is low level and does not work with [Zone]s. It * can not be paused. The data-handler must be set before the first * event is received. */ @patch factory RawReceivePort([Function? handler, String debugName = '']) { _RawReceivePort result = _RawReceivePort(debugName); result.handler = handler; return result; } } final class _ReceivePortImpl extends Stream implements ReceivePort { _ReceivePortImpl([String debugName = '']) : this.fromRawReceivePort(RawReceivePort(null, debugName)); _ReceivePortImpl.fromRawReceivePort(this._rawPort) : _controller = StreamController(sync: true) { _controller.onCancel = close; _rawPort.handler = _controller.add; } SendPort get sendPort { return _rawPort.sendPort; } StreamSubscription listen( void onData(message)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } close() { _rawPort.close(); _controller.close(); } final RawReceivePort _rawPort; final StreamController _controller; } typedef void _ImmediateCallback(); /// The callback that has been registered through `scheduleImmediate`. _ImmediateCallback? _pendingImmediateCallback; /// The closure that should be used as scheduleImmediateClosure, when the VM /// is responsible for the event loop. void _isolateScheduleImmediate(void callback()) { assert( (_pendingImmediateCallback == null) || (_pendingImmediateCallback == callback), ); _pendingImmediateCallback = callback; } @pragma("vm:entry-point", "call") void _runPendingImmediateCallback() { final callback = _pendingImmediateCallback; if (callback != null) { _pendingImmediateCallback = null; callback(); } } /// The embedder can execute this function to get hold of /// [_isolateScheduleImmediate] above. @pragma("vm:entry-point", "call") Function _getIsolateScheduleImmediateClosure() { return _isolateScheduleImmediate; } @pragma("vm:entry-point") final class _RawReceivePort implements RawReceivePort { factory _RawReceivePort(String debugName) { final port = _RawReceivePort._(debugName); _portMap[port._get_id()] = port; return port; } @pragma("vm:external-name", "RawReceivePort_factory") external factory _RawReceivePort._(String debugName); close() { // Close the port and remove it from the handler map. _portMap.remove(this._closeInternal()); } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:external-name", "RawReceivePort_getSendPort") external SendPort get sendPort; bool operator ==(other) { return (other is _RawReceivePort) && (this._get_id() == other._get_id()); } int get hashCode { return sendPort.hashCode; } /**** Internal implementation details ****/ @pragma("vm:external-name", "RawReceivePort_get_id") external int _get_id(); // Called from the VM to retrieve the handler for a message. @pragma("vm:entry-point", "call") static _lookupHandler(int id) { return _portMap[id]?._handler; } // Called from the VM service to enumerate ports. @pragma("vm:entry-point", "call") static _lookupOpenPorts() { return _portMap.values.toList(); } // Called from the VM to dispatch a message. @pragma("vm:entry-point", "call") static _handleMessage(int id, message) { final Function? handler = _portMap[id]?._handler; if (handler == null) { return null; } // If handler or microtasks throw, the VM will drain microtasks again // after handling the error. handler(message); _runPendingImmediateCallback(); return handler; } // Call into the VM to close the VM maintained mappings. @pragma("vm:external-name", "RawReceivePort_closeInternal") external int _closeInternal(); // Set this port as active or inactive in the VM. If inactive, this port // will not be considered live even if it hasn't been explicitly closed. @pragma("vm:external-name", "RawReceivePort_setActive") external void _setActive(bool active); @pragma("vm:external-name", "RawReceivePort_getActive") external bool _getActive(); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:external-name", "RawReceivePort_getHandler") external Function? get _handler; @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:external-name", "RawReceivePort_setHandler") external set _handler(Function? handler); void set handler(Function? value) { _handler = value; } void set keepIsolateAlive(bool value) { _setActive(value); } bool get keepIsolateAlive => _getActive(); static final _portMap = {}; } @pragma("vm:entry-point") final class _SendPort implements SendPort { factory _SendPort._uninstantiable() { throw "Unreachable"; } /*--- public interface ---*/ @pragma("vm:entry-point", "call") void send(message) { _sendInternal(message); } bool operator ==(other) { return (other is _SendPort) && (this._get_id() == other._get_id()); } int get hashCode { return _get_hashcode(); } /*--- private implementation ---*/ @pragma("vm:external-name", "SendPort_get_id") external _get_id(); @pragma("vm:external-name", "SendPort_get_hashcode") external _get_hashcode(); // Forward the implementation of sending messages to the VM. @pragma("vm:external-name", "SendPort_sendInternal_") external void _sendInternal(message); } typedef _UnaryFunction(Never args); typedef _BinaryFunction(Never args, Never message); /** * Takes the real entry point as argument and schedules it to run in the message * queue. */ @pragma("vm:entry-point", "call") void _startMainIsolate(Function entryPoint, List? args) { _delayEntrypointInvocation(entryPoint, args, null, true); } /** * Returns the _startMainIsolate function. This closurization allows embedders * to setup trampolines to the main function. This workaround can be removed * once support for @pragma("vm:entry_point", "get") as documented in * https://github.com/dart-lang/sdk/issues/35720 lands. */ @pragma("vm:entry-point", "call") Function _getStartMainIsolateFunction() { return _startMainIsolate; } /** * Takes the real entry point as argument and schedules it to run in the message * queue. */ @pragma("vm:entry-point", "call") void _startIsolate( Function entryPoint, List? args, Object? message, bool isSpawnUri, ) { _delayEntrypointInvocation(entryPoint, args, message, isSpawnUri); } void _delayEntrypointInvocation( Function entryPoint, List? args, Object? message, bool allowZeroOneOrTwoArgs, ) { final port = RawReceivePort(); port.handler = (_) { port.close(); if (allowZeroOneOrTwoArgs) { if (entryPoint is _BinaryFunction) { (entryPoint as Function)(args, message); } else if (entryPoint is _UnaryFunction) { (entryPoint as Function)(args); } else { entryPoint(); } } else { entryPoint(message); } }; port.sendPort.send(null); } @patch final class Isolate { static final _currentIsolate = _getCurrentIsolate(); static final _rootUri = _getCurrentRootUri(); @patch static Isolate get current => _currentIsolate; @patch String? get debugName => _getDebugName(controlPort); @patch static Future get packageConfig { return Future.value(packageConfigSync); } @patch static Uri? get packageConfigSync { var hook = VMLibraryHooks.packageConfigUriSync; if (hook == null) { throw UnsupportedError("Isolate.packageConfig"); } return hook(); } @patch static Future resolvePackageUri(Uri packageUri) { return Future.value(resolvePackageUriSync(packageUri)); } @patch static Uri? resolvePackageUriSync(Uri packageUri) { var hook = VMLibraryHooks.resolvePackageUriSync; if (hook == null) { throw UnsupportedError("Isolate.resolvePackageUriSync"); } return hook(packageUri); } static bool _packageSupported() => (VMLibraryHooks.packageConfigUriSync != null) && (VMLibraryHooks.resolvePackageUriSync != null); @patch static Future spawn( void entryPoint(T message), T message, { bool paused = false, bool errorsAreFatal = true, SendPort? onExit, SendPort? onError, String? debugName, }) async { // The VM will invoke [_startIsolate] with [entryPoint] as argument. // We do not inherit the package config settings from the parent isolate, // instead we use the values that were set on the command line. var packageConfig = VMLibraryHooks.packageConfigString; var script = VMLibraryHooks.platformScript; if (script == null) { // We do not have enough information to support spawning the new // isolate. throw UnsupportedError("Isolate.spawn"); } if (script.isScheme("package")) { if (Isolate._packageSupported()) { // resolving script uri is not really necessary, but can be useful // for better failed-to-lookup-function-in-a-script spawn errors. script = Isolate.resolvePackageUriSync(script); } } final RawReceivePort readyPort = RawReceivePort( null, 'Isolate.spawn ready', ); try { _spawnFunction( readyPort.sendPort, script.toString(), entryPoint, message, paused, errorsAreFatal, onExit, onError, packageConfig, debugName, ); return await _spawnCommon(readyPort); } catch (e, st) { readyPort.close(); return await Future.error(e, st); } } @pragma("vm:external-name", "Isolate_spawnFunction") external static void _spawnFunction( SendPort readyPort, String uri, Function topLevelFunction, message, bool paused, bool errorsAreFatal, SendPort? onExit, SendPort? onError, String? packageConfig, String? debugName, ); @patch static Future spawnUri( Uri uri, List args, message, { bool paused = false, SendPort? onExit, SendPort? onError, bool errorsAreFatal = true, bool? checked, Map? environment, Uri? packageRoot, Uri? packageConfig, bool automaticPackageResolution = false, String? debugName, }) async { if (environment != null) { throw UnimplementedError("environment"); } // Verify that no mutually exclusive arguments have been passed. if (automaticPackageResolution) { if (packageRoot != null) { throw ArgumentError( "Cannot simultaneously request " "automaticPackageResolution and specify a " "packageRoot.", ); } if (packageConfig != null) { throw ArgumentError( "Cannot simultaneously request " "automaticPackageResolution and specify a " "packageConfig.", ); } } else { if ((packageRoot != null) && (packageConfig != null)) { throw ArgumentError( "Cannot simultaneously specify a " "packageRoot and a packageConfig.", ); } } // Resolve the uri against the current isolate's root Uri first. final Uri spawnedUri = _rootUri!.resolveUri(uri); // Inherit this isolate's package resolution setup if not overridden. if (!automaticPackageResolution && packageConfig == null) { if (Isolate._packageSupported()) { packageConfig = Isolate.packageConfigSync; } } // Ensure to resolve package: URIs being handed in as parameters. if (packageConfig != null) { // Avoid calling resolvePackageUri if not strictly necessary in case // the API is not supported. if (packageConfig.isScheme("package")) { packageConfig = Isolate.resolvePackageUriSync(packageConfig); } } // The VM will invoke [_startIsolate] and not `main`. final packageConfigString = packageConfig?.toString(); final RawReceivePort readyPort = RawReceivePort( null, 'Isolate.spawnUri ready', ); final enableAsserts = checked; try { _spawnUri( readyPort.sendPort, spawnedUri.toString(), args, message, paused, onExit, onError, errorsAreFatal, enableAsserts, null, /* environment */ packageConfigString, debugName, ); return await _spawnCommon(readyPort); } catch (e) { readyPort.close(); rethrow; } } static Future _spawnCommon(RawReceivePort readyPort) { final completer = Completer.sync(); readyPort.handler = (readyMessage) { readyPort.close(); if (readyMessage is List && readyMessage.length == 2) { SendPort controlPort = readyMessage[0]; List capabilities = readyMessage[1]; completer.complete( Isolate( controlPort, pauseCapability: capabilities[0], terminateCapability: capabilities[1], ), ); } else if (readyMessage is String) { // We encountered an error while starting the new isolate. completer.completeError( IsolateSpawnException('Unable to spawn isolate: ${readyMessage}'), ); } else { // This shouldn't happen. completer.completeError( IsolateSpawnException( "Internal error: unexpected format for ready message: " "'${readyMessage}'", ), ); } }; return completer.future; } // TODO(iposva): Cleanup to have only one definition. // These values need to be kept in sync with the class IsolateMessageHandler // in vm/isolate.cc. static const _PAUSE = 1; static const _RESUME = 2; static const _PING = 3; static const _KILL = 4; static const _ADD_EXIT = 5; static const _DEL_EXIT = 6; static const _ADD_ERROR = 7; static const _DEL_ERROR = 8; static const _ERROR_FATAL = 9; // For 'spawnFunction' see internal_patch.dart. @pragma("vm:external-name", "Isolate_spawnUri") external static void _spawnUri( SendPort readyPort, String uri, List args, message, bool paused, SendPort? onExit, SendPort? onError, bool errorsAreFatal, bool? enableAsserts, List? environment, String? packageConfig, String? debugName, ); @pragma("vm:external-name", "Isolate_sendOOB") external static void _sendOOB(port, msg); @pragma("vm:external-name", "Isolate_getDebugName") external static String? _getDebugName(SendPort controlPort); @patch void _pause(Capability resumeCapability) { // _sendOOB expects a fixed length array and hence we create a fixed // length array and assign values to it instead of using [ ... ]. var msg = List.filled(4, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _PAUSE ..[2] = pauseCapability ..[3] = resumeCapability; _sendOOB(controlPort, msg); } @patch void resume(Capability resumeCapability) { var msg = List.filled(4, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _RESUME ..[2] = pauseCapability ..[3] = resumeCapability; _sendOOB(controlPort, msg); } @patch void addOnExitListener(SendPort responsePort, {Object? response}) { var msg = List.filled(4, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _ADD_EXIT ..[2] = responsePort ..[3] = response; _sendOOB(controlPort, msg); } @patch void removeOnExitListener(SendPort responsePort) { var msg = List.filled(3, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _DEL_EXIT ..[2] = responsePort; _sendOOB(controlPort, msg); } @patch void setErrorsFatal(bool errorsAreFatal) { var msg = List.filled(4, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _ERROR_FATAL ..[2] = terminateCapability ..[3] = errorsAreFatal; _sendOOB(controlPort, msg); } @patch void kill({int priority = beforeNextEvent}) { var msg = List.filled(4, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _KILL ..[2] = terminateCapability ..[3] = priority; _sendOOB(controlPort, msg); } @patch void ping( SendPort responsePort, { Object? response, int priority = immediate, }) { var msg = List.filled(5, null) ..[0] = 0 // Make room for OOM message type. ..[1] = _PING ..[2] = responsePort ..[3] = priority ..[4] = response; _sendOOB(controlPort, msg); } @patch void addErrorListener(SendPort port) { var msg = List.filled(3, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _ADD_ERROR ..[2] = port; _sendOOB(controlPort, msg); } @patch void removeErrorListener(SendPort port) { var msg = List.filled(3, null) ..[0] = 0 // Make room for OOB message type. ..[1] = _DEL_ERROR ..[2] = port; _sendOOB(controlPort, msg); } static Isolate _getCurrentIsolate() { List portAndCapabilities = _getPortAndCapabilitiesOfCurrentIsolate(); return Isolate( portAndCapabilities[0], pauseCapability: portAndCapabilities[1], terminateCapability: portAndCapabilities[2], ); } @pragma("vm:external-name", "Isolate_getPortAndCapabilitiesOfCurrentIsolate") external static List _getPortAndCapabilitiesOfCurrentIsolate(); static Uri? _getCurrentRootUri() { try { return Uri.parse(_getCurrentRootUriStr()); } catch (e) { return null; } } @pragma("vm:external-name", "Isolate_getCurrentRootUriStr") external static String _getCurrentRootUriStr(); @pragma("vm:external-name", "Isolate_exit_") external static Never _exit(SendPort? finalMessagePort, Object? message); @pragma("vm:entry-point") static bool _mayExit = true; @patch static Never exit([SendPort? finalMessagePort, Object? message]) { if (!_mayExit) { throw UnsupportedError("Isolate.exit"); } _exit(finalMessagePort, message); } } @patch @pragma("vm:entry-point") abstract final class TransferableTypedData { @patch factory TransferableTypedData.fromList(List chunks) { final int cid = ClassID.getID(chunks); if (cid != ClassID.cidArray && cid != ClassID.cidGrowableObjectArray && cid != ClassID.cidImmutableArray) { chunks = List.unmodifiable(chunks); } return _TransferableTypedDataImpl(chunks); } } @pragma("vm:entry-point") final class _TransferableTypedDataImpl implements TransferableTypedData { @pragma("vm:external-name", "TransferableTypedData_factory") external factory _TransferableTypedDataImpl(List list); ByteBuffer materialize() { return _materializeIntoUint8List().buffer; } @pragma("vm:external-name", "TransferableTypedData_materialize") external Uint8List _materializeIntoUint8List(); } ‚üNN<=Q'(@: O C9 )*4"#53'9 5HF G9E,@53"(&$ !&#G/M'2 +1("&.&:'"1$8./%88 :,$<"N.7?$"#4$%/$+6J!$;>!KK:):$;#$;,&*-4,($H%17?8($4Q "B<OMF8"*Q "E!!!+11( 70 1 7 *, &47 :< 65?%76 #   IND<0G/&)KJ85   ,17'  #  &- /E&!+6 #+6  >-. E6J@)3F!K#/F:;5 #  ,B1)>0-.2 +C!M "!!D! 8O!22" 1,6? -IF,/  -,/  F,/  5,/  -,/"  0,/"  ,/  ),/  ,,/ (J/3PB% 1>2/L E.&- C+#1,*/J?E/C2%dart:isolate-patch/isolate_patch.dartÀeÀh³Lorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/isolate/capability.dart…// Copyright (c) 2014, 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:isolate"; /// An unforgeable object that comes back as equal when passed through other /// isolates. /// /// Sending a capability object to another isolate, and getting it back, /// will produce an object that is equal to the original. /// There is no other way to create objects equal to a capability object. /// /// Capabilities can be used as access guards. /// An isolate can receive requests for operations from other isolates, /// but only allow them if the request contains the correct capability object. /// This allows exposing the same interface to multiple clients, /// but restricting some operations to only those clients /// that have also been given the corresponding capability. /// /// Capabilities can be used inside a single isolate, /// but they have no advantage over /// just using `Object()` to create a unique object, /// and it offers no real security against other code /// running in the same isolate. abstract interface class Capability { /// Create a new unforgeable capability object. external factory Capability(); } NN<MI:J/HOA:<6$56!&2!dart:isolate/capability.dartUorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/timer_impl.dart¾é// Copyright (c) 2013, 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 "isolate_patch.dart"; /// Posts a VM Service event to the 'Timer' stream of kind /// 'TimerSignificantlyOverdue'. The event will contain a 'details' property /// whose value will be a message reporting that a timer was /// [milliSecondsOverdue] ms overdue. @pragma("vm:external-name", "Timer_postTimerEvent") external void _postTimerEvent(int millisecondsOverdue); // Timer heap implemented as a array-based binary heap[0]. // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) // `add`. // // To ensure the timers are ordered by insertion time, the _Timer class has a // `_id` field set when added to the heap. // // [0] http://en.wikipedia.org/wiki/Binary_heap class _TimerHeap { List<_Timer> _list; int _used = 0; _TimerHeap([int initSize = 7]) : _list = List<_Timer>.filled(initSize, _Timer._sentinelTimer); bool get isEmpty => _used == 0; _Timer get first => _list[0]; bool isFirst(_Timer timer) => timer._indexOrNext == 0; void add(_Timer timer) { if (_used == _list.length) { _resize(); } var index = _used++; timer._indexOrNext = index; _list[index] = timer; _bubbleUp(timer); } _Timer removeFirst() { var f = first; remove(f); return f; } void remove(_Timer timer) { _used--; if (isEmpty) { _list[0] = _Timer._sentinelTimer; timer._indexOrNext = null; return; } var last = _list[_used]; if (!identical(last, timer)) { var index = timer._indexOrNext as int; last._indexOrNext = index; _list[index] = last; if (last._compareTo(timer) < 0) { _bubbleUp(last); } else { _bubbleDown(last); } } _list[_used] = _Timer._sentinelTimer; timer._indexOrNext = null; } void _resize() { var newList = List<_Timer>.filled( _list.length * 2 + 1, _Timer._sentinelTimer, ); newList.setRange(0, _used, _list); _list = newList; } void _bubbleUp(_Timer timer) { while (!isFirst(timer)) { _Timer parent = _parent(timer); if (timer._compareTo(parent) < 0) { _swap(timer, parent); } else { break; } } } void _bubbleDown(_Timer timer) { while (true) { var leftIndex = _leftChildIndex(timer._indexOrNext as int); var rightIndex = _rightChildIndex(timer._indexOrNext as int); _Timer newest = timer; if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) { newest = _list[leftIndex]; } if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) { newest = _list[rightIndex]; } if (identical(newest, timer)) { // We are where we should be, break. break; } _swap(newest, timer); } } void _swap(_Timer first, _Timer second) { var newFirstIndex = second._indexOrNext as int; var newSecondIndex = first._indexOrNext as int; first._indexOrNext = newFirstIndex; second._indexOrNext = newSecondIndex; _list[newFirstIndex] = first; _list[newSecondIndex] = second; } _Timer _parent(_Timer timer) => _list[_parentIndex(timer._indexOrNext as int)]; static int _parentIndex(int index) => (index - 1) ~/ 2; static int _leftChildIndex(int index) => 2 * index + 1; static int _rightChildIndex(int index) => 2 * index + 2; } class _Timer implements Timer { // Cancels the timer in the event handler. static const _NO_TIMER = -1; // A generic null timer object that is used to populate unused slots // in TimerHeap. static final _sentinelTimer = _Timer._sentinel(); // We distinguish what kind of message arrived based on the value being sent. static const _ZERO_EVENT = 1; static const _TIMEOUT_EVENT = null; // Timers are ordered by wakeup time. Timers with a timeout value of > 0 do // end up on the TimerHeap. Timers with a timeout of 0 are queued in a list. static final _heap = _TimerHeap(); static _Timer? _firstZeroTimer; static _Timer _lastZeroTimer = _sentinelTimer; // We use an id to be able to sort timers with the same expiration time. // ids are recycled after ID_MASK enqueues or when the timer queue is empty. static const _ID_MASK = 0x1fffffff; static int _idCount = 0; static _RawReceivePort? _receivePort; static SendPort? _sendPort; static bool _receivePortActive = false; static int _scheduledWakeupTime = 0; static bool _handlingCallbacks = false; void Function(Timer)? _callback; // Closure to call when timer fires. null if canceled. int _wakeupTime; // Expiration time. final int _milliSeconds; // Duration specified at creation. final bool _repeating; // Indicates periodic timers. Object? _indexOrNext; // Index if part of the TimerHeap, link otherwise. int _id; // Incrementing id to enable sorting of timers with same expiry. int _tick = 0; // Backing for [tick], // Get the next available id. We accept collisions and reordering when the // _idCount overflows and the timers expire at the same millisecond. static int _nextId() { var result = _idCount; _idCount = (_idCount + 1) & _ID_MASK; return result; } _Timer._sentinel() : _callback = null, _wakeupTime = 0, _milliSeconds = 0, _repeating = false, _indexOrNext = null, _id = -1; _Timer._internal( this._callback, this._wakeupTime, this._milliSeconds, this._repeating, ) : _id = _nextId(); static _Timer _createTimer( void callback(Timer timer), int milliSeconds, bool repeating, ) { // Negative timeouts are treated as if 0 timeout. if (milliSeconds < 0) { milliSeconds = 0; } // Add one because DateTime.now() is assumed to round down // to nearest millisecond, not up, so that time + duration is before // duration milliseconds from now. Using microsecond timers like // Stopwatch allows detecting that the timer fires early. int now = VMLibraryHooks.timerMillisecondClock(); int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); _Timer timer = _Timer._internal( callback, wakeupTime, milliSeconds, repeating, ); // Enqueue this newly created timer in the appropriate structure and // notify if necessary. timer._enqueue(); return timer; } factory _Timer(int milliSeconds, void callback(Timer timer)) { return _createTimer(callback, milliSeconds, false); } factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { return _createTimer(callback, milliSeconds, true); } bool get _isInHeap => _indexOrNext is int; int _compareTo(_Timer other) { int c = _wakeupTime - other._wakeupTime; if (c != 0) return c; return _id - other._id; } bool get isActive => _callback != null; int get tick => _tick; // Cancels a set timer. The timer is removed from the timer heap if it is a // non-zero timer. Zero timers are kept in the list as they need to consume // the corresponding pending message. void cancel() { _callback = null; // Only heap timers are really removed. Zero timers need to consume their // corresponding wakeup message so they are left in the queue. if (!_isInHeap) return; bool update = _heap.isFirst(this); _heap.remove(this); if (update) { _notifyEventHandler(); } } void _advanceWakeupTime() { // Recalculate the next wakeup time. For repeating timers with a 0 timeout // the next wakeup time is now. _id = _nextId(); if (_milliSeconds > 0) { _wakeupTime += _milliSeconds; } else { _wakeupTime = VMLibraryHooks.timerMillisecondClock(); } } // Adds a timer to the heap or timer list. Timers with the same wakeup time // are enqueued in order and notified in FIFO order. void _enqueue() { if (_milliSeconds == 0) { if (_firstZeroTimer == null) { _lastZeroTimer = this; _firstZeroTimer = this; } else { _lastZeroTimer._indexOrNext = this; _lastZeroTimer = this; } // Every zero timer gets its own event. _notifyZeroHandler(); } else { _heap.add(this); if (_heap.isFirst(this)) { _notifyEventHandler(); } } } // Enqueue one message for each zero timer. To be able to distinguish from // EventHandler messages we send a _ZERO_EVENT instead of a _TIMEOUT_EVENT. static void _notifyZeroHandler() { if (!_receivePortActive) { _createTimerHandler(); } _sendPort!.send(_ZERO_EVENT); } // Handle the notification of a zero timer. Make sure to also execute non-zero // timers with a lower expiration time. static List<_Timer> _queueFromZeroEvent() { var pendingTimers = <_Timer>[]; final firstTimer = _firstZeroTimer; if (firstTimer != null) { // Collect pending timers from the timer heap that have an expiration prior // to the currently notified zero timer. _Timer timer; while (!_heap.isEmpty && (_heap.first._compareTo(firstTimer) < 0)) { timer = _heap.removeFirst(); pendingTimers.add(timer); } // Append the first zero timer to the pending timers. timer = firstTimer; _firstZeroTimer = timer._indexOrNext as _Timer?; timer._indexOrNext = null; pendingTimers.add(timer); } return pendingTimers; } static void _notifyEventHandler() { if (_handlingCallbacks) { // While we are already handling callbacks we will not notify the event // handler. _handleTimeout will call _notifyEventHandler once all pending // timers are processed. return; } // If there are no pending timers. Close down the receive port. if ((_firstZeroTimer == null) && _heap.isEmpty) { // No pending timers: Close the receive port and let the event handler // know. if (_sendPort != null) { _cancelWakeup(); _shutdownTimerHandler(); } return; } else if (_heap.isEmpty) { // Only zero timers are left. Cancel any scheduled wakeups. _cancelWakeup(); return; } // Only send a message if the requested wakeup time differs from the // already scheduled wakeup time. var wakeupTime = _heap.first._wakeupTime; if ((_scheduledWakeupTime == 0) || (wakeupTime != _scheduledWakeupTime)) { _scheduleWakeup(wakeupTime); } } static List<_Timer> _queueFromTimeoutEvent() { var pendingTimers = <_Timer>[]; final firstTimer = _firstZeroTimer; if (firstTimer != null) { // Collect pending timers from the timer heap that have an expiration // prior to the next zero timer. // By definition the first zero timer has been scheduled before the // current time, meaning all timers which are "less than" the first zero // timer are expired. The first zero timer will be dispatched when its // corresponding message is delivered. while (!_heap.isEmpty && (_heap.first._compareTo(firstTimer) < 0)) { var timer = _heap.removeFirst(); pendingTimers.add(timer); } } else { // Collect pending timers from the timer heap which have expired at this // time. var currentTime = VMLibraryHooks.timerMillisecondClock(); while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) { var timer = _heap.removeFirst(); pendingTimers.add(timer); } } return pendingTimers; } static void _runTimers(List<_Timer> pendingTimers) { // If there are no pending timers currently reset the id space before we // have a chance to enqueue new timers. if (_heap.isEmpty && (_firstZeroTimer == null)) { _idCount = 0; } // Fast exit if no pending timers. if (pendingTimers.length == 0) { return; } // Trigger all of the pending timers. New timers added as part of the // callbacks will be enqueued now and notified in the next spin at the // earliest. _handlingCallbacks = true; var i = 0; try { for (; i < pendingTimers.length; i++) { // Next pending timer. var timer = pendingTimers[i]; timer._indexOrNext = null; final millisecondsOverdue = VMLibraryHooks.timerMillisecondClock() - timer._wakeupTime; if (!const bool.fromEnvironment("dart.vm.product") && millisecondsOverdue >= 100) { _postTimerEvent(millisecondsOverdue); } // One of the timers in the pending_timers list can cancel // one of the later timers which will set the callback to // null. Or the pending zero timer has been canceled earlier. var callback = timer._callback; if (callback != null) { if (!timer._repeating) { // Mark timer as inactive. timer._callback = null; } else if (timer._milliSeconds > 0) { var ms = timer._milliSeconds; if (millisecondsOverdue > ms) { int missedTicks = millisecondsOverdue ~/ ms; timer._wakeupTime += missedTicks * ms; timer._tick += missedTicks; } } timer._tick += 1; callback(timer); // Re-insert repeating timer if not canceled. if (timer._repeating && (timer._callback != null)) { timer._advanceWakeupTime(); timer._enqueue(); } // Execute pending micro tasks. _runPendingImmediateCallback(); } } } finally { _handlingCallbacks = false; // Re-queue timers we didn't get to. for (i++; i < pendingTimers.length; i++) { var timer = pendingTimers[i]; timer._enqueue(); } _notifyEventHandler(); } } static void _handleMessage(msg) { List<_Timer> pendingTimers; if (msg == _ZERO_EVENT) { pendingTimers = _queueFromZeroEvent(); assert(pendingTimers.length > 0); } else { assert(msg == _TIMEOUT_EVENT); _scheduledWakeupTime = 0; // Consumed the last scheduled wakeup now. pendingTimers = _queueFromTimeoutEvent(); } _runTimers(pendingTimers); // Notify the event handler or shutdown the port if no more pending // timers are present. _notifyEventHandler(); } // Tell the event handler to wake this isolate at a specific time. static void _scheduleWakeup(int wakeupTime) { if (!_receivePortActive) { _createTimerHandler(); } VMLibraryHooks.eventHandlerSendData(null, _sendPort!, wakeupTime); _scheduledWakeupTime = wakeupTime; } // Cancel pending wakeups in the event handler. static void _cancelWakeup() { if (_sendPort != null) { VMLibraryHooks.eventHandlerSendData(null, _sendPort!, _NO_TIMER); _scheduledWakeupTime = 0; } } // Create a receive port and register a message handler for the timer // events. static void _createTimerHandler() { var receivePort = _receivePort; if (receivePort == null) { assert(_sendPort == null); final port = _RawReceivePort('Timer'); port.handler = _handleMessage; _sendPort = port.sendPort; _receivePort = port; _scheduledWakeupTime = 0; } else { receivePort._setActive(true); } _receivePortActive = true; } static void _shutdownTimerHandler() { _scheduledWakeupTime = 0; _receivePort!._setActive(false); _receivePortActive = false; } // The Timer factory registered with the dart:async library by the embedder. static Timer _factory( int milliSeconds, void callback(Timer timer), bool repeating, ) { if (repeating) { return _Timer.periodic(milliSeconds, callback); } return _Timer(milliSeconds, callback); } } @pragma("vm:entry-point", "call") _setupHooks() { VMLibraryHooks.timerFactory = _Timer._factory; } ‚NN<;M=&48;P N+0!D" 9!  (!#-!(*''!&*#BDJ#L$&-,44(*"$"6::; -G4P &NO%"1KO&(*'*D'>7KL(MG* 6?IE>6K%IA8J7-!-*NN(NC'O$$ <N7% ,. !MN%"Q*.$(R/K%"<7! &NPD6M! BI&.O#1$(L'JOM-K)" O@K)"7M,6'%JK .&#$H>*0 CBF( #'$0*,;5* 8?( ** "+1&$ -( %K0HE0G'2 H H &$!-%! $(% O 6+"1"dart:isolate-patch/timer_impl.dartÀeÀh³Corg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/math/math.dart¢�// 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. /// Mathematical constants and functions, plus a random number generator. /// /// To use this library in your code: /// ```dart /// import 'dart:math'; /// ``` /// /// ## Random /// [Random] is a generator of [bool], [int] or [double] values. /// ```dart /// var intValue = Random().nextInt(10); // Value is >= 0 and < 10. /// var doubleValue = Random().nextDouble(); // Value is >= 0.0 and < 1.0. /// var boolValue = Random().nextBool(); // true or false, with equal chance. /// ``` /// /// ## Point /// [Point] is a utility class for representing two-dimensional positions. /// ```dart /// var leftTop = const Point(0, 0); /// var rightBottom = const Point(200, 400); /// ``` /// /// ## Rectangle /// [Rectangle] is a class for representing two-dimensional axis-aligned /// rectangles whose properties are immutable. /// /// Create a rectangle spanned by the points. /// ```dart /// var leftTop = const Point(20, 50); /// var rightBottom = const Point(300, 600); /// var rectangle = Rectangle.fromPoints(leftTop, rightBottom); /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 300 /// print(rectangle.bottom); // 600 /// ``` /// /// Create a rectangle spanned by `(left, top)` and /// `(left+width, top+height)`. /// ```dart /// var rectangle = const Rectangle(20, 50, 300, 600); /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 320 /// print(rectangle.bottom); // 650 /// ``` /// /// ## MutableRectangle /// [MutableRectangle] is a class for representing two-dimensional axis-aligned /// rectangles with mutable properties. /// /// Create a mutable rectangle spanned by `(left, top)` and /// `(left+width, top+height)`. /// ```dart /// var rectangle = MutableRectangle(20, 50, 300, 600); /// print(rectangle); // Rectangle (20, 50) 300 x 600 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 320 /// print(rectangle.bottom); // 650 /// /// // Change rectangle width and height. /// rectangle.width = 200; /// rectangle.height = 100; /// print(rectangle); // Rectangle (20, 50) 200 x 100 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 220 /// print(rectangle.bottom); // 150 /// ``` /// /// {@category Core} library dart.math; import "dart:_internal" show SystemHash; part "point.dart"; part "random.dart"; part "rectangle.dart"; /// Base of the natural logarithms. /// /// Typically written as "e". const double e = 2.718281828459045; /// Natural logarithm of 10. /// /// The natural logarithm of 10 is the number such that `pow(E, LN10) == 10`. /// This value is not exact, but it is the closest representable double to the /// exact mathematical value. const double ln10 = 2.302585092994046; /// Natural logarithm of 2. /// /// The natural logarithm of 2 is the number such that `pow(E, LN2) == 2`. /// This value is not exact, but it is the closest representable double to the /// exact mathematical value. const double ln2 = 0.6931471805599453; /// Base-2 logarithm of [e]. const double log2e = 1.4426950408889634; /// Base-10 logarithm of [e]. const double log10e = 0.4342944819032518; /// The PI constant. const double pi = 3.1415926535897932; /// Square root of 1/2. const double sqrt1_2 = 0.7071067811865476; /// Square root of 2. const double sqrt2 = 1.4142135623730951; /// Returns the lesser of two numbers. /// /// Returns NaN if either argument is NaN. /// The lesser of `-0.0` and `0.0` is `-0.0`. /// If the arguments are otherwise equal (including int and doubles with the /// same mathematical value) then it is unspecified which of the two arguments /// is returned. external T min(T a, T b); /// Returns the larger of two numbers. /// /// Returns NaN if either argument is NaN. /// The larger of `-0.0` and `0.0` is `0.0`. If the arguments are /// otherwise equal (including int and doubles with the same mathematical value) /// then it is unspecified which of the two arguments is returned. external T max(T a, T b); /// A variant of [atan]. /// /// Converts both arguments to [double]s. /// /// Returns the angle in radians between the positive x-axis /// and the vector ([b],[a]). /// The result is in the range -PI..PI. /// /// If [b] is positive, this is the same as `atan(a/b)`. /// /// The result is negative when [a] is negative (including when [a] is the /// double -0.0). /// /// If [a] is equal to zero, the vector ([b],[a]) is considered parallel to /// the x-axis, even if [b] is also equal to zero. The sign of [b] determines /// the direction of the vector along the x-axis. /// /// Returns NaN if either argument is NaN. external double atan2(num a, num b); /// Returns [x] to the power of [exponent]. /// /// If [x] is an [int] and [exponent] is a non-negative [int], the result is /// an [int], otherwise both arguments are converted to doubles first, and the /// result is a [double]. /// /// For integers, the power is always equal to the mathematical result of `x` to /// the power `exponent`, only limited by the available memory. /// /// For doubles, `pow(x, y)` handles edge cases as follows: /// /// - if `y` is zero (0.0 or -0.0), the result is always 1.0. /// - if `x` is 1.0, the result is always 1.0. /// - otherwise, if either `x` or `y` is NaN, then the result is NaN. /// - if `x` is negative (but not -0.0) and `y` is a finite non-integer, the /// result is NaN. /// - if `x` is Infinity and `y` is negative, the result is 0.0. /// - if `x` is Infinity and `y` is positive, the result is Infinity. /// - if `x` is 0.0 and `y` is negative, the result is Infinity. /// - if `x` is 0.0 and `y` is positive, the result is 0.0. /// - if `x` is -Infinity or -0.0 and `y` is an odd integer, then the result is /// `-pow(-x ,y)`. /// - if `x` is -Infinity or -0.0 and `y` is not an odd integer, then the result /// is the same as `pow(-x , y)`. /// - if `y` is Infinity and the absolute value of `x` is less than 1, the /// result is 0.0. /// - if `y` is Infinity and `x` is -1, the result is 1.0. /// - if `y` is Infinity and the absolute value of `x` is greater than 1, /// the result is Infinity. /// - if `y` is -Infinity, the result is `1/pow(x, Infinity)`. /// /// This corresponds to the `pow` function defined in the IEEE Standard /// 754-2008. /// /// Notice that the result may overflow. If integers are represented as 64-bit /// numbers, an integer result may be truncated, and a double result may /// overflow to positive or negative [double.infinity]. external num pow(num x, num exponent); /// Converts [radians] to a [double] and returns the sine of the value. /// /// If [radians] is not a finite number, the result is NaN. external double sin(num radians); /// Converts [radians] to a [double] and returns the cosine of the value. /// /// If [radians] is not a finite number, the result is NaN. external double cos(num radians); /// Converts [radians] to a [double] and returns the tangent of the value. /// /// The tangent function is equivalent to `sin(radians)/cos(radians)` and may be /// infinite (positive or negative) when `cos(radians)` is equal to zero. /// If [radians] is not a finite number, the result is NaN. external double tan(num radians); /// Converts [x] to a [double] and returns its arc cosine in radians. /// /// Returns a value in the range 0..PI, or NaN if [x] is outside /// the range -1..1. external double acos(num x); /// Converts [x] to a [double] and returns its arc sine in radians. /// /// Returns a value in the range -PI/2..PI/2, or NaN if [x] is outside /// the range -1..1. external double asin(num x); /// Converts [x] to a [double] and returns its arc tangent in radians. /// /// Returns a value in the range -PI/2..PI/2, or NaN if [x] is NaN. external double atan(num x); /// Converts [x] to a [double] and returns the positive square root of the /// value. /// /// Returns -0.0 if [x] is -0.0, and NaN if [x] is otherwise negative or NaN. /// ```dart /// var result = sqrt(9.3); /// print(result); // 3.0495901363953815 /// result = sqrt(2); /// print(result); // 1.4142135623730951 /// result = sqrt(0); /// print(result); // 0.0 /// result = sqrt(-2.2); /// print(result); // NaN /// ``` external double sqrt(num x); /// Converts [x] to a [double] and returns the natural exponent, [e], /// to the power [x]. /// /// Returns NaN if [x] is NaN. external double exp(num x); /// Converts [x] to a [double] and returns the natural logarithm of the value. /// /// Returns negative infinity if [x] is equal to zero. /// Returns NaN if [x] is NaN or less than zero. external double log(num x); �NN<J& A DKN K %-I/. '-@! #$4 7! #$P(< 86! #$*6! #$)$$NO'KO')*&+)'+.MO)'+BQC)*=(9KLN2+%,MOQ@<>/FMAFA { final T x; final T y; /// Creates a point with the provided [x] and [y] coordinates. /// /// **Legacy:** New usages of [Point] are discouraged. /// To learn more, check out the [Point] class API docs. const Point(T x, T y) : this.x = x, this.y = y; String toString() => 'Point($x, $y)'; /// Whether [other] is a point with the same coordinates as this point. /// /// Returns `true` if [other] is a [Point] with [x] and [y] /// coordinates equal to the corresponding coordinates of this point, /// and `false` otherwise. /// /// Example: /// ```dart /// var result = const Point(0, 0) == const Point(0, 0); // true /// result = const Point(1.0, 0) == const Point(-1.0, 0); // false /// ``` bool operator ==(Object other) => other is Point && x == other.x && y == other.y; int get hashCode => SystemHash.hash2(x.hashCode, y.hashCode, 0); /// Add [other] to `this`, as if both points were vectors. /// /// Returns the resulting "vector" as a Point. /// /// Example: /// ```dart /// var point = const Point(10, 100) + const Point(10, 10); // Point(20, 110) /// point = const Point(-10, -20) + const Point(10, 100); // Point(0, 80) /// ``` Point operator +(Point other) { return Point((x + other.x) as T, (y + other.y) as T); } /// Subtract [other] from `this`, as if both points were vectors. /// /// Returns the resulting "vector" as a Point. /// /// Example: /// ```dart /// var point = const Point(10, 100) - const Point(10, 10); // Point(0, 90) /// point = const Point(-10, -20) - const Point(100, 100); // Point(-110, -120) /// ``` Point operator -(Point other) { return Point((x - other.x) as T, (y - other.y) as T); } /// Scale this point by [factor] as if it were a vector. /// /// **Important Note**: This function accepts a `num` as its argument only so /// that you can scale `Point` objects by an `int` factor. Because the /// `*` operator always returns the same type of `Point` as it is called on, /// passing in a double [factor] on a `Point` _causes_ _a_ /// _runtime_ _error_. /// /// Example: /// ```dart /// // Integer values. /// var point = const Point(10, 100) * 10; // Point(100, 1000) /// point = const Point(-10, -100) * 5; // Point(-50, -500) /// // Double values. /// var doublePoint = Point(10.0, 100.0) * 1.5; // Point(15.0, 150.0) /// // Runtime error due the invalid type cast. /// var newPoint = const Point(10, 100) * 1.5; // Throws. /// ``` Point operator *(num /*T|int*/ factor) { return Point((x * factor) as T, (y * factor) as T); } /// Get the straight line (Euclidean) distance between the origin (0, 0) and /// this point. /// /// Example: /// ```dart /// var magnitude = const Point(0, 0).magnitude; // 0.0 /// magnitude = const Point(10, 0).magnitude; // 10.0 /// magnitude = const Point(0, -10).magnitude; // 10.0 /// magnitude = const Point(10, 10).magnitude; // 14.142135623730951 /// ``` double get magnitude => sqrt(x * x + y * y); /// Returns the distance between `this` and [other]. /// ```dart /// var distanceTo = const Point(0, 0).distanceTo(const Point(0, 0)); // 0.0 /// distanceTo = const Point(0, 0).distanceTo(const Point(10, 0)); // 10.0 /// distanceTo = const Point(0, 0).distanceTo(const Point(0, -10)); // 10.0 /// distanceTo = const Point(-10, 0).distanceTo(const Point(100, 0)); // 110.0 /// ``` double distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } /// Returns the squared distance between `this` and [other]. /// /// Squared distances can be used for comparisons when the actual value is not /// required. /// /// Example: /// ```dart /// var squaredDistance = /// const Point(0, 0).squaredDistanceTo(const Point(0, 0)); // 0.0 /// squaredDistance = /// const Point(0, 0).squaredDistanceTo(const Point(10, 0)); // 100 /// squaredDistance = /// const Point(0, 0).squaredDistanceTo(const Point(0, -10)); // 100 /// squaredDistance = /// const Point(-10, 0).squaredDistanceTo(const Point(100, 0)); // 12100 /// ``` T squaredDistanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return (dx * dx + dy * dy) as T; } } €ŸNN<@ %-7;/'H8D8?056M>LO A9;2(J>HCE $6C=1PL (=D1NR (=;PQOBA>H2< .;O:99H /7OMNQ &$?QIJKO (%dart:math/point.dartEorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/math/random.dart‹%// 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:math"; /// A generator of random bool, int, or double values. /// /// The default implementation supplies a stream of pseudo-random bits that are /// not suitable for cryptographic purposes. /// /// Use the [Random.secure] constructor for cryptographic purposes. /// /// To create a non-negative random integer uniformly distributed in the range /// from 0, inclusive, to max, exclusive, use [nextInt(int max)]. /// ```dart /// var intValue = Random().nextInt(10); // Value is >= 0 and < 10. /// intValue = Random().nextInt(100) + 50; // Value is >= 50 and < 150. /// ``` /// To create a non-negative random floating point value uniformly distributed /// in the range from 0.0, inclusive, to 1.0, exclusive, use [nextDouble]. /// ```dart /// var doubleValue = Random().nextDouble(); // Value is >= 0.0 and < 1.0. /// doubleValue = Random().nextDouble() * 256; // Value is >= 0.0 and < 256.0. /// ``` /// To create a random Boolean value, use [nextBool]. /// ```dart /// var boolValue = Random().nextBool(); // true or false, with equal chance. /// ``` /// abstract interface class Random { /// Creates a random number generator. /// /// The optional parameter [seed] is used to initialize the /// internal state of the generator. The implementation of the /// random stream can change between releases of the library. external factory Random([int? seed]); /// Creates a cryptographically secure random number generator. /// /// If the program cannot provide a cryptographically secure /// source of random numbers, it throws an [UnsupportedError]. external factory Random.secure(); /// Generates a non-negative random integer uniformly distributed in the range /// from 0, inclusive, to [max], exclusive. /// /// Implementation note: The default implementation supports [max] values /// between 1 and (1<<32) inclusive. /// /// Example: /// ```dart /// var intValue = Random().nextInt(10); // Value is >= 0 and < 10. /// intValue = Random().nextInt(100) + 50; // Value is >= 50 and < 150. /// ``` int nextInt(int max); /// Generates a non-negative random floating point value uniformly distributed /// in the range from 0.0, inclusive, to 1.0, exclusive. /// /// Example: /// ```dart /// var doubleValue = Random().nextDouble(); // Value is >= 0.0 and < 1.0. /// doubleValue = Random().nextDouble() * 256; // Value is >= 0.0 and < 256.0. /// ``` double nextDouble(); /// Generates a random boolean value. /// /// Example: /// ```dart /// var boolValue = Random().nextBool(); // true or false, with equal chance. /// ``` bool nextBool(); } MNN<7P-DOB DHOK KO6 N")>A@(B?A$Q.L'FJ Q;MQ (P dart:math/random.dartUorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/math_patch.dart¤4// 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. /// Note: the VM concatenates all patch files into a single patch file. This /// file is the first patch in "dart:math" which contains all the imports used /// by patches of that library. We plan to change this when we have a shared /// front end and simply use parts. import "dart:_internal" show patch; import "dart:typed_data" show Uint32List; import "dart:_vm" show FinalThreadLocal; /// There are no parts of this patch library. @patch @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") T min(T a, T b) { if (a > b) return b; if (a < b) return a; if (b is double) { // Special case for NaN and -0.0. If one argument is NaN return NaN. // [min] must also distinguish between -0.0 and 0.0. if (a is double) { if (a == 0.0) { // a is either 0.0 or -0.0. b is either 0.0, -0.0 or NaN. // The following returns -0.0 if either a or b is -0.0, and it // returns NaN if b is NaN. num n = (a + b) * a * b; return n as T; } } // Check for NaN and b == -0.0. if (a == 0 && b.isNegative || b.isNaN) return b; return a; } return a; } @patch @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") T max(T a, T b) { if (a > b) return a; if (a < b) return b; if (b is double) { // Special case for NaN and -0.0. If one argument is NaN return NaN. // [max] must also distinguish between -0.0 and 0.0. if (a is double) { if (a == 0.0) { // a is either 0.0 or -0.0. b is either 0.0, -0.0, or NaN. // The following returns 0.0 if either a or b is 0.0, and it // returns NaN if b is NaN. num n = a + b; return n as T; } } // Check for NaN. if (b.isNaN) return b; return a; } // max(-0.0, 0) must return 0. if (b == 0 && a.isNegative) return b; return a; } // If [x] is an [int] and [exponent] is a non-negative [int], the result is // an [int], otherwise the result is a [double]. @patch @pragma("vm:prefer-inline") num pow(num x, num exponent) { if ((x is int) && (exponent is int) && (exponent >= 0)) { return _intPow(x, exponent); } return _doublePow(x.toDouble(), exponent.toDouble()); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Double") external double _doublePow(double base, double exponent); @pragma("vm:recognized", "other") int _intPow(int base, int exponent) { // Exponentiation by squaring. int result = 1; while (exponent != 0) { if ((exponent & 1) == 1) { result *= base; } exponent >>= 1; // Skip unnecessary operation (can overflow to Mint). if (exponent != 0) { base *= base; } } return result; } @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double atan2(num a, num b) => _atan2(a.toDouble(), b.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double sin(num radians) => _sin(radians.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double cos(num radians) => _cos(radians.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double tan(num radians) => _tan(radians.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double acos(num x) => _acos(x.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double asin(num x) => _asin(x.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double atan(num x) => _atan(x.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double sqrt(num x) => _sqrt(x.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double exp(num x) => _exp(x.toDouble()); @patch @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") double log(num x) => _log(x.toDouble()); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _atan2(double a, double b); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _sin(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _cos(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _tan(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _acos(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _asin(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _atan(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _sqrt(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _exp(double x); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") external double _log(double x); // TODO(iposva): Handle patch methods within a patch class correctly. @patch class Random { static final Random _secureRandom = _SecureRandom(); @patch factory Random([int? seed]) { var state = _Random._setupSeed((seed == null) ? _Random._nextSeed() : seed); // Crank a couple of times to distribute the seed bits a bit further. return _Random._withState(state) .._nextState() .._nextState() .._nextState() .._nextState(); } @patch factory Random.secure() => _secureRandom; } class _Random implements Random { int _state; _Random._withState(this._state); // The algorithm used here is Multiply with Carry (MWC) with a Base b = 2^32. // http://en.wikipedia.org/wiki/Multiply-with-carry // The constant A is selected from "Numerical Recipes 3rd Edition" p.348 B1. void _nextState() { const A = 0xffffda61; final state_lo = _state & 0xFFFFFFFF; final state_hi = _state >>> 32; _state = (A * state_lo) + state_hi; } int nextInt(int max) { const limit = 0x3FFFFFFF; if ((max <= 0) || ((max > limit) && (max > _POW2_32))) { throw RangeError.range( max, 1, _POW2_32, "max", "Must be positive and <= 2^32", ); } if ((max & -max) == max) { // Fast case for powers of two. _nextState(); return _state & 0xFFFFFFFF & (max - 1); } var rnd32; var result; do { _nextState(); rnd32 = _state & 0xFFFFFFFF; result = rnd32 % max; } while ((rnd32 - result + max) > _POW2_32); return result; } double nextDouble() { return ((nextInt(1 << 26) * _POW2_27_D) + nextInt(1 << 27)) / _POW2_53_D; } bool nextBool() { return nextInt(2) == 0; } // Constants used by the algorithm. static const _POW2_32 = 1 << 32; static const _POW2_53_D = 1.0 * (1 << 53); static const _POW2_27_D = 1.0 * (1 << 27); // Use a singleton Random object to get a new seed if no seed was passed. @pragma('vm:shared') static final _prng = FinalThreadLocal<_Random>( () => _Random._withState(_initialSeed()), ); // Thomas Wang 64-bit mix. // http://www.concentric.net/~Ttwang/tech/inthash.htm // via. http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm static int _setupSeed(int n) { n = (~n) + (n << 21); // n = (n << 21) - n - 1; n = n ^ (n >>> 24); n = n * 265; // n = (n + (n << 3)) + (n << 8); n = n ^ (n >>> 14); n = n * 21; // n = (n + (n << 2)) + (n << 4); n = n ^ (n >>> 28); n = n + (n << 31); if (n == 0) { n = 0x5a17; } return n; } // Get a seed from the VM's random number provider. @pragma("vm:external-name", "Random_initialSeed") external static int _initialSeed(); static int _nextSeed() { // Trigger the PRNG once to change the internal state. _prng.value._nextState(); return _prng.value._state & 0xFFFFFFFF; } } class _SecureRandom implements Random { _SecureRandom() { // Throw early in constructor if entropy source is not hooked up. _getBytes(1); } // Return count bytes of entropy as a positive integer; count <= 8. @pragma("vm:external-name", "SecureRandom_getBytes") external static int _getBytes(int count); int nextInt(int max) { RangeError.checkValueInInterval( max, 1, _POW2_32, "max", "Must be positive and <= 2^32", ); final byteCount = ((max - 1).bitLength + 7) >> 3; if (byteCount == 0) { return 0; // Not random if max == 1. } var rnd; var result; do { rnd = _getBytes(byteCount); result = rnd % max; } while ((rnd - result + max) > (1 << (byteCount << 3))); return result; } double nextDouble() { return (_getBytes(7) >> 3) / _POW2_53_D; } bool nextBool() { return _getBytes(1).isEven; } // Constants used by the algorithm. static const _POW2_32 = 1 << 32; static const _POW2_53_D = 1.0 * (1 << 53); } �FNN<MOM$$*)."!I9BG$!$5 "!I9CE$!( L1- &#-dart:math-patch/math_patch.dartÀeÀh³Horg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/math/rectangle.dart³ // Copyright (c) 2013, 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:math"; /// A base class for representing two-dimensional axis-aligned rectangles. /// /// This rectangle uses a left-handed Cartesian coordinate system, with x /// directed to the right and y directed down, as per the convention in 2D /// computer graphics. /// /// See also: /// [W3C Coordinate Systems Specification](https://www.w3.org/TR/SVG/coords.html#InitialCoordinateSystem). /// /// The rectangle is the set of points with representable coordinates greater /// than or equal to left/top, and with distance to left/top no greater than /// width/height (to the limit of the precision of the coordinates). /// /// **Legacy:** New usages of [_RectangleBase] are discouraged. /// To learn more, check out the [Rectangle] class API docs. abstract class _RectangleBase { const _RectangleBase(); /// The x-coordinate of the left edge. T get left; /// The y-coordinate of the top edge. T get top; /// The width of the rectangle. T get width; /// The height of the rectangle. T get height; /// The x-coordinate of the right edge. T get right => (left + width) as T; /// The y-coordinate of the bottom edge. T get bottom => (top + height) as T; String toString() { return 'Rectangle ($left, $top) $width x $height'; } bool operator ==(Object other) => other is Rectangle && left == other.left && top == other.top && right == other.right && bottom == other.bottom; int get hashCode => SystemHash.hash4( left.hashCode, top.hashCode, right.hashCode, bottom.hashCode, 0, ); /// Computes the intersection of `this` and [other]. /// /// The intersection of two axis-aligned rectangles, if any, is always another /// axis-aligned rectangle. /// /// Returns the intersection of this and `other`, or `null` if they don't /// intersect. Rectangle? intersection(Rectangle other) { var x0 = max(left, other.left); var x1 = min(left + width, other.left + other.width); if (x0 <= x1) { var y0 = max(top, other.top); var y1 = min(top + height, other.top + other.height); if (y0 <= y1) { return Rectangle(x0, y0, (x1 - x0) as T, (y1 - y0) as T); } } return null; } /// Returns true if `this` intersects [other]. bool intersects(Rectangle other) { return (left <= other.left + other.width && other.left <= left + width && top <= other.top + other.height && other.top <= top + height); } /// Returns a new rectangle which completely contains `this` and [other]. Rectangle boundingBox(Rectangle other) { var right = max(this.left + this.width, other.left + other.width); var bottom = max(this.top + this.height, other.top + other.height); var left = min(this.left, other.left); var top = min(this.top, other.top); return Rectangle(left, top, (right - left) as T, (bottom - top) as T); } /// Tests whether `this` entirely contains [another]. bool containsRectangle(Rectangle another) { return left <= another.left && left + width >= another.left + another.width && top <= another.top && top + height >= another.top + another.height; } /// Tests whether [another] is inside or along the edges of `this`. bool containsPoint(Point another) { return another.x >= left && another.x <= left + width && another.y >= top && another.y <= top + height; } Point get topLeft => Point(this.left, this.top); Point get topRight => Point((this.left + this.width) as T, this.top); Point get bottomRight => Point((this.left + this.width) as T, (this.top + this.height) as T); Point get bottomLeft => Point(this.left, (this.top + this.height) as T); } /// A class for representing two-dimensional rectangles whose properties are /// immutable. /// /// **Legacy:** New usages of [Rectangle] are discouraged. /// /// - If you are using the `Rectangle` class with `dart:html`, /// we recommend migrating to `package:web`. /// To learn how and why to migrate, /// check out the [migration guide](https://dart.dev/go/package-web). /// - If you want to store the boundaries of a rectangle /// in some coordinate system, /// consider using a [record](https://dart.dev/language/records). /// Depending on how you will use it, this could look /// like `var boundaries = (mixX: x1, maxX: x2, minY: y1, maxY: y2)`. /// - If you need to perform intersection calculations or containment checks, /// consider using a dedicated library, such as /// [`package:vector_math`](https://pub.dev/packages/vector_math). /// - If you are developing a Flutter application or package, /// consider using the /// [`Rect`](https://api.flutter.dev/flutter/dart-ui/Rect-class.html) /// type from `dart:ui`. // TODO: @Deprecated( // 'Use records or a dedicated library like package:vector_math instead.') class Rectangle extends _RectangleBase { final T left; final T top; final T width; final T height; /// Create a rectangle spanned by `(left, top)` and /// `(left+width, top+height)`. /// /// The rectangle contains the points /// with x-coordinate between `left` and `left + width`, and /// with y-coordinate between `top` and `top + height`, both inclusive. /// /// The `width` and `height` should be non-negative. /// If `width` or `height` are negative, they are clamped to zero. /// /// If `width` and `height` are zero, the "rectangle" comprises only the /// single point `(left, top)`. /// /// Example: /// ```dart /// var rectangle = const Rectangle(20, 50, 300, 600); /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 320 /// print(rectangle.bottom); // 650 /// ``` /// /// **Legacy:** New usages of [Rectangle] are discouraged. /// To learn more, check out the [Rectangle] class API docs. const Rectangle(this.left, this.top, T width, T height) : width = (width < 0) ? (width == double.negativeInfinity ? 0.0 : (-width * 0)) as dynamic : (width + 0 as dynamic), // Inline _clampToZero. height = (height < 0) ? (height == double.negativeInfinity ? 0.0 : (-height * 0)) as dynamic : (height + 0 as dynamic); /// Create a rectangle spanned by the points [a] and [b]; /// /// The rectangle contains the points /// with x-coordinate between `a.x` and `b.x`, and /// with y-coordinate between `a.y` and `b.y`, both inclusive. /// /// If the distance between `a.x` and `b.x` is not representable /// (which can happen if one or both is a double), /// the actual right edge might be slightly off from `max(a.x, b.x)`. /// Similar for the y-coordinates and the bottom edge. /// /// Example: /// ```dart /// var leftTop = const Point(20, 50); /// var rightBottom = const Point(300, 600); /// /// var rectangle = Rectangle.fromPoints(leftTop, rightBottom); /// print(rectangle); // Rectangle (20, 50) 280 x 550 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 300 /// print(rectangle.bottom); // 600 /// ``` factory Rectangle.fromPoints(Point a, Point b) { T left = min(a.x, b.x); T width = (max(a.x, b.x) - left) as T; T top = min(a.y, b.y); T height = (max(a.y, b.y) - top) as T; return Rectangle(left, top, width, height); } } /// A class for representing two-dimensional axis-aligned rectangles with /// mutable properties. /// /// **Legacy:** New usages of [MutableRectangle] are discouraged. /// /// - If you are using the `MutableRectangle` class with `dart:html`, /// we recommend migrating to `package:web`. /// To learn how and why to migrate, /// check out the [migration guide](https://dart.dev/go/package-web). /// - If you want to store the boundaries of a rectangle /// in some coordinate system, /// consider using a [record](https://dart.dev/language/records). /// Depending on how you will use it, this could look /// like `var boundaries = (mixX: x1, maxX: x2, minY: y1, maxY: y2)`. /// - If you need to perform intersection calculations or containment checks, /// consider using a dedicated library, such as /// [`package:vector_math`](https://pub.dev/packages/vector_math). /// - If you are developing a Flutter application or package, /// consider using the /// [`Rect`](https://api.flutter.dev/flutter/dart-ui/Rect-class.html) /// type from `dart:ui`. // TODO: @Deprecated( // 'Use records or a dedicated library like package:vector_math instead.') class MutableRectangle extends _RectangleBase implements Rectangle { /// The x-coordinate of the left edge. /// /// Setting the value will move the rectangle without changing its width. T left; /// The y-coordinate of the left edge. /// /// Setting the value will move the rectangle without changing its height. T top; T _width; T _height; /// Create a mutable rectangle spanned by `(left, top)` and /// `(left+width, top+height)`. /// /// The rectangle contains the points /// with x-coordinate between `left` and `left + width`, and /// with y-coordinate between `top` and `top + height`, both inclusive. /// /// The `width` and `height` should be non-negative. /// If `width` or `height` are negative, they are clamped to zero. /// /// If `width` and `height` are zero, the "rectangle" comprises only the /// single point `(left, top)`. /// /// Example: /// ```dart /// var rectangle = MutableRectangle(20, 50, 300, 600); /// print(rectangle); // Rectangle (20, 50) 300 x 600 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 320 /// print(rectangle.bottom); // 650 /// /// // Change rectangle width and height. /// rectangle.width = 200; /// rectangle.height = 100; /// /// print(rectangle); // Rectangle (20, 50) 200 x 100 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 220 /// print(rectangle.bottom); // 150 /// ``` /// /// **Legacy:** New usages of [MutableRectangle] are discouraged. /// To learn more, check out the [MutableRectangle] class API docs. MutableRectangle(this.left, this.top, T width, T height) : this._width = (width < 0) ? _clampToZero(width) : (width + 0 as dynamic), this._height = (height < 0) ? _clampToZero(height) : (height + 0 as dynamic); /// Create a mutable rectangle spanned by the points [a] and [b]; /// /// The rectangle contains the points /// with x-coordinate between `a.x` and `b.x`, and /// with y-coordinate between `a.y` and `b.y`, both inclusive. /// /// If the distance between `a.x` and `b.x` is not representable /// (which can happen if one or both is a double), /// the actual right edge might be slightly off from `max(a.x, b.x)`. /// Similar for the y-coordinates and the bottom edge. /// /// Example: /// ```dart /// var leftTop = const Point(20, 50); /// var rightBottom = const Point(300, 600); /// var rectangle = MutableRectangle.fromPoints(leftTop, rightBottom); /// print(rectangle); // Rectangle (20, 50) 280 x 550 /// print(rectangle.left); // 20 /// print(rectangle.top); // 50 /// print(rectangle.right); // 300 /// print(rectangle.bottom); // 600 /// ``` factory MutableRectangle.fromPoints(Point a, Point b) { T left = min(a.x, b.x); T width = (max(a.x, b.x) - left) as T; T top = min(a.y, b.y); T height = (max(a.y, b.y) - top) as T; return MutableRectangle(left, top, width, height); } T get width => _width; /// Sets the width of the rectangle. /// /// The width must be non-negative. /// If a negative width is supplied, it is clamped to zero. /// /// Setting the value will change the right edge of the rectangle, /// but will not change [left]. set width(T width) { if (width < 0) width = _clampToZero(width); _width = width; } T get height => _height; /// Sets the height of the rectangle. /// /// The height must be non-negative. /// If a negative height is supplied, it is clamped to zero. /// /// Setting the value will change the bottom edge of the rectangle, /// but will not change [top]. set height(T height) { if (height < 0) height = _clampToZero(height); _height = height; } } /// Converts a negative [int] or [double] to a zero-value of the same type. /// /// Returns `0` if value is int, `0.0` if value is double. T _clampToZero(T value) { assert(value < 0); if (value == double.negativeInfinity) return 0.0 as dynamic; return (-value * 0) as dynamic; } �oNN<KJKnNME@=/)( "#*&+'7$(7QL3$:$<E1*0&+$L1GH+(N83#86F+ %#9NNQM;?/'H9!D8HN2E>HO;6"(?J7EK"9#"%& =?:OAQ%<(5AC5H9)/B8#"%& 9++3JBF/'H9!D8HN2E>HO@)L )M >"(?J7EK":8#"%&,8#"%& DF; #$"$%D(5AC5H9)/I8#"%& @++:'&>E"3('?F!6L;)?"dart:math/rectangle.dartIorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/mirrors/mirrors.dartÀšG// Copyright (c) 2013, 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. // For the purposes of the mirrors library, we adopt a naming // convention with respect to getters and setters. Specifically, for // some variable or field... // // var myField; // // ...the getter is named 'myField' and the setter is named // 'myField='. This allows us to assign unique names to getters and // setters for the purposes of member lookup. /// Basic reflection in Dart, /// with support for introspection and dynamic invocation. /// /// *Introspection* is that subset of reflection by which a running /// program can examine its own structure. For example, a function /// that prints out the names of all the members of an arbitrary object. /// /// *Dynamic invocation* refers the ability to evaluate code that /// has not been literally specified at compile time, such as calling a method /// whose name is provided as an argument (because it is looked up /// in a database, or provided interactively by the user). /// /// ## How to interpret this library's documentation /// /// As a rule, the names of Dart declarations are represented using /// instances of class [Symbol]. Whenever the doc speaks of an object *s* /// of class [Symbol] denoting a name, it means the string that /// was used to construct *s*. /// /// The documentation frequently abuses notation with /// Dart pseudo-code such as [:o.x(a):], where /// o and a are defined to be objects; what is actually meant in these /// cases is [:o'.x(a'):] where *o'* and *a'* are Dart variables /// bound to *o* and *a* respectively. Furthermore, *o'* and *a'* /// are assumed to be fresh variables (meaning that they are /// distinct from any other variables in the program). /// /// Sometimes the documentation refers to *serializable* objects. /// An object is serializable across isolates if and only if it is an instance of /// num, bool, String, a list of objects that are serializable /// across isolates, or a map with keys and values that are all serializable across /// isolates. /// /// ## Status: Unstable /// /// The dart:mirrors library is unstable and its API might change slightly as a /// result of user feedback. This library is only supported by the Dart VM and /// only available on some platforms. /// /// {@category VM} library dart.mirrors; import "dart:core"; import 'dart:async' show Future; import "dart:_internal" show Since; /// Error thrown when trying to instantiate an abstract class. class AbstractClassInstantiationError extends Error { final String _className; AbstractClassInstantiationError(String className) : _className = className; external String toString(); } /** * A [MirrorSystem] is the main interface used to reflect on a set of * associated libraries. * * At runtime each running isolate has a distinct [MirrorSystem]. * * It is also possible to have a [MirrorSystem] which represents a set * of libraries which are not running -- perhaps at compile-time. In * this case, all available reflective functionality would be * supported, but runtime functionality (such as invoking a function * or inspecting the contents of a variable) would fail dynamically. */ abstract class MirrorSystem { /** * All libraries known to the mirror system, indexed by their URI. * * Returns an unmodifiable map of the libraries with [LibraryMirror.uri] as * keys. * * For a runtime mirror system, only libraries which are currently loaded * are included, and repeated calls of this method may return different maps * as libraries are loaded. */ Map get libraries; /** * Returns the unique library named [libraryName] if it exists. * * If no unique library exists, an error is thrown. */ external LibraryMirror findLibrary(Symbol libraryName); /** * A mirror on the isolate associated with this [MirrorSystem]. * * This may be null if this mirror system is not running. */ IsolateMirror get isolate; /** * A mirror on the [:dynamic:] type. */ TypeMirror get dynamicType; /** * A mirror on the [:void:] type. */ TypeMirror get voidType; /** * A mirror on the [:Never:] type. */ TypeMirror get neverType; /** * Returns the name of [symbol]. */ external static String getName(Symbol symbol); /** * Returns a symbol for [name]. * * If [library] is not a [LibraryMirror] or if [name] is a private identifier * and [library] is `null`, throws an [ArgumentError]. If [name] is a private * identifier, the symbol returned is with respect to [library]. * * The following text is non-normative: * * Using this method may result in larger output. If possible, use * the const constructor of [Symbol] or symbol literals. */ external static Symbol getSymbol(String name, [LibraryMirror? library]); } /** * Returns a [MirrorSystem] for the current isolate. */ external MirrorSystem currentMirrorSystem(); /** * Reflects an instance. * * Returns an [InstanceMirror] reflecting [reflectee]. If [reflectee] is a * function or an instance of a class that has a [:call:] method, the returned * instance mirror will be a [ClosureMirror]. * * Note that since one cannot obtain an object from another isolate, this * function can only be used to obtain mirrors on objects of the current * isolate. */ external InstanceMirror reflect(dynamic reflectee); /** * Reflects a class declaration. * * Let *C* be the original class declaration of the class represented by [key]. * This function returns a [ClassMirror] reflecting *C*. * * If [key] is not an instance of [Type], then this function throws an * [ArgumentError]. If [key] is the Type for dynamic or a function typedef, * throws an [ArgumentError]. * * Note that since one cannot obtain a [Type] object from another isolate, this * function can only be used to obtain class mirrors on classes of the current * isolate. */ external ClassMirror reflectClass(Type key); /** * Reflects the type represented by [key]. * * If [key] is not an instance of [Type], then this function throws an * [ArgumentError]. * * Optionally takes a list of [typeArguments] for generic classes. If the list * is provided, then the [key] must be a generic class type, and the number of * the provided type arguments must be equal to the number of type variables * declared by the class. * * Note that since one cannot obtain a [Type] object from another isolate, this * function can only be used to obtain type mirrors on types of the current * isolate. */ external TypeMirror reflectType(Type key, [List? typeArguments]); /** * A [Mirror] reflects some Dart language entity. * * Every [Mirror] originates from some [MirrorSystem]. */ abstract class Mirror {} /** * An [IsolateMirror] reflects an isolate. */ abstract class IsolateMirror implements Mirror { /** * A unique name used to refer to the isolate in debugging messages. */ String get debugName; /** * Whether this mirror reflects the currently running isolate. */ bool get isCurrent; /** * The root library for the reflected isolate. */ LibraryMirror get rootLibrary; /** * Whether [other] is an [IsolateMirror] on the same isolate as this mirror. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. the isolate being reflected by this mirror is the same isolate being * reflected by [other]. */ bool operator ==(Object other); /** * Loads the library at the given uri into this isolate. * * WARNING: You are strongly encouraged to use Isolate.spawnUri instead when * possible. IsolateMirror.loadUri should only be used when synchronous * communication or shared state with dynamically loaded code is needed. * * If a library with the same canonicalized uri has already been loaded, * the existing library will be returned. (The isolate will not load a new * copy of the library.) * * This behavior is similar to the behavior of an import statement that * appears in the root library, except that the import scope of the root * library is not changed. */ Future loadUri(Uri uri); } /** * A [DeclarationMirror] reflects some entity declared in a Dart program. */ abstract class DeclarationMirror implements Mirror { /** * The simple name for this Dart language entity. * * The simple name is in most cases the identifier name of the entity, * such as 'myMethod' for a method, [:void myMethod() {...}:] or 'mylibrary' * for a [:library 'mylibrary';:] declaration. */ Symbol get simpleName; /** * The fully-qualified name for this Dart language entity. * * This name is qualified by the name of the owner. For instance, * the qualified name of a method 'method' in class 'Class' in * library 'library' is 'library.Class.method'. * * Returns a [Symbol] constructed from a string representing the * fully qualified name of the reflectee. * Let *o* be the [owner] of this mirror, let *r* be the reflectee of * this mirror, let *p* be the fully qualified * name of the reflectee of *o*, and let *s* be the simple name of *r* * computed by [simpleName]. * The fully qualified name of *r* is the * concatenation of *p*, '.', and *s*. * * Because an isolate can contain more than one library with the same name (at * different URIs), a fully-qualified name does not uniquely identify any * language entity. */ Symbol get qualifiedName; /** * A mirror on the owner of this Dart language entity. * * The owner is the declaration immediately surrounding the reflectee: * * * For a library, the owner is [:null:]. * * For a class declaration, typedef or top level function or variable, the * owner is the enclosing library. * * For a mixin application `S with M`, the owner is the owner of `M`. * * For a constructor, the owner is the immediately enclosing class. * * For a method, instance variable or a static variable, the owner is the * immediately enclosing class, unless the class is a mixin application * `S with M`, in which case the owner is `M`. Note that `M` may be an * invocation of a generic. * * For a parameter, local variable or local function the owner is the * immediately enclosing function. */ DeclarationMirror? get owner; /** * Whether this declaration is library private. * * Always returns `false` for a library declaration, * otherwise returns `true` if the declaration's name starts with an * underscore character (`_`), and `false` if it doesn't. */ bool get isPrivate; /** * Whether this declaration is top-level. * * A declaration is considered top-level if its [owner] is a [LibraryMirror]. */ bool get isTopLevel; /** * The source location of this Dart language entity, or [:null:] if the * entity is synthetic. * * If the reflectee is a variable, the returned location gives the position * of the variable name at its point of declaration. * * If the reflectee is a library, class, typedef, function or type variable * with associated metadata, the returned location gives the position of the * first metadata declaration associated with the reflectee. * * Otherwise: * * If the reflectee is a library, the returned location gives the position of * the keyword 'library' at the reflectee's point of declaration, if the * reflectee is a named library, or the first character of the first line in * the compilation unit defining the reflectee if the reflectee is anonymous. * * If the reflectee is an abstract class, the returned location gives the * position of the keyword 'abstract' at the reflectee's point of declaration. * Otherwise, if the reflectee is a class, the returned location gives the * position of the keyword 'class' at the reflectee's point of declaration. * * If the reflectee is a typedef the returned location gives the position of * the of the keyword 'typedef' at the reflectee's point of declaration. * * If the reflectee is a function with a declared return type, the returned * location gives the position of the function's return type at the * reflectee's point of declaration. Otherwise. the returned location gives * the position of the function's name at the reflectee's point of * declaration. * * This operation is optional and may throw an [UnsupportedError]. */ SourceLocation? get location; /** * A list of the metadata associated with this declaration. * * Let *D* be the declaration this mirror reflects. * If *D* is decorated with annotations *A1, ..., An* * where *n > 0*, then for each annotation *Ai* associated * with *D, 1 <= i <= n*, let *ci* be the constant object * specified by *Ai*. Then this method returns a list whose * members are instance mirrors on *c1, ..., cn*. * If no annotations are associated with *D*, then * an empty list is returned. * * If evaluating any of *c1, ..., cn* would cause a * compilation error * the effect is the same as if a non-reflective compilation error * had been encountered. */ List get metadata; } /** * An [ObjectMirror] is a common superinterface of [InstanceMirror], * [ClassMirror], and [LibraryMirror] that represents their shared * functionality. * * For the purposes of the mirrors library, these types are all * object-like, in that they support method invocation and field * access. Real Dart objects are represented by the [InstanceMirror] * type. * * See [InstanceMirror], [ClassMirror], and [LibraryMirror]. */ abstract class ObjectMirror implements Mirror { /** * Invokes the named function and returns a mirror on the result. * * Let *o* be the object reflected by this mirror, let *f* be the simple name * of the member denoted by [memberName], let *a1, ..., an* be the elements * of [positionalArguments], let *k1, ..., km* be the identifiers denoted by * the elements of [namedArguments].keys, and let *v1, ..., vm* be the * elements of [namedArguments].values. Then this method will perform the * method invocation *o.f(a1, ..., an, k1: v1, ..., km: vm)* in a scope that * has access to the private members of *o* (if *o* is a class or library) or * the private members of the class of *o* (otherwise). * * If the invocation returns a result *r*, this method returns the result of * calling [reflect]\(*r*\). * * If the invocation causes a compilation error the effect is the same as if * a non-reflective compilation error had been encountered. * * If the invocation throws an exception *e* (that it does not catch), this * method throws *e*. */ InstanceMirror invoke( Symbol memberName, List positionalArguments, [ Map namedArguments = const {}, ]); /** * Invokes a getter and returns a mirror on the result. * * The getter can be the implicit getter for a field or a user-defined getter * method. * * Let *o* be the object reflected by this mirror, * let *f* be the simple name of the getter denoted by [fieldName]. * * Then this method will perform the getter invocation *o.f* in a scope that * has access to the private members of *o* (if *o* is a class or library) or * the private members of the class of *o* (otherwise). * * If this mirror is an [InstanceMirror], and [fieldName] denotes an instance * method on its reflectee, the result of the invocation is an instance * mirror on a closure corresponding to that method. * * If this mirror is a [LibraryMirror], and [fieldName] denotes a top-level * method in the corresponding library, the result of the invocation is an * instance mirror on a closure corresponding to that method. * * If this mirror is a [ClassMirror], and [fieldName] denotes a static method * in the corresponding class, the result of the invocation is an instance * mirror on a closure corresponding to that method. * * If the invocation returns a result *r*, this method returns the result of * calling [reflect]\(*r*\). * * If the invocation causes a compilation error, the effect is the same as if * a non-reflective compilation error had been encountered. * * If the invocation throws an exception *e* (that it does not catch), this * method throws *e*. */ // TODO(ahe): Remove stuff about scope and private members. [fieldName] is a // capability giving access to private members. InstanceMirror getField(Symbol fieldName); /** * Invokes a setter and returns a mirror on the result. * * The setter may be either the implicit setter for a non-final field or a * user-defined setter method. * * Let *o* be the object reflected by this mirror, * let *f* be the simple name of the getter denoted by [fieldName], * and let *a* be the object bound to [value]. * * Then this method will perform the setter invocation *o.f = a* in a scope * that has access to the private members of *o* (if *o* is a class or * library) or the private members of the class of *o* (otherwise). * * If the invocation returns a result *r*, this method returns the result of * calling [reflect]\([value]\). * * If the invocation causes a compilation error, the effect is the same as if * a non-reflective compilation error had been encountered. * * If the invocation throws an exception *e* (that it does not catch) this * method throws *e*. */ InstanceMirror setField(Symbol fieldName, dynamic value); /** * Performs [invocation] on the reflectee of this [ObjectMirror]. * * Equivalent to * * if (invocation.isGetter) { * return this.getField(invocation.memberName).reflectee; * } else if (invocation.isSetter) { * return this.setField(invocation.memberName, * invocation.positionalArguments[0]).reflectee; * } else { * return this.invoke(invocation.memberName, * invocation.positionalArguments, * invocation.namedArguments).reflectee; * } */ delegate(Invocation invocation); } /** * An [InstanceMirror] reflects an instance of a Dart language object. */ abstract class InstanceMirror implements ObjectMirror { /** * A mirror on the type of the reflectee. * * Returns a mirror on the actual class of the reflectee. * The class of the reflectee may differ from * the object returned by invoking [runtimeType] on * the reflectee. */ ClassMirror get type; /** * Whether [reflectee] will return the instance reflected by this mirror. * * This will always be true in the local case (reflecting instances in the * same isolate), but only true in the remote case if this mirror reflects a * simple value. * * A value is simple if one of the following holds: * * * the value is [:null:] * * the value is of type [num] * * the value is of type [bool] * * the value is of type [String] */ bool get hasReflectee; /** * If the [InstanceMirror] reflects an instance it is meaningful to * have a local reference to, we provide access to the actual * instance here. * * If you access [reflectee] when [hasReflectee] is false, an * exception is thrown. */ dynamic get reflectee; /** * Whether this mirror is equal to [other]. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. either * * a. [hasReflectee] is true and so is * [:identical(reflectee, other.reflectee):], or * * b. the remote objects reflected by this mirror and by [other] are * identical. */ bool operator ==(Object other); } /** * A [ClosureMirror] reflects a closure. * * A [ClosureMirror] provides the ability to execute its reflectee and * introspect its function. */ abstract class ClosureMirror implements InstanceMirror { /** * A mirror on the function associated with this closure. * * The function associated with an implicit closure of a function is that * function. * * The function associated with an instance of a class that has a [:call:] * method is that [:call:] method. * * A Dart implementation might choose to create a class for each closure * expression, in which case [:function:] would be the same as * [:type.declarations[#call]:]. But the Dart language model does not require * this. A more typical implementation involves a single closure class for * each type signature, where the call method dispatches to a function held * in the closure rather the call method * directly implementing the closure body. So one cannot rely on closures from * distinct closure expressions having distinct classes ([:type:]), but one * can rely on them having distinct functions ([:function:]). */ MethodMirror get function; /** * Executes the closure and returns a mirror on the result. * * Let *f* be the closure reflected by this mirror, * let *a1, ..., an* be the elements of [positionalArguments], * let *k1, ..., km* be the identifiers denoted by the elements of * [namedArguments].keys, * and let *v1, ..., vm* be the elements of [namedArguments].values. * * Then this method will perform the method invocation * *f(a1, ..., an, k1: v1, ..., km: vm)*. * * If the invocation returns a result *r*, this method returns the result of * calling [reflect]\(*r*\). * * If the invocation causes a compilation error, the effect is the same as if * a non-reflective compilation error had been encountered. * * If the invocation throws an exception *e* (that it does not catch), this * method throws *e*. */ InstanceMirror apply( List positionalArguments, [ Map namedArguments = const {}, ]); } /** * A [LibraryMirror] reflects a Dart language library, providing * access to the variables, functions, and classes of the * library. */ abstract class LibraryMirror implements DeclarationMirror, ObjectMirror { /** * The absolute uri of the library. */ Uri get uri; /** * Returns an immutable map of the declarations actually given in the library. * * This map includes all regular methods, getters, setters, fields, classes * and typedefs actually declared in the library. The map is keyed by the * simple names of the declarations. */ Map get declarations; /** * Whether this mirror is equal to [other]. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. The library being reflected by this mirror and the library being * reflected by [other] are the same library in the same isolate. */ bool operator ==(Object other); /** * Returns a list of the imports and exports in this library; */ List get libraryDependencies; } /// A mirror on an import or export declaration. abstract class LibraryDependencyMirror implements Mirror { /// Is `true` if this dependency is an import. bool get isImport; /// Is `true` if this dependency is an export. bool get isExport; /// Returns true iff this dependency is a deferred import. Otherwise returns /// false. bool get isDeferred; /// Returns the library mirror of the library that imports or exports the /// [targetLibrary]. LibraryMirror get sourceLibrary; /// Returns the library mirror of the library that is imported or exported, /// or null if the library is not loaded. LibraryMirror? get targetLibrary; /// Returns the prefix if this is a prefixed import and `null` otherwise. Symbol? get prefix; /// Returns the list of show/hide combinators on the import/export /// declaration. List get combinators; /// Returns the source location for this import/export declaration. SourceLocation? get location; List get metadata; /// Returns a future that completes with a library mirror on the library being /// imported or exported when it is loaded, and initiates a load of that /// library if it is not loaded. Future loadLibrary(); } /// A mirror on a show/hide combinator declared on a library dependency. abstract class CombinatorMirror implements Mirror { /// The list of identifiers on the combinator. List get identifiers; /// Is `true` if this is a 'show' combinator. bool get isShow; /// Is `true` if this is a 'hide' combinator. bool get isHide; } /** * A [TypeMirror] reflects a Dart language class, typedef, * function type or type variable. */ abstract class TypeMirror implements DeclarationMirror { /** * Returns true if this mirror reflects dynamic, a non-generic class or * typedef, or an instantiated generic class or typedef in the current * isolate. Otherwise, returns false. */ bool get hasReflectedType; /** * If [:hasReflectedType:] returns true, returns the corresponding [Type]. * Otherwise, an [UnsupportedError] is thrown. */ Type get reflectedType; /** * An immutable list with mirrors for all type variables for this type. * * If this type is a generic declaration or an invocation of a generic * declaration, the returned list contains mirrors on the type variables * declared in the original declaration. * Otherwise, the returned list is empty. * * This list preserves the order of declaration of the type variables. */ List get typeVariables; /** * An immutable list with mirrors for all type arguments for * this type. * * If the reflectee is an invocation of a generic class, * the type arguments are the bindings of its type parameters. * If the reflectee is the original declaration of a generic, * it has no type arguments and this method returns an empty list. * If the reflectee is not generic, then * it has no type arguments and this method returns an empty list. * * This list preserves the order of declaration of the type variables. */ List get typeArguments; /** * Is this the original declaration of this type? * * For most classes, they are their own original declaration. For * generic classes, however, there is a distinction between the * original class declaration, which has unbound type variables, and * the instantiations of generic classes, which have bound type * variables. */ bool get isOriginalDeclaration; /** * A mirror on the original declaration of this type. * * For most classes, they are their own original declaration. For * generic classes, however, there is a distinction between the * original class declaration, which has unbound type variables, and * the instantiations of generic classes, which have bound type * variables. */ TypeMirror get originalDeclaration; /** * Checks the subtype relationship, denoted by `<:` in the language * specification. * * This is the type relationship used in `is` test checks. */ bool isSubtypeOf(TypeMirror other); /** * Checks the assignability relationship, denoted by `<=>` in the language * specification. * * This is the type relationship tested on assignment in checked mode. */ bool isAssignableTo(TypeMirror other); } /** * A [ClassMirror] reflects a Dart language class. */ abstract class ClassMirror implements TypeMirror, ObjectMirror { /** * A mirror on the superclass on the reflectee. * * If this type is [:Object:], the superclass will be null. */ ClassMirror? get superclass; /** * A list of mirrors on the superinterfaces of the reflectee. */ List get superinterfaces; /** * Is the reflectee abstract? */ bool get isAbstract; /** * Is the reflectee an enum? */ bool get isEnum; /** * Returns an immutable map of the declarations actually given in the class * declaration. * * This map includes all regular methods, getters, setters, fields, * constructors and type variables actually declared in the class. Both * static and instance members are included, but no inherited members are * included. The map is keyed by the simple names of the declarations. * * This does not include inherited members. */ Map get declarations; /** * Returns a map of the methods, getters and setters of an instance of the * class. * * The intent is to capture those members that constitute the API of an * instance. Hence fields are not included, but the getters and setters * implicitly introduced by fields are included. The map includes methods, * getters and setters that are inherited as well as those introduced by the * class itself. * * The map is keyed by the simple names of the members. */ Map get instanceMembers; /** * Returns a map of the static methods, getters and setters of the class. * * The intent is to capture those members that constitute the API of a class. * Hence fields are not included, but the getters and setters implicitly * introduced by fields are included. * * The map is keyed by the simple names of the members. */ Map get staticMembers; /** * The mixin of this class. * * If this class is the result of a mixin application of the form S with M, * returns a class mirror on M. Otherwise returns a class mirror on * the reflectee. */ ClassMirror get mixin; /** * Invokes the named constructor and returns a mirror on the result. * * Let *c* be the class reflected by this mirror, * let *a1, ..., an* be the elements of [positionalArguments], * let *k1, ..., km* be the identifiers denoted by the elements of * [namedArguments].keys, * and let *v1, ..., vm* be the elements of [namedArguments].values. * * If [constructorName] was created from the empty string, then this method * will execute the instance creation expression * *new c(a1, ..., an, k1: v1, ..., km: vm)* in a scope that has access to * the private members of *c*. * * Otherwise, let *f* be the simple name of the constructor denoted by * [constructorName]. Then this method will execute the instance creation * expression *new c.f(a1, ..., an, k1: v1, ..., km: vm)* in a scope that has * access to the private members of *c*. * * In either case: * * * If the expression evaluates to a result *r*, this method returns the * result of calling [reflect]\(*r*\). * * If evaluating the expression causes a compilation error, the effect is * the same as if a non-reflective compilation error had been encountered. * * If evaluating the expression throws an exception *e* (that it does not * catch), this method throws *e*. */ InstanceMirror newInstance( Symbol constructorName, List positionalArguments, [ Map namedArguments = const {}, ]); /** * Whether this mirror is equal to [other]. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. This mirror and [other] reflect the same class. * * Note that if the reflected class is an invocation of a generic class, 2. * implies that the reflected class and [other] have equal type arguments. */ bool operator ==(Object other); /** * Returns whether the class denoted by the receiver is a subclass of the * class denoted by the argument. * * Note that the subclass relationship is reflexive. */ bool isSubclassOf(ClassMirror other); } /** * A [FunctionTypeMirror] represents the type of a function in the * Dart language. */ abstract class FunctionTypeMirror implements ClassMirror { /** * Returns the return type of the reflectee. */ TypeMirror get returnType; /** * Returns a list of the parameter types of the reflectee. */ List get parameters; /** * A mirror on the [:call:] method for the reflectee. */ // This is only here because in the past the VM did not implement a call // method on closures. MethodMirror get callMethod; } /** * A [TypeVariableMirror] represents a type parameter of a generic type. */ abstract class TypeVariableMirror extends TypeMirror { /** * A mirror on the type that is the upper bound of this type variable. */ TypeMirror get upperBound; /** * Is the reflectee static? * * For the purposes of the mirrors library, type variables are considered * non-static. */ bool get isStatic; /** * Whether [other] is a [TypeVariableMirror] on the same type variable as this * mirror. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. [:simpleName == other.simpleName:] and [:owner == other.owner:]. */ bool operator ==(Object other); } /** * A [TypedefMirror] represents a typedef in a Dart language program. */ abstract class TypedefMirror implements TypeMirror { /** * The defining type for this typedef. * * If the type referred to by the reflectee is a function type *F*, the * result will be [:FunctionTypeMirror:] reflecting *F* which is abstract * and has an abstract method [:call:] whose signature corresponds to *F*. * For instance [:void f(int):] is the referent for [:typedef void f(int):]. */ FunctionTypeMirror get referent; } /** * A [MethodMirror] reflects a Dart language function, method, * constructor, getter, or setter. */ abstract class MethodMirror implements DeclarationMirror { /** * A mirror on the return type for the reflectee. */ TypeMirror get returnType; /** * The source code for the reflectee, if available. Otherwise null. */ String? get source; /** * A list of mirrors on the parameters for the reflectee. */ List get parameters; /** * A function is considered non-static iff it is permitted to refer to 'this'. * * Note that generative constructors are considered non-static, whereas * factory constructors are considered static. */ bool get isStatic; /** * Is the reflectee abstract? */ bool get isAbstract; /** * Returns true if the reflectee is synthetic, and returns false otherwise. * * A reflectee is synthetic if it is a getter or setter implicitly introduced * for a field or Type, or if it is a constructor that was implicitly * introduced as a default constructor or as part of a mixin application. */ bool get isSynthetic; /** * Is the reflectee a regular function or method? * * A function or method is regular if it is not a getter, setter, or * constructor. Note that operators, by this definition, are * regular methods. */ bool get isRegularMethod; /** * Is the reflectee an operator? */ bool get isOperator; /** * Is the reflectee a getter? */ bool get isGetter; /** * Is the reflectee a setter? */ bool get isSetter; /** * Is the reflectee a constructor? */ bool get isConstructor; /** * The constructor name for named constructors and factory methods. * * For unnamed constructors, this is the empty string. For * non-constructors, this is the empty string. * * For example, [:'bar':] is the constructor name for constructor * [:Foo.bar:] of type [:Foo:]. */ Symbol get constructorName; /** * Is the reflectee a const constructor? */ bool get isConstConstructor; /** * Is the reflectee a generative constructor? */ bool get isGenerativeConstructor; /** * Is the reflectee a redirecting constructor? */ bool get isRedirectingConstructor; /** * Is the reflectee a factory constructor? */ bool get isFactoryConstructor; /** * Is the reflectee an extension method? */ bool get isExtensionMember; /** * Is the reflectee an extension type method? */ bool get isExtensionTypeMember; /** * Whether this mirror is equal to [other]. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. [:simpleName == other.simpleName:] and [:owner == other.owner:]. */ bool operator ==(Object other); } /** * A [VariableMirror] reflects a Dart language variable declaration. */ abstract class VariableMirror implements DeclarationMirror { /** * Returns a mirror on the type of the reflectee. */ TypeMirror get type; /** * Returns [:true:] if the reflectee is a static variable. * Otherwise returns [:false:]. * * For the purposes of the mirror library, top-level variables are * implicitly declared static. */ bool get isStatic; /** * Returns [:true:] if the reflectee is a final variable. * Otherwise returns [:false:]. */ bool get isFinal; /** * Returns [:true:] if the reflectee is declared [:const:]. * Otherwise returns [:false:]. */ bool get isConst; /** * Is the reflectee an extension member? */ bool get isExtensionMember; /** * Is the reflectee an extension type member? */ bool get isExtensionTypeMember; /** * Whether this mirror is equal to [other]. * * The equality holds if and only if * * 1. [other] is a mirror of the same kind, and * 2. [:simpleName == other.simpleName:] and [:owner == other.owner:]. */ bool operator ==(Object other); } /** * A [ParameterMirror] reflects a Dart formal parameter declaration. */ abstract class ParameterMirror implements VariableMirror { /** * A mirror on the type of this parameter. */ TypeMirror get type; /** * Returns [:true:] if the reflectee is an optional parameter. * Otherwise returns [:false:]. */ bool get isOptional; /** * Returns [:true:] if the reflectee is a named parameter. * Otherwise returns [:false:]. */ bool get isNamed; /** * Returns [:true:] if the reflectee has explicitly declared a default value. * Otherwise returns [:false:]. */ bool get hasDefaultValue; /** * Returns the default value of an optional parameter. * * Returns an [InstanceMirror] on the (compile-time constant) * default value for an optional parameter. * If no default value is declared, it defaults to `null` * and a mirror of `null` is returned. * * Returns `null` for a required parameter. */ InstanceMirror? get defaultValue; } /** * A [SourceLocation] describes the span of an entity in Dart source code. */ abstract class SourceLocation { /** * The 1-based line number for this source location. * * A value of 0 means that the line number is unknown. */ int get line; /** * The 1-based column number for this source location. * * A value of 0 means that the column number is unknown. */ int get column; /** * Returns the URI where the source originated. */ Uri get sourceUri; } „¾NN<>FEEEN LO)B6:B<'$%#1"PPC*F;K5-KO.JJ 4!P9GLPO -+GOOMPL H27+1GA1!O'2M";OJKKMJK*J54IO1=DA2C,H1I,)QL9I-O'JHNLK J' 27G<,PJN7NO?PKOPLQMNOKNFNEE >68=<>45 6E%EC@AF =0DPNOILOP:OO>N)E:P 5FOP:PJ7NM@PM7OP>NO2-:M!5F1NIFO#P>M<D$B+7N5>D #G8,<06LMO6"#%F@@.'2,6J")G9<LM%KAPMN+QN@>6AEG9,OP>N)EA: J&QNL'3.'2IG"@91;11O L#N,$LE*F %QK#'I41 00;#9JI(M1JIK+,I.?;A@E+EI&4EBGB"8EBGB&F=&MI)3A2>@) NFJLI.3M JJMO:1LPK(:/NFG4AEGN3M!ILP+L+NON')E.'28NM"L$7(C;/=(8KI7ILQ '2I"F5)JLMO#?#;4F<(QJ1 NPHL4G@#  %F>1D"+0$1%-!+0".'2I"E=4="E!<">"+0".'2I"E;-A"="P"9@.<).$K 799;2 dart:mirrorsXorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/mirrors_patch.dartŒ¹// 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. /// Note: the VM concatenates all patch files into a single patch file. This /// file is the first patch in "dart:_internal" which contains all the imports /// used by patches of that library. We plan to change this when we have a /// shared front end and simply use parts. import "dart:_internal" as internal; import "dart:_internal" show patch; import "dart:async" show Future; import "dart:collection" show UnmodifiableListView, UnmodifiableMapView; /// These are the additional parts of this patch library: part "mirrors_impl.dart"; part "mirror_reference.dart"; /** * Returns a [MirrorSystem] for the current isolate. */ @patch MirrorSystem currentMirrorSystem() { return _Mirrors.currentMirrorSystem(); } /** * Returns an [InstanceMirror] for some Dart language object. * * This only works if this mirror system is associated with the * current running isolate. */ @patch InstanceMirror reflect(dynamic reflectee) { return _Mirrors.reflect(reflectee); } /** * Returns a [ClassMirror] for the class represented by a Dart * Type object. * * This only works with objects local to the current isolate. */ @patch ClassMirror reflectClass(Type key) { return _Mirrors.reflectClass(key); } @patch TypeMirror reflectType(Type key, [List? typeArguments]) { return _Mirrors.reflectType(key, typeArguments); } @patch class MirrorSystem { @patch LibraryMirror findLibrary(Symbol libraryName) { var candidates = libraries.values.where( (lib) => lib.simpleName == libraryName, ); if (candidates.length == 1) { return candidates.single; } if (candidates.length > 1) { var uris = candidates.map((lib) => lib.uri.toString()).toList(); throw Exception( "There are multiple libraries named " "'${getName(libraryName)}': $uris", ); } throw Exception("There is no library named '${getName(libraryName)}'"); } @patch static String getName(Symbol symbol) { return internal.Symbol.computeUnmangledName(symbol as internal.Symbol); } @patch static Symbol getSymbol(String name, [LibraryMirror? library]) { if ((library != null && library is! _LibraryMirror) || ((name.length > 0) && (name[0] == '_') && (library == null))) { throw ArgumentError(library); } if (library != null) { name = _mangleName(name, (library as _LibraryMirror)._reflectee); } return internal.Symbol.unvalidated(name); } @pragma("vm:external-name", "Mirrors_mangleName") external static _mangleName(String name, _MirrorReference lib); } @patch class AbstractClassInstantiationError { @pragma("vm:entry-point") AbstractClassInstantiationError._create( this._className, this._url, this._line, ); @patch String toString() { return "Cannot instantiate abstract class $_className: " "_url '$_url' line $_line"; } // These new fields cannot be declared final, because a constructor exists // in the original version of this patched class. String? _url; int _line = 0; } wNN<MOK+%$!I:5%)>@,&?>%%@3 2-." !G., L )L C;H$H.4B(+ =$M4%dart:mirrors-patch/mirrors_patch.dartÀeÀh³Worg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/mirrors_impl.dartÀ´â// 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 "mirrors_patch.dart"; var _dirty = false; // Set to true by the VM when more libraries are loaded. class _InternalMirrorError extends Error { final String _msg; _InternalMirrorError(this._msg); String toString() => _msg; } String _n(Symbol symbol) => internal.Symbol.getName(symbol as internal.Symbol); Symbol _s(String name) { return internal.Symbol.unvalidated(name); } Symbol? _sOpt(String? name) { if (name == null) return null; return internal.Symbol.unvalidated(name); } Symbol _computeQualifiedName(DeclarationMirror? owner, Symbol simpleName) { if (owner == null) return simpleName; return _s('${_n(owner.qualifiedName)}.${_n(simpleName)}'); } String _makeSignatureString( TypeMirror returnType, List parameters, ) { StringBuffer buf = StringBuffer(); buf.write('('); bool found_optional_positional = false; bool found_optional_named = false; for (int i = 0; i < parameters.length; i++) { var param = parameters[i]; if (param.isOptional && param.isNamed && !found_optional_named) { buf.write('{'); found_optional_named = true; } if (param.isOptional && !param.isNamed && !found_optional_positional) { buf.write('['); found_optional_positional = true; } if (param.isNamed) { buf.write(_n(param.simpleName)); buf.write(': '); } buf.write(_n(param.type.qualifiedName)); if (i < (parameters.length - 1)) { buf.write(', '); } } if (found_optional_named) { buf.write('}'); } if (found_optional_positional) { buf.write(']'); } buf.write(') -> '); buf.write(_n(returnType.qualifiedName)); return buf.toString(); } @pragma("vm:external-name", "DeclarationMirror_location") external SourceLocation? _location(reflectee); @pragma("vm:external-name", "DeclarationMirror_metadata") external List _metadata(reflectee); List _wrapMetadata(List reflectees) { var mirrors = []; for (var reflectee in reflectees) { mirrors.add(reflect(reflectee)); } return UnmodifiableListView(mirrors); } @pragma("vm:external-name", "TypeMirror_subtypeTest") external bool _subtypeTest(Type a, Type b); class _MirrorSystem extends MirrorSystem { final TypeMirror dynamicType = _SpecialTypeMirror._('dynamic'); final TypeMirror voidType = _SpecialTypeMirror._('void'); final TypeMirror neverType = _SpecialTypeMirror._('Never'); var _libraries; Map get libraries { if ((_libraries == null) || _dirty) { _libraries = Map(); for (LibraryMirror lib in _computeLibraries()) { _libraries[lib.uri] = lib; } _libraries = UnmodifiableMapView(_libraries); _dirty = false; } return _libraries; } @pragma("vm:external-name", "MirrorSystem_libraries") external static List _computeLibraries(); IsolateMirror? _isolate; IsolateMirror get isolate { var i = _isolate; if (i != null) return i; return _isolate = _computeIsolate(); } @pragma("vm:external-name", "MirrorSystem_isolate") external static IsolateMirror _computeIsolate(); String toString() => "MirrorSystem for isolate '${isolate.debugName}'"; } class _SourceLocation implements SourceLocation { _SourceLocation._(uriString, this.line, this.column) : this.sourceUri = Uri.parse(uriString); // Line and column positions are 1-origin, or 0 if unknown. final int line; final int column; final Uri sourceUri; String toString() { return column == 0 ? "$sourceUri:$line" : "$sourceUri:$line:$column"; } } class _IsolateMirror extends Mirror implements IsolateMirror { final String debugName; final LibraryMirror rootLibrary; _IsolateMirror._(this.debugName, this.rootLibrary); bool get isCurrent => true; String toString() => "IsolateMirror on '$debugName'"; Future loadUri(Uri uri) async { var result = _loadUri(uri.toString()); if (result == null) { // Censored library. throw Exception("Cannot load $uri"); } return result; } @pragma("vm:external-name", "IsolateMirror_loadUri") external static LibraryMirror? _loadUri(String uri); } class _SyntheticAccessor implements MethodMirror { final DeclarationMirror owner; final Symbol simpleName; final bool isGetter; final bool isStatic; final bool isTopLevel; final _target; _SyntheticAccessor( this.owner, this.simpleName, this.isGetter, this.isStatic, this.isTopLevel, this._target, ); bool get isSynthetic => true; bool get isRegularMethod => false; bool get isOperator => false; bool get isConstructor => false; bool get isConstConstructor => false; bool get isGenerativeConstructor => false; bool get isFactoryConstructor => false; bool get isExternal => false; bool get isRedirectingConstructor => false; bool get isAbstract => false; bool get isExtensionMember => false; bool get isExtensionTypeMember => false; bool get isSetter => !isGetter; bool get isPrivate => _n(simpleName).startsWith('_'); Symbol get qualifiedName => _computeQualifiedName(owner, simpleName); Symbol get constructorName => Symbol.empty; TypeMirror get returnType => _target.type; List get parameters { if (isGetter) return const []; return UnmodifiableListView([ _SyntheticSetterParameter(this, this._target), ]); } SourceLocation? get location => null; List get metadata => const []; String? get source => null; } class _SyntheticSetterParameter implements ParameterMirror { final DeclarationMirror owner; final VariableMirror _target; _SyntheticSetterParameter(this.owner, this._target); Symbol get simpleName => _target.simpleName; Symbol get qualifiedName => _computeQualifiedName(owner, simpleName); TypeMirror get type => _target.type; bool get isOptional => false; bool get isNamed => false; bool get isStatic => false; bool get isTopLevel => false; bool get isConst => false; bool get isFinal => true; bool get isPrivate => false; bool get isExtensionMember => false; bool get isExtensionTypeMember => false; bool get hasDefaultValue => false; InstanceMirror? get defaultValue => null; SourceLocation? get location => null; List get metadata => const []; } abstract class _ObjectMirror extends Mirror implements ObjectMirror { _invoke(reflectee, functionName, arguments, argumentNames); _invokeGetter(reflectee, getterName); _invokeSetter(reflectee, setterName, value); final _reflectee; // May be a MirrorReference or an ordinary object. _ObjectMirror._(this._reflectee); InstanceMirror invoke( Symbol memberName, List positionalArguments, [ Map namedArguments = const {}, ]) { int numPositionalArguments = positionalArguments.length; int numNamedArguments = namedArguments.length; int numArguments = numPositionalArguments + numNamedArguments; List arguments = List.filled(numArguments, null); arguments.setRange(0, numPositionalArguments, positionalArguments); List names = List.filled(numNamedArguments, null); int argumentIndex = numPositionalArguments; int nameIndex = 0; if (numNamedArguments > 0) { namedArguments.forEach((name, value) { arguments[argumentIndex++] = value; names[nameIndex++] = _n(name); }); } return reflect(this._invoke(_reflectee, _n(memberName), arguments, names)); } InstanceMirror getField(Symbol memberName) { return reflect(this._invokeGetter(_reflectee, _n(memberName))); } InstanceMirror setField(Symbol memberName, dynamic value) { this._invokeSetter(_reflectee, _n(memberName), value); return reflect(value); } delegate(Invocation invocation) { if (invocation.isMethod) { return this .invoke( invocation.memberName, invocation.positionalArguments, invocation.namedArguments, ) .reflectee; } if (invocation.isGetter) { return this.getField(invocation.memberName).reflectee; } if (invocation.isSetter) { var unwrapped = _n(invocation.memberName); var withoutEqual = _s(unwrapped.substring(0, unwrapped.length - 1)); var arg = invocation.positionalArguments[0]; this.setField(withoutEqual, arg).reflectee; return arg; } throw "UNREACHABLE"; } } class _InstanceMirror extends _ObjectMirror implements InstanceMirror { _InstanceMirror._(reflectee) : super._(reflectee); ClassMirror? _type; ClassMirror get type { var t = _type; if (t != null) return t; // Note it not safe to use reflectee.runtimeType because runtimeType may // be overridden. return _type = reflectType(_computeType(reflectee)) as ClassMirror; } // LocalInstanceMirrors always reflect local instances bool get hasReflectee => true; get reflectee => _reflectee; String toString() => 'InstanceMirror on ${Error.safeToString(_reflectee)}'; bool operator ==(Object other) { return other is _InstanceMirror && identical(_reflectee, other._reflectee); } int get hashCode { // Avoid hash collisions with the reflectee. This constant is in Smi range // and happens to be the inner padding from RFC 2104. return identityHashCode(_reflectee) ^ 0x36363636; } InstanceMirror getField(Symbol memberName) { return reflect(_invokeGetter(_reflectee, _n(memberName))); } InstanceMirror setField(Symbol memberName, dynamic arg) { _invokeSetter(_reflectee, _n(memberName), arg); return reflect(arg); } // Override to include the receiver in the arguments. InstanceMirror invoke( Symbol memberName, List positionalArguments, [ Map namedArguments = const {}, ]) { int numPositionalArguments = positionalArguments.length + 1; // Receiver. int numNamedArguments = namedArguments.length; int numArguments = numPositionalArguments + numNamedArguments; List arguments = List.filled(numArguments, null); arguments[0] = _reflectee; // Receiver. arguments.setRange(1, numPositionalArguments, positionalArguments); List names = List.filled(numNamedArguments, null); int argumentIndex = numPositionalArguments; int nameIndex = 0; if (numNamedArguments > 0) { namedArguments.forEach((name, value) { arguments[argumentIndex++] = value; names[nameIndex++] = _n(name); }); } return reflect(this._invoke(_reflectee, _n(memberName), arguments, names)); } @pragma("vm:external-name", "InstanceMirror_invoke") external _invoke(reflectee, functionName, arguments, argumentNames); @pragma("vm:external-name", "InstanceMirror_invokeGetter") external _invokeGetter(reflectee, getterName); @pragma("vm:external-name", "InstanceMirror_invokeSetter") external _invokeSetter(reflectee, setterName, value); @pragma("vm:external-name", "InstanceMirror_computeType") external static _computeType(reflectee); } class _ClosureMirror extends _InstanceMirror implements ClosureMirror { _ClosureMirror._(reflectee) : super._(reflectee); MethodMirror? _function; MethodMirror get function { var f = _function; if (f != null) return f; return _function = _computeFunction(reflectee); } InstanceMirror apply( List positionalArguments, [ Map namedArguments = const {}, ]) { return this.invoke(#call, positionalArguments, namedArguments); } String toString() => "ClosureMirror on '${Error.safeToString(_reflectee)}'"; @pragma("vm:external-name", "ClosureMirror_function") external static _computeFunction(reflectee); } abstract class _TypeMirror { Type get _reflectedType; } class _ClassMirror extends _ObjectMirror implements ClassMirror, _TypeMirror { final Type _reflectedType; Symbol? _simpleName; DeclarationMirror? _owner; final bool isAbstract; final bool _isGeneric; // Since Dart 2, mixins are erased by kernel transformation. // Resulting classes have this flag set, and mixed-in type is pulled into // the end of interfaces list. final bool _isTransformedMixinApplication; final bool _isGenericDeclaration; final bool isEnum; Type _instantiator; _ClassMirror._( reflectee, reflectedType, String? simpleName, this._owner, this.isAbstract, this._isGeneric, this._isTransformedMixinApplication, this._isGenericDeclaration, this.isEnum, ) : this._simpleName = _sOpt(simpleName), this._reflectedType = reflectedType, this._instantiator = reflectedType, super._(reflectee); bool get hasReflectedType => !_isGenericDeclaration; Type get reflectedType { if (!hasReflectedType) { throw UnsupportedError("Declarations of generics have no reflected type"); } return _reflectedType; } Symbol get simpleName { // All but anonymous mixin applications have their name set at construction. var n = _simpleName; if (n != null) return n; return _simpleName = this._mixinApplicationName; } Symbol? _qualifiedName; Symbol get qualifiedName { var n = _qualifiedName; if (n != null) return n; return _qualifiedName = _computeQualifiedName(owner, simpleName); } DeclarationMirror? get owner { var o = _owner; if (o != null) return o; var uri = _ClassMirror._libraryUri(_reflectee); return _owner = currentMirrorSystem().libraries[Uri.parse(uri)]; } bool get isPrivate => _n(simpleName).startsWith('_'); bool get isTopLevel => true; SourceLocation? get location { return _location(_reflectee); } _ClassMirror? _trueSuperclassField; _ClassMirror? get _trueSuperclass { if (_trueSuperclassField == null) { Type? supertype = isOriginalDeclaration ? _supertype(_reflectedType) : _supertypeInstantiated(_reflectedType); if (supertype == null) { // Object has no superclass. return null; } var supertypeMirror = reflectType(supertype) as _ClassMirror; supertypeMirror._instantiator = _instantiator; _trueSuperclassField = supertypeMirror; } return _trueSuperclassField; } ClassMirror? get superclass { return _trueSuperclass; } var _superinterfaces; List get superinterfaces { var i = _superinterfaces; if (i != null) return i; var interfaceTypes = isOriginalDeclaration ? _nativeInterfaces(_reflectedType) : _nativeInterfacesInstantiated(_reflectedType); if (_isTransformedMixinApplication) { interfaceTypes = interfaceTypes.sublist(0, interfaceTypes.length - 1); } var interfaceMirrors = []; for (var interfaceType in interfaceTypes) { interfaceMirrors.add(reflectType(interfaceType) as ClassMirror); } return _superinterfaces = UnmodifiableListView( interfaceMirrors, ); } Symbol get _mixinApplicationName { var mixins = []; var klass = this; while (_nativeMixin(klass._reflectedType) != null) { mixins.add(klass.mixin); klass = klass.superclass as _ClassMirror; } return _s( _n(klass.qualifiedName) + ' with ' + mixins.reversed .map((ClassMirror m) => _n(m.qualifiedName)) .join(', '), ); } ClassMirror? _mixin; ClassMirror get mixin { var m = _mixin; if (m != null) return m; Type? mixinType = _nativeMixinInstantiated(_reflectedType, _instantiator); if (mixinType == null) { // The reflectee is not a mixin application. return _mixin = this; } else { return _mixin = reflectType(mixinType) as ClassMirror; } } var _cachedStaticMembers; Map get staticMembers { var m = _cachedStaticMembers; if (m != null) m; var result = Map(); var library = this.owner as LibraryMirror; declarations.values.forEach((decl) { if (decl is MethodMirror && decl.isStatic && !decl.isConstructor) { result[decl.simpleName] = decl; } if (decl is VariableMirror && decl.isStatic) { var getterName = decl.simpleName; result[getterName] = _SyntheticAccessor( this, getterName, true, true, false, decl, ); if (!decl.isFinal) { var setterName = _asSetter(decl.simpleName, library); result[setterName] = _SyntheticAccessor( this, setterName, false, true, false, decl, ); } } }); return _cachedStaticMembers = UnmodifiableMapView( result, ); } var _cachedInstanceMembers; Map get instanceMembers { var m = _cachedInstanceMembers; if (m != null) return m; var result = Map(); var library = this.owner as LibraryMirror; var sup = superclass; if (sup != null) { result.addAll(sup.instanceMembers); } declarations.values.forEach((decl) { if (decl is MethodMirror && !decl.isStatic && !decl.isConstructor && !decl.isAbstract) { result[decl.simpleName] = decl; } if (decl is VariableMirror && !decl.isStatic) { var getterName = decl.simpleName; result[getterName] = _SyntheticAccessor( this, getterName, true, false, false, decl, ); if (!decl.isFinal) { var setterName = _asSetter(decl.simpleName, library); result[setterName] = _SyntheticAccessor( this, setterName, false, false, false, decl, ); } } }); return _cachedInstanceMembers = UnmodifiableMapView( result, ); } Map? _declarations; Map get declarations { var d = _declarations; if (d != null) return d; var decls = Map(); var members = _computeMembers(mixin, _instantiator, _reflectee); for (var member in members) { decls[member.simpleName] = member; } var constructors = _computeConstructors(_instantiator, _reflectee); var stringName = _n(simpleName); for (var constructor in constructors) { constructor._patchConstructorName(stringName); decls[constructor.simpleName] = constructor; } for (var typeVariable in typeVariables) { decls[typeVariable.simpleName] = typeVariable; } return _declarations = UnmodifiableMapView( decls, ); } // Note: returns correct result only for Dart 1 anonymous mixin applications. bool get _isAnonymousMixinApplication { if (mixin == this) return false; // Not a mixin application. return true; } List? _typeVariables; List get typeVariables { var v = _typeVariables; if (v != null) return v; if (!_isTransformedMixinApplication && _isAnonymousMixinApplication) { return _typeVariables = const []; } var result = []; List params = _ClassMirror_type_variables(_reflectee); ClassMirror owner = originalDeclaration; var mirror; for (var i = 0; i < params.length; i += 2) { mirror = _TypeVariableMirror._(params[i + 1], params[i], owner); result.add(mirror); } return _typeVariables = UnmodifiableListView(result); } List? _typeArguments; List get typeArguments { var a = _typeArguments; if (a != null) return a; if (_isGenericDeclaration || (!_isTransformedMixinApplication && _isAnonymousMixinApplication)) { return _typeArguments = const []; } else { return _typeArguments = UnmodifiableListView( _computeTypeArguments(_reflectedType).cast(), ); } } bool get isOriginalDeclaration => !_isGeneric || _isGenericDeclaration; ClassMirror get originalDeclaration { if (isOriginalDeclaration) { return this; } else { return reflectClass(_reflectedType); } } String toString() => "ClassMirror on '${MirrorSystem.getName(simpleName)}'"; InstanceMirror newInstance( Symbol constructorName, List positionalArguments, [ Map namedArguments = const {}, ]) { if (isEnum) throw UnsupportedError("Cannot instantiate enums"); // Native code will add the 1 or 2 implicit arguments depending on whether // we end up invoking a factory or constructor respectively. int numPositionalArguments = positionalArguments.length; int numNamedArguments = namedArguments.length; int numArguments = numPositionalArguments + numNamedArguments; List arguments = List.filled(numArguments, null); arguments.setRange(0, numPositionalArguments, positionalArguments); List names = List.filled(numNamedArguments, null); int argumentIndex = numPositionalArguments; int nameIndex = 0; if (numNamedArguments > 0) { namedArguments.forEach((name, value) { arguments[argumentIndex++] = value; names[nameIndex++] = _n(name); }); } return reflect( _invokeConstructor( _reflectee, _reflectedType, _n(constructorName), arguments, names, ), ); } List get metadata { return _wrapMetadata(_metadata(_reflectee)); } bool operator ==(Object other) { return other is _ClassMirror && this._reflectee == other._reflectee && this._reflectedType == other._reflectedType && this._isGenericDeclaration == other._isGenericDeclaration; } int get hashCode => simpleName.hashCode; bool isSubtypeOf(TypeMirror other) { if (other == currentMirrorSystem().dynamicType) return true; if (other == currentMirrorSystem().voidType) return true; if (other == currentMirrorSystem().neverType) return false; return _subtypeTest(_reflectedType, (other as _TypeMirror)._reflectedType); } bool isAssignableTo(TypeMirror other) { if (other == currentMirrorSystem().dynamicType) return true; if (other == currentMirrorSystem().voidType) return true; if (other == currentMirrorSystem().neverType) return false; final otherReflectedType = (other as _TypeMirror)._reflectedType; return _subtypeTest(_reflectedType, otherReflectedType) || _subtypeTest(otherReflectedType, _reflectedType); } bool isSubclassOf(ClassMirror other) { ClassMirror otherDeclaration = other.originalDeclaration as ClassMirror; ClassMirror? c = this; while (c != null) { c = c.originalDeclaration as ClassMirror; if (c == otherDeclaration) return true; c = c.superclass as ClassMirror?; } return false; } @pragma("vm:external-name", "ClassMirror_libraryUri") external static String _libraryUri(reflectee); @pragma("vm:external-name", "ClassMirror_supertype") external static Type? _supertype(reflectedType); @pragma("vm:external-name", "ClassMirror_supertype_instantiated") external static Type? _supertypeInstantiated(reflectedType); @pragma("vm:external-name", "ClassMirror_interfaces") external static List _nativeInterfaces(reflectedType); @pragma("vm:external-name", "ClassMirror_interfaces_instantiated") external static List _nativeInterfacesInstantiated(reflectedType); @pragma("vm:external-name", "ClassMirror_mixin") external static Type? _nativeMixin(reflectedType); @pragma("vm:external-name", "ClassMirror_mixin_instantiated") external static Type? _nativeMixinInstantiated(reflectedType, instantiator); @pragma("vm:external-name", "ClassMirror_members") external static List _computeMembers(owner, reflectee, instantiator); @pragma("vm:external-name", "ClassMirror_constructors") external List _computeConstructors(reflectee, instantiator); @pragma("vm:external-name", "ClassMirror_invoke") external _invoke(reflectee, memberName, arguments, argumentNames); @pragma("vm:external-name", "ClassMirror_invokeGetter") external _invokeGetter(reflectee, getterName); @pragma("vm:external-name", "ClassMirror_invokeSetter") external _invokeSetter(reflectee, setterName, value); @pragma("vm:external-name", "ClassMirror_invokeConstructor") external static _invokeConstructor( reflectee, type, constructorName, arguments, argumentNames, ); @pragma("vm:external-name", "ClassMirror_type_variables") external static List _ClassMirror_type_variables(reflectee); @pragma("vm:external-name", "ClassMirror_type_arguments") external static List _computeTypeArguments(reflectee); } class _FunctionTypeMirror extends _ClassMirror implements FunctionTypeMirror { final _signatureReflectee; _FunctionTypeMirror._(reflectee, this._signatureReflectee, reflectedType) : super._( reflectee, reflectedType, null, null, false, false, false, false, false, ); bool get _isAnonymousMixinApplication => false; // FunctionTypeMirrors have a simpleName generated from their signature. Symbol? _simpleName; Symbol get simpleName { var n = _simpleName; if (n != null) return n; return _simpleName = _s(_makeSignatureString(returnType, parameters)); } MethodMirror? _callMethod; MethodMirror get callMethod { var m = _callMethod; if (m != null) return m; return _callMethod = _FunctionTypeMirror_call_method(_signatureReflectee); } TypeMirror? _returnType; TypeMirror get returnType { var t = _returnType; if (t != null) return t; return _returnType = reflectType( _FunctionTypeMirror_return_type(_signatureReflectee), ); } List? _parameters; List get parameters { var p = _parameters; if (p != null) return p; return _parameters = UnmodifiableListView( _FunctionTypeMirror_parameters( _signatureReflectee, ).cast(), ); } bool get isOriginalDeclaration => true; ClassMirror get originalDeclaration => this; List get typeVariables => const []; List get typeArguments => const []; List get metadata => const []; SourceLocation? get location => null; String toString() => "FunctionTypeMirror on '${_n(simpleName)}'"; @pragma("vm:external-name", "FunctionTypeMirror_call_method") external MethodMirror _FunctionTypeMirror_call_method(signatureReflectee); @pragma("vm:external-name", "FunctionTypeMirror_return_type") external static Type _FunctionTypeMirror_return_type(signatureReflectee); @pragma("vm:external-name", "FunctionTypeMirror_parameters") external List _FunctionTypeMirror_parameters(signatureReflectee); } abstract class _DeclarationMirror extends Mirror implements DeclarationMirror { final _reflectee; Symbol _simpleName; _DeclarationMirror._(this._reflectee, this._simpleName); Symbol get simpleName => _simpleName; Symbol? _qualifiedName; Symbol get qualifiedName { var n = _qualifiedName; if (n != null) return n; return _qualifiedName = _computeQualifiedName(owner, simpleName); } bool get isPrivate => _n(simpleName).startsWith('_'); SourceLocation? get location { return _location(_reflectee); } List get metadata { return _wrapMetadata(_metadata(_reflectee)); } bool operator ==(Object other) { return other is _DeclarationMirror && this._reflectee == other._reflectee; } int get hashCode => simpleName.hashCode; } class _TypeVariableMirror extends _DeclarationMirror implements TypeVariableMirror, _TypeMirror { _TypeVariableMirror._(reflectee, String simpleName, this._owner) : super._(reflectee, _s(simpleName)); DeclarationMirror? _owner; DeclarationMirror get owner { var o = _owner; if (o != null) return o; return _owner = (_TypeVariableMirror_owner(_reflectee) as TypeMirror) .originalDeclaration; } bool get isStatic => false; bool get isTopLevel => false; TypeMirror? _upperBound; TypeMirror get upperBound { var b = _upperBound; if (b != null) return b; return _upperBound = reflectType( _TypeVariableMirror_upper_bound(_reflectee), ); } bool get hasReflectedType => false; Type get reflectedType { throw UnsupportedError('Type variables have no reflected type'); } Type get _reflectedType => _reflectee; List get typeVariables => const []; List get typeArguments => const []; bool get isOriginalDeclaration => true; TypeMirror get originalDeclaration => this; String toString() => "TypeVariableMirror on '${_n(simpleName)}'"; bool operator ==(Object other) { return other is TypeVariableMirror && simpleName == other.simpleName; // Type variables do not refer to owner. } int get hashCode => simpleName.hashCode; bool isSubtypeOf(TypeMirror other) { if (other == currentMirrorSystem().dynamicType) return true; if (other == currentMirrorSystem().voidType) return true; if (other == currentMirrorSystem().neverType) return false; return _subtypeTest(_reflectedType, (other as _TypeMirror)._reflectedType); } bool isAssignableTo(TypeMirror other) { if (other == currentMirrorSystem().dynamicType) return true; if (other == currentMirrorSystem().voidType) return true; if (other == currentMirrorSystem().neverType) return false; final otherReflectedType = (other as _TypeMirror)._reflectedType; return _subtypeTest(_reflectedType, otherReflectedType) || _subtypeTest(otherReflectedType, _reflectedType); } @pragma("vm:external-name", "TypeVariableMirror_owner") external static DeclarationMirror _TypeVariableMirror_owner(reflectee); @pragma("vm:external-name", "TypeVariableMirror_upper_bound") external static Type _TypeVariableMirror_upper_bound(reflectee); } Symbol _asSetter(Symbol getter, LibraryMirror library) { var unwrapped = MirrorSystem.getName(getter); return MirrorSystem.getSymbol('${unwrapped}=', library); } class _LibraryMirror extends _ObjectMirror implements LibraryMirror { final Symbol simpleName; final Uri uri; _LibraryMirror._(reflectee, String simpleName, String url) : this.simpleName = _s(simpleName), this.uri = Uri.parse(url), super._(reflectee); // The simple name and the qualified name are the same for a library. Symbol get qualifiedName => simpleName; DeclarationMirror? get owner => null; bool get isPrivate => false; bool get isTopLevel => false; Type? get _instantiator => null; Map? _declarations; Map get declarations { var d = _declarations; if (d != null) return d; var decls = Map(); var members = _computeMembers(_reflectee); for (var member in members) { decls[member.simpleName] = member; } return _declarations = UnmodifiableMapView( decls, ); } SourceLocation? get location { return _location(_reflectee); } List get metadata { return _wrapMetadata(_metadata(_reflectee)); } bool operator ==(Object other) { return other is _LibraryMirror && this._reflectee == other._reflectee; } int get hashCode => simpleName.hashCode; String toString() => "LibraryMirror on '${_n(simpleName)}'"; var _cachedLibraryDependencies; get libraryDependencies { var d = _cachedLibraryDependencies; if (d != null) return d; return _cachedLibraryDependencies = UnmodifiableListView( _libraryDependencies(_reflectee).cast(), ); } @pragma("vm:external-name", "LibraryMirror_libraryDependencies") external List _libraryDependencies(reflectee); @pragma("vm:external-name", "LibraryMirror_invoke") external _invoke(reflectee, memberName, arguments, argumentNames); @pragma("vm:external-name", "LibraryMirror_invokeGetter") external _invokeGetter(reflectee, getterName); @pragma("vm:external-name", "LibraryMirror_invokeSetter") external _invokeSetter(reflectee, setterName, value); @pragma("vm:external-name", "LibraryMirror_members") external List _computeMembers(reflectee); } class _LibraryDependencyMirror extends Mirror implements LibraryDependencyMirror { final LibraryMirror sourceLibrary; var _targetMirrorOrPrefix; final List combinators; final Symbol? prefix; final bool isImport; final bool isDeferred; final List metadata; _LibraryDependencyMirror._( this.sourceLibrary, this._targetMirrorOrPrefix, List mutableCombinators, prefixString, this.isImport, this.isDeferred, List unwrappedMetadata, ) : prefix = _sOpt(prefixString), combinators = UnmodifiableListView( mutableCombinators.cast(), ), metadata = _wrapMetadata(unwrappedMetadata); bool get isExport => !isImport; LibraryMirror? get targetLibrary { if (_targetMirrorOrPrefix is _LibraryMirror) { return _targetMirrorOrPrefix; } var mirrorOrNull = _tryUpgradePrefix(_targetMirrorOrPrefix); if (mirrorOrNull != null) { _targetMirrorOrPrefix = mirrorOrNull; } return mirrorOrNull; } Future loadLibrary() { if (_targetMirrorOrPrefix is _LibraryMirror) { return Future.value(_targetMirrorOrPrefix); } var savedPrefix = _targetMirrorOrPrefix; return savedPrefix.loadLibrary().then((_) { return _tryUpgradePrefix(savedPrefix); }); } @pragma("vm:external-name", "LibraryMirror_fromPrefix") external static LibraryMirror _tryUpgradePrefix(libraryPrefix); SourceLocation? get location => null; } class _CombinatorMirror extends Mirror implements CombinatorMirror { final List identifiers; final bool isShow; _CombinatorMirror._(identifierString, this.isShow) : this.identifiers = UnmodifiableListView([ _s(identifierString), ]); bool get isHide => !isShow; } class _MethodMirror extends _DeclarationMirror implements MethodMirror { final Type? _instantiator; final bool isStatic; final int _kindFlags; _MethodMirror._( reflectee, String simpleName, this._owner, this._instantiator, this.isStatic, this._kindFlags, ) : super._(reflectee, _s(simpleName)); static const kAbstract = 0; static const kGetter = 1; static const kSetter = 2; static const kConstructor = 3; static const kConstCtor = 4; static const kGenerativeCtor = 5; static const kRedirectingCtor = 6; static const kFactoryCtor = 7; static const kExternal = 8; static const kSynthetic = 9; static const kExtensionMember = 10; static const kExtensionTypeMember = 11; // These offsets much be kept in sync with those in mirrors.h. bool get isAbstract => 0 != (_kindFlags & (1 << kAbstract)); bool get isGetter => 0 != (_kindFlags & (1 << kGetter)); bool get isSetter => 0 != (_kindFlags & (1 << kSetter)); bool get isConstructor => 0 != (_kindFlags & (1 << kConstructor)); bool get isConstConstructor => 0 != (_kindFlags & (1 << kConstCtor)); bool get isGenerativeConstructor => 0 != (_kindFlags & (1 << kGenerativeCtor)); bool get isRedirectingConstructor => 0 != (_kindFlags & (1 << kRedirectingCtor)); bool get isFactoryConstructor => 0 != (_kindFlags & (1 << kFactoryCtor)); bool get isExternal => 0 != (_kindFlags & (1 << kExternal)); bool get isSynthetic => 0 != (_kindFlags & (1 << kSynthetic)); bool get isExtensionMember => 0 != (_kindFlags & (1 << kExtensionMember)); bool get isExtensionTypeMember => 0 != (_kindFlags & (1 << kExtensionTypeMember)); static const _operators = [ "%", "&", "*", "+", "-", "/", "<", "<<", // "<=", "==", ">", ">=", ">>", "[]", "[]=", "^", "|", "~", "unary-", "~/", ]; bool get isOperator => _operators.contains(_n(simpleName)); DeclarationMirror? _owner; DeclarationMirror get owner { // For nested closures it is possible, that the mirror for the owner has not // been created yet. var o = _owner; if (o != null) return o; return _owner = _MethodMirror_owner(_reflectee, _instantiator); } bool get isPrivate => _n(simpleName).startsWith('_') || _n(constructorName).startsWith('_'); bool get isTopLevel => owner is LibraryMirror; TypeMirror? _returnType; TypeMirror get returnType { var t = _returnType; if (t != null) return t; if (isConstructor) { return _returnType = owner as _ClassMirror; } else { return _returnType = reflectType( _MethodMirror_return_type(_reflectee, _instantiator), ); } } List? _parameters; List get parameters { var p = _parameters; if (p != null) return p; return _parameters = UnmodifiableListView( _MethodMirror_parameters(_reflectee).cast(), ); } bool get isRegularMethod => !isGetter && !isSetter && !isConstructor; Symbol? _constructorName; Symbol get constructorName { var n = _constructorName; if (n != null) return n; if (!isConstructor) { return _constructorName = _s(''); } else { var parts = MirrorSystem.getName(simpleName).split('.'); if (parts.length > 2) { throw _InternalMirrorError( 'Internal error in MethodMirror.constructorName: ' 'malformed name <$simpleName>', ); } else if (parts.length == 2) { LibraryMirror definingLibrary = owner.owner as _LibraryMirror; return _constructorName = MirrorSystem.getSymbol( parts[1], definingLibrary, ); } else { return _constructorName = _s(''); } } } String? get source => _MethodMirror_source(_reflectee); void _patchConstructorName(ownerName) { var cn = _n(constructorName); if (cn == '') { _simpleName = _s(ownerName); } else { _simpleName = _s(ownerName + "." + cn); } } String toString() => "MethodMirror on '${MirrorSystem.getName(simpleName)}'"; @pragma("vm:external-name", "MethodMirror_owner") external static dynamic _MethodMirror_owner(reflectee, instantiator); @pragma("vm:external-name", "MethodMirror_return_type") external static dynamic _MethodMirror_return_type(reflectee, instantiator); @pragma("vm:external-name", "MethodMirror_parameters") external List _MethodMirror_parameters(reflectee); @pragma("vm:external-name", "MethodMirror_source") external static String? _MethodMirror_source(reflectee); } class _VariableMirror extends _DeclarationMirror implements VariableMirror { final DeclarationMirror owner; final bool isStatic; final bool isFinal; final bool isConst; final bool isExtensionMember; final bool isExtensionTypeMember; _VariableMirror._( reflectee, String simpleName, this.owner, this._type, this.isStatic, this.isFinal, this.isConst, this.isExtensionMember, this.isExtensionTypeMember, ) : super._(reflectee, _s(simpleName)); bool get isTopLevel => owner is LibraryMirror; Type? get _instantiator { final o = owner; // Note: need local variable for promotion to happen. if (o is _ClassMirror) { return o._instantiator; } else if (o is _MethodMirror) { return o._instantiator; } else if (o is _LibraryMirror) { return o._instantiator; } else { throw UnsupportedError("unexpected owner ${owner}"); } } TypeMirror? _type; TypeMirror get type { var t = _type; if (t != null) return t; return _type = reflectType(_VariableMirror_type(_reflectee, _instantiator)); } String toString() => "VariableMirror on '${MirrorSystem.getName(simpleName)}'"; @pragma("vm:external-name", "VariableMirror_type") external static _VariableMirror_type(reflectee, instantiator); } class _ParameterMirror extends _VariableMirror implements ParameterMirror { final int _position; final bool isOptional; final bool isNamed; final List? _unmirroredMetadata; _ParameterMirror._( reflectee, String simpleName, DeclarationMirror owner, this._position, this.isOptional, this.isNamed, bool isFinal, this._defaultValueReflectee, this._unmirroredMetadata, ) : super._( reflectee, simpleName, owner, null, // We override the type. false, // isStatic does not apply. isFinal, false, // Not const. false, // Not extension member. false, // Not extension type member. ); Object? _defaultValueReflectee; InstanceMirror? _defaultValue; InstanceMirror? get defaultValue { if (!isOptional) { return null; } if (_defaultValue == null) { _defaultValue = reflect(_defaultValueReflectee); } return _defaultValue; } bool get hasDefaultValue => _defaultValueReflectee != null; SourceLocation? get location { throw UnsupportedError("ParameterMirror.location unimplemented"); } List get metadata { var m = _unmirroredMetadata; if (m == null) return const []; return _wrapMetadata(m); } TypeMirror? _type; TypeMirror get type { var t = _type; if (t != null) return t; return _type = reflectType( _ParameterMirror_type(_reflectee, _position, _instantiator), ); } String toString() => "ParameterMirror on '${_n(simpleName)}'"; @pragma("vm:external-name", "ParameterMirror_type") external static Type _ParameterMirror_type( _reflectee, _position, instantiator, ); } class _SpecialTypeMirror extends Mirror implements TypeMirror, DeclarationMirror { final Symbol simpleName; _SpecialTypeMirror._(String name) : simpleName = _s(name); bool get isPrivate => false; bool get isTopLevel => true; DeclarationMirror? get owner => null; SourceLocation? get location => null; List get metadata => const []; bool get hasReflectedType => simpleName == #dynamic; Type get reflectedType { if (simpleName == #dynamic) return dynamic; throw UnsupportedError("void has no reflected type"); } List get typeVariables => const []; List get typeArguments => const []; bool get isOriginalDeclaration => true; TypeMirror get originalDeclaration => this; Symbol get qualifiedName => simpleName; bool operator ==(Object other) { if (other is! _SpecialTypeMirror) { return false; } return this.simpleName == other.simpleName; } int get hashCode => simpleName.hashCode; String toString() => "TypeMirror on '${_n(simpleName)}'"; bool isSubtypeOf(TypeMirror other) { return simpleName == #dynamic || other is _SpecialTypeMirror; } bool isAssignableTo(TypeMirror other) { return simpleName == #dynamic || other is _SpecialTypeMirror; } } class _Mirrors { static MirrorSystem _currentMirrorSystem = _MirrorSystem(); static MirrorSystem currentMirrorSystem() { return _currentMirrorSystem; } // Creates a new local mirror for some Object. static InstanceMirror reflect(dynamic reflectee) { return reflectee is Function ? _ClosureMirror._(reflectee) : _InstanceMirror._(reflectee); } @pragma("vm:external-name", "Mirrors_makeLocalClassMirror") external static _ClassMirror _makeLocalClassMirror(Type key); @pragma("vm:external-name", "Mirrors_makeLocalTypeMirror") external static TypeMirror _makeLocalTypeMirror(Type key); @pragma("vm:external-name", "Mirrors_instantiateGenericType") external static Type _instantiateGenericType(Type key, typeArguments); static Expando<_ClassMirror> _declarationCache = Expando("ClassMirror"); static Expando _instantiationCache = Expando("TypeMirror"); static ClassMirror reflectClass(Type key) { var classMirror = _declarationCache[key]; if (classMirror == null) { classMirror = _makeLocalClassMirror(key); _declarationCache[key] = classMirror; if (!classMirror._isGeneric) { _instantiationCache[key] = classMirror; } } return classMirror; } static TypeMirror reflectType(Type key, [List? typeArguments]) { if (typeArguments != null) { key = _instantiateType(key, typeArguments); } var typeMirror = _instantiationCache[key]; if (typeMirror == null) { typeMirror = _makeLocalTypeMirror(key); _instantiationCache[key] = typeMirror; if (typeMirror is _ClassMirror && !typeMirror._isGeneric) { _declarationCache[key] = typeMirror; } } return typeMirror; } static Type _instantiateType(Type key, List typeArguments) { if (typeArguments.isEmpty) { throw ArgumentError.value( typeArguments, 'typeArguments', 'Type arguments list cannot be empty.', ); } return _instantiateGenericType(key, typeArguments.toList(growable: false)); } } …÷NN<M+#P,!,L(=$%*%0F#L('-'#+:/:-6$&%86,+B<>**.7#H85)63J27->J?#681++773! % #(-* . '+"8H.-)4D5(A=! 7/H'  '+%,(AF>(/G$ E=3C?H@0!-,' P/D>;$#,' =1K32H5MH9!N#PO:6/?<48 EN3C?,H@0!-,' P7G=1=8<+H44 EDO8/O?L!-$) ,+*7QQ5F!4E8!"&&(.'4%D5.! */,9*M,0GA%"90 ;O3 =0"./)J(5*1 @3 M2$./*)"!(6*1 @3 O142E")H%,53.5K P*A,/K<);-1GN$'!M4 @B J(! +O EDOA=3C?H@0!-,'  &1#$/7C+'A>@P*A>@F?:)M0.(8173D?8BEN35@O5Q:H4E:1:8?& A(D@M@L?MP;(F8!"&1#O+51C* J &3&E)N>*.D#J-+'A>@P*A>@F?::J@C90;F=(!H*( #142/")K !"&1#K+?"((7L C:6E<1<875.)%,' &%$<5 3"%3$A ,(32-0-:B(E"5@ I*!$%!&*A?;;EH&2'3L?AM$70.#> QDM12 (> &)@DH( ?$=* &G: *:*"# .P4H:N9>5;M! $ *1K%& ;QA5AL#!'+(- "!%!7>!F&!4 CA6.(/=((A70:N>*.*#(0+<'B*B>.!15!&(>@==@IKJ..0,%0I!2/.-B-E!!0 P$dart:mirrors-patch/mirrors_impl.dartÀeÀh³[org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/mirror_reference.dart�Ý// Copyright (c) 2013, 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 "mirrors_patch.dart"; @pragma("vm:entry-point") class _MirrorReference { factory _MirrorReference._uninstantiable() { throw "Unreachable"; } @pragma("vm:external-name", "MirrorReference_equals") external bool operator ==(Object other); } NN</8+(dart:mirrors-patch/mirror_reference.dartÀeÀh³Oorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/typed_data/typed_data.dartÀz// Copyright (c) 2013, 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. /// Lists that efficiently handle fixed sized data /// (for example, unsigned 8 byte integers) and SIMD numeric types. /// /// To use this library in your code: /// ```dart /// import 'dart:typed_data'; /// ``` /// {@category Core} /// {@canonicalFor dart:_internal.BytesBuilder} library dart.typed_data; import "dart:_internal" show Since, UnmodifiableListBase; export "dart:_internal" show BytesBuilder; /// A sequence of bytes underlying a typed data object. /// /// Used to process large quantities of binary or numerical data /// more efficiently using a typed view. /// /// It is a compile-time error for a class to attempt to extend or implement /// `ByteBuffer`. abstract final class ByteBuffer { /// The length of this byte buffer, in bytes. int get lengthInBytes; /// Creates a [Uint8List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Uint8List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes] and contains [length] bytes. /// If [length] is omitted, the range extends to the end of the buffer. /// /// The start index and length must describe a valid range of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `length` must not be negative, and /// * `offsetInBytes + length` must not be greater than [lengthInBytes]. Uint8List asUint8List([int offsetInBytes = 0, int? length]); /// Creates a [Int8List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Int8List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes] and contains [length] bytes. /// If [length] is omitted, the range extends to the end of the buffer. /// /// The start index and length must describe a valid range of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `length` must not be negative, and /// * `offsetInBytes + length` must not be greater than [lengthInBytes]. Int8List asInt8List([int offsetInBytes = 0, int? length]); /// Creates a [Uint8ClampedList] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Uint8ClampedList` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes] and contains [length] bytes. /// If [length] is omitted, the range extends to the end of the buffer. /// /// The start index and length must describe a valid range of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `length` must not be negative, and /// * `offsetInBytes + length` must not be greater than [lengthInBytes]. Uint8ClampedList asUint8ClampedList([int offsetInBytes = 0, int? length]); /// Creates a [Uint16List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Uint16List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 16-bit aligned, /// and contains [length] 16-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not even, the last byte can't be part of the view. /// /// The start index and length must describe a valid 16-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by two, /// * `length` must not be negative, and /// * `offsetInBytes + length * 2` must not be greater than [lengthInBytes]. Uint16List asUint16List([int offsetInBytes = 0, int? length]); /// Creates a [Int16List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Int16List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 16-bit aligned, /// and contains [length] 16-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not even, the last byte can't be part of the view. /// /// The start index and length must describe a valid 16-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by two, /// * `length` must not be negative, and /// * `offsetInBytes + length * 2` must not be greater than [lengthInBytes]. Int16List asInt16List([int offsetInBytes = 0, int? length]); /// Creates a [Uint32List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Uint32List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 32-bit aligned, /// and contains [length] 32-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by four, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 32-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by four, /// * `length` must not be negative, and /// * `offsetInBytes + length * 4` must not be greater than [lengthInBytes]. Uint32List asUint32List([int offsetInBytes = 0, int? length]); /// Creates a [Int32List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Int32List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 32-bit aligned, /// and contains [length] 32-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by four, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 32-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by four, /// * `length` must not be negative, and /// * `offsetInBytes + length * 4` must not be greater than [lengthInBytes]. Int32List asInt32List([int offsetInBytes = 0, int? length]); /// Creates a [Uint64List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Uint64List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 64-bit aligned, /// and contains [length] 64-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by eight, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 64-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by eight, /// * `length` must not be negative, and /// * `offsetInBytes + length * 8` must not be greater than [lengthInBytes]. Uint64List asUint64List([int offsetInBytes = 0, int? length]); /// Creates a [Int64List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Int64List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 64-bit aligned, /// and contains [length] 64-bit integers with /// the same endianness as the host ([Endian.host]). /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by eight, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 64-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by eight, /// * `length` must not be negative, and /// * `offsetInBytes + length * 8` must not be greater than [lengthInBytes]. Int64List asInt64List([int offsetInBytes = 0, int? length]); /// Creates a [Int32x4List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Int32x4List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 128-bit aligned, /// and contains [length] 128-bit integers. /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by 16, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 128-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by sixteen, /// * `length` must not be negative, and /// * `offsetInBytes + length * 16` must not be greater than [lengthInBytes]. Int32x4List asInt32x4List([int offsetInBytes = 0, int? length]); /// Creates a [Float32List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Float32List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 32-bit aligned, /// and contains [length] 32-bit integers. /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by four, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 32-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by four, /// * `length` must not be negative, and /// * `offsetInBytes + length * 4` must not be greater than [lengthInBytes]. Float32List asFloat32List([int offsetInBytes = 0, int? length]); /// Creates a [Float64List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Float64List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 64-bit aligned, /// and contains [length] 64-bit integers. /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by eight, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 64-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by eight, /// * `length` must not be negative, and /// * `offsetInBytes + length * 8` must not be greater than [lengthInBytes]. Float64List asFloat64List([int offsetInBytes = 0, int? length]); /// Creates a [Float32x4List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Float32x4List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 128-bit aligned, /// and contains [length] 128-bit integers. /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by 16, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 128-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by sixteen, /// * `length` must not be negative, and /// * `offsetInBytes + length * 16` must not be greater than [lengthInBytes]. Float32x4List asFloat32x4List([int offsetInBytes = 0, int? length]); /// Creates a [Float64x2List] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `Float64x2List` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes], which must be 128-bit aligned, /// and contains [length] 128-bit integers. /// If [length] is omitted, the range extends as far towards the end of /// the buffer as possible - /// if [lengthInBytes] is not divisible by 16, the last bytes can't be part /// of the view. /// /// The start index and length must describe a valid 128-bit aligned range /// of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `offsetInBytes` must be divisible by sixteen, /// * `length` must not be negative, and /// * `offsetInBytes + length * 16` must not be greater than [lengthInBytes]. Float64x2List asFloat64x2List([int offsetInBytes = 0, int? length]); /// Creates a [ByteData] _view_ of a region of this byte buffer. /// /// The view is backed by the bytes of this byte buffer. /// Any changes made to the `ByteData` will also change the buffer, /// and vice versa. /// /// The viewed region start at [offsetInBytes] and contains [length] bytes. /// If [length] is omitted, the range extends to the end of the buffer. /// /// The start index and length must describe a valid range of the buffer: /// /// * `offsetInBytes` must not be negative, /// * `length` must not be negative, and /// * `offsetInBytes + length` must not be greater than [lengthInBytes]. ByteData asByteData([int offsetInBytes = 0, int? length]); } /// A typed view of a sequence of bytes. /// /// It is a compile-time error for a class to attempt to extend or implement /// `TypedData`. abstract final class TypedData { /// The number of bytes in the representation of each element in this list. int get elementSizeInBytes; /// The offset of this view into the underlying byte buffer, in bytes. int get offsetInBytes; /// The length of this view, in bytes. int get lengthInBytes; /// The byte buffer associated with this object. ByteBuffer get buffer; } /// A [TypedData] fixed-length [List]-view on the bytes of [buffer]. @Since("3.5") abstract final class TypedDataList implements TypedData, List {} abstract final class _TypedIntList implements TypedDataList { /// The concatenation of this list and [other]. /// /// If other is also a typed-data integer list, the returned list will /// be a type-data integer list capable of containing all the elements of /// this list and of [other]. /// Otherwise the returned list will be a normal growable `List`. List operator +(List other); } abstract final class _TypedFloatList implements TypedDataList { /// The concatenation of this list and [other]. /// /// If other is also a typed-data floating point number list, /// the returned list will be a type-data float list capable of containing /// all the elements of this list and of [other]. /// Otherwise the returned list will be a normal growable `List`. List operator +(List other); } /// Endianness of number representation. /// /// The order of bytes in memory of a number representation, with /// [little] endian having the least significant byte first, and [big] endian /// (aka. network byte order) having the most significant byte first. /// /// The [host] endian is the native endianness of the underlying platform, /// and the default endianness used by typed-data lists, like [Uint16List], /// on this platform. Always one of [little] or [big] endian. /// /// Can be specified when accessing or updating a sequence of bytes using a /// [ByteData] view. The host endianness can be used if accessing larger /// numbers by their bytes, for example through a [Uint8List] view on /// a buffer written using an [Int64List] view of the same buffer.. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Endian`. final class Endian { final bool _littleEndian; const Endian._(this._littleEndian); static const Endian big = Endian._(false); static const Endian little = Endian._(true); static final Endian host = (ByteData.view(Uint16List.fromList([1]).buffer)).getInt8(0) == 1 ? little : big; } /// A fixed-length, random-access sequence of bytes that also provides random /// and unaligned access to the fixed-width integers and floating point /// numbers represented by those bytes. /// /// `ByteData` may be used to pack and unpack data from external sources /// (such as networks or files systems), and to process large quantities /// of numerical data more efficiently than would be possible /// with ordinary [List] implementations. /// `ByteData` can save space, by eliminating the need for object headers, /// and time, by eliminating the need for data copies. /// /// If data comes in as bytes, they can be converted to `ByteData` by /// sharing the same buffer. /// ```dart /// Uint8List bytes = ...; /// var blob = ByteData.sublistView(bytes); /// if (blob.getUint32(0, Endian.little) == 0x04034b50) { // Zip file marker /// ... /// } /// ``` /// /// Finally, `ByteData` may be used to intentionally reinterpret the bytes /// representing one arithmetic type as another. /// For example this code fragment determine what 32-bit signed integer /// is represented by the bytes of a 32-bit floating point number /// (both stored as big endian): /// ```dart /// var bdata = ByteData(8); /// bdata.setFloat32(0, 3.04); /// int huh = bdata.getInt32(0); // 0x40428f5c /// ``` /// /// It is a compile-time error for a class to attempt to extend or implement /// `ByteData`. abstract final class ByteData implements TypedData { /// Creates a [ByteData] of the specified length (in elements), all of /// whose bytes are initially zero. @pragma("vm:entry-point") external factory ByteData(int length); /// Creates an [ByteData] _view_ of the specified region in [buffer]. /// /// Changes in the [ByteData] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + [length] must be less than or /// equal to the length of [buffer]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `ByteData.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// ByteData.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [ByteData.sublistView] /// which includes this computation: /// ```dart /// ByteData.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory ByteData.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asByteData(offsetInBytes, length); } /// Creates a [ByteData] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. factory ByteData.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); return data.buffer.asByteData( data.offsetInBytes + start * elementSize, (end - start) * elementSize, ); } /// A read-only view of this [ByteData]. @Since("3.3") ByteData asUnmodifiableView(); /// The (possibly negative) integer represented by the byte at the /// specified [byteOffset] in this object, in two's complement binary /// representation. /// /// The return value will be between -128 and 127, inclusive. /// /// The [byteOffset] must be non-negative, and /// less than the length of this object. int getInt8(int byteOffset); /// Sets the byte at the specified [byteOffset] in this object to the /// two's complement binary representation of the specified [value], which /// must fit in a single byte. /// /// In other words, [value] must be between -128 and 127, inclusive. /// /// The [byteOffset] must be non-negative, and /// less than the length of this object. void setInt8(int byteOffset, int value); /// The positive integer represented by the byte at the specified /// [byteOffset] in this object, in unsigned binary form. /// /// The return value will be between 0 and 255, inclusive. /// /// The [byteOffset] must be non-negative, and /// less than the length of this object. int getUint8(int byteOffset); /// Sets the byte at the specified [byteOffset] in this object to the /// unsigned binary representation of the specified [value], which must fit /// in a single byte. /// /// In other words, [value] must be between 0 and 255, inclusive. /// /// The [byteOffset] must be non-negative, and /// less than the length of this object. void setUint8(int byteOffset, int value); /// The (possibly negative) integer represented by the two bytes at /// the specified [byteOffset] in this object, in two's complement binary /// form. /// /// The return value will be between -215 and 215 - 1, /// inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 2` must be less than or equal to the length of this object. int getInt16(int byteOffset, [Endian endian = Endian.big]); /// Sets the two bytes starting at the specified [byteOffset] in this /// object to the two's complement binary representation of the specified /// [value], which must fit in two bytes. /// /// In other words, [value] must lie /// between -215 and 215 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 2` must be less than or equal to the length of this object. void setInt16(int byteOffset, int value, [Endian endian = Endian.big]); /// The positive integer represented by the two bytes starting /// at the specified [byteOffset] in this object, in unsigned binary /// form. /// /// The return value will be between 0 and 216 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 2` must be less than or equal to the length of this object. int getUint16(int byteOffset, [Endian endian = Endian.big]); /// Sets the two bytes starting at the specified [byteOffset] in this object /// to the unsigned binary representation of the specified [value], /// which must fit in two bytes. /// /// In other words, [value] must be between /// 0 and 216 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 2` must be less than or equal to the length of this object. void setUint16(int byteOffset, int value, [Endian endian = Endian.big]); /// The (possibly negative) integer represented by the four bytes at /// the specified [byteOffset] in this object, in two's complement binary /// form. /// /// The return value will be between -231 and 231 - 1, /// inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. int getInt32(int byteOffset, [Endian endian = Endian.big]); /// Sets the four bytes starting at the specified [byteOffset] in this /// object to the two's complement binary representation of the specified /// [value], which must fit in four bytes. /// /// In other words, [value] must lie /// between -231 and 231 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. void setInt32(int byteOffset, int value, [Endian endian = Endian.big]); /// The positive integer represented by the four bytes starting /// at the specified [byteOffset] in this object, in unsigned binary /// form. /// /// The return value will be between 0 and 232 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. int getUint32(int byteOffset, [Endian endian = Endian.big]); /// Sets the four bytes starting at the specified [byteOffset] in this object /// to the unsigned binary representation of the specified [value], /// which must fit in four bytes. /// /// In other words, [value] must be between /// 0 and 232 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. void setUint32(int byteOffset, int value, [Endian endian = Endian.big]); /// The (possibly negative) integer represented by the eight bytes at /// the specified [byteOffset] in this object, in two's complement binary /// form. /// /// The return value will be between -263 and 263 - 1, /// inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. int getInt64(int byteOffset, [Endian endian = Endian.big]); /// Sets the eight bytes starting at the specified [byteOffset] in this /// object to the two's complement binary representation of the specified /// [value], which must fit in eight bytes. /// /// In other words, [value] must lie /// between -263 and 263 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. void setInt64(int byteOffset, int value, [Endian endian = Endian.big]); /// The positive integer represented by the eight bytes starting /// at the specified [byteOffset] in this object, in unsigned binary /// form. /// /// The return value will be between 0 and 264 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. int getUint64(int byteOffset, [Endian endian = Endian.big]); /// Sets the eight bytes starting at the specified [byteOffset] in this object /// to the unsigned binary representation of the specified [value], /// which must fit in eight bytes. /// /// In other words, [value] must be between /// 0 and 264 - 1, inclusive. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. void setUint64(int byteOffset, int value, [Endian endian = Endian.big]); /// The floating point number represented by the four bytes at /// the specified [byteOffset] in this object, in IEEE 754 /// single-precision binary floating-point format (binary32). /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. double getFloat32(int byteOffset, [Endian endian = Endian.big]); /// Sets the four bytes starting at the specified [byteOffset] in this /// object to the IEEE 754 single-precision binary floating-point /// (binary32) representation of the specified [value]. /// /// **Note that this method can lose precision.** The input [value] is /// a 64-bit floating point value, which will be converted to 32-bit /// floating point value by IEEE 754 rounding rules before it is stored. /// If [value] cannot be represented exactly as a binary32, it will be /// converted to the nearest binary32 value. If two binary32 values are /// equally close, the one whose least significant bit is zero will be used. /// Note that finite (but large) values can be converted to infinity, and /// small non-zero values can be converted to zero. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 4` must be less than or equal to the length of this object. void setFloat32(int byteOffset, double value, [Endian endian = Endian.big]); /// The floating point number represented by the eight bytes at /// the specified [byteOffset] in this object, in IEEE 754 /// double-precision binary floating-point format (binary64). /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. double getFloat64(int byteOffset, [Endian endian = Endian.big]); /// Sets the eight bytes starting at the specified [byteOffset] in this /// object to the IEEE 754 double-precision binary floating-point /// (binary64) representation of the specified [value]. /// /// The [byteOffset] must be non-negative, and /// `byteOffset + 8` must be less than or equal to the length of this object. void setFloat64(int byteOffset, double value, [Endian endian = Endian.big]); } /// A fixed-length list of 8-bit signed integers. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low eight bits, /// interpreted as a signed 8-bit two's complement integer with values in the /// range -128 to +127. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Int8List`. abstract final class Int8List implements _TypedIntList { /// Creates an [Int8List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely [length] bytes. external factory Int8List(int length); /// Creates a [Int8List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely `elements.length` /// bytes. external factory Int8List.fromList(List elements); /// Creates an [Int8List] _view_ of the specified region in [buffer]. /// /// Changes in the [Int8List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Int8List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Int8List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Int8List.sublistView] /// which includes this computation: /// ```dart /// Int8List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Int8List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asInt8List(offsetInBytes, length); } /// Creates an [Int8List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. factory Int8List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); return data.buffer.asInt8List( data.offsetInBytes + start * elementSize, (end - start) * elementSize, ); } /// A read-only view of this [Int8List]; @Since("3.3") Int8List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is an `Int8List` containing the elements of this list at /// positions greater than or equal to [start] and less than [end] in the same /// order as they occur in this list. /// /// ```dart /// var numbers = Int8List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Int8List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Int8List sublist(int start, [int? end]); static const int bytesPerElement = 1; } /// A fixed-length list of 8-bit unsigned integers. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low eight bits, /// interpreted as an unsigned 8-bit integer with values in the /// range 0 to 255. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Uint8List`. abstract final class Uint8List implements _TypedIntList { /// Creates a [Uint8List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely [length] bytes. external factory Uint8List(int length); /// Creates a [Uint8List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely `elements.length` /// bytes. external factory Uint8List.fromList(List elements); /// Creates a [Uint8List] _view_ of the specified region in [buffer]. /// /// Changes in the [Uint8List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Uint8List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Uint8List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Uint8List.sublistView] /// which includes this computation: /// ```dart /// Uint8List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Uint8List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asUint8List(offsetInBytes, length); } /// Creates a [Uint8List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. factory Uint8List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); return data.buffer.asUint8List( data.offsetInBytes + start * elementSize, (end - start) * elementSize, ); } /// A read-only view of this [Uint8List]. @Since("3.3") Uint8List asUnmodifiableView(); /// Returns a concatenation of this list and [other]. /// /// If [other] is also a typed-data list, then the return list will be a /// typed data list capable of holding both unsigned 8-bit integers and /// the elements of [other], otherwise it'll be a normal list of integers. List operator +(List other); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Uint8List` containing the elements of this list at /// positions greater than or equal to [start] and less than [end] in the same /// order as they occur in this list. /// /// ```dart /// var numbers = Uint8List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Uint8List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Uint8List sublist(int start, [int? end]); static const int bytesPerElement = 1; } /// A fixed-length list of 8-bit unsigned integers. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are clamped to an unsigned eight bit value. /// That is, all values below zero are stored as zero /// and all values above 255 are stored as 255. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Uint8ClampedList`. abstract final class Uint8ClampedList implements _TypedIntList { /// Creates a [Uint8ClampedList] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely [length] bytes. external factory Uint8ClampedList(int length); /// Creates a [Uint8ClampedList] of the same size as the [elements] /// list and copies over the values clamping when needed. /// /// Values are clamped to fit in the list when they are copied, /// the same way storing values clamps them. /// /// The list is backed by a [ByteBuffer] containing precisely `elements.length` /// bytes. external factory Uint8ClampedList.fromList(List elements); /// Creates a [Uint8ClampedList] _view_ of the specified region in the /// specified byte [buffer]. /// /// Changes in the [Uint8List] will be visible in the byte buffer /// and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Uint8ClampedList.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Uint8ClampedList.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Uint8ClampedList.sublistView] /// which includes this computation: /// ```dart /// Uint8ClampedList.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Uint8ClampedList.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asUint8ClampedList(offsetInBytes, length); } /// Creates a [Uint8ClampedList] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. @Since("3.3") factory Uint8ClampedList.sublistView( TypedData data, [ int start = 0, int? end, ]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); return data.buffer.asUint8ClampedList( data.offsetInBytes + start * elementSize, (end - start) * elementSize, ); } /// A read-only view of this [Uint8ClampedList]. @Since("3.3") Uint8ClampedList asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Uint8ClampedList` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Uint8ClampedList.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Uint8ClampedList /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Uint8ClampedList sublist(int start, [int? end]); static const int bytesPerElement = 1; } /// A fixed-length list of 16-bit signed integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 16 bits, /// interpreted as a signed 16-bit two's complement integer with values in the /// range -32768 to +32767. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Int16List`. abstract final class Int16List implements _TypedIntList { /// Creates an [Int16List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 2 bytes. external factory Int16List(int length); /// Creates a [Int16List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 2 bytes. external factory Int16List.fromList(List elements); /// Creates an [Int16List] _view_ of the specified region in [buffer]. /// /// Changes in the [Int16List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Int16List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Int16List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Int16List.sublistView] /// which includes this computation: /// ```dart /// Int16List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Int16List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asInt16List(offsetInBytes, length); } /// Creates an [Int16List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of two. factory Int16List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asInt16List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Int16List]. @Since("3.3") Int16List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is an `Int16List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Int16List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Int16List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Int16List sublist(int start, [int? end]); static const int bytesPerElement = 2; } /// A fixed-length list of 16-bit unsigned integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 16 bits, /// interpreted as an unsigned 16-bit integer with values in the /// range 0 to 65535. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Uint16List`. abstract final class Uint16List implements _TypedIntList { /// Creates a [Uint16List] of the specified length (in elements), all /// of whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 2 bytes. external factory Uint16List(int length); /// Creates a [Uint16List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 2 bytes. external factory Uint16List.fromList(List elements); /// Creates a [Uint16List] _view_ of the specified region in /// the specified byte buffer. /// /// Changes in the [Uint16List] will be visible in the byte buffer /// and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Uint16List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Uint16List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Uint16List.sublistView] /// which includes this computation: /// ```dart /// Uint16List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Uint16List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asUint16List(offsetInBytes, length); } /// Creates a [Uint16List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of two. factory Uint16List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asUint16List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Uint16List]. @Since("3.3") Uint16List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Uint16List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Uint16List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Uint16List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Uint16List sublist(int start, [int? end]); static const int bytesPerElement = 2; } /// A fixed-length list of 32-bit signed integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 32 bits, /// interpreted as a signed 32-bit two's complement integer with values in the /// range -2147483648 to 2147483647. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Int32List`. abstract final class Int32List implements _TypedIntList { /// Creates an [Int32List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 4 bytes. external factory Int32List(int length); /// Creates a [Int32List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 4 bytes. external factory Int32List.fromList(List elements); /// Creates an [Int32List] _view_ of the specified region in [buffer]. /// /// Changes in the [Int32List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Int32List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Int32List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Int32List.sublistView] /// which includes this computation: /// ```dart /// Int32List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Int32List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asInt32List(offsetInBytes, length); } /// Creates an [Int32List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of four. factory Int32List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asInt32List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Int16List]. @Since("3.3") Int32List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is an `Int32List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Int32List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Int32List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Int32List sublist(int start, [int? end]); static const int bytesPerElement = 4; } /// A fixed-length list of 32-bit unsigned integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 32 bits, /// interpreted as an unsigned 32-bit integer with values in the /// range 0 to 4294967295. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Uint32List`. abstract final class Uint32List implements _TypedIntList { /// Creates a [Uint32List] of the specified length (in elements), all /// of whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 4 bytes. external factory Uint32List(int length); /// Creates a [Uint32List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 4 bytes. external factory Uint32List.fromList(List elements); /// Creates a [Uint32List] _view_ of the specified region in /// the specified byte buffer. /// /// Changes in the [Uint32List] will be visible in the byte buffer /// and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Uint32List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Uint32List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Uint32List.sublistView] /// which includes this computation: /// ```dart /// Uint32List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Uint32List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asUint32List(offsetInBytes, length); } /// Creates a [Uint32List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of four. factory Uint32List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asUint32List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Uint32List]. @Since("3.3") Uint32List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Uint32List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Uint32List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Uint32List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Uint32List sublist(int start, [int? end]); static const int bytesPerElement = 4; } /// A fixed-length list of 64-bit signed integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 64 bits, /// interpreted as a signed 64-bit two's complement integer with values in the /// range -9223372036854775808 to +9223372036854775807. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Int64List`. abstract final class Int64List implements _TypedIntList { /// Creates an [Int64List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 8 bytes. external factory Int64List(int length); /// Creates a [Int64List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 8 bytes. external factory Int64List.fromList(List elements); /// Creates an [Int64List] _view_ of the specified region in [buffer]. /// /// Changes in the [Int64List] will be visible in the byte buffer /// and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Int64List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Int64List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Int64List.sublistView] /// which includes this computation: /// ```dart /// Int64List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Int64List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asInt64List(offsetInBytes, length); } /// Creates an [Int64List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of eight. factory Int64List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asInt64List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Int64List]. @Since("3.3") Int64List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is an `Int64List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Int64List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Int64List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Int64List sublist(int start, [int? end]); static const int bytesPerElement = 8; } /// A fixed-length list of 64-bit unsigned integers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation can be considerably /// more space- and time-efficient than the default [List] implementation. /// /// Integers stored in the list are truncated to their low 64 bits, /// interpreted as an unsigned 64-bit integer with values in the /// range 0 to 18446744073709551615. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Uint64List`. abstract final class Uint64List implements _TypedIntList { /// Creates a [Uint64List] of the specified length (in elements), all /// of whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 8 bytes. external factory Uint64List(int length); /// Creates a [Uint64List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 8 bytes. external factory Uint64List.fromList(List elements); /// Creates an [Uint64List] _view_ of the specified region in /// the specified byte buffer. /// /// Changes in the [Uint64List] will be visible in the byte buffer /// and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Uint64List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Uint64List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Uint64List.sublistView] /// which includes this computation: /// ```dart /// Uint64List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Uint64List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asUint64List(offsetInBytes, length); } /// Creates a [Uint64List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of eight. factory Uint64List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asUint64List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Uint64List]. @Since("3.3") Uint64List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Uint64List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Uint64List.fromList([0, 1, 2, 3, 4]); /// print(numbers.sublist(1, 3)); // [1, 2] /// print(numbers.sublist(1, 3).runtimeType); // Uint64List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1, 2, 3, 4] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Uint64List sublist(int start, [int? end]); static const int bytesPerElement = 8; } /// A fixed-length list of IEEE 754 single-precision binary floating-point /// numbers that is viewable as a [TypedData]. /// /// For long lists, this /// implementation can be considerably more space- and time-efficient than /// the default [List] implementation. /// /// Double values stored in the list are converted to the nearest /// single-precision value. Values read are converted to a double /// value with the same value. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Float32List`. abstract final class Float32List implements _TypedFloatList { /// Creates a [Float32List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 4 bytes. external factory Float32List(int length); /// Creates a [Float32List] with the same length as the [elements] list /// and copies over the elements. /// /// Values are truncated to fit in the list when they are copied, /// the same way storing values truncates them. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 4 bytes. external factory Float32List.fromList(List elements); /// Creates a [Float32List] _view_ of the specified region in [buffer]. /// /// Changes in the [Float32List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Float32List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Float32List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Float32List.sublistView] /// which includes this computation: /// ```dart /// Float32List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Float32List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asFloat32List(offsetInBytes, length); } /// Creates an [Float32List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of four. factory Float32List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asFloat32List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Float32List]. @Since("3.3") Float32List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Float32List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Float32List.fromList([0.0, 1.0, 2.0, 3.0, 4.0]); /// print(numbers.sublist(1, 3)); // [1.0, 2.0] /// print(numbers.sublist(1, 3).runtimeType); // Float32List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1.0, 2.0, 3.0, 4.0] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Float32List sublist(int start, [int? end]); static const int bytesPerElement = 4; } /// A fixed-length list of IEEE 754 double-precision binary floating-point /// numbers that is viewable as a [TypedData]. /// /// For long lists, this /// implementation can be considerably more space- and time-efficient than /// the default [List] implementation. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Float64List`. abstract final class Float64List implements _TypedFloatList { /// Creates a [Float64List] of the specified length (in elements), all of /// whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 8 bytes. external factory Float64List(int length); /// Creates a [Float64List] with the same length as the [elements] list /// and copies over the elements. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 8 bytes. external factory Float64List.fromList(List elements); /// Creates a [Float64List] _view_ of the specified region in [buffer]. /// /// Changes in the [Float64List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Float64List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Float64List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Float64List.sublistView] /// which includes this computation: /// ```dart /// Float64List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Float64List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asFloat64List(offsetInBytes, length); } /// Creates a [Float64List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of eight. factory Float64List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asFloat64List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Float64List]. @Since("3.3") Float64List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Float64List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Float64List.fromList([0.0, 1.0, 2.0, 3.0, 4.0]); /// print(numbers.sublist(1, 3)); // [1.0, 2.0] /// print(numbers.sublist(1, 3).runtimeType); // Float64List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(1)); // [1.0, 2.0, 3.0, 4.0] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Float64List sublist(int start, [int? end]); static const int bytesPerElement = 8; } /// A fixed-length list of [Float32x4] numbers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation will be considerably more /// space- and time-efficient than the default [List] implementation. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Float32x4List`. abstract final class Float32x4List implements TypedDataList, TypedData { /// Creates a [Float32x4List] of the specified length (in elements), /// all of whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 16 bytes. external factory Float32x4List(int length); /// Creates a [Float32x4List] with the same length as the [elements] list /// and copies over the elements. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 16 bytes. external factory Float32x4List.fromList(List elements); /// Creates a [Float32x4List] _view_ of the specified region in [buffer]. /// /// Changes in the [Float32x4List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Float32x4List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Float32x4List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Float32x4List.sublistView] /// which includes this computation: /// ```dart /// Float32x4List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Float32x4List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asFloat32x4List(offsetInBytes, length); } /// Creates a [Float32x4List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of sixteen. factory Float32x4List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asFloat32x4List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Float32x4List]. @Since("3.3") Float32x4List asUnmodifiableView(); /// The concatenation of this list and [other]. /// /// If [other] is also a [Float32x4List], the result is a new [Float32x4List], /// otherwise the result is a normal growable `List`. List operator +(List other); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Float32x4List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Float32x4List.fromList([ /// Float32x4(0, 1, 2, 3), /// Float32x4(1, 2, 3, 4), /// Float32x4(2, 3, 4, 5), /// Float32x4(3, 4, 5, 6), /// Float32x4(4, 5, 6, 7), /// ]); /// print(numbers.sublist(1, 2)); // [Float32x4(1, 2, 3, 4)] /// print(numbers.sublist(1, 2).runtimeType); // Float32x4List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(4)); // [Float32x4(4, 5, 6, 7)] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Float32x4List sublist(int start, [int? end]); static const int bytesPerElement = 16; } /// A fixed-length list of [Int32x4] numbers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation will be considerably more /// space- and time-efficient than the default [List] implementation. abstract final class Int32x4List implements TypedDataList, TypedData { /// Creates a [Int32x4List] of the specified length (in elements), /// all of whose elements are initially zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 16 bytes. external factory Int32x4List(int length); /// Creates a [Int32x4List] with the same length as the [elements] list /// and copies over the elements. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 16 bytes. external factory Int32x4List.fromList(List elements); /// Creates a [Int32x4List] _view_ of the specified region in [buffer]. /// /// Changes in the [Int32x4List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Int32x4List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Int32x4List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Int32x4List.sublistView] /// which includes this computation: /// ```dart /// Int32x4List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Int32x4List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asInt32x4List(offsetInBytes, length); } /// Creates an [Int32x4List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of sixteen. factory Int32x4List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asInt32x4List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Int32x4List]. @Since("3.3") Int32x4List asUnmodifiableView(); /// The concatenation of this list and [other]. /// /// If [other] is also a [Int32x4List], the result is a new [Int32x4List], /// otherwise the result is a normal growable `List`. List operator +(List other); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is an `Int32x4List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Int32x4List.fromList([ /// Int32x4(0, 1, 2, 3), /// Int32x4(1, 2, 3, 4), /// Int32x4(2, 3, 4, 5), /// Int32x4(3, 4, 5, 6), /// Int32x4(4, 5, 6, 7), /// ]); /// print(numbers.sublist(1, 2)); // [Int32x4(1, 2, 3, 4)] /// print(numbers.sublist(1, 2).runtimeType); // Int32x4List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(3)); // [Int32x4(3, 4, 5, 6), Int32x4(4, 5, 6, 7)] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Int32x4List sublist(int start, [int? end]); static const int bytesPerElement = 16; } /// A fixed-length list of [Float64x2] numbers that is viewable as a /// [TypedData]. /// /// For long lists, this implementation will be considerably more /// space- and time-efficient than the default [List] implementation. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Float64x2List`. abstract final class Float64x2List implements TypedDataList, TypedData { /// Creates a [Float64x2List] of the specified length (in elements), /// all of whose elements have all lanes set to zero. /// /// The list is backed by a [ByteBuffer] containing precisely /// [length] times 16 bytes. external factory Float64x2List(int length); /// Creates a [Float64x2List] with the same length as the [elements] list /// and copies over the elements. /// /// The list is backed by a [ByteBuffer] containing precisely /// `elements.length` times 16 bytes. external factory Float64x2List.fromList(List elements); /// The concatenation of this list and [other]. /// /// If [other] is also a [Float64x2List], the result is a new [Float64x2List], /// otherwise the result is a normal growable `List`. List operator +(List other); /// Creates a [Float64x2List] _view_ of the specified region in [buffer]. /// /// Changes in the [Float64x2List] will be visible in the byte /// buffer and vice versa. /// If the [offsetInBytes] index of the region is not specified, /// it defaults to zero (the first byte in the byte buffer). /// If the length is not provided, /// the view extends to the end of the byte buffer. /// /// The [offsetInBytes] and [length] must be non-negative, and /// [offsetInBytes] + ([length] * [bytesPerElement]) must be less than or /// equal to the length of [buffer]. /// /// The [offsetInBytes] must be a multiple of [bytesPerElement]. /// /// Note that when creating a view from a [TypedData] list or byte data, /// that list or byte data may itself be a view on a larger buffer /// with a [TypedData.offsetInBytes] greater than zero. /// Merely doing `Float64x2List.view(other.buffer, 0, count)` may not /// point to the bytes you intended. Instead you may need to do: /// ```dart /// Float64x2List.view(other.buffer, other.offsetInBytes, count) /// ``` /// Alternatively, use [Float64x2List.sublistView] /// which includes this computation: /// ```dart /// Float64x2List.sublistView(other, 0, count); /// ``` /// (The third argument is an end index rather than a length, so if /// you start from a position greater than zero, you need not /// reduce the count correspondingly). factory Float64x2List.view( ByteBuffer buffer, [ int offsetInBytes = 0, int? length, ]) { return buffer.asFloat64x2List(offsetInBytes, length); } /// Creates an [Float64x2List] view on a range of elements of [data]. /// /// Creates a view on the range of `data.buffer` which corresponds /// to the elements of [data] from [start] until [end]. /// If [data] is a typed data list, like [Uint16List], then the view is on /// the bytes of the elements with indices from [start] until [end]. /// If [data] is a [ByteData], it's treated like a list of bytes. /// /// If provided, [start] and [end] must satisfy /// /// 0 ≤ `start` ≤ `end` ≤ *elementCount* /// /// where *elementCount* is the number of elements in [data], which /// is the same as the [List.length] of a typed data list. /// /// If omitted, [start] defaults to zero and [end] to *elementCount*. /// /// The start and end indices of the range of bytes being viewed must be /// multiples of sixteen. factory Float64x2List.sublistView(TypedData data, [int start = 0, int? end]) { int elementSize = data.elementSizeInBytes; end = RangeError.checkValidRange( start, end, data.lengthInBytes ~/ elementSize, ); int byteLength = (end - start) * elementSize; if (byteLength % bytesPerElement != 0) { throw ArgumentError( "The number of bytes to view must be a multiple of " + "$bytesPerElement", ); } return data.buffer.asFloat64x2List( data.offsetInBytes + start * elementSize, byteLength ~/ bytesPerElement, ); } /// A read-only view of this [Float64x2List]. @Since("3.3") Float64x2List asUnmodifiableView(); /// Creates a new list containing the elements between [start] and [end]. /// /// The new list is a `Float64x2List` containing the elements of this /// list at positions greater than or equal to [start] and less than [end] in /// the same order as they occur in this list. /// /// ```dart /// var numbers = Float64x2List.fromList([ /// Float64x2(0, 1), /// Float64x2(1, 2), /// Float64x2(2, 3), /// Float64x2(3, 4), /// Float64x2(4, 5), /// ]); /// print(numbers.sublist(1, 3)); // [Float64x2(1, 2), Float64x2(2, 3)] /// print(numbers.sublist(1, 3).runtimeType); // Float64x2List /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// print(numbers.sublist(3)); // [Float64x2(3, 4), Float64x2(4, 5)] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ `this.length`. /// If `end` is equal to `start`, then the returned list is empty. Float64x2List sublist(int start, [int? end]); static const int bytesPerElement = 16; } /// Four 32-bit floating point values. /// /// Float32x4 stores four 32-bit floating point values in "lanes". /// The lanes are named [x], [y], [z], and [w] respectively. /// /// Single operations can be performed on the multiple values of one or /// more `Float32x4`s, which will perform the corresponding operation /// for each lane of the operands, and provide a new `Float32x4` (or similar /// multi-value) result with the results from each lane. /// /// The `Float32x4` class cannot be extended or implemented. abstract final class Float32x4 { /// Creates a `Float32x4` containing the 32-bit float values of the arguments. /// /// The created value has [Float32x4.x], [Float32x4.y], [Float32x4.z] /// and [Float32x4.w] values that are the 32-bit floating point values /// created from the [x], [y], [z] and [w] arguments by conversion /// from 64-bit floating point to 32-bit floating point values. /// /// The conversion from 64-bit float to 32-bit float loses significant /// precision, and may become zero or infinite even if the original 64-bit /// floating point value was non-zero and finite. external factory Float32x4(double x, double y, double z, double w); /// Creates a `Float32x4` with the same 32-bit float value four times. /// /// The created value has the same [Float32x4.x], [Float32x4.y], [Float32x4.z] /// and [Float32x4.w] value, which is the 32-bit floating point value /// created by converting the 64-bit floating point [value] to a /// 32-bit floating point value. /// /// The conversion from 64-bit float to 32-bit float loses significant /// precision, and may become zero or infinite even if the original 64-bit /// floating point value was non-zero and finite. external factory Float32x4.splat(double value); /// Creates a `Float32x4` with all values being zero. /// /// The created value has the same [Float32x4.x], [Float32x4.y], [Float32x4.z] /// and [Float32x4.w] value, which is the 32-bit floating point zero value. external factory Float32x4.zero(); /// Creates a `Float32x4` with 32-bit float values from the provided bits. /// /// The created value has [Float32x4.x], [Float32x4.y], [Float32x4.z] /// and [Float32x4.w] values, which are the 32-bit floating point values /// of the bit-representations of the corresponding lanes of [bits]. /// /// The conversion is performed using the *platform endianness* for both /// 32-bit integers and 32-bit floating point numbers. external factory Float32x4.fromInt32x4Bits(Int32x4 bits); /// Creates a `Float32x4` with its [x] and [y] lanes set to values from [xy]. /// /// The created value has [Float32x4.x] and [Float32x4.y] values /// which are the conversions of the [Float64x2.x] and [Float64x2.y] lanes /// of [xy] to 32-bit floating point values. /// The [Float32x4.z] and [Float32x4.w] lanes are set to the zero value. external factory Float32x4.fromFloat64x2(Float64x2 xy); /// Lane-wise addition. /// /// Adds the value of each lane of this value /// to the value of the corresponding lane of [other]. /// /// Returns the result for each lane. Float32x4 operator +(Float32x4 other); /// Lane-wise negation. /// /// Returns a result where each lane is the negation of the corresponding /// lane of this value. Float32x4 operator -(); /// Lane-wise subtraction. /// /// Subtracts the value of each lane of [other] /// from the value of the corresponding lane of this value. /// /// Returns the result for each lane. Float32x4 operator -(Float32x4 other); /// Lane-wise multiplication. /// /// Multiplies the value of each lane of this value /// with the value of the corresponding lane of [other]. /// /// Returns the result for each lane. Float32x4 operator *(Float32x4 other); /// Lane-wise division. /// /// Divides the value of each lane of this value /// with the value of the corresponding lane of [other]. /// /// Returns the result for each lane. Float32x4 operator /(Float32x4 other); /// Lane-wise less-than comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *less than* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 lessThan(Float32x4 other); /// Lane-wise less-than-or-equal comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *less than or equal to* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 lessThanOrEqual(Float32x4 other); /// Lane-wise greater-than comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *greater than* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 greaterThan(Float32x4 other); /// Lane-wise greater-than-or-equal comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *greater than or equal to* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 greaterThanOrEqual(Float32x4 other); /// Lane-wise equals comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *equal to* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 equal(Float32x4 other); /// Lane-wise not-equals comparison. /// /// Compares the 32-bit floating point value of each lane of this /// to the value of the corresponding lane of [other], /// using 32-bit floating point comparison. /// _For floating point comparisons, a comparison with a NaN value is /// always false, and -0.0 (negative zero) is considered equal to 0.0 /// (positive zero), and not less strictly than it._ /// The result for a lane is a 32-bit signed integer which is -1 /// (all bits set) if the value from this object is *not equal to* /// the value from [other], and the result is 0 (all bits cleared) if not, /// including if either value is a NaN value. /// /// Returns four values that are always either 0 or -1. Int32x4 notEqual(Float32x4 other); /// Lane-wise multiplication by [scale]. /// /// Returns a result where each lane is the result of multiplying the /// corresponding lane of this value with [scale]. /// This can happen either by converting the lane value to a 64-bit /// floating point value, multiplying it with [scale] and converting /// the result back to a 32-bit floating point value, /// or by converting [scale] to a 32-bit floating point value /// and performing a 32-bit floating point multiplication. /// /// In the latter case it is equivalent to `thisValue * Float32x4.splat(s)`. Float32x4 scale(double scale); /// Lane-wise conversion to absolute value. /// /// Converts each lane's value to a non-negative value /// by negating the value if it is negative, /// and keeping the original value if it is not negative. /// /// Returns the result for each lane. Float32x4 abs(); /// Lane-wise clamp to a range. /// /// Clamps the value of each lane to a minimum value /// of the corresponding lane of [lowerLimit] /// and a maximum value of the corresponding lane of [upperLimit]. /// If the original value is lower than the minimum value, the result is /// the minimum value, and if original value is greater than the maximum /// value, the result is the maximum value. /// The result is unspecified if the maximum value is lower than the minimum /// value, or if any of the three values is a NaN value, other than that /// the result will be one of those three values, or possibly a different /// NaN value if any value is a NaN value. /// /// Returns the result for each lane. Float32x4 clamp(Float32x4 lowerLimit, Float32x4 upperLimit); /// The value of the "x" lane. double get x; /// The value of the "y" lane. double get y; /// The value of the "z" lane. double get z; /// The value of the "w" lane. double get w; /// The sign bits of each lane as single bits. /// /// The sign bits of each lane's 32-bit floating point value /// are stored in the low four bits of this value: /// - The [x] lane in bit 0. /// - The [y] lane in bit 1. /// - The [z] lane in bit 2. /// - The [w] lane in bit 3. int get signMask; // Masks passed to [shuffle] or [shuffleMix]. /// Shuffle mask "xxxx". /// /// Used by [shuffle] and [shuffleMix]. static const int xxxx = 0x00; /// Shuffle mask "xxxy". /// /// Used by [shuffle] and [shuffleMix]. static const int xxxy = 0x40; /// Shuffle mask "xxxz". /// /// Used by [shuffle] and [shuffleMix]. static const int xxxz = 0x80; /// Shuffle mask "xxxw". /// /// Used by [shuffle] and [shuffleMix]. static const int xxxw = 0xC0; /// Shuffle mask "xxyx". /// /// Used by [shuffle] and [shuffleMix]. static const int xxyx = 0x10; /// Shuffle mask "xxyy". /// /// Used by [shuffle] and [shuffleMix]. static const int xxyy = 0x50; /// Shuffle mask "xxyz". /// /// Used by [shuffle] and [shuffleMix]. static const int xxyz = 0x90; /// Shuffle mask "xxyw". /// /// Used by [shuffle] and [shuffleMix]. static const int xxyw = 0xD0; /// Shuffle mask "xxzx". /// /// Used by [shuffle] and [shuffleMix]. static const int xxzx = 0x20; /// Shuffle mask "xxzy". /// /// Used by [shuffle] and [shuffleMix]. static const int xxzy = 0x60; /// Shuffle mask "xxzz". /// /// Used by [shuffle] and [shuffleMix]. static const int xxzz = 0xA0; /// Shuffle mask "xxzw". /// /// Used by [shuffle] and [shuffleMix]. static const int xxzw = 0xE0; /// Shuffle mask "xxwx". /// /// Used by [shuffle] and [shuffleMix]. static const int xxwx = 0x30; /// Shuffle mask "xxwy". /// /// Used by [shuffle] and [shuffleMix]. static const int xxwy = 0x70; /// Shuffle mask "xxwz". /// /// Used by [shuffle] and [shuffleMix]. static const int xxwz = 0xB0; /// Shuffle mask "xxww". /// /// Used by [shuffle] and [shuffleMix]. static const int xxww = 0xF0; /// Shuffle mask "xyxx". /// /// Used by [shuffle] and [shuffleMix]. static const int xyxx = 0x04; /// Shuffle mask "xyxy". /// /// Used by [shuffle] and [shuffleMix]. static const int xyxy = 0x44; /// Shuffle mask "xyxz". /// /// Used by [shuffle] and [shuffleMix]. static const int xyxz = 0x84; /// Shuffle mask "xyxw". /// /// Used by [shuffle] and [shuffleMix]. static const int xyxw = 0xC4; /// Shuffle mask "xyyx". /// /// Used by [shuffle] and [shuffleMix]. static const int xyyx = 0x14; /// Shuffle mask "xyyy". /// /// Used by [shuffle] and [shuffleMix]. static const int xyyy = 0x54; /// Shuffle mask "xyyz". /// /// Used by [shuffle] and [shuffleMix]. static const int xyyz = 0x94; /// Shuffle mask "xyyw". /// /// Used by [shuffle] and [shuffleMix]. static const int xyyw = 0xD4; /// Shuffle mask "xyzx". /// /// Used by [shuffle] and [shuffleMix]. static const int xyzx = 0x24; /// Shuffle mask "xyzy". /// /// Used by [shuffle] and [shuffleMix]. static const int xyzy = 0x64; /// Shuffle mask "xyzz". /// /// Used by [shuffle] and [shuffleMix]. static const int xyzz = 0xA4; /// Shuffle mask "xyzw". /// /// Used by [shuffle] and [shuffleMix]. static const int xyzw = 0xE4; /// Shuffle mask "xywx". /// /// Used by [shuffle] and [shuffleMix]. static const int xywx = 0x34; /// Shuffle mask "xywy". /// /// Used by [shuffle] and [shuffleMix]. static const int xywy = 0x74; /// Shuffle mask "xywz". /// /// Used by [shuffle] and [shuffleMix]. static const int xywz = 0xB4; /// Shuffle mask "xyww". /// /// Used by [shuffle] and [shuffleMix]. static const int xyww = 0xF4; /// Shuffle mask "xzxx". /// /// Used by [shuffle] and [shuffleMix]. static const int xzxx = 0x08; /// Shuffle mask "xzxy". /// /// Used by [shuffle] and [shuffleMix]. static const int xzxy = 0x48; /// Shuffle mask "xzxz". /// /// Used by [shuffle] and [shuffleMix]. static const int xzxz = 0x88; /// Shuffle mask "xzxw". /// /// Used by [shuffle] and [shuffleMix]. static const int xzxw = 0xC8; /// Shuffle mask "xzyx". /// /// Used by [shuffle] and [shuffleMix]. static const int xzyx = 0x18; /// Shuffle mask "xzyy". /// /// Used by [shuffle] and [shuffleMix]. static const int xzyy = 0x58; /// Shuffle mask "xzyz". /// /// Used by [shuffle] and [shuffleMix]. static const int xzyz = 0x98; /// Shuffle mask "xzyw". /// /// Used by [shuffle] and [shuffleMix]. static const int xzyw = 0xD8; /// Shuffle mask "xzzx". /// /// Used by [shuffle] and [shuffleMix]. static const int xzzx = 0x28; /// Shuffle mask "xzzy". /// /// Used by [shuffle] and [shuffleMix]. static const int xzzy = 0x68; /// Shuffle mask "xzzz". /// /// Used by [shuffle] and [shuffleMix]. static const int xzzz = 0xA8; /// Shuffle mask "xzzw". /// /// Used by [shuffle] and [shuffleMix]. static const int xzzw = 0xE8; /// Shuffle mask "xzwx". /// /// Used by [shuffle] and [shuffleMix]. static const int xzwx = 0x38; /// Shuffle mask "xzwy". /// /// Used by [shuffle] and [shuffleMix]. static const int xzwy = 0x78; /// Shuffle mask "xzwz". /// /// Used by [shuffle] and [shuffleMix]. static const int xzwz = 0xB8; /// Shuffle mask "xzww". /// /// Used by [shuffle] and [shuffleMix]. static const int xzww = 0xF8; /// Shuffle mask "xwxx". /// /// Used by [shuffle] and [shuffleMix]. static const int xwxx = 0x0C; /// Shuffle mask "xwxy". /// /// Used by [shuffle] and [shuffleMix]. static const int xwxy = 0x4C; /// Shuffle mask "xwxz". /// /// Used by [shuffle] and [shuffleMix]. static const int xwxz = 0x8C; /// Shuffle mask "xwxw". /// /// Used by [shuffle] and [shuffleMix]. static const int xwxw = 0xCC; /// Shuffle mask "xwyx". /// /// Used by [shuffle] and [shuffleMix]. static const int xwyx = 0x1C; /// Shuffle mask "xwyy". /// /// Used by [shuffle] and [shuffleMix]. static const int xwyy = 0x5C; /// Shuffle mask "xwyz". /// /// Used by [shuffle] and [shuffleMix]. static const int xwyz = 0x9C; /// Shuffle mask "xwyw". /// /// Used by [shuffle] and [shuffleMix]. static const int xwyw = 0xDC; /// Shuffle mask "xwzx". /// /// Used by [shuffle] and [shuffleMix]. static const int xwzx = 0x2C; /// Shuffle mask "xwzy". /// /// Used by [shuffle] and [shuffleMix]. static const int xwzy = 0x6C; /// Shuffle mask "xwzz". /// /// Used by [shuffle] and [shuffleMix]. static const int xwzz = 0xAC; /// Shuffle mask "xwzw". /// /// Used by [shuffle] and [shuffleMix]. static const int xwzw = 0xEC; /// Shuffle mask "xwwx". /// /// Used by [shuffle] and [shuffleMix]. static const int xwwx = 0x3C; /// Shuffle mask "xwwy". /// /// Used by [shuffle] and [shuffleMix]. static const int xwwy = 0x7C; /// Shuffle mask "xwwz". /// /// Used by [shuffle] and [shuffleMix]. static const int xwwz = 0xBC; /// Shuffle mask "xwww". /// /// Used by [shuffle] and [shuffleMix]. static const int xwww = 0xFC; /// Shuffle mask "yxxx". /// /// Used by [shuffle] and [shuffleMix]. static const int yxxx = 0x01; /// Shuffle mask "yxxy". /// /// Used by [shuffle] and [shuffleMix]. static const int yxxy = 0x41; /// Shuffle mask "yxxz". /// /// Used by [shuffle] and [shuffleMix]. static const int yxxz = 0x81; /// Shuffle mask "yxxw". /// /// Used by [shuffle] and [shuffleMix]. static const int yxxw = 0xC1; /// Shuffle mask "yxyx". /// /// Used by [shuffle] and [shuffleMix]. static const int yxyx = 0x11; /// Shuffle mask "yxyy". /// /// Used by [shuffle] and [shuffleMix]. static const int yxyy = 0x51; /// Shuffle mask "yxyz". /// /// Used by [shuffle] and [shuffleMix]. static const int yxyz = 0x91; /// Shuffle mask "yxyw". /// /// Used by [shuffle] and [shuffleMix]. static const int yxyw = 0xD1; /// Shuffle mask "yxzx". /// /// Used by [shuffle] and [shuffleMix]. static const int yxzx = 0x21; /// Shuffle mask "yxzy". /// /// Used by [shuffle] and [shuffleMix]. static const int yxzy = 0x61; /// Shuffle mask "yxzz". /// /// Used by [shuffle] and [shuffleMix]. static const int yxzz = 0xA1; /// Shuffle mask "yxzw". /// /// Used by [shuffle] and [shuffleMix]. static const int yxzw = 0xE1; /// Shuffle mask "yxwx". /// /// Used by [shuffle] and [shuffleMix]. static const int yxwx = 0x31; /// Shuffle mask "yxwy". /// /// Used by [shuffle] and [shuffleMix]. static const int yxwy = 0x71; /// Shuffle mask "yxwz". /// /// Used by [shuffle] and [shuffleMix]. static const int yxwz = 0xB1; /// Shuffle mask "yxww". /// /// Used by [shuffle] and [shuffleMix]. static const int yxww = 0xF1; /// Shuffle mask "yyxx". /// /// Used by [shuffle] and [shuffleMix]. static const int yyxx = 0x05; /// Shuffle mask "yyxy". /// /// Used by [shuffle] and [shuffleMix]. static const int yyxy = 0x45; /// Shuffle mask "yyxz". /// /// Used by [shuffle] and [shuffleMix]. static const int yyxz = 0x85; /// Shuffle mask "yyxw". /// /// Used by [shuffle] and [shuffleMix]. static const int yyxw = 0xC5; /// Shuffle mask "yyyx". /// /// Used by [shuffle] and [shuffleMix]. static const int yyyx = 0x15; /// Shuffle mask "yyyy". /// /// Used by [shuffle] and [shuffleMix]. static const int yyyy = 0x55; /// Shuffle mask "yyyz". /// /// Used by [shuffle] and [shuffleMix]. static const int yyyz = 0x95; /// Shuffle mask "yyyw". /// /// Used by [shuffle] and [shuffleMix]. static const int yyyw = 0xD5; /// Shuffle mask "yyzx". /// /// Used by [shuffle] and [shuffleMix]. static const int yyzx = 0x25; /// Shuffle mask "yyzy". /// /// Used by [shuffle] and [shuffleMix]. static const int yyzy = 0x65; /// Shuffle mask "yyzz". /// /// Used by [shuffle] and [shuffleMix]. static const int yyzz = 0xA5; /// Shuffle mask "yyzw". /// /// Used by [shuffle] and [shuffleMix]. static const int yyzw = 0xE5; /// Shuffle mask "yywx". /// /// Used by [shuffle] and [shuffleMix]. static const int yywx = 0x35; /// Shuffle mask "yywy". /// /// Used by [shuffle] and [shuffleMix]. static const int yywy = 0x75; /// Shuffle mask "yywz". /// /// Used by [shuffle] and [shuffleMix]. static const int yywz = 0xB5; /// Shuffle mask "yyww". /// /// Used by [shuffle] and [shuffleMix]. static const int yyww = 0xF5; /// Shuffle mask "yzxx". /// /// Used by [shuffle] and [shuffleMix]. static const int yzxx = 0x09; /// Shuffle mask "yzxy". /// /// Used by [shuffle] and [shuffleMix]. static const int yzxy = 0x49; /// Shuffle mask "yzxz". /// /// Used by [shuffle] and [shuffleMix]. static const int yzxz = 0x89; /// Shuffle mask "yzxw". /// /// Used by [shuffle] and [shuffleMix]. static const int yzxw = 0xC9; /// Shuffle mask "yzyx". /// /// Used by [shuffle] and [shuffleMix]. static const int yzyx = 0x19; /// Shuffle mask "yzyy". /// /// Used by [shuffle] and [shuffleMix]. static const int yzyy = 0x59; /// Shuffle mask "yzyz". /// /// Used by [shuffle] and [shuffleMix]. static const int yzyz = 0x99; /// Shuffle mask "yzyw". /// /// Used by [shuffle] and [shuffleMix]. static const int yzyw = 0xD9; /// Shuffle mask "yzzx". /// /// Used by [shuffle] and [shuffleMix]. static const int yzzx = 0x29; /// Shuffle mask "yzzy". /// /// Used by [shuffle] and [shuffleMix]. static const int yzzy = 0x69; /// Shuffle mask "yzzz". /// /// Used by [shuffle] and [shuffleMix]. static const int yzzz = 0xA9; /// Shuffle mask "yzzw". /// /// Used by [shuffle] and [shuffleMix]. static const int yzzw = 0xE9; /// Shuffle mask "yzwx". /// /// Used by [shuffle] and [shuffleMix]. static const int yzwx = 0x39; /// Shuffle mask "yzwy". /// /// Used by [shuffle] and [shuffleMix]. static const int yzwy = 0x79; /// Shuffle mask "yzwz". /// /// Used by [shuffle] and [shuffleMix]. static const int yzwz = 0xB9; /// Shuffle mask "yzww". /// /// Used by [shuffle] and [shuffleMix]. static const int yzww = 0xF9; /// Shuffle mask "ywxx". /// /// Used by [shuffle] and [shuffleMix]. static const int ywxx = 0x0D; /// Shuffle mask "ywxy". /// /// Used by [shuffle] and [shuffleMix]. static const int ywxy = 0x4D; /// Shuffle mask "ywxz". /// /// Used by [shuffle] and [shuffleMix]. static const int ywxz = 0x8D; /// Shuffle mask "ywxw". /// /// Used by [shuffle] and [shuffleMix]. static const int ywxw = 0xCD; /// Shuffle mask "ywyx". /// /// Used by [shuffle] and [shuffleMix]. static const int ywyx = 0x1D; /// Shuffle mask "ywyy". /// /// Used by [shuffle] and [shuffleMix]. static const int ywyy = 0x5D; /// Shuffle mask "ywyz". /// /// Used by [shuffle] and [shuffleMix]. static const int ywyz = 0x9D; /// Shuffle mask "ywyw". /// /// Used by [shuffle] and [shuffleMix]. static const int ywyw = 0xDD; /// Shuffle mask "ywzx". /// /// Used by [shuffle] and [shuffleMix]. static const int ywzx = 0x2D; /// Shuffle mask "ywzy". /// /// Used by [shuffle] and [shuffleMix]. static const int ywzy = 0x6D; /// Shuffle mask "ywzz". /// /// Used by [shuffle] and [shuffleMix]. static const int ywzz = 0xAD; /// Shuffle mask "ywzw". /// /// Used by [shuffle] and [shuffleMix]. static const int ywzw = 0xED; /// Shuffle mask "ywwx". /// /// Used by [shuffle] and [shuffleMix]. static const int ywwx = 0x3D; /// Shuffle mask "ywwy". /// /// Used by [shuffle] and [shuffleMix]. static const int ywwy = 0x7D; /// Shuffle mask "ywwz". /// /// Used by [shuffle] and [shuffleMix]. static const int ywwz = 0xBD; /// Shuffle mask "ywww". /// /// Used by [shuffle] and [shuffleMix]. static const int ywww = 0xFD; /// Shuffle mask "zxxx". /// /// Used by [shuffle] and [shuffleMix]. static const int zxxx = 0x02; /// Shuffle mask "zxxy". /// /// Used by [shuffle] and [shuffleMix]. static const int zxxy = 0x42; /// Shuffle mask "zxxz". /// /// Used by [shuffle] and [shuffleMix]. static const int zxxz = 0x82; /// Shuffle mask "zxxw". /// /// Used by [shuffle] and [shuffleMix]. static const int zxxw = 0xC2; /// Shuffle mask "zxyx". /// /// Used by [shuffle] and [shuffleMix]. static const int zxyx = 0x12; /// Shuffle mask "zxyy". /// /// Used by [shuffle] and [shuffleMix]. static const int zxyy = 0x52; /// Shuffle mask "zxyz". /// /// Used by [shuffle] and [shuffleMix]. static const int zxyz = 0x92; /// Shuffle mask "zxyw". /// /// Used by [shuffle] and [shuffleMix]. static const int zxyw = 0xD2; /// Shuffle mask "zxzx". /// /// Used by [shuffle] and [shuffleMix]. static const int zxzx = 0x22; /// Shuffle mask "zxzy". /// /// Used by [shuffle] and [shuffleMix]. static const int zxzy = 0x62; /// Shuffle mask "zxzz". /// /// Used by [shuffle] and [shuffleMix]. static const int zxzz = 0xA2; /// Shuffle mask "zxzw". /// /// Used by [shuffle] and [shuffleMix]. static const int zxzw = 0xE2; /// Shuffle mask "zxwx". /// /// Used by [shuffle] and [shuffleMix]. static const int zxwx = 0x32; /// Shuffle mask "zxwy". /// /// Used by [shuffle] and [shuffleMix]. static const int zxwy = 0x72; /// Shuffle mask "zxwz". /// /// Used by [shuffle] and [shuffleMix]. static const int zxwz = 0xB2; /// Shuffle mask "zxww". /// /// Used by [shuffle] and [shuffleMix]. static const int zxww = 0xF2; /// Shuffle mask "zyxx". /// /// Used by [shuffle] and [shuffleMix]. static const int zyxx = 0x06; /// Shuffle mask "zyxy". /// /// Used by [shuffle] and [shuffleMix]. static const int zyxy = 0x46; /// Shuffle mask "zyxz". /// /// Used by [shuffle] and [shuffleMix]. static const int zyxz = 0x86; /// Shuffle mask "zyxw". /// /// Used by [shuffle] and [shuffleMix]. static const int zyxw = 0xC6; /// Shuffle mask "zyyx". /// /// Used by [shuffle] and [shuffleMix]. static const int zyyx = 0x16; /// Shuffle mask "zyyy". /// /// Used by [shuffle] and [shuffleMix]. static const int zyyy = 0x56; /// Shuffle mask "zyyz". /// /// Used by [shuffle] and [shuffleMix]. static const int zyyz = 0x96; /// Shuffle mask "zyyw". /// /// Used by [shuffle] and [shuffleMix]. static const int zyyw = 0xD6; /// Shuffle mask "zyzx". /// /// Used by [shuffle] and [shuffleMix]. static const int zyzx = 0x26; /// Shuffle mask "zyzy". /// /// Used by [shuffle] and [shuffleMix]. static const int zyzy = 0x66; /// Shuffle mask "zyzz". /// /// Used by [shuffle] and [shuffleMix]. static const int zyzz = 0xA6; /// Shuffle mask "zyzw". /// /// Used by [shuffle] and [shuffleMix]. static const int zyzw = 0xE6; /// Shuffle mask "zywx". /// /// Used by [shuffle] and [shuffleMix]. static const int zywx = 0x36; /// Shuffle mask "zywy". /// /// Used by [shuffle] and [shuffleMix]. static const int zywy = 0x76; /// Shuffle mask "zywz". /// /// Used by [shuffle] and [shuffleMix]. static const int zywz = 0xB6; /// Shuffle mask "zyww". /// /// Used by [shuffle] and [shuffleMix]. static const int zyww = 0xF6; /// Shuffle mask "zzxx". /// /// Used by [shuffle] and [shuffleMix]. static const int zzxx = 0x0A; /// Shuffle mask "zzxy". /// /// Used by [shuffle] and [shuffleMix]. static const int zzxy = 0x4A; /// Shuffle mask "zzxz". /// /// Used by [shuffle] and [shuffleMix]. static const int zzxz = 0x8A; /// Shuffle mask "zzxw". /// /// Used by [shuffle] and [shuffleMix]. static const int zzxw = 0xCA; /// Shuffle mask "zzyx". /// /// Used by [shuffle] and [shuffleMix]. static const int zzyx = 0x1A; /// Shuffle mask "zzyy". /// /// Used by [shuffle] and [shuffleMix]. static const int zzyy = 0x5A; /// Shuffle mask "zzyz". /// /// Used by [shuffle] and [shuffleMix]. static const int zzyz = 0x9A; /// Shuffle mask "zzyw". /// /// Used by [shuffle] and [shuffleMix]. static const int zzyw = 0xDA; /// Shuffle mask "zzzx". /// /// Used by [shuffle] and [shuffleMix]. static const int zzzx = 0x2A; /// Shuffle mask "zzzy". /// /// Used by [shuffle] and [shuffleMix]. static const int zzzy = 0x6A; /// Shuffle mask "zzzz". /// /// Used by [shuffle] and [shuffleMix]. static const int zzzz = 0xAA; /// Shuffle mask "zzzw". /// /// Used by [shuffle] and [shuffleMix]. static const int zzzw = 0xEA; /// Shuffle mask "zzwx". /// /// Used by [shuffle] and [shuffleMix]. static const int zzwx = 0x3A; /// Shuffle mask "zzwy". /// /// Used by [shuffle] and [shuffleMix]. static const int zzwy = 0x7A; /// Shuffle mask "zzwz". /// /// Used by [shuffle] and [shuffleMix]. static const int zzwz = 0xBA; /// Shuffle mask "zzww". /// /// Used by [shuffle] and [shuffleMix]. static const int zzww = 0xFA; /// Shuffle mask "zwxx". /// /// Used by [shuffle] and [shuffleMix]. static const int zwxx = 0x0E; /// Shuffle mask "zwxy". /// /// Used by [shuffle] and [shuffleMix]. static const int zwxy = 0x4E; /// Shuffle mask "zwxz". /// /// Used by [shuffle] and [shuffleMix]. static const int zwxz = 0x8E; /// Shuffle mask "zwxw". /// /// Used by [shuffle] and [shuffleMix]. static const int zwxw = 0xCE; /// Shuffle mask "zwyx". /// /// Used by [shuffle] and [shuffleMix]. static const int zwyx = 0x1E; /// Shuffle mask "zwyy". /// /// Used by [shuffle] and [shuffleMix]. static const int zwyy = 0x5E; /// Shuffle mask "zwyz". /// /// Used by [shuffle] and [shuffleMix]. static const int zwyz = 0x9E; /// Shuffle mask "zwyw". /// /// Used by [shuffle] and [shuffleMix]. static const int zwyw = 0xDE; /// Shuffle mask "zwzx". /// /// Used by [shuffle] and [shuffleMix]. static const int zwzx = 0x2E; /// Shuffle mask "zwzy". /// /// Used by [shuffle] and [shuffleMix]. static const int zwzy = 0x6E; /// Shuffle mask "zwzz". /// /// Used by [shuffle] and [shuffleMix]. static const int zwzz = 0xAE; /// Shuffle mask "zwzw". /// /// Used by [shuffle] and [shuffleMix]. static const int zwzw = 0xEE; /// Shuffle mask "zwwx". /// /// Used by [shuffle] and [shuffleMix]. static const int zwwx = 0x3E; /// Shuffle mask "zwwy". /// /// Used by [shuffle] and [shuffleMix]. static const int zwwy = 0x7E; /// Shuffle mask "zwwz". /// /// Used by [shuffle] and [shuffleMix]. static const int zwwz = 0xBE; /// Shuffle mask "zwww". /// /// Used by [shuffle] and [shuffleMix]. static const int zwww = 0xFE; /// Shuffle mask "wxxx". /// /// Used by [shuffle] and [shuffleMix]. static const int wxxx = 0x03; /// Shuffle mask "wxxy". /// /// Used by [shuffle] and [shuffleMix]. static const int wxxy = 0x43; /// Shuffle mask "wxxz". /// /// Used by [shuffle] and [shuffleMix]. static const int wxxz = 0x83; /// Shuffle mask "wxxw". /// /// Used by [shuffle] and [shuffleMix]. static const int wxxw = 0xC3; /// Shuffle mask "wxyx". /// /// Used by [shuffle] and [shuffleMix]. static const int wxyx = 0x13; /// Shuffle mask "wxyy". /// /// Used by [shuffle] and [shuffleMix]. static const int wxyy = 0x53; /// Shuffle mask "wxyz". /// /// Used by [shuffle] and [shuffleMix]. static const int wxyz = 0x93; /// Shuffle mask "wxyw". /// /// Used by [shuffle] and [shuffleMix]. static const int wxyw = 0xD3; /// Shuffle mask "wxzx". /// /// Used by [shuffle] and [shuffleMix]. static const int wxzx = 0x23; /// Shuffle mask "wxzy". /// /// Used by [shuffle] and [shuffleMix]. static const int wxzy = 0x63; /// Shuffle mask "wxzz". /// /// Used by [shuffle] and [shuffleMix]. static const int wxzz = 0xA3; /// Shuffle mask "wxzw". /// /// Used by [shuffle] and [shuffleMix]. static const int wxzw = 0xE3; /// Shuffle mask "wxwx". /// /// Used by [shuffle] and [shuffleMix]. static const int wxwx = 0x33; /// Shuffle mask "wxwy". /// /// Used by [shuffle] and [shuffleMix]. static const int wxwy = 0x73; /// Shuffle mask "wxwz". /// /// Used by [shuffle] and [shuffleMix]. static const int wxwz = 0xB3; /// Shuffle mask "wxww". /// /// Used by [shuffle] and [shuffleMix]. static const int wxww = 0xF3; /// Shuffle mask "wyxx". /// /// Used by [shuffle] and [shuffleMix]. static const int wyxx = 0x07; /// Shuffle mask "wyxy". /// /// Used by [shuffle] and [shuffleMix]. static const int wyxy = 0x47; /// Shuffle mask "wyxz". /// /// Used by [shuffle] and [shuffleMix]. static const int wyxz = 0x87; /// Shuffle mask "wyxw". /// /// Used by [shuffle] and [shuffleMix]. static const int wyxw = 0xC7; /// Shuffle mask "wyyx". /// /// Used by [shuffle] and [shuffleMix]. static const int wyyx = 0x17; /// Shuffle mask "wyyy". /// /// Used by [shuffle] and [shuffleMix]. static const int wyyy = 0x57; /// Shuffle mask "wyyz". /// /// Used by [shuffle] and [shuffleMix]. static const int wyyz = 0x97; /// Shuffle mask "wyyw". /// /// Used by [shuffle] and [shuffleMix]. static const int wyyw = 0xD7; /// Shuffle mask "wyzx". /// /// Used by [shuffle] and [shuffleMix]. static const int wyzx = 0x27; /// Shuffle mask "wyzy". /// /// Used by [shuffle] and [shuffleMix]. static const int wyzy = 0x67; /// Shuffle mask "wyzz". /// /// Used by [shuffle] and [shuffleMix]. static const int wyzz = 0xA7; /// Shuffle mask "wyzw". /// /// Used by [shuffle] and [shuffleMix]. static const int wyzw = 0xE7; /// Shuffle mask "wywx". /// /// Used by [shuffle] and [shuffleMix]. static const int wywx = 0x37; /// Shuffle mask "wywy". /// /// Used by [shuffle] and [shuffleMix]. static const int wywy = 0x77; /// Shuffle mask "wywz". /// /// Used by [shuffle] and [shuffleMix]. static const int wywz = 0xB7; /// Shuffle mask "wyww". /// /// Used by [shuffle] and [shuffleMix]. static const int wyww = 0xF7; /// Shuffle mask "wzxx". /// /// Used by [shuffle] and [shuffleMix]. static const int wzxx = 0x0B; /// Shuffle mask "wzxy". /// /// Used by [shuffle] and [shuffleMix]. static const int wzxy = 0x4B; /// Shuffle mask "wzxz". /// /// Used by [shuffle] and [shuffleMix]. static const int wzxz = 0x8B; /// Shuffle mask "wzxw". /// /// Used by [shuffle] and [shuffleMix]. static const int wzxw = 0xCB; /// Shuffle mask "wzyx". /// /// Used by [shuffle] and [shuffleMix]. static const int wzyx = 0x1B; /// Shuffle mask "wzyy". /// /// Used by [shuffle] and [shuffleMix]. static const int wzyy = 0x5B; /// Shuffle mask "wzyz". /// /// Used by [shuffle] and [shuffleMix]. static const int wzyz = 0x9B; /// Shuffle mask "wzyw". /// /// Used by [shuffle] and [shuffleMix]. static const int wzyw = 0xDB; /// Shuffle mask "wzzx". /// /// Used by [shuffle] and [shuffleMix]. static const int wzzx = 0x2B; /// Shuffle mask "wzzy". /// /// Used by [shuffle] and [shuffleMix]. static const int wzzy = 0x6B; /// Shuffle mask "wzzz". /// /// Used by [shuffle] and [shuffleMix]. static const int wzzz = 0xAB; /// Shuffle mask "wzzw". /// /// Used by [shuffle] and [shuffleMix]. static const int wzzw = 0xEB; /// Shuffle mask "wzwx". /// /// Used by [shuffle] and [shuffleMix]. static const int wzwx = 0x3B; /// Shuffle mask "wzwy". /// /// Used by [shuffle] and [shuffleMix]. static const int wzwy = 0x7B; /// Shuffle mask "wzwz". /// /// Used by [shuffle] and [shuffleMix]. static const int wzwz = 0xBB; /// Shuffle mask "wzww". /// /// Used by [shuffle] and [shuffleMix]. static const int wzww = 0xFB; /// Shuffle mask "wwxx". /// /// Used by [shuffle] and [shuffleMix]. static const int wwxx = 0x0F; /// Shuffle mask "wwxy". /// /// Used by [shuffle] and [shuffleMix]. static const int wwxy = 0x4F; /// Shuffle mask "wwxz". /// /// Used by [shuffle] and [shuffleMix]. static const int wwxz = 0x8F; /// Shuffle mask "wwxw". /// /// Used by [shuffle] and [shuffleMix]. static const int wwxw = 0xCF; /// Shuffle mask "wwyx". /// /// Used by [shuffle] and [shuffleMix]. static const int wwyx = 0x1F; /// Shuffle mask "wwyy". /// /// Used by [shuffle] and [shuffleMix]. static const int wwyy = 0x5F; /// Shuffle mask "wwyz". /// /// Used by [shuffle] and [shuffleMix]. static const int wwyz = 0x9F; /// Shuffle mask "wwyw". /// /// Used by [shuffle] and [shuffleMix]. static const int wwyw = 0xDF; /// Shuffle mask "wwzx". /// /// Used by [shuffle] and [shuffleMix]. static const int wwzx = 0x2F; /// Shuffle mask "wwzy". /// /// Used by [shuffle] and [shuffleMix]. static const int wwzy = 0x6F; /// Shuffle mask "wwzz". /// /// Used by [shuffle] and [shuffleMix]. static const int wwzz = 0xAF; /// Shuffle mask "wwzw". /// /// Used by [shuffle] and [shuffleMix]. static const int wwzw = 0xEF; /// Shuffle mask "wwwx". /// /// Used by [shuffle] and [shuffleMix]. static const int wwwx = 0x3F; /// Shuffle mask "wwwy". /// /// Used by [shuffle] and [shuffleMix]. static const int wwwy = 0x7F; /// Shuffle mask "wwwz". /// /// Used by [shuffle] and [shuffleMix]. static const int wwwz = 0xBF; /// Shuffle mask "wwww". /// /// Used by [shuffle] and [shuffleMix]. static const int wwww = 0xFF; /// Shuffle the lane values based on the [mask]. /// /// The [mask] must be one of the 256 shuffle masks from [xxxx] to [wwww]. /// /// Creates a new [Float32x4] whose lane values are taken from the /// lanes of this value based on the lanes of the shuffle mask, /// with the result's [x] lane being taken from the lane of the first /// letter of the shuffle mask's name, the [y] lane from the second letter, /// [z] lane from the third letter and [w] lane from the fourth letter. /// /// For example, the shuffle mask [wxyz] creates a new `Float32x4` /// whose [x] lane is the [w] lane of this value, because the first letter /// of the shuffle mask's name, `wxyz` is "w". Then the `y`, `z` and `w` /// lanes of the result are the values of the `x`, `y` and `z` lanes /// of this value. /// /// The [xyzw] "identity shuffle" mask gives a result with the same lanes /// as the original. /// /// Some masks preserve the values of all lanes, but may permute them. /// Other masks duplicates some lanes and discards the values of others. /// /// For example, doing `v1.shuffle(yyyy)` is equivalent to /// `Float32x4.splat(v1.y)`. Float32x4 shuffle(int mask); /// Mixes lanes chosen from two [Float32x4] values using a [mask]. /// /// Creates a new [Float32x4] where the [x] and [y] lanes are chosen /// from the lanes of this value selected by the first two letters of the /// [mask]'s name, and the [z] and [w] lanes are the lanes of [other] /// selected by the last two letters of the `mask`'s name. /// /// For example, `v1.shuffleMix(v2, Float32x4.xyzw)` is equivalent /// to `Float32x4(v1.x, v1.y, v2.z, v2.w)`. /// /// If [other] is the same value as this `Float32x4`, this function /// is the same as [shuffle]. That is, doing /// `v1.shuffleMix(v1, mask)` is equivalent to `v1.shuffle(mask)`. Float32x4 shuffleMix(Float32x4 other, int mask); /// This value, but with the value of the [Float32x4.x] lane set to [x]. /// /// Returns a new [Float32x4] with the same values for the [y], [z] /// and [w] lanes as this value, and with a [Float32x4.x] lane /// having the value [x] converted to a 32-bit floating point number. Float32x4 withX(double x); /// This value, but with the value of the [Float32x4.y] lane set to [y]. /// /// Returns a new [Float32x4] with the same values for the [x], [z] /// and [w] lanes as this value, and with a [Float32x4.y] lane /// having the value [y] converted to a 32-bit floating point number. Float32x4 withY(double y); /// This value, but with the value of the [Float32x4.z] lane set to [z]. /// /// Returns a new [Float32x4] with the same values for the [x], [y] /// and [w] lanes as this value, and with a [Float32x4.z] lane /// having the value [z] converted to a 32-bit floating point number. Float32x4 withZ(double z); /// This value, but with the value of the [Float32x4.w] lane set to [w]. /// /// Returns a new [Float32x4] with the same values for the [x], [y] /// and [z] lanes as this value, and with a [Float32x4.w] lane /// having the value [w] converted to a 32-bit floating point number. Float32x4 withW(double w); /// Lane-wise minimum. /// /// For each lane select the lesser of the lane value of this and [other]. /// /// The result is the lesser of the two lane values if either is lesser. /// The result is unspecified if either lane contains a NaN value or /// if the values are -0.0 and 0.0, so that neither value is smaller /// or greater than the other. /// Different platforms may give different results in those cases, /// but always one of the lane values. /// /// Returns the result for each lane. Float32x4 min(Float32x4 other); /// Lane-wise maximum. /// /// For each lane select the greater of the lane value of this and [other]. /// /// The result is the greater of the two lane values if either is greater. /// The result is unspecified if either lane contains a NaN value or /// if the values are -0.0 and 0.0, so that neither value is smaller /// or greater than the other. /// Different platforms may give different results in those cases, /// but always one of the lane values. /// /// Returns the result for each lane. Float32x4 max(Float32x4 other); /// Lane-wise square root. /// /// For each lane compute the 32-bit floating point square root of the /// lane's value. /// /// The result for a lane is a NaN value if the original value /// is less than zero or if it is a NaN value. /// The result for a negative zero, -0.0, is the same value again. /// The result for positive infinity is positive infinity. /// Otherwise the result is a positive value which approximates /// the mathematical square root of the original value. /// /// Returns the result for each lane. Float32x4 sqrt(); /// Lane-wise reciprocal. /// /// For each lane compute the result of dividing 1.0 by the lane's value. /// /// If the value is a NaN value, so is the result. /// If the value is infinite, the result is a zero value with the same sign. /// If the value is zero, the result is infinite with the same sign. /// Otherwise the result is an approximation of the mathematical result /// of dividing 1 by the (finite, non-zero) value of the lane. /// /// Returns the result for each lane. Float32x4 reciprocal(); /// Lane-wise approximation of reciprocal square root. /// /// Approximates the same result as [reciprocal] followed by [sqrt], /// or [sqrt] followed by [reciprocal], /// but may be more precise and/or efficient due to computing the result /// directly, rather than not creating a an intermediate result, /// and possibly by working entirely at a reduced precision. /// /// The result can differ between platforms due to differences in /// approximation and precision, and for values where the order of [sqrt] and /// [reciprocal] makes a difference. /// The latter applies specifically to `-0.0` /// where `sqrt(-0.0)` is defined to be -0.0, /// and `reciprocal` of that is -Infinity. /// In the opposite order it computes `sqrt` of -Infinity which is NaN. Float32x4 reciprocalSqrt(); } /// Int32x4 and operations. /// /// Int32x4 stores 4 32-bit bit-masks in "lanes". /// The lanes are "x", "y", "z", and "w" respectively. abstract final class Int32x4 { external factory Int32x4(int x, int y, int z, int w); external factory Int32x4.bool(bool x, bool y, bool z, bool w); external factory Int32x4.fromFloat32x4Bits(Float32x4 x); /// The bit-wise or operator. Int32x4 operator |(Int32x4 other); /// The bit-wise and operator. Int32x4 operator &(Int32x4 other); /// The bit-wise xor operator. Int32x4 operator ^(Int32x4 other); /// Addition operator. Int32x4 operator +(Int32x4 other); /// Subtraction operator. Int32x4 operator -(Int32x4 other); /// Extract 32-bit mask from x lane. int get x; /// Extract 32-bit mask from y lane. int get y; /// Extract 32-bit mask from z lane. int get z; /// Extract 32-bit mask from w lane. int get w; /// Extract the top bit from each lane return them in the first 4 bits. /// "x" lane is bit 0. /// "y" lane is bit 1. /// "z" lane is bit 2. /// "w" lane is bit 3. int get signMask; /// Mask passed to [shuffle] or [shuffleMix]. static const int xxxx = 0x00; static const int xxxy = 0x40; static const int xxxz = 0x80; static const int xxxw = 0xC0; static const int xxyx = 0x10; static const int xxyy = 0x50; static const int xxyz = 0x90; static const int xxyw = 0xD0; static const int xxzx = 0x20; static const int xxzy = 0x60; static const int xxzz = 0xA0; static const int xxzw = 0xE0; static const int xxwx = 0x30; static const int xxwy = 0x70; static const int xxwz = 0xB0; static const int xxww = 0xF0; static const int xyxx = 0x04; static const int xyxy = 0x44; static const int xyxz = 0x84; static const int xyxw = 0xC4; static const int xyyx = 0x14; static const int xyyy = 0x54; static const int xyyz = 0x94; static const int xyyw = 0xD4; static const int xyzx = 0x24; static const int xyzy = 0x64; static const int xyzz = 0xA4; static const int xyzw = 0xE4; static const int xywx = 0x34; static const int xywy = 0x74; static const int xywz = 0xB4; static const int xyww = 0xF4; static const int xzxx = 0x08; static const int xzxy = 0x48; static const int xzxz = 0x88; static const int xzxw = 0xC8; static const int xzyx = 0x18; static const int xzyy = 0x58; static const int xzyz = 0x98; static const int xzyw = 0xD8; static const int xzzx = 0x28; static const int xzzy = 0x68; static const int xzzz = 0xA8; static const int xzzw = 0xE8; static const int xzwx = 0x38; static const int xzwy = 0x78; static const int xzwz = 0xB8; static const int xzww = 0xF8; static const int xwxx = 0x0C; static const int xwxy = 0x4C; static const int xwxz = 0x8C; static const int xwxw = 0xCC; static const int xwyx = 0x1C; static const int xwyy = 0x5C; static const int xwyz = 0x9C; static const int xwyw = 0xDC; static const int xwzx = 0x2C; static const int xwzy = 0x6C; static const int xwzz = 0xAC; static const int xwzw = 0xEC; static const int xwwx = 0x3C; static const int xwwy = 0x7C; static const int xwwz = 0xBC; static const int xwww = 0xFC; static const int yxxx = 0x01; static const int yxxy = 0x41; static const int yxxz = 0x81; static const int yxxw = 0xC1; static const int yxyx = 0x11; static const int yxyy = 0x51; static const int yxyz = 0x91; static const int yxyw = 0xD1; static const int yxzx = 0x21; static const int yxzy = 0x61; static const int yxzz = 0xA1; static const int yxzw = 0xE1; static const int yxwx = 0x31; static const int yxwy = 0x71; static const int yxwz = 0xB1; static const int yxww = 0xF1; static const int yyxx = 0x05; static const int yyxy = 0x45; static const int yyxz = 0x85; static const int yyxw = 0xC5; static const int yyyx = 0x15; static const int yyyy = 0x55; static const int yyyz = 0x95; static const int yyyw = 0xD5; static const int yyzx = 0x25; static const int yyzy = 0x65; static const int yyzz = 0xA5; static const int yyzw = 0xE5; static const int yywx = 0x35; static const int yywy = 0x75; static const int yywz = 0xB5; static const int yyww = 0xF5; static const int yzxx = 0x09; static const int yzxy = 0x49; static const int yzxz = 0x89; static const int yzxw = 0xC9; static const int yzyx = 0x19; static const int yzyy = 0x59; static const int yzyz = 0x99; static const int yzyw = 0xD9; static const int yzzx = 0x29; static const int yzzy = 0x69; static const int yzzz = 0xA9; static const int yzzw = 0xE9; static const int yzwx = 0x39; static const int yzwy = 0x79; static const int yzwz = 0xB9; static const int yzww = 0xF9; static const int ywxx = 0x0D; static const int ywxy = 0x4D; static const int ywxz = 0x8D; static const int ywxw = 0xCD; static const int ywyx = 0x1D; static const int ywyy = 0x5D; static const int ywyz = 0x9D; static const int ywyw = 0xDD; static const int ywzx = 0x2D; static const int ywzy = 0x6D; static const int ywzz = 0xAD; static const int ywzw = 0xED; static const int ywwx = 0x3D; static const int ywwy = 0x7D; static const int ywwz = 0xBD; static const int ywww = 0xFD; static const int zxxx = 0x02; static const int zxxy = 0x42; static const int zxxz = 0x82; static const int zxxw = 0xC2; static const int zxyx = 0x12; static const int zxyy = 0x52; static const int zxyz = 0x92; static const int zxyw = 0xD2; static const int zxzx = 0x22; static const int zxzy = 0x62; static const int zxzz = 0xA2; static const int zxzw = 0xE2; static const int zxwx = 0x32; static const int zxwy = 0x72; static const int zxwz = 0xB2; static const int zxww = 0xF2; static const int zyxx = 0x06; static const int zyxy = 0x46; static const int zyxz = 0x86; static const int zyxw = 0xC6; static const int zyyx = 0x16; static const int zyyy = 0x56; static const int zyyz = 0x96; static const int zyyw = 0xD6; static const int zyzx = 0x26; static const int zyzy = 0x66; static const int zyzz = 0xA6; static const int zyzw = 0xE6; static const int zywx = 0x36; static const int zywy = 0x76; static const int zywz = 0xB6; static const int zyww = 0xF6; static const int zzxx = 0x0A; static const int zzxy = 0x4A; static const int zzxz = 0x8A; static const int zzxw = 0xCA; static const int zzyx = 0x1A; static const int zzyy = 0x5A; static const int zzyz = 0x9A; static const int zzyw = 0xDA; static const int zzzx = 0x2A; static const int zzzy = 0x6A; static const int zzzz = 0xAA; static const int zzzw = 0xEA; static const int zzwx = 0x3A; static const int zzwy = 0x7A; static const int zzwz = 0xBA; static const int zzww = 0xFA; static const int zwxx = 0x0E; static const int zwxy = 0x4E; static const int zwxz = 0x8E; static const int zwxw = 0xCE; static const int zwyx = 0x1E; static const int zwyy = 0x5E; static const int zwyz = 0x9E; static const int zwyw = 0xDE; static const int zwzx = 0x2E; static const int zwzy = 0x6E; static const int zwzz = 0xAE; static const int zwzw = 0xEE; static const int zwwx = 0x3E; static const int zwwy = 0x7E; static const int zwwz = 0xBE; static const int zwww = 0xFE; static const int wxxx = 0x03; static const int wxxy = 0x43; static const int wxxz = 0x83; static const int wxxw = 0xC3; static const int wxyx = 0x13; static const int wxyy = 0x53; static const int wxyz = 0x93; static const int wxyw = 0xD3; static const int wxzx = 0x23; static const int wxzy = 0x63; static const int wxzz = 0xA3; static const int wxzw = 0xE3; static const int wxwx = 0x33; static const int wxwy = 0x73; static const int wxwz = 0xB3; static const int wxww = 0xF3; static const int wyxx = 0x07; static const int wyxy = 0x47; static const int wyxz = 0x87; static const int wyxw = 0xC7; static const int wyyx = 0x17; static const int wyyy = 0x57; static const int wyyz = 0x97; static const int wyyw = 0xD7; static const int wyzx = 0x27; static const int wyzy = 0x67; static const int wyzz = 0xA7; static const int wyzw = 0xE7; static const int wywx = 0x37; static const int wywy = 0x77; static const int wywz = 0xB7; static const int wyww = 0xF7; static const int wzxx = 0x0B; static const int wzxy = 0x4B; static const int wzxz = 0x8B; static const int wzxw = 0xCB; static const int wzyx = 0x1B; static const int wzyy = 0x5B; static const int wzyz = 0x9B; static const int wzyw = 0xDB; static const int wzzx = 0x2B; static const int wzzy = 0x6B; static const int wzzz = 0xAB; static const int wzzw = 0xEB; static const int wzwx = 0x3B; static const int wzwy = 0x7B; static const int wzwz = 0xBB; static const int wzww = 0xFB; static const int wwxx = 0x0F; static const int wwxy = 0x4F; static const int wwxz = 0x8F; static const int wwxw = 0xCF; static const int wwyx = 0x1F; static const int wwyy = 0x5F; static const int wwyz = 0x9F; static const int wwyw = 0xDF; static const int wwzx = 0x2F; static const int wwzy = 0x6F; static const int wwzz = 0xAF; static const int wwzw = 0xEF; static const int wwwx = 0x3F; static const int wwwy = 0x7F; static const int wwwz = 0xBF; static const int wwww = 0xFF; /// Shuffle the lane values. [mask] must be one of the 256 shuffle constants. Int32x4 shuffle(int mask); /// Shuffle the lane values in this [Int32x4] and [other]. The returned /// Int32x4 will have XY lanes from this [Int32x4] and ZW lanes from [other]. /// Uses the same [mask] as [shuffle]. Int32x4 shuffleMix(Int32x4 other, int mask); /// Returns a new [Int32x4] copied from this [Int32x4] with a new x value. Int32x4 withX(int x); /// Returns a new [Int32x4] copied from this [Int32x4] with a new y value. Int32x4 withY(int y); /// Returns a new [Int32x4] copied from this [Int32x4] with a new z value. Int32x4 withZ(int z); /// Returns a new [Int32x4] copied from this [Int32x4] with a new w value. Int32x4 withW(int w); /// Extracted x value. Returns false for 0, true for any other value. bool get flagX; /// Extracted y value. Returns false for 0, true for any other value. bool get flagY; /// Extracted z value. Returns false for 0, true for any other value. bool get flagZ; /// Extracted w value. Returns false for 0, true for any other value. bool get flagW; /// Returns a new [Int32x4] copied from this [Int32x4] with a new x value. Int32x4 withFlagX(bool x); /// Returns a new [Int32x4] copied from this [Int32x4] with a new y value. Int32x4 withFlagY(bool y); /// Returns a new [Int32x4] copied from this [Int32x4] with a new z value. Int32x4 withFlagZ(bool z); /// Returns a new [Int32x4] copied from this [Int32x4] with a new w value. Int32x4 withFlagW(bool w); /// Merge [trueValue] and [falseValue] based on this [Int32x4] bit mask: /// Select bit from [trueValue] when bit in this [Int32x4] is on. /// Select bit from [falseValue] when bit in this [Int32x4] is off. Float32x4 select(Float32x4 trueValue, Float32x4 falseValue); } /// Float64x2 immutable value type and operations. /// /// Float64x2 stores 2 64-bit floating point values in "lanes". /// The lanes are "x" and "y" respectively. /// /// It is a compile-time error for a class to attempt to extend or implement /// `Float64x2`. abstract final class Float64x2 { external factory Float64x2(double x, double y); external factory Float64x2.splat(double v); external factory Float64x2.zero(); /// Uses the "x" and "y" lanes from [v]. external factory Float64x2.fromFloat32x4(Float32x4 v); /// Addition operator. Float64x2 operator +(Float64x2 other); /// Negate operator. Float64x2 operator -(); /// Subtraction operator. Float64x2 operator -(Float64x2 other); /// Multiplication operator. Float64x2 operator *(Float64x2 other); /// Division operator. Float64x2 operator /(Float64x2 other); /// Returns a copy of this [Float64x2] each lane being scaled by [s]. /// Equivalent to this * new Float64x2.splat(s) Float64x2 scale(double s); /// The lane-wise absolute value of this [Float64x2]. Float64x2 abs(); /// Lane-wise clamp this [Float64x2] to be in the range /// [lowerLimit]-[upperLimit]. Float64x2 clamp(Float64x2 lowerLimit, Float64x2 upperLimit); /// Extracted x value. double get x; /// Extracted y value. double get y; /// Extract the sign bits from each lane return them in the first 2 bits. /// "x" lane is bit 0. /// "y" lane is bit 1. int get signMask; /// Returns a new [Float64x2] copied from this [Float64x2] with a new x /// value. Float64x2 withX(double x); /// Returns a new [Float64x2] copied from this [Float64x2] with a new y /// value. Float64x2 withY(double y); /// The lane-wise minimum value in this [Float64x2] or [other]. Float64x2 min(Float64x2 other); /// The lane-wise maximum value in this [Float64x2] or [other]. Float64x2 max(Float64x2 other); /// The lane-wise square root of this [Float64x2]. Float64x2 sqrt(); } ’ŒNN<3D& 0:+8A)M"0D;GNJL.+K?C;FNJL.+K=K;NNJL.+KME;HP17JOL.2+OAD;GP17JOL.2+O?E;HP17JPL.3+OAD;GP17JPL.3+O?E;HP17JQL.4+OAD;GP17JQL.4+O?F;IQ.JNM.6+PCF;IP-JPL.3+OCF;IP-JQL.4+OCH;KQ.JNM.6+PGH;KQ.JNM.6+PGC;FNJL.+K=)M!NI)3EGC2IL I)H2@M4L/)BNFKL>LIFDM&-/G NH(II>*K7F ,M K1HB! /M5I&)H<C?%6A6'KE:CC> 0'- F@)5BE:MGD24F=HL/& )#0#+!EH@1+HM!G1++D<=1+ HND1+,FL O1P>HL,'A1PJAG M1P?OF#.+1PKGL O1P>IL-'A1PJBG M1P?PF$.+1PKHL O1P>JL.'A1PJCG M1P?QF%.+1PKA=@1PCID:IGKIKOL61POB=@1PCJD:1PO2 0'- F@)5CE:MGD24F=HL/& )#0#+!LLQ(8.< E1 A+E+(4 E1 A+E-(H E1 A+E-(H E1 A+E-(K/K'BBM>L)@,J$D2@'@J?C?%6AL'CKE:FCA 3'0 F@)8FE:MGD24F=HKO/& )2-? &0%.$LFP1E2? E9 A+E.(K0K'M>L)@,J$@'@J?C?%6AL'CKE:FCA 3'0 F@)8EE:MGD24F=HKO/& )2-? &0%.$LFP1E2? E9 A+E.(EBFM#5G0@.L$@(ELAC?%6AL'CKE:HCC 5'2 F@):GE:MGD24F=HKQ/& )2-? (0%0&2QC5LHP1- ?A E< A+E0)CBFPE0@,J$@(AJ?C?%6AL'CKE:FCA 3'0 F@)8FE:MGD24F=HKO/& )2-? &0%.$2MA1LGP1+ =? EO A+E.)EBFM#5G8@.L$@(E2QC5LAC?%6AL'CKE:HCC 5'2 F@):HE:MGD24F=HKQ/& )2-? (0%0&LHP1- JA EG A+E0)'C=HFM9=!QHIEBIM4FIQHC#IM428QN%MHKGK9<PCM/K:09()L2>() 6;()3;()&D9.HH7CBM0:%/D9.HH7CNM0:,)D9.HH7CEM0:(2D9.HH7CQM0:/#D9.HH7CAM0:"'D9.HH7CEM0:%+H5FG8@=O!.9/<("70FKK.OKL-(?!!!!1?50* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3MEBHNJEMKGLIK=EGLH=E.F/E3KFAHKFAHKFAHKFAHMKGG!E)("NMGG!E)("IA1E=B:(L5OGJA(9G*KC?DP'00-J278A; %!%!%%%' ' ' ' J0 PJP)/MMMMHHHHMMMMKDF?3@,M!2.%+9))))H28:!?LJ J B"B"5dart:typed_dataœœÀh³©)Àe[org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/typed_data_patch.dartÀ°è// Copyright (c) 2013, 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. /// Note: the VM concatenates all patch files into a single patch file. This /// file is the first patch in "dart:typed_data" which contains all the imports /// used by patches of that library. We plan to change this when we have a /// shared front end and simply use parts. import "dart:_internal" show ClassID, CodeUnits, ExpandIterable, FollowedByIterable, IterableElementError, ListMapView, Lists, MappedIterable, MappedIterable, ReversedListIterable, SkipWhileIterable, Sort, SubListIterable, TakeWhileIterable, WhereIterable, WhereTypeIterable, patch, unsafeCast; import "dart:collection" show ListBase; import 'dart:math' show Random; /// There are no parts in patch library: @patch class ByteData implements TypedData { @patch @pragma("vm:recognized", "other") factory ByteData(int length) { final list = Uint8List(length) as _TypedList; _rangeCheck(list.lengthInBytes, 0, length); return _ByteDataView._(list, 0, length); } factory ByteData._view(_TypedList typedData, int offsetInBytes, int length) { _rangeCheck(typedData.lengthInBytes, offsetInBytes, length); return _ByteDataView._(typedData, offsetInBytes, length); } } // Based class for _TypedList that provides common methods for implementing // the collection and list interfaces. // This class does not extend ListBase since that would add type arguments // to instances of _TypeListBase. Instead the subclasses use type specific // mixins (like _IntListMixin, _DoubleListMixin) to implement ListBase. abstract final class _TypedListBase { @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataBase_length") external int get length; int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; _TypedList get _typedData; // Method(s) implementing the Collection interface. String join([String separator = ""]) { StringBuffer buffer = StringBuffer(); buffer.writeAll(this as Iterable, separator); return buffer.toString(); } bool get isEmpty { return this.length == 0; } bool get isNotEmpty => !isEmpty; // Method(s) implementing the List interface. set length(newLength) { throw UnsupportedError("Cannot resize a fixed-length list"); } void clear() { throw UnsupportedError("Cannot remove from a fixed-length list"); } bool remove(Object? element) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeRange(int start, int end) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void replaceRange(int start, int end, Iterable iterable) { throw UnsupportedError("Cannot remove from a fixed-length list"); } @pragma("vm:prefer-inline") void _setRange(int start, int end, Iterable from, [int skipCount = 0]) { // Range check all numeric inputs. if (0 > start || start > end || end > length) { RangeError.checkValidRange(start, end, length); // Always throws. assert(false); } if (skipCount < 0) { throw RangeError.range(skipCount, 0, null, "skipCount"); } if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.elementSizeInBytes == elementSizeInBytes) { // Check that from has enough elements, which is assumed by // _fastSetRange, using the more efficient _TypedListBase length getter. final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; return _fastSetRange(start, count, fromAsTyped, skipCount); } } // _slowSetRange checks that from has enough elements internally. return _slowSetRange(start, end, from, skipCount); } // Method(s) implementing the Object interface. String toString() => ListBase.listToString(this as List); // Internal utility methods. void _fastSetRange(int start, int count, _TypedListBase from, int skipCount); void _slowSetRange(int start, int end, Iterable from, int skipCount); @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => false; // Performs a copy of the [count] elements starting at [skipCount] in [from] // to [this] starting at [start]. // // Primarily called by Dart code to handle clamping. // // Element sizes of [this] and [from] must match. [this] must be a clamped // typed data object, and the values in [from] must require possible clamping // (tests at caller). @pragma("vm:external-name", "TypedDataBase_setClampedRange") @pragma("vm:entry-point") external void _setClampedRange( int start, int count, _TypedListBase from, int skipOffset, ); // Performs a copy of the [count] elements starting at [skipCount] in [from] // to [this] starting at [start]. // // The element sizes of [this] and [from] must be 1 (test at caller). @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _memMove1( int start, int count, _TypedListBase from, int skipCount, ); // Performs a copy of the [count] elements starting at [skipCount] in [from] // to [this] starting at [start]. // // The element sizes of [this] and [from] must be 2 (test at caller). @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _memMove2( int start, int count, _TypedListBase from, int skipCount, ); // Performs a copy of the [count] elements starting at [skipCount] in [from] // to [this] starting at [start]. // // The element sizes of [this] and [from] must be 4 (test at caller). @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _memMove4( int start, int count, _TypedListBase from, int skipCount, ); // Performs a copy of the [count] elements starting at [skipCount] in [from] // to [this] starting at [start]. // // The element sizes of [this] and [from] must be 8 (test at caller). @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _memMove8( int start, int count, _TypedListBase from, int skipCount, ); // Performs a copy of the elements starting at [skipCount] in [from] to // [this] starting at [start] (inclusive) and ending at [end] (exclusive). // // The element sizes of [this] and [from] must be 16 (test at caller). @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _memMove16( int start, int count, _TypedListBase from, int skipCount, ); } base mixin _IntListMixin on _TypedListBase implements TypedDataList { int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; Iterable whereType() => WhereTypeIterable(this); Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other); List cast() => List.castFrom(this); void set first(int value) { if (length == 0) throw IterableElementError.tooFew(); this[0] = value; } void set last(int value) { if (length == 0) throw IterableElementError.tooFew(); this[length - 1] = value; } int indexWhere(bool test(int element), [int start = 0]) { if (start < 0) start = 0; for (int i = start; i < length; i++) { if (test(this[i])) return i; } return -1; } int lastIndexWhere(bool test(int element), [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (test(this[i])) return i; } return -1; } List operator +(List other) => [...this, ...other]; bool contains(Object? element) { var len = this.length; for (var i = 0; i < len; ++i) { if (this[i] == element) return true; } return false; } void shuffle([Random? random]) { random ??= Random(); var i = this.length; while (i > 1) { int pos = random.nextInt(i); i -= 1; var tmp = this[i]; this[i] = this[pos]; this[pos] = tmp; } } Iterable where(bool f(int element)) => WhereIterable(this, f); Iterable take(int n) => SubListIterable(this, 0, n); Iterable takeWhile(bool test(int element)) => TakeWhileIterable(this, test); Iterable skip(int n) => SubListIterable(this, n, null); Iterable skipWhile(bool test(int element)) => SkipWhileIterable(this, test); Iterable get reversed => ReversedListIterable(this); Map asMap() => ListMapView(this); Iterable getRange(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); return SubListIterable(this, start, endIndex); } Iterator get iterator => _TypedListIterator(this); List toList({bool growable = true}) { return List.of(this, growable: growable); } Set toSet() { return Set.of(this); } void forEach(void f(int element)) { var len = this.length; for (var i = 0; i < len; i++) { f(this[i]); } } int reduce(int combine(int value, int element)) { var len = this.length; if (len == 0) throw IterableElementError.noElement(); var value = this[0]; for (var i = 1; i < len; ++i) { value = combine(value, this[i]); } return value; } T fold(T initialValue, T combine(T initialValue, int element)) { var len = this.length; for (var i = 0; i < len; ++i) { initialValue = combine(initialValue, this[i]); } return initialValue; } Iterable map(T f(int element)) => MappedIterable(this, f); Iterable expand(Iterable f(int element)) => ExpandIterable(this, f); bool every(bool f(int element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (!f(this[i])) return false; } return true; } bool any(bool f(int element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (f(this[i])) return true; } return false; } int firstWhere(bool test(int element), {int orElse()?}) { var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) return element; } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } int lastWhere(bool test(int element), {int orElse()?}) { var len = this.length; for (var i = len - 1; i >= 0; --i) { var element = this[i]; if (test(element)) { return element; } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } int singleWhere(bool test(int element), {int orElse()?}) { var result = null; bool foundMatching = false; var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) { if (foundMatching) { throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } int elementAt(int index) { return this[index]; } void add(int value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void addAll(Iterable value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void insert(int index, int value) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void insertAll(int index, Iterable values) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void sort([int compare(int a, int b)?]) { Sort.sort(this, compare ?? Comparable.compare); } int indexOf(int element, [int start = 0]) { if (start >= this.length) { return -1; } else if (start < 0) { start = 0; } for (int i = start; i < this.length; i++) { if (this[i] == element) return i; } return -1; } int lastIndexOf(int element, [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (this[i] == element) return i; } return -1; } int removeLast() { throw UnsupportedError("Cannot remove from a fixed-length list"); } int removeAt(int index) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeWhere(bool test(int element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void retainWhere(bool test(int element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } int get first { if (length > 0) return this[0]; throw IterableElementError.noElement(); } int get last { if (length > 0) return this[length - 1]; throw IterableElementError.noElement(); } int get single { if (length == 1) return this[0]; if (length == 0) throw IterableElementError.noElement(); throw IterableElementError.tooMany(); } void setAll(int index, Iterable iterable) { final end = iterable.length + index; setRange(index, end, iterable); } void fillRange(int start, int end, [int? fillValue]) { RangeError.checkValidRange(start, end, this.length); if (start == end) return; if (fillValue == null) { throw ArgumentError.notNull("fillValue"); } for (var i = start; i < end; ++i) { this[i] = fillValue; } } @pragma("vm:prefer-inline") void setRange(int start, int end, Iterable from, [int skipCount = 0]) => _setRange(start, end, from, skipCount); } base mixin _TypedIntListMixin> on _IntListMixin implements List { SpawnedType _createList(int length); void _slowSetRange(int start, int end, Iterable from, int skipCount) { // The numeric inputs have already been checked, all that's left is to // check that from has enough elements when applicable. if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.buffer == this.buffer) { final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; // Different element sizes, but same buffer means that we need // an intermediate structure. // TODO(srdjan): Optimize to skip copying if the range does not overlap. final fromAsList = from as List; final tempBuffer = _createList(count); for (var i = 0; i < count; i++) { tempBuffer[i] = fromAsList[skipCount + i]; } for (var i = start; i < end; i++) { this[i] = tempBuffer[i - start]; } return; } } List otherList; int otherStart; if (from is List) { otherList = from; otherStart = skipCount; } else { otherList = from.skip(skipCount).toList(growable: false); otherStart = 0; } final count = end - start; if ((otherList.length - otherStart) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; Lists.copy(otherList, otherStart, this, start, count); } SpawnedType sublist(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); var length = endIndex - start; SpawnedType result = _createList(length); result.setRange(0, length, this, start); return result; } } base mixin _DoubleListMixin on _TypedListBase implements TypedDataList { int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; Iterable whereType() => WhereTypeIterable(this); Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other); List cast() => List.castFrom(this); void set first(double value) { if (length == 0) throw IterableElementError.tooFew(); this[0] = value; } void set last(double value) { if (length == 0) throw IterableElementError.tooFew(); this[length - 1] = value; } int indexWhere(bool test(double element), [int start = 0]) { if (start < 0) start = 0; for (int i = start; i < length; i++) { if (test(this[i])) return i; } return -1; } int lastIndexWhere(bool test(double element), [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (test(this[i])) return i; } return -1; } List operator +(List other) => [...this, ...other]; bool contains(Object? element) { var len = this.length; for (var i = 0; i < len; ++i) { if (this[i] == element) return true; } return false; } void shuffle([Random? random]) { random ??= Random(); var i = this.length; while (i > 1) { int pos = random.nextInt(i); i -= 1; var tmp = this[i]; this[i] = this[pos]; this[pos] = tmp; } } Iterable where(bool f(double element)) => WhereIterable(this, f); Iterable take(int n) => SubListIterable(this, 0, n); Iterable takeWhile(bool test(double element)) => TakeWhileIterable(this, test); Iterable skip(int n) => SubListIterable(this, n, null); Iterable skipWhile(bool test(double element)) => SkipWhileIterable(this, test); Iterable get reversed => ReversedListIterable(this); Map asMap() => ListMapView(this); Iterable getRange(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); return SubListIterable(this, start, endIndex); } Iterator get iterator => _TypedListIterator(this); List toList({bool growable = true}) { return List.of(this, growable: growable); } Set toSet() { return Set.of(this); } void forEach(void f(double element)) { var len = this.length; for (var i = 0; i < len; i++) { f(this[i]); } } double reduce(double combine(double value, double element)) { var len = this.length; if (len == 0) throw IterableElementError.noElement(); var value = this[0]; for (var i = 1; i < len; ++i) { value = combine(value, this[i]); } return value; } T fold(T initialValue, T combine(T initialValue, double element)) { var len = this.length; for (var i = 0; i < len; ++i) { initialValue = combine(initialValue, this[i]); } return initialValue; } Iterable map(T f(double element)) => MappedIterable(this, f); Iterable expand(Iterable f(double element)) => ExpandIterable(this, f); bool every(bool f(double element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (!f(this[i])) return false; } return true; } bool any(bool f(double element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (f(this[i])) return true; } return false; } double firstWhere(bool test(double element), {double orElse()?}) { var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) return element; } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } double lastWhere(bool test(double element), {double orElse()?}) { var len = this.length; for (var i = len - 1; i >= 0; --i) { var element = this[i]; if (test(element)) { return element; } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } double singleWhere(bool test(double element), {double orElse()?}) { var result = null; bool foundMatching = false; var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) { if (foundMatching) { throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } double elementAt(int index) { return this[index]; } void add(double value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void addAll(Iterable value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void insert(int index, double value) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void insertAll(int index, Iterable values) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void sort([int compare(double a, double b)?]) { Sort.sort(this, compare ?? Comparable.compare); } int indexOf(double element, [int start = 0]) { if (start >= this.length) { return -1; } else if (start < 0) { start = 0; } for (int i = start; i < this.length; i++) { if (this[i] == element) return i; } return -1; } int lastIndexOf(double element, [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (this[i] == element) return i; } return -1; } double removeLast() { throw UnsupportedError("Cannot remove from a fixed-length list"); } double removeAt(int index) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeWhere(bool test(double element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void retainWhere(bool test(double element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } double get first { if (length > 0) return this[0]; throw IterableElementError.noElement(); } double get last { if (length > 0) return this[length - 1]; throw IterableElementError.noElement(); } double get single { if (length == 1) return this[0]; if (length == 0) throw IterableElementError.noElement(); throw IterableElementError.tooMany(); } void setAll(int index, Iterable iterable) { final end = iterable.length + index; setRange(index, end, iterable); } void fillRange(int start, int end, [double? fillValue]) { // TODO(eernst): Could use zero as default and not throw; issue . RangeError.checkValidRange(start, end, this.length); if (start == end) return; if (fillValue == null) { throw ArgumentError.notNull("fillValue"); } for (var i = start; i < end; ++i) { this[i] = fillValue; } } @pragma("vm:prefer-inline") void setRange( int start, int end, Iterable from, [ int skipCount = 0, ]) => _setRange(start, end, from, skipCount); } base mixin _TypedDoubleListMixin> on _DoubleListMixin implements List { SpawnedType _createList(int length); void _slowSetRange(int start, int end, Iterable from, int skipCount) { // The numeric inputs have already been checked, all that's left is to // check that from has enough elements when applicable. if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.buffer == this.buffer) { final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; // Different element sizes, but same buffer means that we need // an intermediate structure. // TODO(srdjan): Optimize to skip copying if the range does not overlap. final fromAsList = from as List; final tempBuffer = _createList(count); for (var i = 0; i < count; i++) { tempBuffer[i] = fromAsList[skipCount + i]; } for (var i = start; i < end; i++) { this[i] = tempBuffer[i - start]; } return; } } List otherList; int otherStart; if (from is List) { otherList = from; otherStart = skipCount; } else { otherList = from.skip(skipCount).toList(growable: false); otherStart = 0; } final count = end - start; if ((otherList.length - otherStart) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; Lists.copy(otherList, otherStart, this, start, count); } SpawnedType sublist(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); var length = endIndex - start; SpawnedType result = _createList(length); result.setRange(0, length, this, start); return result; } } base mixin _Float32x4ListMixin on _TypedListBase implements TypedDataList { int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; Float32x4List _createList(int length); Iterable whereType() => WhereTypeIterable(this); Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other); List cast() => List.castFrom(this); void set first(Float32x4 value) { if (length == 0) throw IterableElementError.tooFew(); this[0] = value; } void set last(Float32x4 value) { if (length == 0) throw IterableElementError.tooFew(); this[length - 1] = value; } int indexWhere(bool test(Float32x4 element), [int start = 0]) { if (start < 0) start = 0; for (int i = start; i < length; i++) { if (test(this[i])) return i; } return -1; } int lastIndexWhere(bool test(Float32x4 element), [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (test(this[i])) return i; } return -1; } List operator +(List other) => [...this, ...other]; bool contains(Object? element) { var len = this.length; for (var i = 0; i < len; ++i) { if (this[i] == element) return true; } return false; } void shuffle([Random? random]) { random ??= Random(); var i = this.length; while (i > 1) { int pos = random.nextInt(i); i -= 1; var tmp = this[i]; this[i] = this[pos]; this[pos] = tmp; } } void _slowSetRange(int start, int end, Iterable from, int skipCount) { // The numeric inputs have already been checked, all that's left is to // check that from has enough elements when applicable. if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.buffer == this.buffer) { final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; // Different element sizes, but same buffer means that we need // an intermediate structure. // TODO(srdjan): Optimize to skip copying if the range does not overlap. final fromAsList = from as List; final tempBuffer = _createList(count); for (var i = 0; i < count; i++) { tempBuffer[i] = fromAsList[skipCount + i]; } for (var i = start; i < end; i++) { this[i] = tempBuffer[i - start]; } return; } } List otherList; int otherStart; if (from is List) { otherList = from; otherStart = skipCount; } else { otherList = from.skip(skipCount).toList(growable: false); otherStart = 0; } final count = end - start; if ((otherList.length - otherStart) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; Lists.copy(otherList, otherStart, this, start, count); } Iterable where(bool f(Float32x4 element)) => WhereIterable(this, f); Iterable take(int n) => SubListIterable(this, 0, n); Iterable takeWhile(bool test(Float32x4 element)) => TakeWhileIterable(this, test); Iterable skip(int n) => SubListIterable(this, n, null); Iterable skipWhile(bool test(Float32x4 element)) => SkipWhileIterable(this, test); Iterable get reversed => ReversedListIterable(this); Map asMap() => ListMapView(this); Iterable getRange(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); return SubListIterable(this, start, endIndex); } Iterator get iterator => _TypedListIterator(this); List toList({bool growable = true}) { return List.of(this, growable: growable); } Set toSet() { return Set.of(this); } void forEach(void f(Float32x4 element)) { var len = this.length; for (var i = 0; i < len; i++) { f(this[i]); } } Float32x4 reduce(Float32x4 combine(Float32x4 value, Float32x4 element)) { var len = this.length; if (len == 0) throw IterableElementError.noElement(); var value = this[0]; for (var i = 1; i < len; ++i) { value = combine(value, this[i]); } return value; } T fold(T initialValue, T combine(T initialValue, Float32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { initialValue = combine(initialValue, this[i]); } return initialValue; } Iterable map(T f(Float32x4 element)) => MappedIterable(this, f); Iterable expand(Iterable f(Float32x4 element)) => ExpandIterable(this, f); bool every(bool f(Float32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (!f(this[i])) return false; } return true; } bool any(bool f(Float32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (f(this[i])) return true; } return false; } Float32x4 firstWhere(bool test(Float32x4 element), {Float32x4 orElse()?}) { var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) return element; } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float32x4 lastWhere(bool test(Float32x4 element), {Float32x4 orElse()?}) { var len = this.length; for (var i = len - 1; i >= 0; --i) { var element = this[i]; if (test(element)) { return element; } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float32x4 singleWhere(bool test(Float32x4 element), {Float32x4 orElse()?}) { var result = null; bool foundMatching = false; var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) { if (foundMatching) { throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float32x4 elementAt(int index) { return this[index]; } void add(Float32x4 value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void addAll(Iterable value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void insert(int index, Float32x4 value) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void insertAll(int index, Iterable values) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void sort([int compare(Float32x4 a, Float32x4 b)?]) { if (compare == null) { throw "SIMD don't have default compare."; } Sort.sort(this, compare); } int indexOf(Float32x4 element, [int start = 0]) { if (start >= this.length) { return -1; } else if (start < 0) { start = 0; } for (int i = start; i < this.length; i++) { if (this[i] == element) return i; } return -1; } int lastIndexOf(Float32x4 element, [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (this[i] == element) return i; } return -1; } Float32x4 removeLast() { throw UnsupportedError("Cannot remove from a fixed-length list"); } Float32x4 removeAt(int index) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeWhere(bool test(Float32x4 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void retainWhere(bool test(Float32x4 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } Float32x4 get first { if (length > 0) return this[0]; throw IterableElementError.noElement(); } Float32x4 get last { if (length > 0) return this[length - 1]; throw IterableElementError.noElement(); } Float32x4 get single { if (length == 1) return this[0]; if (length == 0) throw IterableElementError.noElement(); throw IterableElementError.tooMany(); } Float32x4List sublist(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); var length = endIndex - start; Float32x4List result = _createList(length); result.setRange(0, length, this, start); return result; } void setAll(int index, Iterable iterable) { final end = iterable.length + index; setRange(index, end, iterable); } void fillRange(int start, int end, [Float32x4? fillValue]) { RangeError.checkValidRange(start, end, this.length); if (start == end) return; if (fillValue == null) { throw ArgumentError.notNull("fillValue"); } for (var i = start; i < end; ++i) { this[i] = fillValue; } } @pragma("vm:prefer-inline") void setRange( int start, int end, Iterable from, [ int skipCount = 0, ]) => _setRange(start, end, from, skipCount); } base mixin _Int32x4ListMixin on _TypedListBase implements TypedDataList { int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; Int32x4List _createList(int length); Iterable whereType() => WhereTypeIterable(this); Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other); List cast() => List.castFrom(this); void set first(Int32x4 value) { if (length == 0) throw IterableElementError.tooFew(); this[0] = value; } void set last(Int32x4 value) { if (length == 0) throw IterableElementError.tooFew(); this[length - 1] = value; } int indexWhere(bool test(Int32x4 element), [int start = 0]) { if (start < 0) start = 0; for (int i = start; i < length; i++) { if (test(this[i])) return i; } return -1; } int lastIndexWhere(bool test(Int32x4 element), [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (test(this[i])) return i; } return -1; } List operator +(List other) => [...this, ...other]; bool contains(Object? element) { var len = this.length; for (var i = 0; i < len; ++i) { if (this[i] == element) return true; } return false; } void shuffle([Random? random]) { random ??= Random(); var i = this.length; while (i > 1) { int pos = random.nextInt(i); i -= 1; var tmp = this[i]; this[i] = this[pos]; this[pos] = tmp; } } void _slowSetRange(int start, int end, Iterable from, int skipCount) { // The numeric inputs have already been checked, all that's left is to // check that from has enough elements when applicable. if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.buffer == this.buffer) { final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; // Different element sizes, but same buffer means that we need // an intermediate structure. // TODO(srdjan): Optimize to skip copying if the range does not overlap. final fromAsList = from as List; final tempBuffer = _createList(count); for (var i = 0; i < count; i++) { tempBuffer[i] = fromAsList[skipCount + i]; } for (var i = start; i < end; i++) { this[i] = tempBuffer[i - start]; } return; } } List otherList; int otherStart; if (from is List) { otherList = from; otherStart = skipCount; } else { otherList = from.skip(skipCount).toList(growable: false); otherStart = 0; } final count = end - start; if ((otherList.length - otherStart) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; Lists.copy(otherList, otherStart, this, start, count); } Iterable where(bool f(Int32x4 element)) => WhereIterable(this, f); Iterable take(int n) => SubListIterable(this, 0, n); Iterable takeWhile(bool test(Int32x4 element)) => TakeWhileIterable(this, test); Iterable skip(int n) => SubListIterable(this, n, null); Iterable skipWhile(bool test(Int32x4 element)) => SkipWhileIterable(this, test); Iterable get reversed => ReversedListIterable(this); Map asMap() => ListMapView(this); Iterable getRange(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); return SubListIterable(this, start, endIndex); } Iterator get iterator => _TypedListIterator(this); List toList({bool growable = true}) { return List.of(this, growable: growable); } Set toSet() { return Set.of(this); } void forEach(void f(Int32x4 element)) { var len = this.length; for (var i = 0; i < len; i++) { f(this[i]); } } Int32x4 reduce(Int32x4 combine(Int32x4 value, Int32x4 element)) { var len = this.length; if (len == 0) throw IterableElementError.noElement(); var value = this[0]; for (var i = 1; i < len; ++i) { value = combine(value, this[i]); } return value; } T fold(T initialValue, T combine(T initialValue, Int32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { initialValue = combine(initialValue, this[i]); } return initialValue; } Iterable map(T f(Int32x4 element)) => MappedIterable(this, f); Iterable expand(Iterable f(Int32x4 element)) => ExpandIterable(this, f); bool every(bool f(Int32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (!f(this[i])) return false; } return true; } bool any(bool f(Int32x4 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (f(this[i])) return true; } return false; } Int32x4 firstWhere(bool test(Int32x4 element), {Int32x4 orElse()?}) { var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) return element; } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Int32x4 lastWhere(bool test(Int32x4 element), {Int32x4 orElse()?}) { var len = this.length; for (var i = len - 1; i >= 0; --i) { var element = this[i]; if (test(element)) { return element; } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Int32x4 singleWhere(bool test(Int32x4 element), {Int32x4 orElse()?}) { var result = null; bool foundMatching = false; var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) { if (foundMatching) { throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Int32x4 elementAt(int index) { return this[index]; } void add(Int32x4 value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void addAll(Iterable value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void insert(int index, Int32x4 value) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void insertAll(int index, Iterable values) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void sort([int compare(Int32x4 a, Int32x4 b)?]) { if (compare == null) { throw "SIMD don't have default compare."; } Sort.sort(this, compare); } int indexOf(Int32x4 element, [int start = 0]) { if (start >= this.length) { return -1; } else if (start < 0) { start = 0; } for (int i = start; i < this.length; i++) { if (this[i] == element) return i; } return -1; } int lastIndexOf(Int32x4 element, [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (this[i] == element) return i; } return -1; } Int32x4 removeLast() { throw UnsupportedError("Cannot remove from a fixed-length list"); } Int32x4 removeAt(int index) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeWhere(bool test(Int32x4 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void retainWhere(bool test(Int32x4 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } Int32x4 get first { if (length > 0) return this[0]; throw IterableElementError.noElement(); } Int32x4 get last { if (length > 0) return this[length - 1]; throw IterableElementError.noElement(); } Int32x4 get single { if (length == 1) return this[0]; if (length == 0) throw IterableElementError.noElement(); throw IterableElementError.tooMany(); } Int32x4List sublist(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); var length = endIndex - start; Int32x4List result = _createList(length); result.setRange(0, length, this, start); return result; } void setAll(int index, Iterable iterable) { final end = iterable.length + index; setRange(index, end, iterable); } void fillRange(int start, int end, [Int32x4? fillValue]) { RangeError.checkValidRange(start, end, this.length); if (start == end) return; if (fillValue == null) { throw ArgumentError.notNull("fillValue"); } for (var i = start; i < end; ++i) { this[i] = fillValue; } } @pragma("vm:prefer-inline") void setRange( int start, int end, Iterable from, [ int skipCount = 0, ]) => _setRange(start, end, from, skipCount); } base mixin _Float64x2ListMixin on _TypedListBase implements TypedDataList { int get elementSizeInBytes; int get offsetInBytes; _ByteBuffer get buffer; Float64x2List _createList(int length); Iterable whereType() => WhereTypeIterable(this); Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other); List cast() => List.castFrom(this); void set first(Float64x2 value) { if (length == 0) throw IterableElementError.tooFew(); this[0] = value; } void set last(Float64x2 value) { if (length == 0) throw IterableElementError.tooFew(); this[length - 1] = value; } int indexWhere(bool test(Float64x2 element), [int start = 0]) { if (start < 0) start = 0; for (int i = start; i < length; i++) { if (test(this[i])) return i; } return -1; } int lastIndexWhere(bool test(Float64x2 element), [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (test(this[i])) return i; } return -1; } List operator +(List other) => [...this, ...other]; bool contains(Object? element) { var len = this.length; for (var i = 0; i < len; ++i) { if (this[i] == element) return true; } return false; } void shuffle([Random? random]) { random ??= Random(); var i = this.length; while (i > 1) { int pos = random.nextInt(i); i -= 1; var tmp = this[i]; this[i] = this[pos]; this[pos] = tmp; } } void _slowSetRange(int start, int end, Iterable from, int skipCount) { // The numeric inputs have already been checked, all that's left is to // check that from has enough elements when applicable. if (from is _TypedListBase) { // Note: _TypedListBase is not related to Iterable so there is no // promotion here. final fromAsTyped = unsafeCast<_TypedListBase>(from); if (fromAsTyped.buffer == this.buffer) { final count = end - start; if ((fromAsTyped.length - skipCount) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; // Different element sizes, but same buffer means that we need // an intermediate structure. // TODO(srdjan): Optimize to skip copying if the range does not overlap. final fromAsList = from as List; final tempBuffer = _createList(count); for (var i = 0; i < count; i++) { tempBuffer[i] = fromAsList[skipCount + i]; } for (var i = start; i < end; i++) { this[i] = tempBuffer[i - start]; } return; } } List otherList; int otherStart; if (from is List) { otherList = from; otherStart = skipCount; } else { otherList = from.skip(skipCount).toList(growable: false); otherStart = 0; } final count = end - start; if ((otherList.length - otherStart) < count) { throw IterableElementError.tooFew(); } if (count == 0) return; Lists.copy(otherList, otherStart, this, start, count); } Iterable where(bool f(Float64x2 element)) => WhereIterable(this, f); Iterable take(int n) => SubListIterable(this, 0, n); Iterable takeWhile(bool test(Float64x2 element)) => TakeWhileIterable(this, test); Iterable skip(int n) => SubListIterable(this, n, null); Iterable skipWhile(bool test(Float64x2 element)) => SkipWhileIterable(this, test); Iterable get reversed => ReversedListIterable(this); Map asMap() => ListMapView(this); Iterable getRange(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); return SubListIterable(this, start, endIndex); } Iterator get iterator => _TypedListIterator(this); List toList({bool growable = true}) { return List.of(this, growable: growable); } Set toSet() { return Set.of(this); } void forEach(void f(Float64x2 element)) { var len = this.length; for (var i = 0; i < len; i++) { f(this[i]); } } Float64x2 reduce(Float64x2 combine(Float64x2 value, Float64x2 element)) { var len = this.length; if (len == 0) throw IterableElementError.noElement(); var value = this[0]; for (var i = 1; i < len; ++i) { value = combine(value, this[i]); } return value; } T fold(T initialValue, T combine(T initialValue, Float64x2 element)) { var len = this.length; for (var i = 0; i < len; ++i) { initialValue = combine(initialValue, this[i]); } return initialValue; } Iterable map(T f(Float64x2 element)) => MappedIterable(this, f); Iterable expand(Iterable f(Float64x2 element)) => ExpandIterable(this, f); bool every(bool f(Float64x2 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (!f(this[i])) return false; } return true; } bool any(bool f(Float64x2 element)) { var len = this.length; for (var i = 0; i < len; ++i) { if (f(this[i])) return true; } return false; } Float64x2 firstWhere(bool test(Float64x2 element), {Float64x2 orElse()?}) { var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) return element; } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float64x2 lastWhere(bool test(Float64x2 element), {Float64x2 orElse()?}) { var len = this.length; for (var i = len - 1; i >= 0; --i) { var element = this[i]; if (test(element)) { return element; } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float64x2 singleWhere(bool test(Float64x2 element), {Float64x2 orElse()?}) { var result = null; bool foundMatching = false; var len = this.length; for (var i = 0; i < len; ++i) { var element = this[i]; if (test(element)) { if (foundMatching) { throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); throw IterableElementError.noElement(); } Float64x2 elementAt(int index) { return this[index]; } void add(Float64x2 value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void addAll(Iterable value) { throw UnsupportedError("Cannot add to a fixed-length list"); } void insert(int index, Float64x2 value) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void insertAll(int index, Iterable values) { throw UnsupportedError("Cannot insert into a fixed-length list"); } void sort([int compare(Float64x2 a, Float64x2 b)?]) { if (compare == null) { throw "SIMD don't have default compare."; } Sort.sort(this, compare); } int indexOf(Float64x2 element, [int start = 0]) { if (start >= this.length) { return -1; } else if (start < 0) { start = 0; } for (int i = start; i < this.length; i++) { if (this[i] == element) return i; } return -1; } int lastIndexOf(Float64x2 element, [int? start]) { int startIndex = (start == null || start >= this.length) ? this.length - 1 : start; for (int i = startIndex; i >= 0; i--) { if (this[i] == element) return i; } return -1; } Float64x2 removeLast() { throw UnsupportedError("Cannot remove from a fixed-length list"); } Float64x2 removeAt(int index) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void removeWhere(bool test(Float64x2 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } void retainWhere(bool test(Float64x2 element)) { throw UnsupportedError("Cannot remove from a fixed-length list"); } Float64x2 get first { if (length > 0) return this[0]; throw IterableElementError.noElement(); } Float64x2 get last { if (length > 0) return this[length - 1]; throw IterableElementError.noElement(); } Float64x2 get single { if (length == 1) return this[0]; if (length == 0) throw IterableElementError.noElement(); throw IterableElementError.tooMany(); } Float64x2List sublist(int start, [int? end]) { int endIndex = RangeError.checkValidRange(start, end, this.length); var length = endIndex - start; Float64x2List result = _createList(length); result.setRange(0, length, this, start); return result; } void setAll(int index, Iterable iterable) { final end = iterable.length + index; setRange(index, end, iterable); } void fillRange(int start, int end, [Float64x2? fillValue]) { RangeError.checkValidRange(start, end, this.length); if (start == end) return; if (fillValue == null) { throw ArgumentError.notNull("fillValue"); } for (var i = start; i < end; ++i) { this[i] = fillValue; } } @pragma("vm:prefer-inline") void setRange( int start, int end, Iterable from, [ int skipCount = 0, ]) => _setRange(start, end, from, skipCount); } @pragma("vm:entry-point") final class _ByteBuffer implements ByteBuffer { final _TypedList _data; _ByteBuffer(this._data); @pragma("vm:entry-point") factory _ByteBuffer._New(data) => _ByteBuffer(data); // Forward calls to _data. int get lengthInBytes => _data.lengthInBytes; int get hashCode => _data.hashCode; bool operator ==(Object other) => (other is _ByteBuffer) && identical(_data, other._data); ByteData asByteData([int offsetInBytes = 0, int? length]) { length ??= this.lengthInBytes - offsetInBytes; _rangeCheck(this._data.lengthInBytes, offsetInBytes, length); return _ByteDataView._(this._data, offsetInBytes, length); } Int8List asInt8List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Int8List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Int8List.bytesPerElement, ); return _Int8ArrayView._(this._data, offsetInBytes, length); } Uint8List asUint8List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Uint8List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Uint8List.bytesPerElement, ); return _Uint8ArrayView._(this._data, offsetInBytes, length); } Uint8ClampedList asUint8ClampedList([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Uint8ClampedList.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Uint8ClampedList.bytesPerElement, ); return _Uint8ClampedArrayView._(this._data, offsetInBytes, length); } Int16List asInt16List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Int16List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Int16List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Int16List.bytesPerElement); return _Int16ArrayView._(this._data, offsetInBytes, length); } Uint16List asUint16List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Uint16List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Uint16List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Uint16List.bytesPerElement); return _Uint16ArrayView._(this._data, offsetInBytes, length); } Int32List asInt32List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Int32List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Int32List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Int32List.bytesPerElement); return _Int32ArrayView._(this._data, offsetInBytes, length); } Uint32List asUint32List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Uint32List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Uint32List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Uint32List.bytesPerElement); return _Uint32ArrayView._(this._data, offsetInBytes, length); } Int64List asInt64List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Int64List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Int64List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Int64List.bytesPerElement); return _Int64ArrayView._(this._data, offsetInBytes, length); } Uint64List asUint64List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Uint64List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Uint64List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Uint64List.bytesPerElement); return _Uint64ArrayView._(this._data, offsetInBytes, length); } Float32List asFloat32List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Float32List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Float32List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Float32List.bytesPerElement); return _Float32ArrayView._(this._data, offsetInBytes, length); } Float64List asFloat64List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Float64List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Float64List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Float64List.bytesPerElement); return _Float64ArrayView._(this._data, offsetInBytes, length); } Float32x4List asFloat32x4List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Float32x4List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Float32x4List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Float32x4List.bytesPerElement); return _Float32x4ArrayView._(this._data, offsetInBytes, length); } Int32x4List asInt32x4List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Int32x4List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Int32x4List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Int32x4List.bytesPerElement); return _Int32x4ArrayView._(this._data, offsetInBytes, length); } Float64x2List asFloat64x2List([int offsetInBytes = 0, int? length]) { length ??= (this.lengthInBytes - offsetInBytes) ~/ Float64x2List.bytesPerElement; _rangeCheck( this.lengthInBytes, offsetInBytes, length * Float64x2List.bytesPerElement, ); _offsetAlignmentCheck(offsetInBytes, Float64x2List.bytesPerElement); return _Float64x2ArrayView._(this._data, offsetInBytes, length); } } abstract final class _TypedList extends _TypedListBase { int get elementSizeInBytes; // Default method implementing parts of the TypedData interface. int get offsetInBytes { return 0; } int get lengthInBytes { return length * elementSizeInBytes; } _ByteBuffer get buffer => _ByteBuffer(this); // Internal utility methods. _TypedList get _typedData => this; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getInt8(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setInt8(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getUint8(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setUint8(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getInt16(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setInt16(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getUint16(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setUint16(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getInt32(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setInt32(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getUint32(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setUint32(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getInt64(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setInt64(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int _getUint64(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setUint64(int offsetInBytes, int value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external double _getFloat32(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setFloat32(int offsetInBytes, double value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external double _getFloat64(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setFloat64(int offsetInBytes, double value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external Float32x4 _getFloat32x4(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setFloat32x4(int offsetInBytes, Float32x4 value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external Int32x4 _getInt32x4(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setInt32x4(int offsetInBytes, Int32x4 value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external Float64x2 _getFloat64x2(int offsetInBytes); @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external void _setFloat64x2(int offsetInBytes, Float64x2 value); // The _nativeGetX and _nativeSetX methods are only used as a fallback when // unboxed double or SIMD values are unsupported by the flow graph compiler. @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "TypedData_GetFloat32") external double _nativeGetFloat32(int offsetInBytes); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypedData_SetFloat32") external void _nativeSetFloat32(int offsetInBytes, double value); @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "TypedData_GetFloat64") external double _nativeGetFloat64(int offsetInBytes); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypedData_SetFloat64") external void _nativeSetFloat64(int offsetInBytes, double value); @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "TypedData_GetFloat32x4") external Float32x4 _nativeGetFloat32x4(int offsetInBytes); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypedData_SetFloat32x4") external void _nativeSetFloat32x4(int offsetInBytes, Float32x4 value); @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "TypedData_GetInt32x4") external Int32x4 _nativeGetInt32x4(int offsetInBytes); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypedData_SetInt32x4") external void _nativeSetInt32x4(int offsetInBytes, Int32x4 value); @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "TypedData_GetFloat64x2") external Float64x2 _nativeGetFloat64x2(int offsetInBytes); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypedData_SetFloat64x2") external void _nativeSetFloat64x2(int offsetInBytes, Float64x2 value); /** * Stores the [CodeUnits] as UTF-16 units into this TypedData at * positions [start]..[end] (uint16 indices). */ void _setCodeUnits( CodeUnits units, int byteStart, int length, int skipCount, ) { assert(byteStart + length * Uint16List.bytesPerElement <= lengthInBytes); String string = CodeUnits.stringOf(units); int sliceEnd = skipCount + length; RangeError.checkValidRange( skipCount, sliceEnd, string.length, "skipCount", "skipCount + length", ); for (int i = 0; i < length; i++) { _setUint16( byteStart + i * Uint16List.bytesPerElement, string.codeUnitAt(skipCount + i), ); } } } @patch class Int8List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int8List) @pragma("vm:prefer-inline") external factory Int8List(int length); @patch factory Int8List.fromList(List elements) { return Int8List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Int8List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int8List { factory _Int8List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt8(index, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int8List.bytesPerElement; } // Method(s) implementing the Int8List interface. Int8List asUnmodifiableView() => _UnmodifiableInt8ArrayView(this); // Internal utility methods. Int8List _createList(int length) { return Int8List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @patch class Uint8List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint8List) @pragma("vm:prefer-inline") external factory Uint8List(int length); @patch factory Uint8List.fromList(List elements) { return Uint8List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Uint8List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint8List { factory _Uint8List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint8(index, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8List interface. Uint8List asUnmodifiableView() => _UnmodifiableUint8ArrayView(this); // Internal utility methods. Uint8List _createList(int length) { return Uint8List(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @patch class Uint8ClampedList { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint8ClampedList) @pragma("vm:prefer-inline") external factory Uint8ClampedList(int length); @patch factory Uint8ClampedList.fromList(List elements) { return Uint8ClampedList(elements.length) ..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Uint8ClampedList extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint8ClampedList { factory _Uint8ClampedList._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint8(index, _toClampedUint8(value)); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8ClampedList interface. Uint8ClampedList asUnmodifiableView() => _UnmodifiableUint8ClampedArrayView(this); // Internal utility methods. Uint8ClampedList _createList(int length) { return Uint8ClampedList(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => from._containsUnsignedBytes ? _memMove1(start, count, from, skipCount) : _setClampedRange(start, count, from, skipCount); } @patch class Int16List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int16List) @pragma("vm:prefer-inline") external factory Int16List(int length); @patch factory Int16List.fromList(List elements) { return Int16List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Int16List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int16List { factory _Int16List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt16(index * Int16List.bytesPerElement, value); } @pragma("vm:prefer-inline") @override void setRange(int start, int end, Iterable from, [int skipCount = 0]) { if (from is CodeUnits) { end = RangeError.checkValidRange(start, end, this.length); int length = end - start; int byteStart = this.offsetInBytes + start * Int16List.bytesPerElement; _setCodeUnits(from, byteStart, length, skipCount); } else { super.setRange(start, end, from, skipCount); } } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int16List.bytesPerElement; } // Method(s) implementing the Int16List interface. Int16List asUnmodifiableView() => _UnmodifiableInt16ArrayView(this); // Internal utility methods. Int16List _createList(int length) { return Int16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @patch class Uint16List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint16List) @pragma("vm:prefer-inline") external factory Uint16List(int length); @patch factory Uint16List.fromList(List elements) { return Uint16List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Uint16List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint16List { factory _Uint16List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint16(index * Uint16List.bytesPerElement, value); } @pragma("vm:prefer-inline") @override void setRange(int start, int end, Iterable from, [int skipCount = 0]) { if (from is CodeUnits) { end = RangeError.checkValidRange(start, end, this.length); int length = end - start; int byteStart = this.offsetInBytes + start * Uint16List.bytesPerElement; _setCodeUnits(from, byteStart, length, skipCount); } else { super.setRange(start, end, from, skipCount); } } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint16List.bytesPerElement; } // Method(s) implementing the Uint16List interface. Uint16List asUnmodifiableView() => _UnmodifiableUint16ArrayView(this); // Internal utility methods. Uint16List _createList(int length) { return Uint16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @patch class Int32List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32List) @pragma("vm:prefer-inline") external factory Int32List(int length); @patch factory Int32List.fromList(List elements) { return Int32List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Int32List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int32List { factory _Int32List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt32(index * Int32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32List.bytesPerElement; } // Method(s) implementing the Int32List interface. Int32List asUnmodifiableView() => _UnmodifiableInt32ArrayView(this); // Internal utility methods. Int32List _createList(int length) { return Int32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @patch class Uint32List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint32List) @pragma("vm:prefer-inline") external factory Uint32List(int length); @patch factory Uint32List.fromList(List elements) { return Uint32List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Uint32List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint32List { factory _Uint32List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint32(index * Uint32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint32List.bytesPerElement; } // Method(s) implementing the Uint32List interface. Uint32List asUnmodifiableView() => _UnmodifiableUint32ArrayView(this); // Internal utility methods. Uint32List _createList(int length) { return Uint32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @patch class Int64List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int64List) @pragma("vm:prefer-inline") external factory Int64List(int length); @patch factory Int64List.fromList(List elements) { return Int64List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Int64List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int64List { factory _Int64List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt64(index * Int64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int64List.bytesPerElement; } // Method(s) implementing the Int64List interface. Int64List asUnmodifiableView() => _UnmodifiableInt64ArrayView(this); // Internal utility methods. Int64List _createList(int length) { return Int64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @patch class Uint64List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint64List) @pragma("vm:prefer-inline") external factory Uint64List(int length); @patch factory Uint64List.fromList(List elements) { return Uint64List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Uint64List extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint64List { factory _Uint64List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint64(index * Uint64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint64List.bytesPerElement; } // Method(s) implementing the Uint64List interface. Uint64List asUnmodifiableView() => _UnmodifiableUint64ArrayView(this); // Internal utility methods. Uint64List _createList(int length) { return Uint64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @patch class Float32List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32List) @pragma("vm:prefer-inline") external factory Float32List(int length); @patch factory Float32List.fromList(List elements) { return Float32List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Float32List extends _TypedList with _DoubleListMixin, _TypedDoubleListMixin implements Float32List { factory _Float32List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _setFloat32(index * Float32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32List.bytesPerElement; } // Method(s) implementing the Float32List interface. Float32List asUnmodifiableView() => _UnmodifiableFloat32ArrayView(this); // Internal utility methods. Float32List _createList(int length) { return Float32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @patch class Float64List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64List) @pragma("vm:prefer-inline") external factory Float64List(int length); @patch factory Float64List.fromList(List elements) { return Float64List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Float64List extends _TypedList with _DoubleListMixin, _TypedDoubleListMixin implements Float64List { factory _Float64List._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _setFloat64(index * Float64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64List.bytesPerElement; } // Method(s) implementing the Float64List interface. Float64List asUnmodifiableView() => _UnmodifiableFloat64ArrayView(this); // Internal utility methods. Float64List _createList(int length) { return Float64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @patch class Float32x4List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4List) @pragma("vm:prefer-inline") external factory Float32x4List(int length); @patch factory Float32x4List.fromList(List elements) { return Float32x4List(elements.length) ..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Float32x4List extends _TypedList with _Float32x4ListMixin implements Float32x4List { factory _Float32x4List._uninstantiable() { throw "Unreachable"; } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float32x4) external Float32x4 operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, Float32x4 value) { index = _typedDataIndexCheck(this, index, length); _setFloat32x4(index * Float32x4List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32x4List.bytesPerElement; } // Method(s) implementing the Float32x4List interface. Float32x4List asUnmodifiableView() => _UnmodifiableFloat32x4ArrayView(this); // Internal utility methods. Float32x4List _createList(int length) { return Float32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @patch class Int32x4List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4List) @pragma("vm:prefer-inline") external factory Int32x4List(int length); @patch factory Int32x4List.fromList(List elements) { return Int32x4List(elements.length)..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Int32x4List extends _TypedList with _Int32x4ListMixin implements Int32x4List { factory _Int32x4List._uninstantiable() { throw "Unreachable"; } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Int32x4) external Int32x4 operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, Int32x4 value) { index = _typedDataIndexCheck(this, index, length); _setInt32x4(index * Int32x4List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32x4List.bytesPerElement; } // Method(s) implementing the Int32x4List interface. Int32x4List asUnmodifiableView() => _UnmodifiableInt32x4ArrayView(this); // Internal utility methods. Int32x4List _createList(int length) { return Int32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @patch class Float64x2List { @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2List) @pragma("vm:prefer-inline") external factory Float64x2List(int length); @patch factory Float64x2List.fromList(List elements) { return Float64x2List(elements.length) ..setRange(0, elements.length, elements); } } @pragma("vm:entry-point") final class _Float64x2List extends _TypedList with _Float64x2ListMixin implements Float64x2List { factory _Float64x2List._uninstantiable() { throw "Unreachable"; } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float64x2) external Float64x2 operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, Float64x2 value) { index = _typedDataIndexCheck(this, index, length); _setFloat64x2(index * Float64x2List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64x2List.bytesPerElement; } // Method(s) implementing the Float64x2List interface. Float64x2List asUnmodifiableView() => _UnmodifiableFloat64x2ArrayView(this); // Internal utility methods. Float64x2List _createList(int length) { return Float64x2List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalInt8Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int8List { factory _ExternalInt8Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt8(index, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int8List.bytesPerElement; } // Method(s) implementing the Int8List interface. Int8List asUnmodifiableView() => _UnmodifiableInt8ArrayView(this); // Internal utility methods. Int8List _createList(int length) { return Int8List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalUint8Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint8List { factory _ExternalUint8Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint8(index, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8ClampedList interface. Uint8List asUnmodifiableView() => _UnmodifiableUint8ArrayView(this); // Internal utility methods. Uint8List _createList(int length) { return Uint8List(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalUint8ClampedArray extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint8ClampedList { factory _ExternalUint8ClampedArray._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:recognized", "graph-intrinsic") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint8(index, _toClampedUint8(value)); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8ClampedList interface. Uint8ClampedList asUnmodifiableView() => _UnmodifiableUint8ClampedArrayView(this); // Internal utility methods. Uint8ClampedList _createList(int length) { return Uint8ClampedList(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => from._containsUnsignedBytes ? _memMove1(start, count, from, skipCount) : _setClampedRange(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalInt16Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int16List { factory _ExternalInt16Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt16(index * Int16List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int16List.bytesPerElement; } // Method(s) implementing the Int16List interface. Int16List asUnmodifiableView() => _UnmodifiableInt16ArrayView(this); // Internal utility methods. Int16List _createList(int length) { return Int16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalUint16Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint16List { factory _ExternalUint16Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint16(index * Uint16List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint16List.bytesPerElement; } // Method(s) implementing the Uint16List interface. Uint16List asUnmodifiableView() => _UnmodifiableUint16ArrayView(this); // Internal utility methods. Uint16List _createList(int length) { return Uint16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalInt32Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int32List { factory _ExternalInt32Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt32(index * Int32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32List.bytesPerElement; } // Method(s) implementing the Int32List interface. Int32List asUnmodifiableView() => _UnmodifiableInt32ArrayView(this); // Internal utility methods. Int32List _createList(int length) { return Int32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalUint32Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint32List { factory _ExternalUint32Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint32(index * Uint32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint32List.bytesPerElement; } // Method(s) implementing the Uint32List interface. Uint32List asUnmodifiableView() => _UnmodifiableUint32ArrayView(this); // Internal utility methods. Uint32List _createList(int length) { return Uint32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalInt64Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Int64List { factory _ExternalInt64Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setInt64(index * Int64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int64List.bytesPerElement; } // Method(s) implementing the Int64List interface. Int64List asUnmodifiableView() => _UnmodifiableInt64ArrayView(this); // Internal utility methods. Int64List _createList(int length) { return Int64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalUint64Array extends _TypedList with _IntListMixin, _TypedIntListMixin implements Uint64List { factory _ExternalUint64Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _setUint64(index * Uint64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint64List.bytesPerElement; } // Method(s) implementing the Uint64List interface. Uint64List asUnmodifiableView() => _UnmodifiableUint64ArrayView(this); // Internal utility methods. Uint64List _createList(int length) { return Uint64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalFloat32Array extends _TypedList with _DoubleListMixin, _TypedDoubleListMixin implements Float32List { factory _ExternalFloat32Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _setFloat32(index * Float32List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32List.bytesPerElement; } // Method(s) implementing the Float32List interface. Float32List asUnmodifiableView() => _UnmodifiableFloat32ArrayView(this); // Internal utility methods. Float32List _createList(int length) { return Float32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalFloat64Array extends _TypedList with _DoubleListMixin, _TypedDoubleListMixin implements Float64List { factory _ExternalFloat64Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _setFloat64(index * Float64List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64List.bytesPerElement; } // Method(s) implementing the Float64List interface. Float64List asUnmodifiableView() => _UnmodifiableFloat64ArrayView(this); // Internal utility methods. Float64List _createList(int length) { return Float64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalFloat32x4Array extends _TypedList with _Float32x4ListMixin implements Float32x4List { factory _ExternalFloat32x4Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float32x4) external Float32x4 operator [](int index); void operator []=(int index, Float32x4 value) { index = _typedDataIndexCheck(this, index, length); _setFloat32x4(index * Float32x4List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32x4List.bytesPerElement; } // Method(s) implementing the Float32x4 interface. Float32x4List asUnmodifiableView() => _UnmodifiableFloat32x4ArrayView(this); // Internal utility methods. Float32x4List _createList(int length) { return Float32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalInt32x4Array extends _TypedList with _Int32x4ListMixin implements Int32x4List { factory _ExternalInt32x4Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Int32x4) external Int32x4 operator [](int index); void operator []=(int index, Int32x4 value) { index = _typedDataIndexCheck(this, index, length); _setInt32x4(index * Int32x4List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32x4List.bytesPerElement; } // Method(s) implementing the Int32x4List interface. Int32x4List asUnmodifiableView() => _UnmodifiableInt32x4ArrayView(this); // Internal utility methods. Int32x4List _createList(int length) { return Int32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ExternalFloat64x2Array extends _TypedList with _Float64x2ListMixin implements Float64x2List { factory _ExternalFloat64x2Array._uninstantiable() { throw "Unreachable"; } // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float64x2) external Float64x2 operator [](int index); void operator []=(int index, Float64x2 value) { index = _typedDataIndexCheck(this, index, length); _setFloat64x2(index * Float64x2List.bytesPerElement, value); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64x2List.bytesPerElement; } // Method(s) implementing the Float64x2List interface. Float64x2List asUnmodifiableView() => _UnmodifiableFloat64x2ArrayView(this); // Internal utility methods. Float64x2List _createList(int length) { return Float64x2List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @patch @pragma('vm:deeply-immutable') class Float32x4 { @patch @pragma("vm:prefer-inline") factory Float32x4(double x, double y, double z, double w) { return _Float32x4FromDoubles(x, y, z, w); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_fromDoubles") external static _Float32x4 _Float32x4FromDoubles( double x, double y, double z, double w, ); @patch @pragma("vm:prefer-inline") factory Float32x4.splat(double value) => _Float32x4Splat(value); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_splat") external static _Float32x4 _Float32x4Splat(double v); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_zero") external factory Float32x4.zero(); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_fromInt32x4Bits") external factory Float32x4.fromInt32x4Bits(Int32x4 x); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_fromFloat64x2") external factory Float32x4.fromFloat64x2(Float64x2 xy); } @pragma('vm:deeply-immutable') @pragma("vm:entry-point") final class _Float32x4 implements Float32x4 { @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_add") external Float32x4 operator +(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_negate") external Float32x4 operator -(); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_sub") external Float32x4 operator -(Float32x4 other); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_mul") external Float32x4 operator *(Float32x4 other); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "Float32x4_div") external Float32x4 operator /(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmplt") external Int32x4 lessThan(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmplte") external Int32x4 lessThanOrEqual(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmpgt") external Int32x4 greaterThan(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmpgte") external Int32x4 greaterThanOrEqual(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmpequal") external Int32x4 equal(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Float32x4_cmpnequal") external Int32x4 notEqual(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_scale") external Float32x4 scale(double scale); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_abs") external Float32x4 abs(); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_clamp") external Float32x4 clamp(Float32x4 lowerLimit, Float32x4 upperLimit); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float32x4_getX") external double get x; @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float32x4_getY") external double get y; @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float32x4_getZ") external double get z; @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float32x4_getW") external double get w; @pragma("vm:recognized", "other") @pragma("vm:external-name", "Float32x4_getSignMask") external int get signMask; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_shuffle") external Float32x4 shuffle(int mask); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_shuffleMix") external Float32x4 shuffleMix(Float32x4 zw, int mask); @pragma("vm:prefer-inline") Float32x4 withX(double x) { return _withX(x); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_setX") external Float32x4 _withX(double x); @pragma("vm:prefer-inline") Float32x4 withY(double y) { return _withY(y); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_setY") external Float32x4 _withY(double y); @pragma("vm:prefer-inline") Float32x4 withZ(double z) { return _withZ(z); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_setZ") external Float32x4 _withZ(double z); @pragma("vm:prefer-inline") Float32x4 withW(double w) { return _withW(w); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_setW") external Float32x4 _withW(double w); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_min") external Float32x4 min(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_max") external Float32x4 max(Float32x4 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_sqrt") external Float32x4 sqrt(); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_reciprocal") external Float32x4 reciprocal(); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Float32x4_reciprocalSqrt") external Float32x4 reciprocalSqrt(); } @patch @pragma('vm:deeply-immutable') class Int32x4 { @patch @pragma("vm:prefer-inline") factory Int32x4(int x, int y, int z, int w) { return _Int32x4FromInts(x, y, z, w); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_fromInts") external static _Int32x4 _Int32x4FromInts(int x, int y, int z, int w); @patch @pragma("vm:prefer-inline") factory Int32x4.bool(bool x, bool y, bool z, bool w) { return _Int32x4FromBools(x, y, z, w); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_fromBools") external static _Int32x4 _Int32x4FromBools(bool x, bool y, bool z, bool w); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_fromFloat32x4Bits") external factory Int32x4.fromFloat32x4Bits(Float32x4 x); } @pragma('vm:deeply-immutable') @pragma("vm:entry-point") final class _Int32x4 implements Int32x4 { @pragma("vm:external-name", "Int32x4_or") external Int32x4 operator |(Int32x4 other); @pragma("vm:external-name", "Int32x4_and") external Int32x4 operator &(Int32x4 other); @pragma("vm:external-name", "Int32x4_xor") external Int32x4 operator ^(Int32x4 other); @pragma("vm:external-name", "Int32x4_add") external Int32x4 operator +(Int32x4 other); @pragma("vm:external-name", "Int32x4_sub") external Int32x4 operator -(Int32x4 other); @pragma("vm:external-name", "Int32x4_getX") external int get x; @pragma("vm:external-name", "Int32x4_getY") external int get y; @pragma("vm:external-name", "Int32x4_getZ") external int get z; @pragma("vm:external-name", "Int32x4_getW") external int get w; @pragma("vm:recognized", "other") @pragma("vm:external-name", "Int32x4_getSignMask") external int get signMask; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_shuffle") external Int32x4 shuffle(int mask); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_shuffleMix") external Int32x4 shuffleMix(Int32x4 zw, int mask); @pragma("vm:prefer-inline") Int32x4 withX(int x) { return _withX(x); } @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setX") external Int32x4 _withX(int x); @pragma("vm:prefer-inline") Int32x4 withY(int y) { return _withY(y); } @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setY") external Int32x4 _withY(int y); @pragma("vm:prefer-inline") Int32x4 withZ(int z) { return _withZ(z); } @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setZ") external Int32x4 _withZ(int z); @pragma("vm:prefer-inline") Int32x4 withW(int w) { return _withW(w); } @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setW") external Int32x4 _withW(int w); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", bool) @pragma("vm:external-name", "Int32x4_getFlagX") external bool get flagX; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", bool) @pragma("vm:external-name", "Int32x4_getFlagY") external bool get flagY; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", bool) @pragma("vm:external-name", "Int32x4_getFlagZ") external bool get flagZ; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", bool) @pragma("vm:external-name", "Int32x4_getFlagW") external bool get flagW; @pragma("vm:prefer-inline", _Int32x4) Int32x4 withFlagX(bool x) { return _withFlagX(x); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setFlagX") external Int32x4 _withFlagX(bool x); @pragma("vm:prefer-inline", _Int32x4) Int32x4 withFlagY(bool y) { return _withFlagY(y); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setFlagY") external Int32x4 _withFlagY(bool y); @pragma("vm:prefer-inline", _Int32x4) Int32x4 withFlagZ(bool z) { return _withFlagZ(z); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setFlagZ") external Int32x4 _withFlagZ(bool z); @pragma("vm:prefer-inline", _Int32x4) Int32x4 withFlagW(bool w) { return _withFlagW(w); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4) @pragma("vm:external-name", "Int32x4_setFlagW") external Int32x4 _withFlagW(bool w); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4) @pragma("vm:external-name", "Int32x4_select") external Float32x4 select(Float32x4 trueValue, Float32x4 falseValue); } @patch @pragma('vm:deeply-immutable') class Float64x2 { @patch @pragma("vm:prefer-inline") factory Float64x2(double x, double y) { return _Float64x2FromDoubles(x, y); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_fromDoubles") external static _Float64x2 _Float64x2FromDoubles(double x, double y); @patch @pragma("vm:prefer-inline") factory Float64x2.splat(double v) { return _Float64x2Splat(v); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_splat") external static _Float64x2 _Float64x2Splat(double v); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_zero") external factory Float64x2.zero(); @patch @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_fromFloat32x4") external factory Float64x2.fromFloat32x4(Float32x4 v); } @pragma('vm:deeply-immutable') @pragma("vm:entry-point") final class _Float64x2 implements Float64x2 { @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "Float64x2_add") external Float64x2 operator +(Float64x2 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_negate") external Float64x2 operator -(); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "Float64x2_sub") external Float64x2 operator -(Float64x2 other); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "Float64x2_mul") external Float64x2 operator *(Float64x2 other); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "Float64x2_div") external Float64x2 operator /(Float64x2 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_scale") external Float64x2 scale(double s); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_abs") external Float64x2 abs(); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_clamp") external Float64x2 clamp(Float64x2 lowerLimit, Float64x2 upperLimit); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float64x2_getX") external double get x; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Double") @pragma("vm:external-name", "Float64x2_getY") external double get y; @pragma("vm:recognized", "other") @pragma("vm:external-name", "Float64x2_getSignMask") external int get signMask; @pragma("vm:prefer-inline") Float64x2 withX(double x) { return _withX(x); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_setX") external Float64x2 _withX(double x); @pragma("vm:prefer-inline") Float64x2 withY(double y) { return _withY(y); } @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_setY") external Float64x2 _withY(double y); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_min") external Float64x2 min(Float64x2 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_max") external Float64x2 max(Float64x2 other); @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2) @pragma("vm:external-name", "Float64x2_sqrt") external Float64x2 sqrt(); } final class _TypedListIterator implements Iterator { final List _array; final int _length; int _position; E? _current; _TypedListIterator(List array) : _array = array, _length = array.length, _position = -1 { assert(array is _TypedList || array is _TypedListView); } bool moveNext() { int nextPosition = _position + 1; if (nextPosition < _length) { _current = _array[nextPosition]; _position = nextPosition; return true; } _position = _length; _current = null; return false; } E get current => _current as E; } abstract final class _TypedListView extends _TypedListBase implements TypedData { // Method(s) implementing the TypedData interface. int get lengthInBytes { return length * elementSizeInBytes; } _ByteBuffer get buffer { return _typedData.buffer; } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataView_typedData") external _TypedList get _typedData; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataView_offsetInBytes") external int get offsetInBytes; } @pragma("vm:entry-point") final class _Int8ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Int8List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int8ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Int8ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setInt8( offsetInBytes + (index * Int8List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int8List.bytesPerElement; } // Method(s) implementing the Int8List interface. Int8List asUnmodifiableView() => _UnmodifiableInt8ArrayView(this); // Internal utility methods. Int8List _createList(int length) { return Int8List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Uint8ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Uint8List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint8ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Uint8ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setUint8( offsetInBytes + (index * Uint8List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8List interface. Uint8List asUnmodifiableView() => _UnmodifiableUint8ArrayView(this); // Internal utility methods. Uint8List _createList(int length) { return Uint8List(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove1(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Uint8ClampedArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Uint8ClampedList { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint8ClampedArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Uint8ClampedArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setUint8( offsetInBytes + (index * Uint8List.bytesPerElement), _toClampedUint8(value), ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint8List.bytesPerElement; } // Method(s) implementing the Uint8ClampedList interface. Uint8ClampedList asUnmodifiableView() => _UnmodifiableUint8ClampedArrayView(this); // Internal utility methods. Uint8ClampedList _createList(int length) { return Uint8ClampedList(length); } @pragma("vm:prefer-inline") bool get _containsUnsignedBytes => true; @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => from._containsUnsignedBytes ? _memMove1(start, count, from, skipCount) : _setClampedRange(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Int16ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Int16List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int16ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Int16ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setInt16( offsetInBytes + (index * Int16List.bytesPerElement), value, ); } @pragma("vm:prefer-inline") @override void setRange(int start, int end, Iterable from, [int skipCount = 0]) { if (from is CodeUnits) { end = RangeError.checkValidRange(start, end, this.length); int length = end - start; int byteStart = this.offsetInBytes + start * Int16List.bytesPerElement; _typedData._setCodeUnits(from, byteStart, length, skipCount); } else { super.setRange(start, end, from, skipCount); } } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int16List.bytesPerElement; } // Method(s) implementing the Int16List interface. Int16List asUnmodifiableView() => _UnmodifiableInt16ArrayView(this); // Internal utility methods. Int16List _createList(int length) { return Int16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Uint16ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Uint16List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint16ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Uint16ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Smi") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setUint16( offsetInBytes + (index * Uint16List.bytesPerElement), value, ); } @pragma("vm:prefer-inline") @override void setRange(int start, int end, Iterable from, [int skipCount = 0]) { if (from is CodeUnits) { end = RangeError.checkValidRange(start, end, this.length); int length = end - start; int byteStart = this.offsetInBytes + start * Uint16List.bytesPerElement; _typedData._setCodeUnits(from, byteStart, length, skipCount); } else { super.setRange(start, end, from, skipCount); } } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint16List.bytesPerElement; } // Method(s) implementing the Uint16List interface. Uint16List asUnmodifiableView() => _UnmodifiableUint16ArrayView(this); // Internal utility methods. Uint16List _createList(int length) { return Uint16List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove2(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Int32ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Int32List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Int32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setInt32( offsetInBytes + (index * Int32List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32List.bytesPerElement; } // Method(s) implementing the Int32List interface. Int32List asUnmodifiableView() => _UnmodifiableInt32ArrayView(this); // Internal utility methods. Int32List _createList(int length) { return Int32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Uint32ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Uint32List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Uint32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setUint32( offsetInBytes + (index * Uint32List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint32List.bytesPerElement; } // Method(s) implementing the Uint32List interface. Uint32List asUnmodifiableView() => _UnmodifiableUint32ArrayView(this); // Internal utility methods. Uint32List _createList(int length) { return Uint32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Int64ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Int64List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Int64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setInt64( offsetInBytes + (index * Int64List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int64List.bytesPerElement; } // Method(s) implementing the Int16List interface. Int64List asUnmodifiableView() => _UnmodifiableInt64ArrayView(this); // Internal utility methods. Int64List _createList(int length) { return Int64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Uint64ArrayView extends _TypedListView with _IntListMixin, _TypedIntListMixin implements Uint64List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Uint64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Uint64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") external int operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, int value) { index = _typedDataIndexCheck(this, index, length); _typedData._setUint64( offsetInBytes + (index * Uint64List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Uint64List.bytesPerElement; } // Method(s) implementing the Uint64List interface. Uint64List asUnmodifiableView() => _UnmodifiableUint64ArrayView(this); // Internal utility methods. Uint64List _createList(int length) { return Uint64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Float32ArrayView extends _TypedListView with _DoubleListMixin, _TypedDoubleListMixin implements Float32List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Float32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _typedData._setFloat32( offsetInBytes + (index * Float32List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32List.bytesPerElement; } // Method(s) implementing the Float32List interface. Float32List asUnmodifiableView() => _UnmodifiableFloat32ArrayView(this); // Internal utility methods. Float32List _createList(int length) { return Float32List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove4(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Float64ArrayView extends _TypedListView with _DoubleListMixin, _TypedDoubleListMixin implements Float64List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Float64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", "dart:core#_Double") external double operator [](int index); @pragma("vm:prefer-inline") void operator []=(int index, double value) { index = _typedDataIndexCheck(this, index, length); _typedData._setFloat64( offsetInBytes + (index * Float64List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64List.bytesPerElement; } // Method(s) implementing the Float64List interface. Float64List asUnmodifiableView() => _UnmodifiableFloat64ArrayView(this); // Internal utility methods. Float64List _createList(int length) { return Float64List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove8(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Float32x4ArrayView extends _TypedListView with _Float32x4ListMixin implements Float32x4List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float32x4ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Float32x4ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float32x4) external Float32x4 operator [](int index); void operator []=(int index, Float32x4 value) { index = _typedDataIndexCheck(this, index, length); _typedData._setFloat32x4( offsetInBytes + (index * Float32x4List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float32x4List.bytesPerElement; } // Method(s) implementing the Float32x4List interface. Float32x4List asUnmodifiableView() => _UnmodifiableFloat32x4ArrayView(this); // Internal utility methods. Float32x4List _createList(int length) { return Float32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Int32x4ArrayView extends _TypedListView with _Int32x4ListMixin implements Int32x4List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Int32x4ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Int32x4ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Int32x4) external Int32x4 operator [](int index); void operator []=(int index, Int32x4 value) { index = _typedDataIndexCheck(this, index, length); _typedData._setInt32x4( offsetInBytes + (index * Int32x4List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Int32x4List.bytesPerElement; } // Method(s) implementing the Int32x4List interface. Int32x4List asUnmodifiableView() => _UnmodifiableInt32x4ArrayView(this); // Internal utility methods. Int32x4List _createList(int length) { return Int32x4List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _Float64x2ArrayView extends _TypedListView with _Float64x2ListMixin implements Float64x2List { // Constructor. @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _Float64x2ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _Float64x2ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the List interface. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:idempotent") @pragma("vm:exact-result-type", _Float64x2) external Float64x2 operator [](int index); void operator []=(int index, Float64x2 value) { index = _typedDataIndexCheck(this, index, length); _typedData._setFloat64x2( offsetInBytes + (index * Float64x2List.bytesPerElement), value, ); } // Method(s) implementing the TypedData interface. int get elementSizeInBytes { return Float64x2List.bytesPerElement; } // Method(s) implementing the Float64x2List interface. Float64x2List asUnmodifiableView() => _UnmodifiableFloat64x2ArrayView(this); // Internal utility methods. Float64x2List _createList(int length) { return Float64x2List(length); } @pragma("vm:prefer-inline") void _fastSetRange( int start, int count, _TypedListBase from, int skipCount, ) => _memMove16(start, count, from, skipCount); } @pragma("vm:entry-point") final class _ByteDataView implements ByteData { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _ByteDataView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _ByteDataView._( _TypedList buffer, int offsetInBytes, int length, ); // Method(s) implementing the TypedData interface. _ByteBuffer get buffer { return _typedData.buffer; } int get lengthInBytes { return length; } int get elementSizeInBytes { return 1; } // Method(s) implementing the ByteData interface. ByteData asUnmodifiableView() => _UnmodifiableByteDataView(this); @pragma("vm:prefer-inline") int getInt8(int byteOffset) { byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); return _typedData._getInt8(offsetInBytes + byteOffset); } @pragma("vm:prefer-inline") void setInt8(int byteOffset, int value) { byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setInt8(offsetInBytes + byteOffset, value); } @pragma("vm:prefer-inline") int getUint8(int byteOffset) { byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); return _typedData._getUint8(offsetInBytes + byteOffset); } @pragma("vm:prefer-inline") void setUint8(int byteOffset, int value) { byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setUint8(offsetInBytes + byteOffset, value); } @pragma("vm:prefer-inline") int getInt16(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 1]. _byteDataByteOffsetCheck(this, byteOffset + 1, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getInt16(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap16(result).toSigned(16); } @pragma("vm:prefer-inline") void setInt16(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 1]. _byteDataByteOffsetCheck(this, byteOffset + 1, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setInt16( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap16(value), ); } @pragma("vm:prefer-inline") int getUint16(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 1]. _byteDataByteOffsetCheck(this, byteOffset + 1, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getUint16(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap16(result); } @pragma("vm:prefer-inline") void setUint16(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 1]. _byteDataByteOffsetCheck(this, byteOffset + 1, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setUint16( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap16(value), ); } @pragma("vm:prefer-inline") int getInt32(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getInt32(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap32(result).toSigned(32); } @pragma("vm:prefer-inline") void setInt32(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setInt32( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap32(value), ); } @pragma("vm:prefer-inline") int getUint32(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getUint32(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap32(result); } @pragma("vm:prefer-inline") void setUint32(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setUint32( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap32(value), ); } @pragma("vm:prefer-inline") int getInt64(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getInt64(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap64(result).toSigned(64); } @pragma("vm:prefer-inline") void setInt64(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setInt64( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap64(value), ); } @pragma("vm:prefer-inline") int getUint64(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); var result = _typedData._getUint64(offsetInBytes + byteOffset); if (identical(endian, Endian.host)) { return result; } return _byteSwap64(result); } @pragma("vm:prefer-inline") void setUint64(int byteOffset, int value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); _typedData._setUint64( offsetInBytes + byteOffset, identical(endian, Endian.host) ? value : _byteSwap64(value), ); } @pragma("vm:prefer-inline") double getFloat32(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); if (identical(endian, Endian.host)) { return _typedData._getFloat32(offsetInBytes + byteOffset); } _convU32[0] = _byteSwap32( _typedData._getUint32(offsetInBytes + byteOffset), ); return _convF32[0]; } @pragma("vm:prefer-inline") void setFloat32(int byteOffset, double value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 3]. _byteDataByteOffsetCheck(this, byteOffset + 3, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); if (identical(endian, Endian.host)) { _typedData._setFloat32(offsetInBytes + byteOffset, value); return; } _convF32[0] = value; _typedData._setUint32(offsetInBytes + byteOffset, _byteSwap32(_convU32[0])); } @pragma("vm:prefer-inline") double getFloat64(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); if (identical(endian, Endian.host)) { return _typedData._getFloat64(offsetInBytes + byteOffset); } _convU64[0] = _byteSwap64( _typedData._getUint64(offsetInBytes + byteOffset), ); return _convF64[0]; } @pragma("vm:prefer-inline") void setFloat64(int byteOffset, double value, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 7]. _byteDataByteOffsetCheck(this, byteOffset + 7, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); if (identical(endian, Endian.host)) { _typedData._setFloat64(offsetInBytes + byteOffset, value); return; } _convF64[0] = value; _typedData._setUint64(offsetInBytes + byteOffset, _byteSwap64(_convU64[0])); } Float32x4 getFloat32x4(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 15]. _byteDataByteOffsetCheck(this, byteOffset + 15, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); // TODO(johnmccutchan) : Need to resolve this for endianity. return _typedData._getFloat32x4(offsetInBytes + byteOffset); } void setFloat32x4( int byteOffset, Float32x4 value, [ Endian endian = Endian.big, ]) { // Check bounds for range [byteOffset, byteOffset + 15]. _byteDataByteOffsetCheck(this, byteOffset + 15, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); // TODO(johnmccutchan) : Need to resolve this for endianity. _typedData._setFloat32x4(offsetInBytes + byteOffset, value); } Float64x2 getFloat64x2(int byteOffset, [Endian endian = Endian.big]) { // Check bounds for range [byteOffset, byteOffset + 15]. _byteDataByteOffsetCheck(this, byteOffset + 15, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); // TODO(johnmccutchan) : Need to resolve this for endianity. return _typedData._getFloat64x2(offsetInBytes + byteOffset); } void setFloat64x2( int byteOffset, Float64x2 value, [ Endian endian = Endian.big, ]) { // Check bounds for range [byteOffset, byteOffset + 15]. _byteDataByteOffsetCheck(this, byteOffset + 15, length); byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length); // TODO(johnmccutchan) : Need to resolve this for endianity. _typedData._setFloat64x2(offsetInBytes + byteOffset, value); } @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataView_typedData") external _TypedList get _typedData; @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataView_offsetInBytes") external int get offsetInBytes; @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:prefer-inline") @pragma("vm:external-name", "TypedDataBase_length") external int get length; } @pragma("vm:prefer-inline") int _byteSwap16(int value) { return ((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8); } @pragma("vm:prefer-inline") int _byteSwap32(int value) { value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); value = ((value & 0xFFFF0000) >> 16) | ((value & 0x0000FFFF) << 16); return value; } @pragma("vm:prefer-inline") int _byteSwap64(int value) { return (_byteSwap32(value) << 32) | _byteSwap32(value >> 32); } final _convU32 = Uint32List(2); final _convU64 = Uint64List.view(_convU32.buffer); final _convF32 = Float32List.view(_convU32.buffer); final _convF64 = Float64List.view(_convU32.buffer); // Top level utility methods. @pragma("vm:exact-result-type", "dart:core#_Smi") int _toClampedUint8(int value) { if (value < 0) return 0; if (value > 0xFF) return 0xFF; return value; } // Length should be non-negative. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") int _typedDataIndexCheck(Object indexable, int index, int length) { if (index < 0 || index >= length) { throw IndexError.withLength( index, length, indexable: indexable, name: "index", ); } return index; } // Length should be non-negative. @pragma("vm:recognized", "other") @pragma("vm:prefer-inline") int _byteDataByteOffsetCheck(ByteData indexable, int byteOffset, int length) { if (byteOffset < 0 || byteOffset >= length) { throw IndexError.withLength( byteOffset, length, indexable: indexable, name: "byteOffset", ); } return byteOffset; } // In addition to explicitly checking the range, this method implicitly ensures // that all arguments are non-null (a no such method error gets thrown // otherwise). void _rangeCheck(int listLength, int start, int length) { if (length < 0) { throw RangeError.value(length); } if (start < 0) { throw RangeError.value(start); } if (start + length > listLength) { throw RangeError.value(start + length); } } void _offsetAlignmentCheck(int offset, int alignment) { if ((offset % alignment) != 0) { throw RangeError( 'Offset ($offset) must be a multiple of ' 'BYTES_PER_ELEMENT ($alignment)', ); } } @pragma("vm:entry-point") final class _UnmodifiableInt8ArrayView extends _Int8ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableInt8ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableInt8ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableInt8ArrayView(Int8List list) => _UnmodifiableInt8ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Int8List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableUint8ArrayView extends _Uint8ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableUint8ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableUint8ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableUint8ArrayView(Uint8List list) => _UnmodifiableUint8ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Uint8List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableUint8ClampedArrayView extends _Uint8ClampedArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableUint8ClampedArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableUint8ClampedArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableUint8ClampedArrayView(Uint8ClampedList list) => _UnmodifiableUint8ClampedArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Uint8ClampedList asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableInt16ArrayView extends _Int16ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableInt16ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableInt16ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableInt16ArrayView(Int16List list) => _UnmodifiableInt16ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Int16List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableUint16ArrayView extends _Uint16ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableUint16ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableUint16ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableUint16ArrayView(Uint16List list) => _UnmodifiableUint16ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Uint16List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableInt32ArrayView extends _Int32ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableInt32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableInt32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableInt32ArrayView(Int32List list) => _UnmodifiableInt32ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Int32List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableUint32ArrayView extends _Uint32ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableUint32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableUint32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableUint32ArrayView(Uint32List list) => _UnmodifiableUint32ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Uint32List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableInt64ArrayView extends _Int64ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableInt64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableInt64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableInt64ArrayView(Int64List list) => _UnmodifiableInt64ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Int64List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableUint64ArrayView extends _Uint64ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableUint64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableUint64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableUint64ArrayView(Uint64List list) => _UnmodifiableUint64ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Uint64List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableFloat32ArrayView extends _Float32ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableFloat32ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableFloat32ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableFloat32ArrayView(Float32List list) => _UnmodifiableFloat32ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, double value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Float32List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableFloat64ArrayView extends _Float64ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableFloat64ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableFloat64ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableFloat64ArrayView(Float64List list) => _UnmodifiableFloat64ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, double value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Float64List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableFloat32x4ArrayView extends _Float32x4ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableFloat32x4ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableFloat32x4ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableFloat32x4ArrayView(Float32x4List list) => _UnmodifiableFloat32x4ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, Float32x4 value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Float32x4List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableInt32x4ArrayView extends _Int32x4ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableInt32x4ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableInt32x4ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableInt32x4ArrayView(Int32x4List list) => _UnmodifiableInt32x4ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, Int32x4 value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Int32x4List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableFloat64x2ArrayView extends _Float64x2ArrayView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableFloat64x2ArrayView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableFloat64x2ArrayView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableFloat64x2ArrayView(Float64x2List list) => _UnmodifiableFloat64x2ArrayView._( unsafeCast<_TypedListBase>(list)._typedData, list.offsetInBytes, list.length, ); void operator []=(int index, Float64x2 value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); Float64x2List asUnmodifiableView() => this; } @pragma("vm:entry-point") final class _UnmodifiableByteDataView extends _ByteDataView { @pragma("vm:recognized", "other") @pragma("vm:exact-result-type", _UnmodifiableByteDataView) @pragma("vm:prefer-inline") @pragma("vm:idempotent") external factory _UnmodifiableByteDataView._( _TypedList buffer, int offsetInBytes, int length, ); @pragma("vm:prefer-inline") factory _UnmodifiableByteDataView(ByteData data) => _UnmodifiableByteDataView._( unsafeCast<_ByteDataView>(data).buffer._data, data.offsetInBytes, data.lengthInBytes, ); void setInt8(int byteOffset, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setUint8(int byteOffset, int value) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setInt16(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setUint16(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setInt32(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setUint32(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setInt64(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setUint64(int byteOffset, int value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setFloat32(int byteOffset, double value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setFloat64(int byteOffset, double value, [Endian endian = Endian.big]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } void setFloat32x4( int byteOffset, Float32x4 value, [ Endian endian = Endian.big, ]) { throw UnsupportedError("Cannot modify an unmodifiable list"); } _ByteBuffer get buffer => _UnmodifiableByteBufferView(_typedData.buffer); ByteData asUnmodifiableView() => this; } final class _UnmodifiableByteBufferView extends _ByteBuffer { _UnmodifiableByteBufferView(ByteBuffer data) : super(unsafeCast<_ByteBuffer>(data)._data); Uint8List asUint8List([int offsetInBytes = 0, int? length]) => _UnmodifiableUint8ArrayView(super.asUint8List(offsetInBytes, length)); Int8List asInt8List([int offsetInBytes = 0, int? length]) => _UnmodifiableInt8ArrayView(super.asInt8List(offsetInBytes, length)); Uint8ClampedList asUint8ClampedList([int offsetInBytes = 0, int? length]) => _UnmodifiableUint8ClampedArrayView( super.asUint8ClampedList(offsetInBytes, length), ); Uint16List asUint16List([int offsetInBytes = 0, int? length]) => _UnmodifiableUint16ArrayView(super.asUint16List(offsetInBytes, length)); Int16List asInt16List([int offsetInBytes = 0, int? length]) => _UnmodifiableInt16ArrayView(super.asInt16List(offsetInBytes, length)); Uint32List asUint32List([int offsetInBytes = 0, int? length]) => _UnmodifiableUint32ArrayView(super.asUint32List(offsetInBytes, length)); Int32List asInt32List([int offsetInBytes = 0, int? length]) => _UnmodifiableInt32ArrayView(super.asInt32List(offsetInBytes, length)); Uint64List asUint64List([int offsetInBytes = 0, int? length]) => _UnmodifiableUint64ArrayView(super.asUint64List(offsetInBytes, length)); Int64List asInt64List([int offsetInBytes = 0, int? length]) => _UnmodifiableInt64ArrayView(super.asInt64List(offsetInBytes, length)); Int32x4List asInt32x4List([int offsetInBytes = 0, int? length]) => _UnmodifiableInt32x4ArrayView(super.asInt32x4List(offsetInBytes, length)); Float32List asFloat32List([int offsetInBytes = 0, int? length]) => _UnmodifiableFloat32ArrayView(super.asFloat32List(offsetInBytes, length)); Float64List asFloat64List([int offsetInBytes = 0, int? length]) => _UnmodifiableFloat64ArrayView(super.asFloat64List(offsetInBytes, length)); Float32x4List asFloat32x4List([int offsetInBytes = 0, int? length]) => _UnmodifiableFloat32x4ArrayView( super.asFloat32x4List(offsetInBytes, length), ); Float64x2List asFloat64x2List([int offsetInBytes = 0, int? length]) => _UnmodifiableFloat64x2ArrayView( super.asFloat64x2List(offsetInBytes, length), ); ByteData asByteData([int offsetInBytes = 0, int? length]) => _UnmodifiableByteDataView(super.asByteData(offsetInBytes, length)); } —dNN<MPK+ ( )& $!20-PA>L'NKK&.466)*2#0AF!F)F=FK'4H?"H7!: :?+#@=,#F#$+##4&G;-J;-G95H;E06!)$@:$'I$5Q:*'$%%$#E$)),D)),F $0 &), A(A)F7F241 0(2=,(FF0F0F$,-,%=*6)$K39$,$L:$'L$50-=-*$%($#N$)),M)),O $0 &),#A+A,F:F804 0(5=,(F"F3F3F$,-,%=*1H#0-9)$?90( 0/('<;?8":!:@+#A=,#H#$+##IK<"HK39$,$L:$'L$50-=-*$%($#N$)),M)),O $0 &),#A+A,F:F804 0(5=,(F"F3F3F$,-,%=*1H#0-9)$?90( 0070&$?>3B?>Q)@@K*AN0*1H@K*EABL+FB@K*EABL+FB@K*EABL+FBDM,GCDM,GCHO.IEDM,GCHO.IE9C(/%$4,$8$4-$9$4-$9$4.$:$-$9$.$:$-$9$.$:$72$>$72$>$.7$C$,3$?$.7$CNO$768$6D$768$6D$.8=$8I$,69$6E$.8=$8IC0N/' '4*  $-) 2N)5(0$4'.,75%4E%1 $.* 3O*6)0$4'.,75&5G&+1 $51 :-01="00$4'.,7.5&<+0-%+#19 $.* 3O*6)0$4'.,79 OA N9 35&5G&1 $/+ 4P+7*0$4'.,7; OA O9 35'6I'1 $.* 3O*6)0$'.,795&5G&1 $/+ 4P+7*0$'.,7;5'6I'1 $.* 3O*6)0$'.,795&5G&1 $/+ 4P+7*0$'.,7;5'6I'1 $0, 8Q,>+0$7*./7=5(7K( 1 $0, 8Q,>+0$7*./7=5(7K( 1 $2. =*0.-$.-.27A5*9O*"2 $0, 9Q,+$,+.07=5(7K( 2 $2. =*0.-$.-.27A5*9O*"22510$4',75%4E%13620$4'.,75&40$7*/7=5(7K( 15>40$7*/7=5(7K( 1760$.-27A5*5O*"2540$,+07=5(7K( 2760$.-27A5*9O*"2 >.$.74 C$.18 $.0% $.;9 $.9:.../2$.2#../2../2./2$,1.$,25$,11$,28$,4+$,5.$.1*$./$.1H.70.70.70.70$7$.3($.69$.0'$.0'$.0'$.0'$./+$./+$.0$.6#$.:' 0)$,2I 9*$,3N $,;;*,.-.-.-.-.....$5$,1&$,45,.",.",.",."$(2$(2$(2$(2($,2'($,2'($,2'($,2'$.0H *($.7H &$.18 $.0% $.99../2$.2#./2./2./2$.1&$./$.1H$70$70$7$.0'$.0'$./+$./+$.0;$<&"' ";5($9&$4="25$2%0$4',7: 5%4E%136$3&0$4',7; 5&5G&+1:="$:-0$4',7;5&<+0-%+#1936$3&0$4',7;  OA ND 35&5G&147$4'0$4',7<  OA OD 35'6I'136$3&0$',7; 5&5G&147$4'0$',7< 5'6I'136$3&0$',7; 5&5G&147$4'0$',7< 5'6I'15>$5(0$7*/7= 5(7K( 15>$5(0$7*/7= 5(7K( 17$7*0$.-27? 5*9O*"25$5(0$,+07= 5(7K( 27$7*0$.-27? 5*9O*"20$1$54D E<,E<!E=-E=?<17$5 ,BL)B$?29%5 ,BL*P$F9G,5 ,BL1B$?29%5 ,BL*D$@3;&5 ,BL+B$?29%5 ,BL*D$@3;&5 ,BL+B$?29%5 ,BL*D$@3;&5 ,BL+F$A4='5 /BL,F$A4='5 /BL,J$C6A)5 2BL.F$A4='5 0BL,J$C6A)5 2BL.>$=06#6 ,B-BKBLBKBLBKBLBPBPB BL)>/2AM?KO*9 COAMCOAMCOAMEQEQEQI'6 I'6 ?J+dart:typed_data-patch/typed_data_patch.dartÀeÀh³Aorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_vm/_vm.dart�ï// Copyright (c) 2025, 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. library dart._vm; import "dart:_internal" show unsafeCast; @pragma("vm:deeply-immutable") @pragma('vm:entry-point') final class ThreadLocal { /// Creates dart thread local variable. ThreadLocal() : _id = _allocateId(); /// Returns the value of this thread-local variable or throws [StateError] /// if it has no value. T get value { if (!_hasValue(_id)) { throw StateError( "Attempt to access variable that was not assigned a value.", ); } return unsafeCast(_getValue(_id)); } /// Sets the value of this variable. Overwrites old value if it was previously /// set. set value(T newValue) { _setValue(_id, newValue); } /// Returns `true` if some value was assigned to this variable. bool get hasValue => _hasValue(_id); // Clears this variable of its assigned value. void clearValue() { _clearValue(_id); } @pragma("vm:external-name", "ThreadLocal_allocateId") external static int _allocateId(); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "ThreadLocal_hasValue") external static bool _hasValue(int id); @pragma("vm:recognized", "graph-intrinsic") @pragma("vm:external-name", "ThreadLocal_getValue") external static Object? _getValue(int id); @pragma("vm:external-name", "ThreadLocal_setValue") external static void _setValue(int id, Object? value); @pragma("vm:external-name", "ThreadLocal_clearValue") external static void _clearValue(int id); final int _id; } @pragma("vm:deeply-immutable") @pragma('vm:entry-point') final class ScopedThreadLocal { /// Creates scoped thread-local variable with given [initializer] function. /// /// [initializer] must be trivially shareable. ScopedThreadLocal([this._initializer]); /// Execute [f] binding this [ScopedThreadLocal] to the given /// [value] for the duration of the execution. R runWith(T newValue, R Function(T) f) { bool hadValue = variable.hasValue; T? previousValue = hadValue ? variable.value : null; variable.value = newValue; R result = f(newValue); if (hadValue) { variable.value = previousValue as T; } else { variable.clearValue(); } return result; } /// Execute [f] initializing this [ScopedThreadLocal] using default initializer if needed. /// Throws [StateError] if this [ScopedThreadLocal] does not have an initializer. R runInitialized(R Function(T) f) { bool hadValue = variable.hasValue; T? previousValue = hadValue ? variable.value : null; if (!variable.hasValue) { if (_initializer == null) { throw StateError( "No initializer was provided for this ScopedThreadLocal.", ); } variable.value = _initializer!(); } R result = f(variable.value); if (hadValue) { variable.value = previousValue as T; } else { variable.clearValue(); } return result; } /// Returns the value specified by the closest enclosing invocation of /// [runWith] or [runInititalized] or throws [StateError] if this /// [ScopedThreadLocal] is not bound to a value. T get value => variable.value; /// Returns `true` if this [ScopedThreadLocal] is bound to a value. bool get isBound => variable.hasValue; final T Function()? _initializer; final variable = ThreadLocal(); } @pragma("vm:deeply-immutable") @pragma('vm:entry-point') final class FinalThreadLocal { /// Creates thread local value with the given [initializer] function. /// /// The value can be assigned only once, remains assigned for the duration /// of this dart thread lifetime. /// /// [initializer] must be trivially shareable. FinalThreadLocal(this._initializer); /// Returns the value bound to [FinalThreadLocal]. T get value { if (!variable.hasValue) { final v = _initializer(); variable.value = v; return v; } return unsafeCast(ThreadLocal._getValue(variable._id)); } set value(_) { throw StateError("Final value can not be updated"); } final T Function() _initializer; final variable = ThreadLocal(); } €“NN<)*'ME *Q B'18%.6*.6-698,#N1*@1.'9+ ]T)'9"E ("+ ID3!F)$%"HM$1'5 ?8#%dart:_vmÀeÀh³Uorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/html/dartium/nativewrappers.dartƒ�// 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. library nativewrappers; base class NativeFieldWrapperClass1 {} base class NativeFieldWrapperClass2 extends NativeFieldWrapperClass1 {} base class NativeFieldWrapperClass3 extends NativeFieldWrapperClass2 {} base class NativeFieldWrapperClass4 extends NativeFieldWrapperClass3 {} /// Gets the value of the native field of [object]. /// /// Throws an exception if [object] is null or if the native field was not set. /// /// NOTE: This is function is temporary and will be deprecated in the near /// future. @pragma("vm:recognized", "other") @pragma("vm:external-name", "FullyRecognizedMethod_NoNative") @pragma("vm:idempotent") external int _getNativeField(NativeFieldWrapperClass1 object); NN<'HHH4PK ">?dart:nativewrappersÀeÀh³?org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/io.dart¤d// 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. /// File, socket, HTTP, and other I/O support for non-web applications. /// /// **Important:** Browser-based apps can't use this library. /// Only the following can import and use the dart:io library: /// - Servers /// - Command-line scripts /// - Flutter mobile apps /// - Flutter desktop apps /// /// This library allows you to work with files, directories, /// sockets, processes, HTTP servers and clients, and more. /// Many operations related to input and output are asynchronous /// and are handled using [Future]s or [Stream]s, both of which /// are defined in the [dart:async /// library](../dart-async/dart-async-library.html). /// /// To use the dart:io library in your code: /// ```dart /// import 'dart:io'; /// ``` /// For an introduction to I/O in Dart, see the [dart:io library /// tour](https://dart.dev/guides/libraries/library-tour#dartio). /// /// ## File, Directory, and Link /// /// An instance of [File], [Directory], or [Link] represents a file, /// directory, or link, respectively, in the native file system. /// /// You can manipulate the file system through objects of these types. /// For example, you can rename a file or directory: /// ```dart /// File myFile = File('myFile.txt'); /// myFile.rename('yourFile.txt').then((_) => print('file renamed')); /// ``` /// Many methods provided by the [File], [Directory], and [Link] classes /// run asynchronously and return a [Future]. /// /// ## FileSystemEntity /// /// [File], [Directory], and [Link] all extend [FileSystemEntity]. /// In addition to being the superclass for these classes, /// FileSystemEntity has a number of static methods for working with paths. /// /// To get information about a path, /// you can use the [FileSystemEntity] static methods /// such as [FileSystemEntity.isDirectory], [FileSystemEntity.isFile], /// and [FileSystemEntity.exists]. /// Because file system access involves I/O, these methods /// are asynchronous and return a [Future]. /// ```dart /// FileSystemEntity.isDirectory(myPath).then((isDir) { /// if (isDir) { /// print('$myPath is a directory'); /// } else { /// print('$myPath is not a directory'); /// } /// }); /// ``` /// ## HttpServer and HttpClient /// /// The classes [HttpClient] and [HttpServer] provide low-level HTTP /// functionality. /// /// Instead of using these classes directly, consider using more /// developer-friendly and composable APIs found in packages. /// /// For HTTP clients, look at [`package:http`](https://pub.dev/packages/http). /// /// For HTTP servers, look at /// [Write HTTP servers](https://dart.dev/tutorials/server/httpserver) on /// [dart.dev](https://dart.dev/). /// /// ## Process /// /// The [Process] class provides a way to run a process on /// the native machine. /// For example, the following code spawns a process that recursively lists /// the files under `web`. /// ```dart /// Process.start('ls', ['-R', 'web']).then((process) { /// stdout.addStream(process.stdout); /// stderr.addStream(process.stderr); /// process.exitCode.then(print); /// }); /// ``` /// Using [Process.start] returns a [Future], /// which completes with a [Process] object when the process has started. /// This [Process] object allows you to interact /// with the process while it is running. /// Using [Process.run] returns a [Future], /// which completes with a [ProcessResult] object when the spawned process has /// terminated. This [ProcessResult] object collects the output and exit code /// from the process. /// /// When using [Process.start], /// you need to read all data coming on the [Process.stdout] and [Process.stderr] /// streams, otherwise the system resources will not be freed. /// /// ## WebSocket /// /// The [WebSocket] class provides support for the web socket protocol. This /// allows full-duplex communications between client and server applications. /// /// A web socket server uses a normal HTTP server for accepting web socket /// connections. The initial handshake is a HTTP request which is then upgraded to a /// web socket connection. /// The server upgrades the request using [WebSocketTransformer] /// and listens for the data on the returned web socket. /// For example, here's a mini server that listens for 'ws' data /// on a WebSocket: /// ```dart import:async /// runZoned(() async { /// var server = await HttpServer.bind('127.0.0.1', 4040); /// server.listen((HttpRequest req) async { /// if (req.uri.path == '/ws') { /// var socket = await WebSocketTransformer.upgrade(req); /// socket.listen(handleMsg); /// } /// }); /// }, onError: (e) => print("An error occurred.")); /// ``` /// The client connects to the [WebSocket] using the [WebSocket.connect] method /// and a URI that uses the Web Socket protocol. /// The client can write to the [WebSocket] with the [WebSocket.add] method. /// For example, /// ```dart /// var socket = await WebSocket.connect('ws://127.0.0.1:4040/ws'); /// socket.add('Hello, World!'); /// ``` /// Check out the /// [websocket_sample](https://github.com/dart-lang/dart-samples/tree/master/html5/web/websockets/basics) /// app, which uses [WebSocket]s to communicate with a server. /// /// ## Socket and ServerSocket /// /// Clients and servers use [Socket]s to communicate using the TCP protocol. /// Use [ServerSocket] on the server side and [Socket] on the client. /// The server creates a listening socket using the `bind()` method and /// then listens for incoming connections on the socket. For example: /// ```dart import:convert /// ServerSocket.bind('127.0.0.1', 4041) /// .then((serverSocket) { /// serverSocket.listen((socket) { /// socket.transform(utf8.decoder).listen(print); /// }); /// }); /// ``` /// A client connects a [Socket] using the `connect()` method, /// which returns a [Future]. /// Using `write()`, `writeln()`, or `writeAll()` are the easiest ways to /// send data over the socket. /// For example: /// ```dart /// Socket.connect('127.0.0.1', 4041).then((socket) { /// socket.write('Hello, World!'); /// }); /// ``` /// Besides [Socket] and [ServerSocket], the [RawSocket] and /// [RawServerSocket] classes are available for lower-level access /// to async socket IO. /// /// ## Standard output, error, and input streams /// /// This library provides the standard output, error, and input /// streams, named [stdout], [stderr], and [stdin], respectively. /// /// The [stdout] and [stderr] streams are both [IOSink]s and have the same set /// of methods and properties. /// /// To write a string to [stdout]: /// ```dart /// stdout.writeln('Hello, World!'); /// ``` /// To write a list of objects to [stderr]: /// ```dart /// stderr.writeAll([ 'That ', 'is ', 'an ', 'error.', '\n']); /// ``` /// The standard input stream is a true [Stream], so it inherits /// properties and methods from the [Stream] class. /// /// To read text synchronously from the command line /// (the program blocks waiting for user to type information): /// ```dart /// String? inputText = stdin.readLineSync(); /// ``` /// {@category VM} library dart.io; import 'dart:async'; import 'dart:async' as dart_async show runZoned; import 'dart:_internal' hide Symbol; import 'dart:collection' show HashMap, HashSet, Queue, ListQueue, MapBase, UnmodifiableMapView; import 'dart:convert'; import 'dart:developer' hide log; import 'dart:_http' show HttpClient, HttpProfiler, ServerSocketBase; import 'dart:isolate'; import 'dart:math'; import 'dart:typed_data'; export 'dart:_http' show HttpDate, BadCertificateCallback, HttpOverrides, WebSocketStatus, CompressionOptions, WebSocketTransformer, WebSocket, WebSocketException, HttpServer, HttpConnectionsInfo, HttpHeaders, HeaderValue, HttpSession, ContentType, SameSite, Cookie, HttpRequest, HttpResponse, HttpClient, HttpClientRequest, HttpClientResponse, HttpClientResponseCompressionState, HttpClientCredentials, HttpClientBasicCredentials, HttpClientBearerCredentials, HttpClientDigestCredentials, HttpConnectionInfo, RedirectInfo, HttpException, RedirectException; @Deprecated("Import BytesBuilder from dart:typed_data instead") export 'dart:_internal' show BytesBuilder; export 'dart:_internal' show HttpStatus; part 'common.dart'; part 'data_transformer.dart'; part 'directory.dart'; part 'directory_impl.dart'; part 'embedder_config.dart'; part 'eventhandler.dart'; part 'file.dart'; part 'file_impl.dart'; part 'file_system_entity.dart'; part 'io_resource_info.dart'; part 'io_sink.dart'; part 'io_service.dart'; part 'link.dart'; part 'namespace_impl.dart'; part 'network_profiling.dart'; part 'overrides.dart'; part 'platform.dart'; part 'platform_impl.dart'; part 'process.dart'; part 'secure_server_socket.dart'; part 'secure_socket.dart'; part 'security_context.dart'; part 'service_object.dart'; part 'socket.dart'; part 'stdio.dart'; part 'string_transformer.dart'; part 'sync_socket.dart'; � NN<H>?=OJ#;L 8(($.J1*,ON R?MNKUA9A=.%@$ 5P1M D!j?MFHF)'8 ?J 6%=C1@BO# %, ?A45? .1%K"E  ,$%%@+) " dart:ioÀdÙÀh³Corg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/common.dart‘Ç// 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:io"; // Constants used when working with native ports. // These must match the constants in runtime/bin/dartutils.h class CObject. const int _successResponse = 0; const int _illegalArgumentResponse = 1; const int _osErrorResponse = 2; const int _fileClosedResponse = 3; const int _errorResponseErrorType = 0; const int _osErrorResponseErrorCode = 1; const int _osErrorResponseMessage = 2; // POSIX error codes. // See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html const _ePerm = 1; const _eNoEnt = 2; const _eAccess = 13; const _eExist = 17; // Windows error codes. // See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- const _errorFileNotFound = 2; const _errorPathNotFound = 3; const _errorAccessDenied = 5; const _errorInvalidDrive = 15; const _errorCurrentDirectory = 16; const _errorNoMoreFiles = 18; const _errorWriteProtect = 19; const _errorBadLength = 24; const _errorSharingViolation = 32; const _errorLockViolation = 33; const _errorBadNetpath = 53; const _errorNetworkAccessDenied = 65; const _errorBadNetName = 67; const _errorFileExists = 80; const _errorDriveLocked = 108; const _errorInvalidName = 123; const _errorBadPathName = 161; const _errorAlreadyExists = 183; const _errorFilenameExedRange = 206; /// If the [response] is an error, throws an [Exception] or an [Error]. void _checkForErrorResponse(Object? response, String message, String path) { if (response is List && response[0] != _successResponse) { switch (response[_errorResponseErrorType]) { case _illegalArgumentResponse: throw ArgumentError("$message: $path"); case _osErrorResponse: var err = OSError( response[_osErrorResponseMessage] as String, response[_osErrorResponseErrorCode] as int, ); throw FileSystemException._fromOSError(err, message, path); case _fileClosedResponse: throw FileSystemException("File closed", path); default: throw AssertionError("Unknown error"); } } } /// Base class for all IO related exceptions. abstract class IOException implements Exception { String toString() => "IOException"; } /// An [Exception] holding information about an error from the /// operating system. @pragma("vm:entry-point") class OSError implements Exception { /// Constant used to indicate that no OS error code is available. static const int noErrorCode = -1; /// Error message supplied by the operating system. This will be empty if no /// message is associated with the error. final String message; /// Error code supplied by the operating system. /// /// Will have the value [OSError.noErrorCode] if there is no error code /// associated with the error. final int errorCode; /// Creates an OSError object from a message and an errorCode. @pragma("vm:entry-point") const OSError([this.message = "", this.errorCode = noErrorCode]); /// Converts an OSError object to a string representation. String toString() { StringBuffer sb = StringBuffer(); sb.write("OS Error"); if (message.isNotEmpty) { sb ..write(": ") ..write(message); if (errorCode != noErrorCode) { sb ..write(", errno = ") ..write(errorCode.toString()); } } else if (errorCode != noErrorCode) { sb ..write(": errno = ") ..write(errorCode.toString()); } return sb.toString(); } } // Object for holding a buffer and an offset. class _BufferAndStart { List buffer; int start; _BufferAndStart(this.buffer, this.start); } // Ensure that the input List can be serialized through a native port. _BufferAndStart _ensureFastAndSerializableByteData( List buffer, int start, int end, ) { if ((buffer is Uint8List) && (buffer.buffer.lengthInBytes == buffer.length)) { // Send typed data directly, unless it is a partial view, in which case we // would rather copy than drag in the potentially much large backing store. // See issue 50206. return _BufferAndStart(buffer, start); } int length = end - start; var newBuffer = Uint8List(length); newBuffer.setRange(0, length, buffer, start); return _BufferAndStart(newBuffer, 0); } class _IOCrypto { external static Uint8List getRandomBytes(int count); } €�NN<2L ( #')'NX## &!%HMF1%076 D 8/.2&?%D%O,3J!AD=& & )+ '. ,G4 QOP+%0(7dart:io/common.dartÀeÀh³Worg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/common_patch.dart‡¢// 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. /// Note: the VM concatenates all patch files into a single patch file. This /// file is the first patch in "dart:io" which contains all the imports used by /// patches of that library. We plan to change this when we have a shared front /// end and simply use parts. import "dart:_internal" show VMLibraryHooks, patch, ClassID; import "dart:async" show Completer, Future, MultiStreamController, Stream, StreamConsumer, StreamController, StreamSubscription, Timer, Zone, scheduleMicrotask; import "dart:collection" show HashMap, Queue; import "dart:convert" show Encoding, utf8; import "dart:developer" show registerExtension; import "dart:isolate" show RawReceivePort, ReceivePort, SendPort; import "dart:ffi" as ffi; import "dart:math" show min; import "dart:nativewrappers" show NativeFieldWrapperClass1; import "dart:typed_data" show Uint8List, BytesBuilder; /// These are the additional parts of this patch library: part "directory_patch.dart"; part "eventhandler_patch.dart"; part "file_patch.dart"; part "file_system_entity_patch.dart"; part "filter_patch.dart"; part "io_service_patch.dart"; part "namespace_patch.dart"; part "platform_patch.dart"; part "process_patch.dart"; part "socket_patch.dart"; part "stdio_patch.dart"; part "secure_socket_patch.dart"; part "sync_socket_patch.dart"; @patch class _IOCrypto { @patch @pragma("vm:external-name", "Crypto_GetRandomBytes") external static Uint8List getRandomBytes(int count); } @pragma("vm:entry-point", "call") _setupHooks() { VMLibraryHooks.eventHandlerSendData = _EventHandler._sendData; VMLibraryHooks.timerMillisecondClock = _EventHandler._timerMillisecondClock; } ENN<MPP= .+0B<7: &! 77"AOdart:io-patch/common_patch.dartÀeÀh³Morg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/data_transformer.dartÀPú// Copyright (c) 2013, 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:io"; /// Exposes ZLib options for input parameters. /// /// See http://www.zlib.net/manual.html for more documentation. abstract final class ZLibOption { /// Minimal value for [ZLibCodec.windowBits], [ZLibEncoder.windowBits] /// and [ZLibDecoder.windowBits]. static const int minWindowBits = 8; /// Maximal value for [ZLibCodec.windowBits], [ZLibEncoder.windowBits] /// and [ZLibDecoder.windowBits]. static const int maxWindowBits = 15; /// Default value for [ZLibCodec.windowBits], [ZLibEncoder.windowBits] /// and [ZLibDecoder.windowBits]. static const int defaultWindowBits = 15; /// Minimal value for [ZLibCodec.level] and [ZLibEncoder.level]. static const int minLevel = -1; /// Maximal value for [ZLibCodec.level] and [ZLibEncoder.level] static const int maxLevel = 9; /// Default value for [ZLibCodec.level] and [ZLibEncoder.level]. static const int defaultLevel = 6; /// Minimal value for [ZLibCodec.memLevel] and [ZLibEncoder.memLevel]. static const int minMemLevel = 1; /// Maximal value for [ZLibCodec.memLevel] and [ZLibEncoder.memLevel]. static const int maxMemLevel = 9; /// Default value for [ZLibCodec.memLevel] and [ZLibEncoder.memLevel]. static const int defaultMemLevel = 8; /// Recommended strategy for data produced by a filter (or predictor) static const int strategyFiltered = 1; /// Use this strategy to force Huffman encoding only (no string match) static const int strategyHuffmanOnly = 2; /// Use this strategy to limit match distances to one (run-length encoding) static const int strategyRle = 3; /// This strategy prevents the use of dynamic Huffman codes, allowing for a /// simpler decoder static const int strategyFixed = 4; /// Recommended strategy for normal data static const int strategyDefault = 0; } /// An instance of the default implementation of the [ZLibCodec]. const ZLibCodec zlib = ZLibCodec._default(); /// The [ZLibCodec] encodes raw bytes to ZLib compressed bytes and decodes ZLib /// compressed bytes to raw bytes. final class ZLibCodec extends Codec, List> { /// When true, `GZip` frames will be added to the compressed data. final bool gzip; /// The compression-[level] can be set in the range of `-1..9`, with `6` being /// the default compression level. Levels above `6` will have higher /// compression rates at the cost of more CPU and memory usage. Levels below /// `6` will use less CPU and memory at the cost of lower compression rates. final int level; /// Specifies how much memory should be allocated for the internal compression /// state. `1` uses minimum memory but is slow and reduces compression ratio; /// `9` uses maximum memory for optimal speed. The default value is `8`. /// /// The memory requirements for deflate are (in bytes): /// ```dart /// (1 << (windowBits + 2)) + (1 << (memLevel + 9)) /// ``` /// that is: 128K for windowBits = 15 + 128K for memLevel = 8 (default values) final int memLevel; /// Tunes the compression algorithm. Use the value strategyDefault for normal /// data, strategyFiltered for data produced by a filter (or predictor), /// strategyHuffmanOnly to force Huffman encoding only (no string match), or /// strategyRle to limit match distances to one (run-length encoding). final int strategy; /// Base two logarithm of the window size (the size of the history buffer). It /// should be in the range 8..15. Larger values result in better compression at /// the expense of memory usage. The default value is 15 final int windowBits; /// When true, deflate generates raw data with no zlib header or trailer, and /// will not compute an adler32 check value final bool raw; /// Initial compression dictionary. /// /// It should consist of strings (byte sequences) that are likely to be /// encountered later in the data to be compressed, with the most commonly used /// strings preferably put towards the end of the dictionary. Using a /// dictionary is most useful when the data to be compressed is short and can /// be predicted with good accuracy; the data can then be compressed better /// than with the default empty dictionary. final List? dictionary; ZLibCodec({ this.level = ZLibOption.defaultLevel, this.windowBits = ZLibOption.defaultWindowBits, this.memLevel = ZLibOption.defaultMemLevel, this.strategy = ZLibOption.strategyDefault, this.dictionary, this.raw = false, this.gzip = false, }) { _validateZLibeLevel(level); _validateZLibMemLevel(memLevel); _validateZLibStrategy(strategy); _validateZLibWindowBits(windowBits); } const ZLibCodec._default() : level = ZLibOption.defaultLevel, windowBits = ZLibOption.defaultWindowBits, memLevel = ZLibOption.defaultMemLevel, strategy = ZLibOption.strategyDefault, raw = false, gzip = false, dictionary = null; /// Get a [ZLibEncoder] for encoding to `ZLib` compressed data. ZLibEncoder get encoder => ZLibEncoder( gzip: false, level: level, windowBits: windowBits, memLevel: memLevel, strategy: strategy, dictionary: dictionary, raw: raw, ); /// Get a [ZLibDecoder] for decoding `ZLib` compressed data. ZLibDecoder get decoder => ZLibDecoder(windowBits: windowBits, dictionary: dictionary, raw: raw); } /// An instance of the default implementation of the [GZipCodec]. const GZipCodec gzip = GZipCodec._default(); /// The [GZipCodec] encodes raw bytes to GZip compressed bytes and decodes GZip /// compressed bytes to raw bytes. final class GZipCodec extends Codec, List> { /// When true, `GZip` frames will be added to the compressed data. final bool gzip; /// The compression-[level] can be set in the range of `-1..9`, with `6` being /// the default compression level. Levels above `6` will have higher /// compression rates at the cost of more CPU and memory usage. Levels below /// `6` will use less CPU and memory at the cost of lower compression rates. final int level; /// Specifies how much memory should be allocated for the internal compression /// state. `1` uses minimum memory but is slow and reduces compression ratio; /// `9` uses maximum memory for optimal speed. The default value is `8`. /// /// The memory requirements for deflate are (in bytes): /// ```dart /// (1 << (windowBits + 2)) + (1 << (memLevel + 9)) /// ``` /// that is: 128K for windowBits = 15 + 128K for memLevel = 8 (default values) final int memLevel; /// Tunes the compression algorithm. Use the value /// [ZLibOption.strategyDefault] for normal data, /// [ZLibOption.strategyFiltered] for data produced by a filter /// (or predictor), [ZLibOption.strategyHuffmanOnly] to force Huffman /// encoding only (no string match), or [ZLibOption.strategyRle] to limit /// match distances to one (run-length encoding). final int strategy; /// Base two logarithm of the window size (the size of the history buffer). It /// should be in the range `8..15`. Larger values result in better compression /// at the expense of memory usage. The default value is `15` final int windowBits; /// Initial compression dictionary. /// /// It should consist of strings (byte sequences) that are likely to be /// encountered later in the data to be compressed, with the most commonly used /// strings preferably put towards the end of the dictionary. Using a /// dictionary is most useful when the data to be compressed is short and can /// be predicted with good accuracy; the data can then be compressed better /// than with the default empty dictionary. final List? dictionary; /// When true, deflate generates raw data with no zlib header or trailer, and /// will not compute an adler32 check value final bool raw; GZipCodec({ this.level = ZLibOption.defaultLevel, this.windowBits = ZLibOption.defaultWindowBits, this.memLevel = ZLibOption.defaultMemLevel, this.strategy = ZLibOption.strategyDefault, this.dictionary, this.raw = false, this.gzip = true, }) { _validateZLibeLevel(level); _validateZLibMemLevel(memLevel); _validateZLibStrategy(strategy); _validateZLibWindowBits(windowBits); } const GZipCodec._default() : level = ZLibOption.defaultLevel, windowBits = ZLibOption.defaultWindowBits, memLevel = ZLibOption.defaultMemLevel, strategy = ZLibOption.strategyDefault, raw = false, gzip = true, dictionary = null; /// Get a [ZLibEncoder] for encoding to `GZip` compressed data. ZLibEncoder get encoder => ZLibEncoder( gzip: true, level: level, windowBits: windowBits, memLevel: memLevel, strategy: strategy, dictionary: dictionary, raw: raw, ); /// Get a [ZLibDecoder] for decoding `GZip` compressed data. ZLibDecoder get decoder => ZLibDecoder( gzip: true, windowBits: windowBits, dictionary: dictionary, raw: raw, ); } /// The [ZLibEncoder] encoder is used by [ZLibCodec] and [GZipCodec] to compress /// data. final class ZLibEncoder extends Converter, List> { /// When true, `GZip` frames will be added to the compressed data. final bool gzip; /// The compression-[level] can be set in the range of `-1..9`, with `6` being /// the default compression level. Levels above `6` will have higher /// compression rates at the cost of more CPU and memory usage. Levels below /// `6` will use less CPU and memory at the cost of lower compression rates. final int level; /// Specifies how much memory should be allocated for the internal compression /// state. `1` uses minimum memory but is slow and reduces compression ratio; /// `9` uses maximum memory for optimal speed. The default value is `8`. /// /// The memory requirements for deflate are (in bytes): /// ```dart /// (1 << (windowBits + 2)) + (1 << (memLevel + 9)) /// ``` /// that is: 128K for windowBits = 15 + 128K for memLevel = 8 (default values) final int memLevel; /// Tunes the compression algorithm. Use the value /// [ZLibOption.strategyDefault] for normal data, /// [ZLibOption.strategyFiltered] for data produced by a filter /// (or predictor), [ZLibOption.strategyHuffmanOnly] to force Huffman /// encoding only (no string match), or [ZLibOption.strategyRle] to limit /// match distances to one (run-length encoding). final int strategy; /// Base two logarithm of the window size (the size of the history buffer). It /// should be in the range `8..15`. Larger values result in better compression /// at the expense of memory usage. The default value is `15` final int windowBits; /// Initial compression dictionary. /// /// It should consist of strings (byte sequences) that are likely to be /// encountered later in the data to be compressed, with the most commonly used /// strings preferably put towards the end of the dictionary. Using a /// dictionary is most useful when the data to be compressed is short and can /// be predicted with good accuracy; the data can then be compressed better /// than with the default empty dictionary. final List? dictionary; /// When true, deflate generates raw data with no zlib header or trailer, and /// will not compute an adler32 check value final bool raw; ZLibEncoder({ this.gzip = false, this.level = ZLibOption.defaultLevel, this.windowBits = ZLibOption.defaultWindowBits, this.memLevel = ZLibOption.defaultMemLevel, this.strategy = ZLibOption.strategyDefault, this.dictionary, this.raw = false, }) { _validateZLibeLevel(level); _validateZLibMemLevel(memLevel); _validateZLibStrategy(strategy); _validateZLibWindowBits(windowBits); } /// Convert a list of bytes using the options given to the ZLibEncoder /// constructor. List convert(List bytes) { _BufferSink sink = _BufferSink(); startChunkedConversion(sink) ..add(bytes) ..close(); return sink.builder.takeBytes(); } /// Start a chunked conversion using the options given to the [ZLibEncoder] /// constructor. /// /// Accepts any `Sink>`, but prefers a [ByteConversionSink], /// and converts any other sink to a [ByteConversionSink] before /// using it. ByteConversionSink startChunkedConversion(Sink> sink) { if (sink is! ByteConversionSink) { sink = ByteConversionSink.from(sink); } return _ZLibEncoderSink._( sink, gzip, level, windowBits, memLevel, strategy, dictionary, raw, ); } } /// The [ZLibDecoder] is used by [ZLibCodec] and [GZipCodec] to decompress data. final class ZLibDecoder extends Converter, List> { /// When true, all concatenated compressed data sets in the input are /// decompressed and concatenated in the output. final bool gzip; /// Base two logarithm of the window size (the size of the history buffer). It /// should be in the range `8..15`. Larger values result in better compression /// at the expense of memory usage. The default value is `15`. final int windowBits; /// Initial compression dictionary. /// /// It should consist of strings (byte sequences) that are likely to be /// encountered later in the data to be compressed, with the most commonly used /// strings preferably put towards the end of the dictionary. Using a /// dictionary is most useful when the data to be compressed is short and can /// be predicted with good accuracy; the data can then be compressed better /// than with the default empty dictionary. final List? dictionary; /// When true, deflate generates raw data with no zlib header or trailer, and /// will not compute an adler32 check value final bool raw; ZLibDecoder({ this.gzip = false, this.windowBits = ZLibOption.defaultWindowBits, this.dictionary, this.raw = false, }) { _validateZLibWindowBits(windowBits); } /// Convert a list of bytes using the options given to the [ZLibDecoder] /// constructor. List convert(List bytes) { _BufferSink sink = _BufferSink(); startChunkedConversion(sink) ..add(bytes) ..close(); return sink.builder.takeBytes(); } /// Start a chunked conversion. /// /// Accepts any `Sink>`, but prefers a [ByteConversionSink], /// and converts any other sink to a [ByteConversionSink] before /// using it. ByteConversionSink startChunkedConversion(Sink> sink) { if (sink is! ByteConversionSink) { sink = ByteConversionSink.from(sink); } return _ZLibDecoderSink._(sink, gzip, windowBits, dictionary, raw); } } /// The [RawZLibFilter] class provides a low-level interface to zlib. abstract interface class RawZLibFilter { /// Returns a [RawZLibFilter] whose [process] and [processed] methods /// compress data. factory RawZLibFilter.deflateFilter({ bool gzip = false, int level = ZLibOption.defaultLevel, int windowBits = ZLibOption.defaultWindowBits, int memLevel = ZLibOption.defaultMemLevel, int strategy = ZLibOption.strategyDefault, List? dictionary, bool raw = false, }) { return _makeZLibDeflateFilter( gzip, level, windowBits, memLevel, strategy, dictionary, raw, ); } /// Returns a [RawZLibFilter] whose [process] and [processed] methods /// decompress data. factory RawZLibFilter.inflateFilter({ bool gzip = false, int windowBits = ZLibOption.defaultWindowBits, List? dictionary, bool raw = false, }) { return _makeZLibInflateFilter(gzip, windowBits, dictionary, raw); } /// Process a chunk of data. /// /// This method must only be called when [processed] returns `null`. void process(List data, int start, int end); /// Get a chunk of processed data. /// /// When there are no more data available, [processed] will return `null`. /// Set [flush] to `false` for non-final calls /// to improve performance of some filters. /// /// The last call to [processed] should have [end] set to `true`. This will /// make sure an 'end' packet is written on the stream. // TODO: Which stream? List? processed({bool flush = true, bool end = false}); external static RawZLibFilter _makeZLibDeflateFilter( bool gzip, int level, int windowBits, int memLevel, int strategy, List? dictionary, bool raw, ); external static RawZLibFilter _makeZLibInflateFilter( bool gzip, int windowBits, List? dictionary, bool raw, ); } class _BufferSink extends ByteConversionSink { final BytesBuilder builder = BytesBuilder(copy: false); void add(List chunk) { builder.add(chunk); } void addSlice(List chunk, int start, int end, bool isLast) { if (chunk is Uint8List) { Uint8List list = chunk; builder.add( Uint8List.view(list.buffer, list.offsetInBytes + start, end - start), ); } else { builder.add(chunk.sublist(start, end)); } } void close() {} } class _ZLibEncoderSink extends _FilterSink { _ZLibEncoderSink._( ByteConversionSink sink, bool gzip, int level, int windowBits, int memLevel, int strategy, List? dictionary, bool raw, ) : super( sink, RawZLibFilter._makeZLibDeflateFilter( gzip, level, windowBits, memLevel, strategy, dictionary, raw, ), ); } class _ZLibDecoderSink extends _FilterSink { _ZLibDecoderSink._( ByteConversionSink sink, bool gzip, int windowBits, List? dictionary, bool raw, ) : super( sink, RawZLibFilter._makeZLibInflateFilter(gzip, windowBits, dictionary, raw), ); } class _FilterSink extends ByteConversionSink { final RawZLibFilter _filter; final ByteConversionSink _sink; bool _closed = false; bool _empty = true; _FilterSink(this._sink, this._filter); void add(List data) { addSlice(data, 0, data.length, false); } void addSlice(List data, int start, int end, bool isLast) { if (_closed) return; RangeError.checkValidRange(start, end, data.length); try { _empty = false; _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( data, start, end, ); _filter.process( bufferAndStart.buffer, bufferAndStart.start, end - (start - bufferAndStart.start), ); List? out; while (true) { final out = _filter.processed(flush: false); if (out == null) break; _sink.add(out); } } catch (e) { _closed = true; rethrow; } if (isLast) close(); } void close() { if (_closed) return; // Be sure to send process an empty chunk of data. Without this, the empty // message would not have a GZip frame (if compressed with GZip). if (_empty) _filter.process(const [], 0, 0); try { while (true) { final out = _filter.processed(end: true); if (out == null) break; _sink.add(out); } } catch (e) { // TODO(kevmoo): not sure why this isn't a try/finally _closed = true; rethrow; } _closed = true; _sink.close(); } } void _validateZLibWindowBits(int windowBits) { if (ZLibOption.minWindowBits > windowBits || ZLibOption.maxWindowBits < windowBits) { throw RangeError.range( windowBits, ZLibOption.minWindowBits, ZLibOption.maxWindowBits, ); } } void _validateZLibeLevel(int level) { if (ZLibOption.minLevel > level || ZLibOption.maxLevel < level) { throw RangeError.range(level, ZLibOption.minLevel, ZLibOption.maxLevel); } } void _validateZLibMemLevel(int memLevel) { if (ZLibOption.minMemLevel > memLevel || ZLibOption.maxMemLevel < memLevel) { throw RangeError.range( memLevel, ZLibOption.minMemLevel, ZLibOption.maxMemLevel, ); } } void _validateZLibStrategy(int strategy) { const strategies = [ ZLibOption.strategyFiltered, ZLibOption.strategyHuffmanOnly, ZLibOption.strategyRle, ZLibOption.strategyFixed, ZLibOption.strategyDefault, ]; if (strategies.indexOf(strategy) == -1) { throw ArgumentError("Unsupported 'strategy'"); } } ‚vNN</@"I$&I$'I$+C"B!C%I$I$I(H)I,N$N&+(B-P# data, int start, int end); @pragma("vm:external-name", "Filter_Processed") external List? processed({bool flush = true, bool end = false}); } base class _ZLibInflateFilter extends _FilterImpl { _ZLibInflateFilter( bool gzip, int windowBits, List? dictionary, bool raw, ) { _init(gzip, windowBits, dictionary, raw); } @pragma("vm:external-name", "Filter_CreateZLibInflate") external void _init( bool gzip, int windowBits, List? dictionary, bool raw, ); } base class _ZLibDeflateFilter extends _FilterImpl { _ZLibDeflateFilter( bool gzip, int level, int windowBits, int memLevel, int strategy, List? dictionary, bool raw, ) { _init(gzip, level, windowBits, memLevel, strategy, dictionary, raw); } @pragma("vm:external-name", "Filter_CreateZLibDeflate") external void _init( bool gzip, int level, int windowBits, int memLevel, int strategy, List? dictionary, bool raw, ); } @patch class RawZLibFilter { @patch static RawZLibFilter _makeZLibDeflateFilter( bool gzip, int level, int windowBits, int memLevel, int strategy, List? dictionary, bool raw, ) => _ZLibDeflateFilter( gzip, level, windowBits, memLevel, strategy, dictionary, raw, ); @patch static RawZLibFilter _makeZLibInflateFilter( bool gzip, int windowBits, List? dictionary, bool raw, ) => _ZLibInflateFilter(gzip, windowBits, dictionary, raw); } WNN<80=2H4.:4I: /   />dart:io-patch/filter_patch.dartÀeÀh³Forg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/directory.dartºÓ// 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:io"; /// A reference to a directory (or _folder_) on the file system. /// /// A [Directory] is an object holding a [path] on which operations can /// be performed. The path to the directory can be [absolute] or relative. /// It allows access to the [parent] directory, /// since it is a [FileSystemEntity]. /// /// The [Directory] also provides static access to the system's temporary /// file directory, [systemTemp], and the ability to access and change /// the [current] directory. /// /// Create a new [Directory] to give access the directory with the specified /// path: /// ```dart /// var myDir = Directory('myDir'); /// ``` /// Most instance methods of [Directory] exist in both synchronous /// and asynchronous variants, for example, [create] and [createSync]. /// Unless you have a specific reason for using the synchronous version /// of a method, prefer the asynchronous version to avoid blocking your program. /// /// ## Create a directory /// /// The following code sample creates a directory using the [create] method. /// By setting the `recursive` parameter to true, you can create the /// named directory and all its necessary parent directories, /// if they do not already exist. /// ```dart /// import 'dart:io'; /// /// void main() async { /// // Creates dir/ and dir/subdir/. /// var directory = await Directory('dir/subdir').create(recursive: true); /// print(directory.path); /// } /// ``` /// ## List the entries of a directory /// /// Use the [list] or [listSync] methods to get the files and directories /// contained in a directory. /// Set `recursive` to true to recursively list all subdirectories. /// Set `followLinks` to true to follow symbolic links. /// The list method returns a [Stream] of [FileSystemEntity] objects. /// Listen on the stream to access each object as it is found: /// ```dart /// import 'dart:io'; /// /// void main() async { /// // Get the system temp directory. /// var systemTempDir = Directory.systemTemp; /// /// // List directory contents, recursing into sub-directories, /// // but not following symbolic links. /// await for (var entity in /// systemTempDir.list(recursive: true, followLinks: false)) { /// print(entity.path); /// } /// } /// ``` /// ## The use of asynchronous methods /// /// I/O operations can block a program for some period of time while it waits for /// the operation to complete. To avoid this, all /// methods involving I/O have an asynchronous variant which returns a [Future]. /// This future completes when the I/O operation finishes. While the I/O /// operation is in progress, the Dart program is not blocked, /// and can perform other operations. /// /// For example, /// the [exists] method, which determines whether the directory exists, /// returns a boolean value asynchronously using a [Future]. /// ```dart /// import 'dart:io'; /// /// void main() async { /// final myDir = Directory('dir'); /// var isThere = await myDir.exists(); /// print(isThere ? 'exists' : 'nonexistent'); /// } /// ``` /// /// In addition to [exists], the [stat], [rename], /// and other methods are also asynchronous. /// /// ## Other resources /// /// * The [Files and directories](https://dart.dev/guides/libraries/library-tour#files-and-directories) /// section of the library tour. /// /// * [Write Command-Line Apps](https://dart.dev/tutorials/server/cmdline), /// a tutorial about writing command-line apps, includes information about /// files and directories. @pragma("vm:entry-point") abstract interface class Directory implements FileSystemEntity { /// Gets the path of this directory. String get path; /// Creates a [Directory] object. /// /// If [path] is a relative path, it will be interpreted relative to the /// current working directory (see [Directory.current]), when used. /// /// If [path] is an absolute path, it will be immune to changes to the /// current working directory. @pragma("vm:entry-point") factory Directory(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _Directory(path); } return overrides.createDirectory(path); } @pragma("vm:entry-point") factory Directory.fromRawPath(Uint8List path) { // TODO(bkonyi): Handle overrides. return _Directory.fromRawPath(path); } /// Create a [Directory] from a URI. /// /// If [uri] cannot reference a directory this throws [UnsupportedError]. factory Directory.fromUri(Uri uri) => Directory(uri.toFilePath()); /// Creates a directory object pointing to the current working /// directory. static Directory get current { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _Directory.current; } return overrides.getCurrentDirectory(); } /// A [Uri] representing the directory's location. /// /// The URI's scheme is always "file" if the entity's [path] is /// absolute, otherwise the scheme will be empty and the URI relative. /// The URI's path always ends in a slash ('/'). Uri get uri; /// Sets the current working directory of the Dart process. /// /// This affects all running isolates. /// The new value set can be either a [Directory] or a [String]. /// /// The new value is passed to the OS's system call unchanged, so a /// relative path passed as the new working directory will be /// resolved by the OS. /// /// Note that setting the current working directory is a synchronous /// operation and that it changes the working directory of *all* /// isolates. /// /// Use this with care — especially when working with asynchronous /// operations and multiple isolates. Changing the working directory, /// while asynchronous operations are pending or when other isolates /// are working with the file system, can lead to unexpected results. static void set current(dynamic path) { // Disallow implicit casts to avoid bugs like // . // // This can be removed if `strict-casts` is enabled. path as Object?; final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { _Directory.current = path; return; } // IOOverrides.setCurrentDirectory accepts only a [String]. overrides.setCurrentDirectory(switch (path) { String s => s, Directory d => d.path, _ => throw ArgumentError( '${Error.safeToString(path)} is not a String or' ' Directory', ), }); } /// Creates the directory if it doesn't exist. /// /// If [recursive] is false, only the last directory in the path is /// created. If [recursive] is true, all non-existing path components /// are created. If the directory already exists nothing is done. /// /// Returns a `Future` that completes with this /// directory once it has been created. If the directory cannot be /// created the future completes with an exception. Future create({bool recursive = false}); /// Synchronously creates the directory if it doesn't exist. /// /// If [recursive] is false, only the last directory in the path is /// created. If [recursive] is true, all non-existing path components /// are created. If the directory already exists nothing is done. /// /// If the directory cannot be created an exception is thrown. void createSync({bool recursive = false}); /// The system temp directory. /// /// This is the directory provided by the operating system for creating /// temporary files and directories in. /// The location of the system temporary directory is platform-dependent, /// and may be controlled by an environment variable on some platforms. static Directory get systemTemp { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _Directory.systemTemp; } return overrides.getSystemTempDirectory(); } /// Creates a temporary directory in this directory. /// /// Additional random characters are appended to [prefix] /// to produce a unique directory name. /// If [prefix] is missing or null, the empty string is used as [prefix]. /// /// Returns a `Future` that completes with the newly /// created temporary directory. Future createTemp([String? prefix]); /// Synchronously creates a temporary directory in this directory. /// /// Additional random characters are appended to [prefix] to produce /// a unique directory name. If [prefix] is missing or null, the empty /// string is used as [prefix]. /// /// Returns the newly created temporary directory. Directory createTempSync([String? prefix]); Future resolveSymbolicLinks(); String resolveSymbolicLinksSync(); /// Renames this directory. /// /// Returns a `Future` that completes /// with a [Directory] for the renamed directory. /// /// If [newPath] identifies an existing directory, then the behavior is /// platform-specific. On all platforms, the future completes with a /// [FileSystemException] if the existing directory is not empty. On POSIX /// systems, if [newPath] identifies an existing empty directory then that /// directory is deleted before this directory is renamed. /// /// If [newPath] identifies an existing file or link, the operation /// fails and the future completes with a [FileSystemException]. Future rename(String newPath); /// Synchronously renames this directory. /// /// Returns a [Directory] for the renamed directory. /// /// If [newPath] identifies an existing directory, then the behavior is /// platform-specific. On all platforms, a [FileSystemException] is thrown /// if the existing directory is not empty. On POSIX systems, if [newPath] /// identifies an existing empty directory then that directory is deleted /// before this directory is renamed. /// /// If [newPath] identifies an existing file or link the operation /// fails and a [FileSystemException] is thrown. Directory renameSync(String newPath); /// Deletes this [Directory]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to an empty directory, then that directory is /// deleted. If [path] corresponds to a link, and that link resolves /// to a directory, then the link at [path] will be deleted. In all /// other cases, [delete] completes with a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [Directory] cannot be deleted, then [delete] completes with a /// [FileSystemException]. Future delete({bool recursive = false}); /// Synchronously deletes this [Directory]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to an empty directory, then that directory is /// deleted. If [path] corresponds to a link, and that link resolves /// to a directory, then the link at [path] will be deleted. In all /// other cases, [delete] throws a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [Directory] cannot be deleted, then [delete] throws a /// [FileSystemException]. void deleteSync({bool recursive = false}); /// A [Directory] whose path is the absolute path of this [Directory]. /// /// The absolute path is computed by prefixing /// a relative path with the current working directory, /// or by returning an absolute path unchanged. Directory get absolute; /// Lists the sub-directories and files of this [Directory]. /// /// Optionally recurses into sub-directories. /// /// If [followLinks] is `false`, then any symbolic links found /// are reported as [Link] objects, rather than as directories or files, /// and are not recursed into. /// /// If [followLinks] is `true`, then working links are reported as /// directories or files, depending on what they point to, /// and links to directories are recursed into if [recursive] is `true`. /// /// Broken links are reported as [Link] objects. /// /// If a symbolic link makes a loop in the file system, then a recursive /// listing will not follow a link twice in the /// same recursive descent, but will report it as a [Link] /// the second time it is seen. /// /// The result is a [Stream] of [FileSystemEntity] objects for the /// directories, files, and links. The [Stream] will be in an arbitrary /// order and does not include the special entries `'.'` and `'..'`. Stream list({ bool recursive = false, bool followLinks = true, }); /// Lists the sub-directories and files of this [Directory]. /// Optionally recurses into sub-directories. /// /// If [followLinks] is `false`, then any symbolic links found /// are reported as [Link] objects, rather than as directories or files, /// and are not recursed into. /// /// If [followLinks] is `true`, then working links are reported as /// directories or files, depending on what they point to, /// and links to directories are recursed into if [recursive] is `true`. /// /// Broken links are reported as [Link] objects. /// /// If a link makes a loop in the file system, then a recursive /// listing will not follow a link twice in the /// same recursive descent, but will report it as a [Link] /// the second time it is seen. /// /// Returns a [List] containing [FileSystemEntity] objects for the /// directories, files, and links. The [List] will be in an arbitrary order /// and does not include the special entries `'.'` and `'..'`. List listSync({ bool recursive = false, bool followLinks = true, }); /// Returns a human readable representation of this [Directory]. String toString(); } ��NN<AHK0&JGM $CGHQME>" 'M'JD8F? (0B+E'R2QI?&H= &*13-h#LMA'$KFI!#8,2')'LEA!8!,5BI3>)CF@GCEHGH*2898!@2 9 1FHD=E66?FHDA-!J*LJ$8$/7<*LB#2EGI"5.)%34JGMM=FC,,7JMML(E3( !MJIG LIHKOE8L=.!MJI? LIHKOE8D-I1:2?0AK!E=K3K2="EJG"?0AK!E=K3B2="ENA$Cdart:io/directory.dartÀeÀh³Korg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/directory_impl.dart±¶// 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:io"; class _Directory extends FileSystemEntity implements Directory { final String _path; final Uint8List _rawPath; _Directory(this._path) : _rawPath = FileSystemEntity._toUtf8Array(_path); _Directory.fromRawPath(Uint8List rawPath) : _rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawPath), _path = FileSystemEntity._toStringFromUtf8Array(rawPath); String get path => _path; external static _current(_Namespace namespace); external static _setCurrent(_Namespace namespace, Uint8List rawPath); external static _createTemp(_Namespace namespace, Uint8List rawPath); external static String _systemTemp(_Namespace namespace); external static _exists(_Namespace namespace, Uint8List rawPath); external static _create(_Namespace namespace, Uint8List rawPath); external static _deleteNative( _Namespace namespace, Uint8List rawPath, bool recursive, ); external static _rename( _Namespace namespace, Uint8List rawPath, String newPath, ); external static void _fillWithDirectoryListing( _Namespace namespace, List list, Uint8List rawPath, bool recursive, bool followLinks, ); static Directory get current { var result = _current(_Namespace._namespace); if (result is OSError) { throw FileSystemException._fromOSError( result, "Getting current working directory failed", "", ); } return _Directory(result); } static void set current(Object? path) { var _rawPath = switch (path) { // For our internal Directory implementation, go ahead and use the raw // path. _Directory d => d._rawPath, // Fall back to the String-based path. Directory d => FileSystemEntity._toUtf8Array(d.path), String s => FileSystemEntity._toUtf8Array(s), _ => throw ArgumentError( '${Error.safeToString(path)} is not a String or' ' Directory', ), }; var result = _setCurrent(_Namespace._namespace, _rawPath); if (result is ArgumentError) throw result; if (result is OSError) { throw FileSystemException._fromOSError( result, "Setting current working directory failed", path.toString(), ); } } Uri get uri { return Uri.directory(path); } Future exists() { return _File._dispatchWithNamespace(_IOService.directoryExists, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Exists failed", path); return response == 1; }); } bool existsSync() { var result = _exists(_Namespace._namespace, _rawPath); if (result is OSError) { throw FileSystemException("Exists failed", path, result); } return (result == 1); } Directory get absolute => Directory(_absolutePath); Future create({bool recursive = false}) { if (recursive) { return exists().then((exists) { if (exists) return this; if (path != parent.path) { return parent.create(recursive: true).then((_) { return create(); }); } else { return create(); } }); } else { return _File._dispatchWithNamespace(_IOService.directoryCreate, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Creation failed", path); return this; }); } } void createSync({bool recursive = false}) { if (recursive) { if (existsSync()) return; if (path != parent.path) { parent.createSync(recursive: true); } } var result = _create(_Namespace._namespace, _rawPath); if (result is OSError) { throw FileSystemException._fromOSError(result, "Creation failed", path); } } static Directory get systemTemp => Directory(_systemTemp(_Namespace._namespace)); Future createTemp([String? prefix]) { prefix ??= ''; if (path == '') { throw ArgumentError( "Directory.createTemp called with an empty path. " "To use the system temp directory, use Directory.systemTemp", ); } String fullPrefix; // FIXME(bkonyi): here we're using `path` directly, which might cause // issues if it is not UTF-8 encoded. if (path.endsWith('/') || (Platform.isWindows && path.endsWith('\\'))) { fullPrefix = "$path$prefix"; } else { fullPrefix = "$path${Platform.pathSeparator}$prefix"; } return _File._dispatchWithNamespace(_IOService.directoryCreateTemp, [ null, FileSystemEntity._toUtf8Array(fullPrefix), ]).then((response) { _checkForErrorResponse( response, "Creation of temporary directory failed", path, ); return Directory(response as String); }); } Directory createTempSync([String? prefix]) { prefix ??= ''; if (path == '') { throw ArgumentError( "Directory.createTemp called with an empty path. " "To use the system temp directory, use Directory.systemTemp", ); } String fullPrefix; // FIXME(bkonyi): here we're using `path` directly, which might cause // issues if it is not UTF-8 encoded. if (path.endsWith('/') || (Platform.isWindows && path.endsWith('\\'))) { fullPrefix = "$path$prefix"; } else { fullPrefix = "$path${Platform.pathSeparator}$prefix"; } var result = _createTemp( _Namespace._namespace, FileSystemEntity._toUtf8Array(fullPrefix), ); if (result is OSError) { throw FileSystemException._fromOSError( result, "Creation of temporary directory failed", fullPrefix, ); } return Directory(result); } Future _delete({bool recursive = false}) { return _File._dispatchWithNamespace(_IOService.directoryDelete, [ null, _rawPath, recursive, ]).then((response) { _checkForErrorResponse(response, "Deletion failed", path); return this; }); } void _deleteSync({bool recursive = false}) { var result = _deleteNative(_Namespace._namespace, _rawPath, recursive); if (result is OSError) { throw FileSystemException._fromOSError(result, "Deletion failed", path); } } Future rename(String newPath) { return _File._dispatchWithNamespace(_IOService.directoryRename, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse(response, "Rename failed", path); return Directory(newPath); }); } Directory renameSync(String newPath) { var result = _rename(_Namespace._namespace, _rawPath, newPath); if (result is OSError) { throw FileSystemException._fromOSError(result, "Rename failed", path); } return Directory(newPath); } Stream list({ bool recursive = false, bool followLinks = true, }) { return _AsyncDirectoryLister( // FIXME(bkonyi): here we're using `path` directly, which might cause issues // if it is not UTF-8 encoded. FileSystemEntity._toUtf8Array( FileSystemEntity._ensureTrailingPathSeparators(path), ), recursive, followLinks, ).stream; } List listSync({ bool recursive = false, bool followLinks = true, }) { var result = []; _fillWithDirectoryListing( _Namespace._namespace, result, // FIXME(bkonyi): here we're using `path` directly, which might cause issues // if it is not UTF-8 encoded. FileSystemEntity._toUtf8Array( FileSystemEntity._ensureTrailingPathSeparators(path), ), recursive, followLinks, ); return result; } String toString() => "Directory: '$path'"; } abstract class _AsyncDirectoryListerOps { external factory _AsyncDirectoryListerOps._(int pointer); int? _getPointer(); } class _AsyncDirectoryLister { static const int listFile = 0; static const int listDirectory = 1; static const int listLink = 2; static const int listError = 3; static const int listDone = 4; static const int responseType = 0; static const int responsePath = 1; static const int responseComplete = 1; static const int responseError = 2; final Uint8List rawPath; final bool recursive; final bool followLinks; final controller = StreamController(sync: true); bool canceled = false; bool nextRunning = false; bool closed = false; _AsyncDirectoryListerOps? _ops; Completer closeCompleter = Completer(); _AsyncDirectoryLister(this.rawPath, this.recursive, this.followLinks) { controller ..onListen = onListen ..onResume = onResume ..onCancel = onCancel; } // WARNING: // Calling this function will increase the reference count on the native // object that implements the async directory lister operations. It should // only be called to pass the pointer to the IO Service, which will decrement // the reference count when it is finished with it. int? _pointer() { return _ops?._getPointer(); } Stream get stream => controller.stream; void onListen() { _File._dispatchWithNamespace(_IOService.directoryListStart, [ null, rawPath, recursive, followLinks, ]).then((response) { if (response is int) { _ops = _AsyncDirectoryListerOps._(response); next(); } else if (response is Error) { controller.addError(response, response.stackTrace); close(); } else { error(response as List); close(); } }); } void onResume() { if (!nextRunning) { next(); } } Future onCancel() { canceled = true; // If we are active, but not requesting, close. if (!nextRunning) { close(); } return closeCompleter.future; } void next() { if (canceled) { close(); return; } if (controller.isPaused || nextRunning) { return; } var pointer = _pointer(); if (pointer == null) { return; } nextRunning = true; _IOService._dispatch(_IOService.directoryListNext, [pointer]).then(( result, ) { nextRunning = false; if (result is List) { next(); assert(result.length % 2 == 0); for (int i = 0; i < result.length; i++) { assert(i % 2 == 0); switch (result[i++]) { case listFile: controller.add(File.fromRawPath(result[i])); break; case listDirectory: controller.add(Directory.fromRawPath(result[i])); break; case listLink: controller.add(Link.fromRawPath(result[i])); break; case listError: error(result[i]); break; case listDone: canceled = true; return; } } } else { controller.addError(FileSystemException("Internal error")); } }); } void _cleanup() { controller.close(); closeCompleter.complete(); _ops = null; } void close() { if (closed) { return; } if (nextRunning) { return; } closed = true; var pointer = _pointer(); if (pointer == null) { _cleanup(); } else { _IOService._dispatch(_IOService.directoryListStop, [ pointer, ]).whenComplete(_cleanup); } } void error(List message) { var errorResponseInfo = message[responseError]! as List; var errorType = errorResponseInfo[_errorResponseErrorType]; if (errorType == _illegalArgumentResponse) { controller.addError(ArgumentError()); } else if (errorType == _osErrorResponse) { var err = OSError( errorResponseInfo[_osErrorResponseMessage] as String, errorResponseInfo[_osErrorResponseErrorCode] as int, ); var errorPath = message[responsePath]; if (errorPath == null) { errorPath = utf8.decode(rawPath, allowMalformed: true); } else if (errorPath is Uint8List) { errorPath = utf8.decode(errorPath, allowMalformed: true); } controller.addError( FileSystemException._fromOSError( err, "Directory listing failed", errorPath as String, ), ); } else { controller.addError(FileSystemException("Internal error")); } } } �ËNN<AL,G@2HH $'S%%> -*<!&!"!%%)&E"*JKMP6 <B 5&<*4".I(2!; @;  D ;!&F@1,0>= -@+B*& Bdart:io/directory_impl.dartZorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/directory_patch.dartй// 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 "common_patch.dart"; @patch class _Directory { @patch @pragma("vm:external-name", "Directory_Current") external static _current(_Namespace namespace); @patch @pragma("vm:external-name", "Directory_SetCurrent") external static _setCurrent(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "Directory_CreateTemp") external static _createTemp(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "Directory_SystemTemp") external static String _systemTemp(_Namespace namespace); @patch @pragma("vm:external-name", "Directory_Exists") external static _exists(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "Directory_Create") external static _create(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "Directory_Delete") external static _deleteNative( _Namespace namespace, Uint8List rawPath, bool recursive, ); @patch @pragma("vm:external-name", "Directory_Rename") external static _rename( _Namespace namespace, Uint8List rawPath, String newPath, ); @patch @pragma("vm:external-name", "Directory_FillWithDirectoryListing") external static void _fillWithDirectoryListing( _Namespace namespace, List list, Uint8List rawPath, bool recursive, bool followLinks, ); } @patch class _AsyncDirectoryListerOps { @patch factory _AsyncDirectoryListerOps._(int pointer) => _AsyncDirectoryListerOpsImpl._().._setPointer(pointer); } base class _AsyncDirectoryListerOpsImpl extends NativeFieldWrapperClass1 implements _AsyncDirectoryListerOps { _AsyncDirectoryListerOpsImpl._(); @pragma("vm:external-name", "Directory_SetAsyncDirectoryListerPointer") external void _setPointer(int pointer); @pragma("vm:external-name", "Directory_GetAsyncDirectoryListerPointer") external int _getPointer(); } // Corelib 'Uri.base' implementation. // Uri.base is susceptible to changes in the current working directory. Uri _uriBaseClosure() { var overrides = IOOverrides.current; if (overrides != null) { return overrides.getCurrentDirectory().uri; } var result = _Directory._current(_Namespace._namespace); if (result is OSError) { throw FileSystemException._fromOSError( result, "Getting current working directory failed", "", ); } return Uri.directory(result as String); } @pragma("vm:entry-point", "call") _getUriBaseClosure() => _uriBaseClosure; [NN< 32 6H 6H 6< 2D 2D 2! 2 D2!! 5>I*$J*J&H'0;,2 *")"dart:io-patch/directory_patch.dartÀeÀh³Lorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/embedder_config.dartƒ»// Copyright (c) 2017, 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:io"; /// Embedder-specific, fine-grained `dart:io` configuration. /// /// This class contains per-Isolate flags that an embedder can set to put /// fine-grained limitations on what process-visible operations Isolates are /// permitted to use (e.g. [exit]). By default, the whole `dart:io` API is /// enabled. When a disallowed operation is attempted, an `UnsupportedError` is /// thrown. /// /// Embedders should not modify these flags directly and should instead /// configure `dart:io` by passing appropriate settings to /// `dart::bin::SetupDartIoLibrary`. @pragma('vm:entry-point') abstract class _EmbedderConfig { /// Whether the isolate may call [exit]. @pragma("vm:entry-point") static bool _mayExit = true; } NN<=JMKP H;%!+dart:io/embedder_config.dartÀeÀh³Iorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/eventhandler.dart�T// 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:io"; class _EventHandler { external static void _sendData(Object? sender, SendPort sendPort, int data); } NN<Odart:io/eventhandler.dart]org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/eventhandler_patch.dart‚// 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 "common_patch.dart"; @patch class _EventHandler { @patch @pragma("vm:external-name", "EventHandler_SendData") external static void _sendData(Object? sender, SendPort sendPort, int data); @pragma("vm:external-name", "EventHandler_TimerMillisecondClock") external static int _timerMillisecondClock(); } NN< 7OD0%dart:io-patch/eventhandler_patch.dartÀeÀh³Aorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/file.dartÀ· // Copyright (c) 2013, 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:io"; /// The modes in which a [File] can be opened. class FileMode { /// The mode for opening a file only for reading. static const read = FileMode._internal(0); /// Mode for opening a file for reading and writing. The file is /// overwritten if it already exists. The file is created if it does not /// already exist. static const write = FileMode._internal(1); /// Mode for opening a file for reading and writing to the /// end of it. The file is created if it does not already exist. static const append = FileMode._internal(2); /// Mode for opening a file for writing *only*. The file is /// overwritten if it already exists. The file is created if it does not /// already exist. static const writeOnly = FileMode._internal(3); /// Mode for opening a file for writing *only* to the /// end of it. The file is created if it does not already exist. static const writeOnlyAppend = FileMode._internal(4); final int _mode; const FileMode._internal(this._mode); } /// Type of lock when requesting a lock on a file. class FileLock { /// Shared file lock. static const shared = FileLock._internal(1); /// Exclusive file lock. static const exclusive = FileLock._internal(2); /// Blocking shared file lock. static const blockingShared = FileLock._internal(3); /// Blocking exclusive file lock. static const blockingExclusive = FileLock._internal(4); final int _type; const FileLock._internal(this._type); } /// A reference to a file on the file system. /// /// A `File` holds a [path] on which operations can be performed. /// You can get the parent directory of the file using [parent], /// a property inherited from [FileSystemEntity]. /// /// Create a new `File` object with a pathname to access the specified file on the /// file system from your program. /// ```dart /// var myFile = File('file.txt'); /// ``` /// The `File` class contains methods for manipulating files and their contents. /// Using methods in this class, you can open and close files, read to and write /// from them, create and delete them, and check for their existence. /// /// When reading or writing a file, you can use streams (with [openRead]), /// random access operations (with [open]), /// or convenience methods such as [readAsString], /// /// Most methods in this class occur in synchronous and asynchronous pairs, /// for example, [readAsString] and [readAsStringSync]. /// Unless you have a specific reason for using the synchronous version /// of a method, prefer the asynchronous version to avoid blocking your program. /// /// ## If path is a link /// /// If [path] is a symbolic link, rather than a file, /// then the methods of `File` operate on the ultimate target of the /// link, except for [delete] and [deleteSync], which operate on /// the link. /// /// ## Read from a file /// /// The following code sample reads the entire contents from a file as a string /// using the asynchronous [readAsString] method: /// ```dart /// import 'dart:async'; /// import 'dart:io'; /// /// void main() { /// File('file.txt').readAsString().then((String contents) { /// print(contents); /// }); /// } /// ``` /// A more flexible and useful way to read a file is with a [Stream]. /// Open the file with [openRead], which returns a stream that /// provides the data in the file as chunks of bytes. /// Read the stream to process the file contents when available. /// You can use various transformers in succession to manipulate the /// file content into the required format, or to prepare it for output. /// /// You might want to use a stream to read large files, /// to manipulate the data with transformers, /// or for compatibility with another API, such as [WebSocket]s. /// ```dart /// import 'dart:io'; /// import 'dart:convert'; /// import 'dart:async'; /// /// void main() async { /// final file = File('file.txt'); /// Stream lines = file.openRead() /// .transform(utf8.decoder) // Decode bytes to UTF-8. /// .transform(LineSplitter()); // Convert stream to individual lines. /// try { /// await for (var line in lines) { /// print('$line: ${line.length} characters'); /// } /// print('File is now closed.'); /// } catch (e) { /// print('Error: $e'); /// } /// } /// ``` /// ## Write to a file /// /// To write a string to a file, use the [writeAsString] method: /// ```dart /// import 'dart:io'; /// /// void main() async { /// final filename = 'file.txt'; /// var file = await File(filename).writeAsString('some content'); /// // Do something with the file. /// } /// ``` /// You can also write to a file using a [Stream]. Open the file with /// [openWrite], which returns an [IOSink] to which you can write data. /// Be sure to close the sink with the [IOSink.close] method. /// ```dart /// import 'dart:io'; /// /// void main() async { /// var file = File('file.txt'); /// var sink = file.openWrite(); /// sink.write('FILE ACCESSED ${DateTime.now()}\n'); /// await sink.flush(); /// /// // Close the IOSink to free system resources. /// await sink.close(); /// } /// ``` /// ## The use of asynchronous methods /// /// To avoid unintentional blocking of the program, /// several methods are asynchronous and return a [Future]. For example, /// the [length] method, which gets the length of a file, returns a [Future]. /// Wait for the future to get the result when it's ready. /// ```dart /// import 'dart:io'; /// /// void main() async { /// final file = File('file.txt'); /// /// var length = await file.length(); /// print(length); /// } /// ``` /// In addition to length, the [exists], [lastModified], [stat], and /// other methods, are asynchronous. /// /// ## Special 'nul' file /// /// On Linux and Mac '/dev/null' and on Windows '\\?\NUL' refer to a special file, /// such that all writes to it get consumed and disappear, and all reads produce empty /// output. Note that on Windows 'nul'(without '\\?\'-prefix) refers to a regular file /// named 'nul' in current directory. /// /// ## Other resources /// /// * The [Files and directories](https://dart.dev/guides/libraries/library-tour#files-and-directories) /// section of the library tour. /// /// * [Write Command-Line Apps](https://dart.dev/tutorials/server/cmdline), /// a tutorial about writing command-line apps, includes information about /// files and directories. @pragma("vm:entry-point") abstract interface class File implements FileSystemEntity { /// Creates a [File] object. /// /// If [path] is a relative path, it will be interpreted relative to the /// current working directory (see [Directory.current]), when used. /// /// If [path] is an absolute path, it will be immune to changes to the /// current working directory. @pragma("vm:entry-point") factory File(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _File(path); } return overrides.createFile(path); } /// Create a [File] object from a URI. /// /// If [uri] cannot reference a file this throws [UnsupportedError]. factory File.fromUri(Uri uri) => File(uri.toFilePath()); /// Creates a [File] object from a raw path. /// /// A raw path is a sequence of bytes, as paths are represented by the OS. @pragma("vm:entry-point") factory File.fromRawPath(Uint8List rawPath) { // TODO(bkonyi): Handle overrides. return _File.fromRawPath(rawPath); } /// Creates the file. /// /// Returns a `Future` that completes with /// the file when it has been created. /// /// If [recursive] is `false`, the default, the file is created only if /// all directories in its path already exist. If [recursive] is `true`, any /// non-existing parent paths are created first. /// /// If [exclusive] is `true` and to-be-created file already exists, this /// operation completes the future with a [PathExistsException]. /// /// If [exclusive] is `false`, existing files are left untouched by [create]. /// Calling [create] on an existing file still might fail if there are /// restrictive permissions on the file. /// /// Completes the future with a [FileSystemException] if the operation fails. Future create({bool recursive = false, bool exclusive = false}); /// Synchronously creates the file. /// /// If [recursive] is `false`, the default, the file is created /// only if all directories in its path already exist. /// If [recursive] is `true`, all non-existing parent paths are created first. /// /// If [exclusive] is `true` and to-be-created file already exists, then /// a [FileSystemException] is thrown. /// /// If [exclusive] is `false`, existing files are left untouched by /// [createSync]. Calling [createSync] on an existing file still might fail /// if there are restrictive permissions on the file. /// /// Throws a [FileSystemException] if the operation fails. void createSync({bool recursive = false, bool exclusive = false}); /// Renames this file. /// /// Returns a `Future` that completes /// with a [File] for the renamed file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// This means that simply changing the name of a file, /// but keeping it the original directory, /// requires creating a new complete path with the new name /// at the end. Example: /// ```dart /// Future changeFileNameOnly(File file, String newFileName) { /// var path = file.path; /// var lastSeparator = path.lastIndexOf(Platform.pathSeparator); /// var newPath = path.substring(0, lastSeparator + 1) + newFileName; /// return file.rename(newPath); /// } /// ``` /// On some platforms, a rename operation cannot move a file between /// different file systems. If that is the case, instead [copy] the /// file to the new location and then remove the original. /// /// If [newPath] identifies an existing file or link, that entity is /// removed first. If [newPath] identifies an existing directory, the /// operation fails and the future completes with a [FileSystemException]. Future rename(String newPath); /// Synchronously renames this file. /// /// Returns a [File] for the renamed file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// This means that simply changing the name of a file, /// but keeping it the original directory, /// requires creating a new complete path with the new name /// at the end. Example: /// ```dart /// File changeFileNameOnlySync(File file, String newFileName) { /// var path = file.path; /// var lastSeparator = path.lastIndexOf(Platform.pathSeparator); /// var newPath = path.substring(0, lastSeparator + 1) + newFileName; /// return file.renameSync(newPath); /// } /// ``` /// On some platforms, a rename operation cannot move a file between /// different file systems. If that is the case, instead [copySync] the /// file to the new location and then [deleteSync] the original. /// /// If [newPath] identifies an existing file or link, that entity is /// removed first. If [newPath] identifies an existing directory the /// operation throws a [FileSystemException]. File renameSync(String newPath); /// Deletes this [File]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a regular file, named pipe or socket, then /// that path is deleted. If [path] corresponds to a link, and that link /// resolves to a file, then the link at [path] will be deleted. In all /// other cases, [delete] completes with a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [File] cannot be deleted, then [delete] completes with a /// [FileSystemException]. Future delete({bool recursive = false}); /// Synchronously deletes this [File]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a regular file, named pipe or socket, then /// that path is deleted. If [path] corresponds to a link, and that link /// resolves to a file, then the link at [path] will be deleted. In all /// other cases, [delete] throws a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [File] cannot be deleted, then [delete] throws a /// [FileSystemException]. void deleteSync({bool recursive = false}); /// Copies this file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// /// Returns a `Future` that completes /// with a [File] for the copied file. /// /// If [newPath] identifies an existing file, that file is /// removed first. If [newPath] identifies an existing directory, the /// operation fails and the future completes with an exception. Future copy(String newPath); /// Synchronously copies this file. /// /// If [newPath] is a relative path, it is resolved against /// the current working directory ([Directory.current]). /// /// Returns a [File] for the copied file. /// /// If [newPath] identifies an existing file, that file is /// removed first. If [newPath] identifies an existing directory the /// operation fails and an exception is thrown. File copySync(String newPath); /// The length of the file. /// /// Returns a `Future` that completes with the length in bytes. Future length(); /// The length of the file provided synchronously. /// /// Throws a [FileSystemException] if the operation fails. int lengthSync(); /// A [File] with the absolute path of [path]. /// /// The absolute path is computed by prefixing /// a relative path with the current working directory, /// or returning an absolute path unchanged. File get absolute; /// The last-accessed time of the file. /// /// Returns a `Future` that completes with the date and time when the /// file was last accessed, if the information is available. /// /// Throws a [FileSystemException] if the operation fails. Future lastAccessed(); /// The last-accessed time of the file. /// /// Returns the date and time when the file was last accessed, /// if the information is available. Blocks until the information can be returned /// or it is determined that the information is not available. /// /// Throws a [FileSystemException] if the operation fails. DateTime lastAccessedSync(); /// Modifies the time the file was last accessed. /// /// Returns a [Future] that completes once the operation has completed. /// /// Throws a [FileSystemException] if the time cannot be set. Future setLastAccessed(DateTime time); /// Synchronously modifies the time the file was last accessed. /// /// Throws a [FileSystemException] if the time cannot be set. void setLastAccessedSync(DateTime time); /// Get the last-modified time of the file. /// /// Returns a `Future` that completes with the date and time when the /// file was last modified, if the information is available. /// /// Throws a [FileSystemException] if the operation fails. Future lastModified(); /// Get the last-modified time of the file. /// /// Returns the date and time when the file was last modified, /// if the information is available. Blocks until the information can be returned /// or it is determined that the information is not available. /// /// Throws a [FileSystemException] if the operation fails. DateTime lastModifiedSync(); /// Modifies the time the file was last modified. /// /// Returns a [Future] that completes once the operation has completed. /// /// Throws a [FileSystemException] if the time cannot be set. Future setLastModified(DateTime time); /// Synchronously modifies the time the file was last modified. /// /// If the attributes cannot be set, throws a [FileSystemException]. void setLastModifiedSync(DateTime time); /// Opens the file for random access operations. /// /// Returns a `Future` that completes with the opened /// random access file. [RandomAccessFile]s must be closed using the /// [RandomAccessFile.close] method. /// /// Files can be opened in three modes: /// /// * [FileMode.read]: open the file for reading. /// /// * [FileMode.write]: open the file for both reading and writing and /// truncate the file to length zero. If the file does not exist the /// file is created. /// /// * [FileMode.append]: same as [FileMode.write] except that the file is /// not truncated. /// /// Throws a [FileSystemException] if the operation fails. Future open({FileMode mode = FileMode.read}); /// Synchronously opens the file for random access operations. /// /// The result is a [RandomAccessFile] on which random access operations /// can be performed. Opened [RandomAccessFile]s must be closed using /// the [RandomAccessFile.close] method. /// /// See [open] for information on the [mode] argument. /// /// Throws a [FileSystemException] if the operation fails. RandomAccessFile openSync({FileMode mode = FileMode.read}); /// Creates a new independent [Stream] for the contents of this file. /// /// If [start] is present, the file will be read from byte-offset [start]. /// Otherwise from the beginning (index 0). /// /// If [end] is present, only bytes up to byte-index [end] will be read. /// Otherwise, until end of file. /// /// In order to make sure that system resources are freed, the stream /// must be read to completion or the subscription on the stream must /// be cancelled. /// /// If [File] is a [named pipe](https://en.wikipedia.org/wiki/Named_pipe) /// then the returned [Stream] will wait until the write side of the pipe /// is closed before signaling "done". If there are no writers attached /// to the pipe when it is opened, then [Stream.listen] will wait until /// a writer opens the pipe. /// /// An error opening or reading the file will appear as a /// [FileSystemException] error event on the returned [Stream], after which /// the [Stream] is closed. For example: /// /// ```dart /// // This example will print the "Error reading file" message and the /// // `await for` loop will complete normally, without seeing any data /// // events. /// final stream = File('does-not-exist') /// .openRead() /// .handleError((e) => print('Error reading file: $e')); /// await for (final data in stream) { /// print(data); /// } /// ``` Stream> openRead([int? start, int? end]); /// Creates a new independent [IOSink] for the file. /// /// The [IOSink] must be closed when no longer used, to free /// system resources. /// /// An [IOSink] for a file can be opened in two modes: /// /// * [FileMode.write]: truncates the file to length zero. /// * [FileMode.append]: sets the initial write position to the end /// of the file. /// /// When writing strings through the returned [IOSink] the encoding /// specified using [encoding] will be used. The returned [IOSink] /// has an `encoding` property which can be changed after the /// [IOSink] has been created. /// /// The returned [IOSink] does not transform newline characters (`"\n"`) to /// the platform's conventional line ending (e.g. `"\r\n"` on Windows). Write /// a [Platform.lineTerminator] if a platform-specific line ending is needed. /// /// If an error occurs while opening or writing to the file, the [IOSink.done] /// [IOSink.flush], and [IOSink.close] futures will all complete with a /// [FileSystemException]. You must handle errors from the [IOSink.done] /// future or the error will be uncaught. /// /// For example, [FutureExtensions.ignore] the [IOSink.done] error and /// remember to `await` the [IOSink.flush] and [IOSink.close] calls within a /// `try`/`catch`: /// /// ```dart /// final sink = File('/tmp').openWrite(); // Can't write to /tmp /// sink.done.ignore(); /// sink.write("This is a test"); /// try { /// // If one of these isn't awaited, then errors will pass silently! /// await sink.flush(); /// await sink.close(); /// } on FileSystemException catch (e) { /// print('Error writing file: $e'); /// } /// ``` /// /// To handle errors asynchronously outside of the context of [IOSink.flush] /// and [IOSink.close], you can [Future.catchError] the [IOSink.done]. /// /// ```dart /// final sink = File('/tmp').openWrite(); // Can't write to /tmp /// sink.done.catchError((e) { /// // Handle the error. /// }); /// ``` IOSink openWrite({FileMode mode = FileMode.write, Encoding encoding = utf8}); /// Reads the entire file contents as a list of bytes. /// /// Returns a `Future` that completes with the list of bytes that /// is the contents of the file. Future readAsBytes(); /// Synchronously reads the entire file contents as a list of bytes. /// /// Throws a [FileSystemException] if the operation fails. Uint8List readAsBytesSync(); /// Reads the entire file contents as a string using the given /// [Encoding]. /// /// Returns a `Future` that completes with the string once /// the file contents has been read. Future readAsString({Encoding encoding = utf8}); /// Synchronously reads the entire file contents as a string using the /// given [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. String readAsStringSync({Encoding encoding = utf8}); /// Reads the entire file contents as lines of text using the given /// [Encoding]. /// /// Returns a `Future>` that completes with the lines /// once the file contents has been read. Future> readAsLines({Encoding encoding = utf8}); /// Synchronously reads the entire file contents as lines of text /// using the given [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. List readAsLinesSync({Encoding encoding = utf8}); /// Writes a list of bytes to a file. /// /// Opens the file, writes the list of bytes to it, and closes the file. /// Returns a `Future` that completes with this [File] object once /// the entire operation has completed. /// /// By default [writeAsBytes] creates the file for writing and truncates the /// file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// The elements of [bytes] should be integers in the range 0 to 255. /// Any integer, which is not in that range, is converted to a byte before /// being written. The conversion is equivalent to doing /// `value.toUnsigned(8)`. /// /// If the argument [flush] is set to `true`, the data written will be /// flushed to the file system before the returned future completes. Future writeAsBytes( List bytes, { FileMode mode = FileMode.write, bool flush = false, }); /// Synchronously writes a list of bytes to a file. /// /// Opens the file, writes the list of bytes to it and closes the file. /// /// By default [writeAsBytesSync] creates the file for writing and truncates /// the file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// The elements of [bytes] should be integers in the range 0 to 255. /// Any integer, which is not in that range, is converted to a byte before /// being written. The conversion is equivalent to doing /// `value.toUnsigned(8)`. /// /// If the [flush] argument is set to `true` data written will be /// flushed to the file system before returning. /// /// Throws a [FileSystemException] if the operation fails. void writeAsBytesSync( List bytes, { FileMode mode = FileMode.write, bool flush = false, }); /// Writes a string to a file. /// /// Opens the file, writes the string in the given encoding, and closes the /// file. Returns a `Future` that completes with this [File] object /// once the entire operation has completed. /// /// By default [writeAsString] creates the file for writing and truncates the /// file if it already exists. In order to append the bytes to an existing /// file, pass [FileMode.append] as the optional mode parameter. /// /// If the argument [flush] is set to `true`, the data written will be /// flushed to the file system before the returned future completes. /// /// This method does not transform newline characters (`"\n"`) to the /// platform conventional line ending (e.g. `"\r\n"` on Windows). Use /// [Platform.lineTerminator] to separate lines in [contents] if platform /// conventional line endings are needed. Future writeAsString( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }); /// Synchronously writes a string to a file. /// /// Opens the file, writes the string in the given encoding, and closes the /// file. /// /// By default [writeAsStringSync] creates the file for writing and /// truncates the file if it already exists. In order to append the bytes /// to an existing file, pass [FileMode.append] as the optional mode /// parameter. /// /// If the [flush] argument is set to `true`, data written will be /// flushed to the file system before returning. /// /// This method does not transform newline characters (`"\n"`) to the /// platform conventional line ending (e.g. `"\r\n"` on Windows). Use /// [Platform.lineTerminator] to separate lines in [contents] if platform /// conventional line endings are needed. /// /// Throws a [FileSystemException] if the operation fails. void writeAsStringSync( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }); /// Get the path of the file. String get path; } /// Random access to the data in a file. /// /// `RandomAccessFile` objects are obtained by calling the /// `open` method on a [File] object. /// /// A `RandomAccessFile` has both asynchronous and synchronous /// methods. The asynchronous methods all return a [Future] /// whereas the synchronous methods will return the result directly, /// and block the current isolate until the result is ready. /// /// At most one asynchronous method can be pending on a given `RandomAccessFile` /// instance at the time. If another asynchronous method is called when one is /// already in progress, a [FileSystemException] is thrown. /// /// If an asynchronous method is pending, it is also not possible to call any /// synchronous methods. This will also throw a [FileSystemException]. abstract interface class RandomAccessFile { /// Closes the file. /// /// Returns a [Future] that completes when it has been closed. Future close(); /// Synchronously closes the file. /// /// Throws a [FileSystemException] if the operation fails. void closeSync(); /// Reads a byte from the file. /// /// Returns a `Future` that completes with the byte, /// or with -1 if end-of-file has been reached. Future readByte(); /// Synchronously reads a single byte from the file. /// /// If end-of-file has been reached -1 is returned. /// /// Throws a [FileSystemException] if the operation fails. int readByteSync(); /// Reads up to [count] bytes from a file. /// /// May return fewer than [count] bytes. This can happen, for example, when /// reading past the end of a file or when reading from a pipe that does not /// currently contain additional data. /// /// An empty [Uint8List] will only be returned when reading past the end of /// the file or when [count] is `0`. Future read(int count); /// Synchronously reads up to [count] bytes from a file /// /// May return fewer than [count] bytes. This can happen, for example, when /// reading past the end of a file or when reading from a pipe that does not /// currently contain additional data. /// /// An empty [Uint8List] will only be returned when reading past the end of /// the file or when [count] is `0`. /// /// Throws a [FileSystemException] if the operation fails. Uint8List readSync(int count); /// Reads bytes into an existing [buffer]. /// /// Reads bytes and writes them into the range of [buffer] /// from [start] to [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns the number of bytes read. This maybe be less than `end - start` /// if the file doesn't have that many bytes to read. Future readInto(List buffer, [int start = 0, int? end]); /// Synchronously reads into an existing [buffer]. /// /// Reads bytes and writes them into the range of [buffer] /// from [start] to [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns the number of bytes read. This maybe be less than `end - start` /// if the file doesn't have that many bytes to read. /// /// Throws a [FileSystemException] if the operation fails. int readIntoSync(List buffer, [int start = 0, int? end]); /// Writes a single byte to the file. /// /// Returns a `Future` that completes with this /// random access file when the write completes. Future writeByte(int value); /// Synchronously writes a single byte to the file. /// /// Returns 1 on success. /// /// Throws a [FileSystemException] if the operation fails. int writeByteSync(int value); /// Writes from a [buffer] to the file. /// /// Will read the buffer from index [start] to index [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns a `Future` that completes with this /// [RandomAccessFile] when the write completes. Future writeFrom( List buffer, [ int start = 0, int? end, ]); /// Synchronously writes from a [buffer] to the file. /// /// Will read the buffer from index [start] to index [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Throws a [FileSystemException] if the operation fails. void writeFromSync(List buffer, [int start = 0, int? end]); /// Writes a string to the file using the given [Encoding]. /// /// Returns a `Future` that completes with this /// random access file when the write completes. Future writeString( String string, { Encoding encoding = utf8, }); /// Synchronously writes a single string to the file using the given /// [Encoding]. /// /// Throws a [FileSystemException] if the operation fails. void writeStringSync(String string, {Encoding encoding = utf8}); /// Gets the current byte position in the file. /// /// Returns a `Future` that completes with the position. Future position(); /// Synchronously gets the current byte position in the file. /// /// Throws a [FileSystemException] if the operation fails. int positionSync(); /// Sets the byte position in the file. /// /// Returns a `Future` that completes with this /// random access file when the position has been set. Future setPosition(int position); /// Synchronously sets the byte position in the file. /// /// Throws a [FileSystemException] if the operation fails. void setPositionSync(int position); /// Truncates (or extends) the file to [length] bytes. /// /// Returns a `Future` that completes with this /// random access file when the truncation has been performed. Future truncate(int length); /// Synchronously truncates (or extends) the file to [length] bytes. /// /// Throws a [FileSystemException] if the operation fails. void truncateSync(int length); /// Gets the length of the file. /// /// Returns a `Future` that completes with the length in bytes. Future length(); /// Synchronously gets the length of the file. /// /// Throws a [FileSystemException] if the operation fails. int lengthSync(); /// Flushes the contents of the file to disk. /// /// Returns a `Future` that completes with this /// random access file when the flush operation completes. Future flush(); /// Synchronously flushes the contents of the file to disk. /// /// Throws a [FileSystemException] if the operation fails. void flushSync(); /// Locks the file or part of the file. /// /// By default an exclusive lock will be obtained, but that can be overridden /// by the [mode] argument. /// /// Locks the byte range from [start] to [end] of the file, with the /// byte at position `end` not included. If no arguments are /// specified, the full file is locked, If only `start` is specified /// the file is locked from byte position `start` to the end of the /// file, no matter how large it grows. It is possible to specify an /// explicit value of `end` which is past the current length of the file. /// /// To obtain an exclusive lock on a file, it must be opened for writing. /// /// If [mode] is [FileLock.exclusive] or [FileLock.shared], an error is /// signaled if the lock cannot be obtained. If [mode] is /// [FileLock.blockingExclusive] or [FileLock.blockingShared], the /// returned [Future] is resolved only when the lock has been obtained. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// On Linux and OS X this uses advisory locks, which have the /// surprising semantics that all locks associated with a given file /// are removed when *any* file descriptor for that file is closed by /// the process. Note that this does not actually lock the file for /// access. Also note that advisory locks are on a process /// level. This means that several isolates in the same process can /// obtain an exclusive lock on the same file. /// /// On Windows the regions used for lock and unlock needs to match. If that /// is not the case unlocking will result in the OS error "The segment is /// already unlocked". /// /// On Windows, locking a file associates the lock with the specific file /// handle used to acquire the lock. If the same file is opened again with a /// different handle (even within the same process), attempts to write to the /// locked region using the new handle will fail. To ensure successful writes /// after locking, use the same [RandomAccessFile] object that acquired the /// lock for subsequent write operations. Future lock([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]); /// Synchronously locks the file or part of the file. /// /// By default an exclusive lock will be obtained, but that can be overridden /// by the [mode] argument. /// /// Locks the byte range from [start] to [end] of the file ,with the /// byte at position `end` not included. If no arguments are /// specified, the full file is locked, If only `start` is specified /// the file is locked from byte position `start` to the end of the /// file, no matter how large it grows. It is possible to specify an /// explicit value of `end` which is past the current length of the file. /// /// To obtain an exclusive lock on a file it must be opened for writing. /// /// If [mode] is [FileLock.exclusive] or [FileLock.shared], an exception is /// thrown if the lock cannot be obtained. If [mode] is /// [FileLock.blockingExclusive] or [FileLock.blockingShared], the /// call returns only after the lock has been obtained. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// On Linux and OS X this uses advisory locks, which have the /// surprising semantics that all locks associated with a given file /// are removed when *any* file descriptor for that file is closed by /// the process. Note that this does not actually lock the file for /// access. Also note that advisory locks are on a process /// level. This means that several isolates in the same process can /// obtain an exclusive lock on the same file. /// /// On Windows the regions used for lock and unlock needs to match. If that /// is not the case unlocking will result in the OS error "The segment is /// already unlocked". /// /// On Windows, locking a file associates the lock with the specific file /// handle used to acquire the lock. If the same file is opened again with a /// different handle (even within the same process), attempts to write to the /// locked region using the new handle will fail. To ensure successful writes /// after locking, use the same [RandomAccessFile] object that acquired the /// lock for subsequent write operations. void lockSync([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]); /// Unlocks the file or part of the file. /// /// Unlocks the byte range from [start] to [end] of the file, with /// the byte at position `end` not included. If no arguments are /// specified, the full file is unlocked, If only `start` is /// specified the file is unlocked from byte position `start` to the /// end of the file. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// See [lock] for more details. Future unlock([int start = 0, int end = -1]); /// Synchronously unlocks the file or part of the file. /// /// Unlocks the byte range from [start] to [end] of the file, with /// the byte at position `end` not included. If no arguments are /// specified, the full file is unlocked, If only `start` is /// specified the file is unlocked from byte position `start` to the /// end of the file. /// /// *NOTE* file locking does have slight differences in behavior across /// platforms: /// /// See [lockSync] for more details. void unlockSync([int start = 0, int end = -1]); /// Returns a human-readable string for this random access file. String toString(); /// The path of the file underlying this random access file. String get path; } /// Exception thrown when a file operation fails. @pragma("vm:entry-point") class FileSystemException implements IOException { /// Message describing the error. /// /// The message does not include any detailed information from /// the underlying OS error. Check [osError] for that information. final String message; /// The file system path on which the error occurred. /// /// Can be `null` if the exception does not relate directly /// to a file system path. final String? path; /// The underlying OS error. /// /// Can be `null` if the exception is not raised due to an OS error. final OSError? osError; /// Creates a new file system exception with optional parts. /// /// Creates an exception with [FileSystemException.message], /// [FileSystemException.path] and [FileSystemException.osError] /// values take from the optional parameters of the same name. /// /// The [message] and [path] path defaults to empty strings if omitted, /// and [osError] defaults to `null`. const FileSystemException([this.message = "", this.path = "", this.osError]); /// Create a new file system exception based on an [OSError.errorCode]. /// /// For example, if `errorCode == 2` then a [PathNotFoundException] /// will be returned. @pragma("vm:entry-point") factory FileSystemException._fromOSError( OSError err, String message, String path, ) { if (Platform.isWindows) { switch (err.errorCode) { case _errorAccessDenied: case _errorCurrentDirectory: case _errorWriteProtect: case _errorBadLength: case _errorSharingViolation: case _errorLockViolation: case _errorNetworkAccessDenied: case _errorDriveLocked: return PathAccessException(path, err, message); case _errorFileExists: case _errorAlreadyExists: return PathExistsException(path, err, message); case _errorFileNotFound: case _errorPathNotFound: case _errorInvalidDrive: case _errorInvalidName: case _errorNoMoreFiles: case _errorBadNetpath: case _errorBadNetName: case _errorBadPathName: case _errorFilenameExedRange: return PathNotFoundException(path, err, message); default: return FileSystemException(message, path, err); } } else { switch (err.errorCode) { case _ePerm: case _eAccess: return PathAccessException(path, err, message); case _eExist: return PathExistsException(path, err, message); case _eNoEnt: return PathNotFoundException(path, err, message); default: return FileSystemException(message, path, err); } } } String _toStringHelper(String className) { StringBuffer sb = StringBuffer(); sb.write(className); if (message.isNotEmpty) { sb.write(": $message"); if (path != null) { sb.write(", path = '$path'"); } if (osError != null) { sb.write(" ($osError)"); } } else if (osError != null) { sb.write(": $osError"); if (path != null) { sb.write(", path = '$path'"); } } else if (path != null) { sb.write(": $path"); } return sb.toString(); } String toString() { return _toStringHelper("FileSystemException"); } } /// Exception thrown when a file operation fails because the necessary access /// rights are not available. @Since("2.19") class PathAccessException extends FileSystemException { const PathAccessException(String path, OSError osError, [String message = ""]) : super(message, path, osError); String toString() => _toStringHelper("PathAccessException"); } /// Exception thrown when a file operation fails because the target path /// already exists. @Since("2.19") class PathExistsException extends FileSystemException { const PathExistsException(String path, OSError osError, [String message = ""]) : super(message, path, osError); String toString() => _toStringHelper("PathExistsException"); } /// Exception thrown when a file operation fails because a file or /// directory does not exist. @Since("2.19") class PathNotFoundException extends FileSystemException { const PathNotFoundException( String path, OSError osError, [ String message = "", ]) : super(message, path, osError); String toString() => _toStringHelper("PathNotFoundException"); } /// The "read" end of an [Pipe] created by [Pipe.create]. /// /// The read stream will continue to listen until the "write" end of the /// pipe (i.e. [Pipe.write]) is closed. /// /// ```dart /// final pipe = await Pipe.create(); /// pipe.read.transform(utf8.decoder).listen((data) { /// print(data); /// }, onDone: () => print('Done')); /// ``` abstract interface class ReadPipe implements Stream> {} /// The "write" end of an [Pipe] created by [Pipe.create]. /// /// ```dart /// final pipe = await Pipe.create(); /// pipe.write.add("Hello World!".codeUnits); /// await pipe.write.flush(); /// await pipe.write.close(); /// ``` abstract interface class WritePipe implements IOSink {} /// An anonymous pipe that can be used to send data in a single direction i.e. /// data written to [write] can be read using [read]. /// /// On macOS and Linux (excluding Android), either the [read] or [write] /// portion of the pipe can be transmitted to another process and used for /// interprocess communication. /// /// For example: /// ```dart /// final pipe = await Pipe.create(); /// final socket = await RawSocket.connect(address, 0); /// socket.sendMessage([ /// SocketControlMessage.fromHandles( /// [ResourceHandle.fromReadPipe(pipe.read)]) /// ], 'Hello'.codeUnits); /// pipe.write.add('Hello over pipe!'.codeUnits); /// await pipe.write.flush(); /// await pipe.write.close(); /// ``` abstract interface class Pipe { /// The read end of the [Pipe]. ReadPipe get read; /// The write end of the [Pipe]. WritePipe get write; // Create an anonymous pipe. static Future create() { return _Pipe.create(); } /// Synchronously creates an anonymous pipe. factory Pipe.createSync() { return _Pipe.createSync(); } } „ÚNN</4-CK.=C/>K28C8(3/2!7$:(.BA2S# #QQFK,3L8HQ6EAP2 ? F?6AEH8.A %-AN (5 &A #E%FH> ##74'4IN; %(E%SWW'h#LM<KFI!8')G;/M0''3)JO3KCPI+PI&B9QK)FN8=E.*>;:->GFJ% GF=GHM''->;:->CFJ) GJCGG0#!MNMG LIHKOE8G=)!MNM? LIHKOE8?->;.)=HB%&>;,=G2!G5=11:/*R?=#*ATA=4J@)B@+.R?=#.ATA=4J@)BG+3JG'*4IGL=BAKH+9=>HM.K$HHLLJJL;3+D3&8>L;3+=C>D3(G=C2@@=*D968=&9DA1G=!#G1=0D=$>=*PG?GFGLLJG??CAJ(PJF,!%!%"( :":!!!  &<: ::<:-&&!"&3N8Q%?I8Q%?C:&A:I( &6%B; &.8O6IK  &8/&B2 "#!/dart:io/file.dartÀ@ñÀh³ÀAÀeœœForg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/file_impl.dartÀˆÇ// Copyright (c) 2013, 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:io"; // Read the file in blocks of size 64k. const int _blockSize = 64 * 1024; // The maximum number of bytes to read in a single call to `read`. // // On Windows and macOS, it is an error to call // `read/_read(fildes, buf, nbyte)` with `nbyte >= INT_MAX`. // // The POSIX specification states that the behavior of `read` is // implementation-defined if `nbyte > SSIZE_MAX`. On Linux, the `read` will // transfer at most 0x7ffff000 bytes and return the number of bytes actually. // transfered. // // A smaller value has the advantage of: // 1. making vm-service clients (e.g. debugger) more responsive // 2. reducing memory overhead (since `readInto` copies memory) // // A bigger value reduces the number of system calls. const int _maxReadSize = 16 * 1024 * 1024; // 16MB. class _FileStream extends Stream> { // Stream controller. late StreamController _controller; // Information about the underlying file. String? _path; RandomAccessFile? _openedFile; int _position; int? _end; final Completer _closeCompleter = new Completer(); // Has the stream been paused or unsubscribed? bool _unsubscribed = false; // Is there a read currently in progress? bool _readInProgress = true; bool _closed = false; bool _atEnd = false; _FileStream(this._path, int? position, this._end) : _position = position ?? 0; _FileStream.forStdin() : _position = 0; _FileStream.forRandomAccessFile(RandomAccessFile f) : _position = 0, _openedFile = f; StreamSubscription listen( void onData(Uint8List event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { _controller = new StreamController( sync: true, onListen: _start, onResume: _readBlock, onCancel: () { _unsubscribed = true; return _closeFile(); }, ); return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } Future _closeFile() { if (_readInProgress || _closed) { return _closeCompleter.future; } _closed = true; void done() { _closeCompleter.complete(); _controller.close(); } _openedFile!.close().catchError(_controller.addError).whenComplete(done); return _closeCompleter.future; } void _readBlock() { // Don't start a new read if one is already in progress. if (_readInProgress) return; if (_atEnd) { _closeFile(); return; } _readInProgress = true; int readBytes = _blockSize; final end = _end; if (end != null) { readBytes = min(readBytes, end - _position); if (readBytes < 0) { _readInProgress = false; if (!_unsubscribed) { _controller.addError(new RangeError("Bad end position: $end")); _closeFile(); _unsubscribed = true; } return; } } _openedFile! .read(readBytes) .then((block) { _readInProgress = false; if (_unsubscribed) { _closeFile(); return; } _position += block.length; // read() may return less than `readBytes` if `_openFile` is a pipe or // terminal or if a signal is received. Only a empty return indicates // that the write side of the pipe is closed or that we are at the end // of a file. // See https://man7.org/linux/man-pages/man2/read.2.html if (block.length == 0 || (_end != null && _position == _end)) { _atEnd = true; } if (!_atEnd && !_controller.isPaused) { _readBlock(); } if (block.length > 0) { _controller.add(block); } if (_atEnd) { _closeFile(); } }) .catchError((e, s) { if (!_unsubscribed) { _controller.addError(e, s); _closeFile(); _unsubscribed = true; } }); } void _start() { if (_position < 0) { _controller.addError(new RangeError("Bad start position: $_position")); _controller.close(); _closeCompleter.complete(); return; } void onReady(RandomAccessFile file) { _openedFile = file; _readInProgress = false; _readBlock(); } void onOpenFile(RandomAccessFile file) { if (_position > 0) { file .setPosition(_position) .then( onReady, onError: (e, s) { _controller.addError(e, s); _readInProgress = false; _closeFile(); }, ); } else { onReady(file); } } void openFailed(error, stackTrace) { _controller.addError(error, stackTrace); _controller.close(); _closeCompleter.complete(); } final path = _path; final openedFile = _openedFile; if (openedFile != null) { onOpenFile(openedFile); } else if (path != null) { new File( path, ).open(mode: FileMode.read).then(onOpenFile, onError: openFailed); } else { try { onOpenFile(_File._openStdioSync(0)); } catch (e, s) { openFailed(e, s); } } } } class _FileStreamConsumer implements StreamConsumer> { File? _file; Future _openFuture; _FileStreamConsumer(File file, FileMode mode) : _file = file, _openFuture = file.open(mode: mode); _FileStreamConsumer._fromStdio(int fd) : _openFuture = new Future.value(_File._openStdioSync(fd)); _FileStreamConsumer.fromRandomAccessFile(RandomAccessFile f) : _openFuture = Future.value(f); Future addStream(Stream> stream) { Completer completer = new Completer.sync(); _openFuture .then((openedFile) { late StreamSubscription> _subscription; void error(e, StackTrace stackTrace) { _subscription.cancel(); openedFile.close(); completer.completeError(e, stackTrace); } _subscription = stream.listen( (d) { _subscription.pause(); try { openedFile .writeFrom(d, 0, d.length) .then((_) => _subscription.resume(), onError: error); } catch (e, stackTrace) { error(e, stackTrace); } }, onDone: () { completer.complete(_file); }, onError: error, cancelOnError: true, ); }) .catchError(completer.completeError); return completer.future; } Future close() => _openFuture.then((openedFile) => openedFile.close()).then((_) => _file); } // Class for encapsulating the native implementation of files. class _File extends FileSystemEntity implements File { final String _path; final Uint8List _rawPath; _File(this._path) : _rawPath = FileSystemEntity._toUtf8Array(_path); _File.fromRawPath(Uint8List rawPath) : _rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawPath), _path = FileSystemEntity._toStringFromUtf8Array(rawPath); String get path => _path; // WARNING: // Calling this function will increase the reference count on the native // namespace object. It should only be called to pass the pointer to the // IOService, which will decrement the reference count when it is finished // with it. static int _namespacePointer() => _Namespace._namespacePointer; static Future _dispatchWithNamespace(int request, List data) { data[0] = _namespacePointer(); return _IOService._dispatch(request, data); } Future exists() { return _dispatchWithNamespace(_IOService.fileExists, [null, _rawPath]).then( (response) { _checkForErrorResponse(response, "Cannot check existence", path); return response as bool; }, ); } external static _exists(_Namespace namespace, Uint8List rawPath); bool existsSync() { var result = _exists(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot check existence of file", path); return result; } File get absolute => new File(_absolutePath); Future create({bool recursive = false, bool exclusive = false}) { var result = recursive ? parent.create(recursive: true) : new Future.value(null); return result .then( (_) => _dispatchWithNamespace(_IOService.fileCreate, [ null, _rawPath, exclusive, ]), ) .then((response) { _checkForErrorResponse(response, "Cannot create file", path); return this; }); } external static _create( _Namespace namespace, Uint8List rawPath, bool exclusive, ); external static _createLink( _Namespace namespace, Uint8List rawPath, String target, ); external static List _createPipe(_Namespace namespace); external static _linkTarget(_Namespace namespace, Uint8List rawPath); void createSync({bool recursive = false, bool exclusive = false}) { if (recursive) { parent.createSync(recursive: true); } var result = _create(_Namespace._namespace, _rawPath, exclusive); throwIfError(result, "Cannot create file", path); } Future _delete({bool recursive = false}) { if (recursive) { return new Directory(path).delete(recursive: true).then((_) => this); } return _dispatchWithNamespace(_IOService.fileDelete, [null, _rawPath]).then( (response) { _checkForErrorResponse(response, "Cannot delete file", path); return this; }, ); } external static _deleteNative(_Namespace namespace, Uint8List rawPath); external static _deleteLinkNative(_Namespace namespace, Uint8List rawPath); void _deleteSync({bool recursive = false}) { if (recursive) { return new Directory.fromRawPath(_rawPath).deleteSync(recursive: true); } var result = _deleteNative(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot delete file", path); } Future rename(String newPath) { return _dispatchWithNamespace(_IOService.fileRename, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse( response, "Cannot rename file to '$newPath'", path, ); return new File(newPath); }); } external static _rename( _Namespace namespace, Uint8List oldPath, String newPath, ); external static _renameLink( _Namespace namespace, Uint8List oldPath, String newPath, ); File renameSync(String newPath) { var result = _rename(_Namespace._namespace, _rawPath, newPath); throwIfError(result, "Cannot rename file to '$newPath'", path); return new File(newPath); } Future copy(String newPath) { return _dispatchWithNamespace(_IOService.fileCopy, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse(response, "Cannot copy file to '$newPath'", path); return new File(newPath); }); } external static _copy( _Namespace namespace, Uint8List oldPath, String newPath, ); File copySync(String newPath) { var result = _copy(_Namespace._namespace, _rawPath, newPath); throwIfError(result, "Cannot copy file to '$newPath'", path); return new File(newPath); } Future open({FileMode mode = FileMode.read}) { if (mode != FileMode.read && mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { return new Future.error( new ArgumentError('Invalid file mode for this operation'), ); } return _dispatchWithNamespace(_IOService.fileOpen, [ null, _rawPath, mode._mode, ]).then((response) { _checkForErrorResponse(response, "Cannot open file", path); return _RandomAccessFile(response as int, path); }); } Future length() { return _dispatchWithNamespace(_IOService.fileLengthFromPath, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot retrieve length of file", path); return response as int; }); } external static _lengthFromPath(_Namespace namespace, Uint8List rawPath); int lengthSync() { var result = _lengthFromPath(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot retrieve length of file", path); return result; } Future lastAccessed() { return _dispatchWithNamespace(_IOService.fileLastAccessed, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot retrieve access time", path); return DateTime.fromMillisecondsSinceEpoch(response as int); }); } external static _lastAccessed(_Namespace namespace, Uint8List rawPath); DateTime lastAccessedSync() { var ms = _lastAccessed(_Namespace._namespace, _rawPath); throwIfError(ms, "Cannot retrieve access time", path); return new DateTime.fromMillisecondsSinceEpoch(ms); } Future setLastAccessed(DateTime time) { int millis = time.millisecondsSinceEpoch; return _dispatchWithNamespace(_IOService.fileSetLastAccessed, [ null, _rawPath, millis, ]).then((response) { _checkForErrorResponse(response, "Cannot set access time", path); return null; }); } external static _setLastAccessed( _Namespace namespace, Uint8List rawPath, int millis, ); void setLastAccessedSync(DateTime time) { int millis = time.millisecondsSinceEpoch; var result = _setLastAccessed(_Namespace._namespace, _rawPath, millis); if (result is OSError) { throw new FileSystemException( "Failed to set file access time", path, result, ); } } Future lastModified() { return _dispatchWithNamespace(_IOService.fileLastModified, [ null, _rawPath, ]).then((response) { _checkForErrorResponse( response, "Cannot retrieve modification time", path, ); return DateTime.fromMillisecondsSinceEpoch(response as int); }); } external static _lastModified(_Namespace namespace, Uint8List rawPath); DateTime lastModifiedSync() { var ms = _lastModified(_Namespace._namespace, _rawPath); throwIfError(ms, "Cannot retrieve modification time", path); return new DateTime.fromMillisecondsSinceEpoch(ms); } Future setLastModified(DateTime time) { int millis = time.millisecondsSinceEpoch; return _dispatchWithNamespace(_IOService.fileSetLastModified, [ null, _rawPath, millis, ]).then((response) { _checkForErrorResponse(response, "Cannot set modification time", path); return null; }); } external static _setLastModified( _Namespace namespace, Uint8List rawPath, int millis, ); void setLastModifiedSync(DateTime time) { int millis = time.millisecondsSinceEpoch; var result = _setLastModified(_Namespace._namespace, _rawPath, millis); if (result is OSError) { throw new FileSystemException( "Failed to set file modification time", path, result, ); } } external static _open(_Namespace namespace, Uint8List rawPath, int mode); RandomAccessFile openSync({FileMode mode = FileMode.read}) { if (mode != FileMode.read && mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { throw new ArgumentError('Invalid file mode for this operation'); } var id = _open(_Namespace._namespace, _rawPath, mode._mode); throwIfError(id, "Cannot open file", path); return new _RandomAccessFile(id, _path); } external static int _openStdio(int fd); static RandomAccessFile _openStdioSync(int fd) { var id = _openStdio(fd); if (id == 0) { throw new FileSystemException("Cannot open stdio file for: $fd"); } return new _RandomAccessFile(id, ""); } Stream> openRead([int? start, int? end]) { return new _FileStream(path, start, end); } IOSink openWrite({FileMode mode = FileMode.write, Encoding encoding = utf8}) { if (mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { throw new ArgumentError('Invalid file mode for this operation'); } var consumer = new _FileStreamConsumer(this, mode); return new IOSink(consumer, encoding: encoding); } Future readAsBytes() { Future readUnsized(RandomAccessFile file) { var builder = new BytesBuilder(copy: false); var completer = new Completer(); void read() { file.read(_blockSize).then((data) { if (data.length > 0) { builder.add(data); read(); } else { completer.complete(builder.takeBytes()); } }, onError: completer.completeError); } read(); return completer.future; } Future readSized(RandomAccessFile file, int length) { var data = Uint8List(length); var offset = 0; var completer = new Completer(); void read() { file.readInto(data, offset, min(offset + _maxReadSize, length)).then(( readSize, ) { if (readSize > 0) { offset += readSize; read(); } else { assert(readSize == 0); if (offset < length) { data = Uint8List.sublistView(data, 0, offset); } completer.complete(data); } }, onError: completer.completeError); } read(); return completer.future; } return open().then((file) { return file .length() .then((length) { if (length == 0) { // May be character device, try to read it in chunks. return readUnsized(file); } return readSized(file, length); }) .whenComplete(file.close); }); } Uint8List readAsBytesSync() { var opened = openSync(); try { Uint8List data; var length = opened.lengthSync(); if (length == 0) { // May be character device, try to read it in chunks. var builder = new BytesBuilder(copy: false); do { data = opened.readSync(_blockSize); if (data.length > 0) { builder.add(data); } } while (data.length > 0); data = builder.takeBytes(); } else { data = Uint8List(length); var offset = 0; while (offset < length) { final readSize = opened.readIntoSync( data, offset, min(offset + _maxReadSize, length), ); if (readSize == 0) { break; } offset += readSize; } if (offset < length) { data = Uint8List.view(data.buffer, 0, offset); } } return data; } finally { opened.closeSync(); } } String _tryDecode(List bytes, Encoding encoding) { try { return encoding.decode(bytes); } catch (_) { throw new FileSystemException( "Failed to decode data using encoding '${encoding.name}'", path, ); } } Future readAsString({Encoding encoding = utf8}) async => _tryDecode(await readAsBytes(), encoding); String readAsStringSync({Encoding encoding = utf8}) => _tryDecode(readAsBytesSync(), encoding); Future> readAsLines({Encoding encoding = utf8}) => readAsString(encoding: encoding).then(const LineSplitter().convert); List readAsLinesSync({Encoding encoding = utf8}) => const LineSplitter().convert(readAsStringSync(encoding: encoding)); Future writeAsBytes( List bytes, { FileMode mode = FileMode.write, bool flush = false, }) { return open(mode: mode).then((file) { return file .writeFrom(bytes, 0, bytes.length) .then((_) { if (flush) return file.flush().then((_) => this); return this; }) .whenComplete(file.close); }); } void writeAsBytesSync( List bytes, { FileMode mode = FileMode.write, bool flush = false, }) { RandomAccessFile opened = openSync(mode: mode); try { opened.writeFromSync(bytes, 0, bytes.length); if (flush) opened.flushSync(); } finally { opened.closeSync(); } } Future writeAsString( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }) { try { return writeAsBytes(encoding.encode(contents), mode: mode, flush: flush); } catch (e) { return new Future.error(e); } } void writeAsStringSync( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }) { writeAsBytesSync(encoding.encode(contents), mode: mode, flush: flush); } String toString() => "File: '$path'"; static throwIfError(Object result, String msg, String path) { if (result is OSError) { throw FileSystemException._fromOSError(result, msg, path); } } } abstract class _RandomAccessFileOps { external factory _RandomAccessFileOps._(int pointer); int _getPointer(); int get fd; int close(); readByte(); read(int bytes); readInto(List buffer, int start, int? end); writeByte(int value); writeFrom(List buffer, int start, int? end); position(); setPosition(int position); truncate(int length); length(); flush(); lock(int lock, int start, int end); } @pragma("vm:entry-point") class _RandomAccessFile implements RandomAccessFile { static bool _connectedResourceHandler = false; final String path; bool _asyncDispatched = false; late _FileResourceInfo _resourceInfo; _RandomAccessFileOps _ops; @pragma("vm:entry-point") _RandomAccessFile(int pointer, this.path) : _ops = new _RandomAccessFileOps._(pointer) { _resourceInfo = new _FileResourceInfo(this); _maybeConnectHandler(); } void _maybePerformCleanup() { if (closed) { _FileResourceInfo.fileClosed(_resourceInfo); } } _maybeConnectHandler() { if (!const bool.fromEnvironment("dart.vm.product") && !_connectedResourceHandler) { // TODO(ricow): We probably need to set these in some initialization code. // We need to make sure that these are always available from the // observatory even if no files (or sockets for the socket ones) are // open. registerExtension( 'ext.dart.io.getOpenFiles', _FileResourceInfo.getOpenFiles, ); registerExtension( 'ext.dart.io.getOpenFileById', _FileResourceInfo.getOpenFileInfoMapByID, ); _connectedResourceHandler = true; } } Future close() { return _dispatch(_IOService.fileClose, [null], markClosed: true).then(( result, ) { if (result == -1) { throw new FileSystemException("Cannot close file", path); } closed = closed || (result == 0); _maybePerformCleanup(); }); } void closeSync() { _checkAvailable(); var id = _ops.close(); if (id == -1) { throw new FileSystemException("Cannot close file", path); } closed = closed || (id == 0); _maybePerformCleanup(); } Future readByte() { return _dispatch(_IOService.fileReadByte, [null]).then((response) { _checkForErrorResponse(response, "readByte failed", path); _resourceInfo.addRead(1); return response as int; }); } int readByteSync() { _checkAvailable(); var result = _ops.readByte(); if (result is OSError) { throw new FileSystemException("readByte failed", path, result); } _resourceInfo.addRead(1); return result; } Future read(int bytes) { return _dispatch(_IOService.fileRead, [null, bytes]).then((response) { _checkForErrorResponse(response, "read failed", path); var result = (response as List)[1] as Uint8List; _resourceInfo.addRead(result.length); return result; }); } Uint8List readSync(int bytes) { _checkAvailable(); var result = _ops.read(bytes); if (result is! Uint8List) { throw new FileSystemException("readSync failed", path, result as OSError); } _resourceInfo.addRead(result.length); return result; } Future readInto(List buffer, [int start = 0, int? end]) { end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return new Future.value(0); } int length = end - start; return _dispatch(_IOService.fileReadInto, [null, length]).then((response) { _checkForErrorResponse(response, "readInto failed", path); var responseList = response as List; var read = responseList[1] as int; var data = responseList[2] as List; buffer.setRange(start, start + read, data); _resourceInfo.addRead(read); return read; }); } int readIntoSync(List buffer, [int start = 0, int? end]) { _checkAvailable(); end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return 0; } var result = _ops.readInto(buffer, start, end); if (result is OSError) { throw new FileSystemException("readInto failed", path, result); } _resourceInfo.addRead(result); return result; } Future writeByte(int value) { return _dispatch(_IOService.fileWriteByte, [null, value]).then((response) { _checkForErrorResponse(response, "writeByte failed", path); _resourceInfo.addWrite(1); return this; }); } int writeByteSync(int value) { _checkAvailable(); var result = _ops.writeByte(value); if (result is OSError) { throw new FileSystemException("writeByte failed", path, result); } _resourceInfo.addWrite(1); return result; } Future writeFrom( List buffer, [ int start = 0, int? end, ]) { end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return new Future.value(this); } _BufferAndStart result; try { result = _ensureFastAndSerializableByteData(buffer, start, end); } catch (e) { return new Future.error(e); } List request = new List.filled(4, null); request[0] = null; request[1] = result.buffer; request[2] = result.start; request[3] = end - (start - result.start); return _dispatch(_IOService.fileWriteFrom, request).then((response) { _checkForErrorResponse(response, "writeFrom failed", path); _resourceInfo.addWrite(end! - (start - result.start)); return this; }); } void writeFromSync(List buffer, [int start = 0, int? end]) { _checkAvailable(); end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return; } _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, start, end, ); var result = _ops.writeFrom( bufferAndStart.buffer, bufferAndStart.start, end - (start - bufferAndStart.start), ); if (result is OSError) { throw new FileSystemException("writeFrom failed", path, result); } _resourceInfo.addWrite(end - (start - bufferAndStart.start)); } Future writeString( String string, { Encoding encoding = utf8, }) { var data = encoding.encode(string); return writeFrom(data, 0, data.length); } void writeStringSync(String string, {Encoding encoding = utf8}) { var data = encoding.encode(string); writeFromSync(data, 0, data.length); } Future position() { return _dispatch(_IOService.filePosition, [null]).then((response) { _checkForErrorResponse(response, "position failed", path); return response as int; }); } int positionSync() { _checkAvailable(); var result = _ops.position(); if (result is OSError) { throw new FileSystemException("position failed", path, result); } return result; } Future setPosition(int position) { return _dispatch(_IOService.fileSetPosition, [null, position]).then(( response, ) { _checkForErrorResponse(response, "setPosition failed", path); return this; }); } void setPositionSync(int position) { _checkAvailable(); var result = _ops.setPosition(position); if (result is OSError) { throw new FileSystemException("setPosition failed", path, result); } } Future truncate(int length) { return _dispatch(_IOService.fileTruncate, [null, length]).then((response) { _checkForErrorResponse(response, "truncate failed", path); return this; }); } void truncateSync(int length) { _checkAvailable(); var result = _ops.truncate(length); if (result is OSError) { throw new FileSystemException("truncate failed", path, result); } } Future length() { return _dispatch(_IOService.fileLength, [null]).then((response) { _checkForErrorResponse(response, "length failed", path); return response as int; }); } int lengthSync() { _checkAvailable(); var result = _ops.length(); if (result is OSError) { throw new FileSystemException("length failed", path, result); } return result; } Future flush() { return _dispatch(_IOService.fileFlush, [null]).then((response) { _checkForErrorResponse(response, "flush failed", path); return this; }); } void flushSync() { _checkAvailable(); var result = _ops.flush(); if (result is OSError) { throw new FileSystemException("flush failed", path, result); } } static const int lockUnlock = 0; // static const int lockShared = 1; // static const int lockExclusive = 2; // static const int lockBlockingShared = 3; // static const int lockBlockingExclusive = 4; int _fileLockValue(FileLock fl) => fl._type; Future lock([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]) { if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { throw new ArgumentError(); } int lock = _fileLockValue(mode); return _dispatch(_IOService.fileLock, [null, lock, start, end]).then(( response, ) { _checkForErrorResponse(response, 'lock failed', path); return this; }); } Future unlock([int start = 0, int end = -1]) { if (start == end) { throw new ArgumentError(); } return _dispatch(_IOService.fileLock, [null, lockUnlock, start, end]).then(( response, ) { _checkForErrorResponse(response, 'unlock failed', path); return this; }); } void lockSync([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]) { _checkAvailable(); if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { throw new ArgumentError(); } int lock = _fileLockValue(mode); var result = _ops.lock(lock, start, end); if (result is OSError) { throw new FileSystemException('lock failed', path, result); } } void unlockSync([int start = 0, int end = -1]) { _checkAvailable(); if (start == end) { throw new ArgumentError(); } var result = _ops.lock(lockUnlock, start, end); if (result is OSError) { throw new FileSystemException('unlock failed', path, result); } } bool closed = false; int get fd => _ops.fd; // WARNING: // Calling this function will increase the reference count on the native // object that implements the file operations. It should only be called to // pass the pointer to the IO Service, which will decrement the reference // count when it is finished with it. int _pointer() => _ops._getPointer(); Future _dispatch(int request, List data, {bool markClosed = false}) { if (closed) { return new Future.error(new FileSystemException("File closed", path)); } if (_asyncDispatched) { var msg = "An async operation is currently pending"; return new Future.error(new FileSystemException(msg, path)); } if (markClosed) { // Set closed to true to ensure that no more async requests can be issued // for this file. closed = true; } _asyncDispatched = true; data[0] = _pointer(); return _IOService._dispatch(request, data).whenComplete(() { _asyncDispatched = false; }); } void _checkAvailable() { if (_asyncDispatched) { throw new FileSystemException( "An async operation is currently pending", path, ); } if (closed) { throw new FileSystemException("File closed", path); } } } class _ReadPipe extends _FileStream implements ReadPipe { _ReadPipe(RandomAccessFile file) : super.forRandomAccessFile(file); } class _WritePipe extends _IOSinkImpl implements WritePipe { RandomAccessFile _file; _WritePipe(file) : this._file = file, super(_FileStreamConsumer.fromRandomAccessFile(file), utf8); } class _Pipe implements Pipe { final ReadPipe _readPipe; final WritePipe _writePipe; ReadPipe get read => _readPipe; WritePipe get write => _writePipe; _Pipe(this._readPipe, this._writePipe); static Future<_Pipe> create() { final completer = Completer<_Pipe>.sync(); _File._dispatchWithNamespace(_IOService.fileCreatePipe, [null]).then(( response, ) { final filePointers = (response as List).cast(); completer.complete( _Pipe( _ReadPipe(_RandomAccessFile(filePointers[0], '')), _WritePipe(_RandomAccessFile(filePointers[1], '')), ), ); }); return completer.future; } factory _Pipe.createSync() { final filePointers = _File._createPipe(_Namespace._namespace); return _Pipe( _ReadPipe(_RandomAccessFile(filePointers[0] as int, '')), _WritePipe(_RandomAccessFile(filePointers[1] as int, '')), ); } } „âNN<("C0=ALN)@@64.0,! 51,Q*6(%3 &$&%"N#=! 3!J # %QPQCJ 2 "$   (" N"*- $ ,))/"$I -A(0+)@?%6><1$ 4 )%/J(&)! .O?7G'G@KKMBJ#0QJ! D;B0J)"A H CHF*F63LQF JN/NA6(; , $DD&9 P "BBC!"#&,C 9 B7C PLCB$A MCJ =;8*.D H$,.L%* $A - CJ =A8*.D N$,.L%0 L?!"#&,GA0-*3H*7.Q"#&,G85$;32,!5 .E$2O  ##=& . D(, %  (>5 .! #$""00   9 : %%C C19/BK>J$*-> %$4 4%$ P"$K(@A&834 &61!(,31 3:&QGK$( '2 (LB(@"HA "F&K=@,"# Q*FA"PA4)/2#BA4F#2PB!!(G&A% G"6 /JB=DAI !,GB((,D()HA"F7JD'-I2PA"(FF? D%E>C#&).1/"(H!%K=C!Q?(H!%.B3!4DKML((QM;CPA %3 ::F<C"%*"/K;=> C@Adart:io/file_impl.dart‘.‚»Àh³ÀeUorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/file_patch.dartÀWã// 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 "common_patch.dart"; @patch class _File { @patch @pragma("vm:external-name", "File_Exists") external static _exists(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_Create") external static _create( _Namespace namespace, Uint8List rawPath, bool exclusive, ); @patch @pragma("vm:external-name", "File_CreateLink") external static _createLink( _Namespace namespace, Uint8List rawPath, String target, ); @patch @pragma("vm:external-name", "File_CreatePipe") external static List _createPipe(_Namespace namespace); @patch @pragma("vm:external-name", "File_LinkTarget") external static _linkTarget(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_Delete") external static _deleteNative(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_DeleteLink") external static _deleteLinkNative(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_Rename") external static _rename( _Namespace namespace, Uint8List oldPath, String newPath, ); @patch @pragma("vm:external-name", "File_RenameLink") external static _renameLink( _Namespace namespace, Uint8List oldPath, String newPath, ); @patch @pragma("vm:external-name", "File_Copy") external static _copy( _Namespace namespace, Uint8List oldPath, String newPath, ); @patch @pragma("vm:external-name", "File_LengthFromPath") external static _lengthFromPath(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_LastModified") external static _lastModified(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_SetLastModified") external static _setLastModified( _Namespace namespace, Uint8List rawPath, int millis, ); @patch @pragma("vm:external-name", "File_LastAccessed") external static _lastAccessed(_Namespace namespace, Uint8List rawPath); @patch @pragma("vm:external-name", "File_SetLastAccessed") external static _setLastAccessed( _Namespace namespace, Uint8List rawPath, int millis, ); @patch @pragma("vm:external-name", "File_Open") external static _open(_Namespace namespace, Uint8List rawPath, int mode); @patch @pragma("vm:external-name", "File_OpenStdio") external static int _openStdio(int fd); } @patch class _RandomAccessFileOps { @patch factory _RandomAccessFileOps._(int pointer) => _RandomAccessFileOpsImpl._().._setPointer(pointer); } @pragma("vm:entry-point") base class _RandomAccessFileOpsImpl extends NativeFieldWrapperClass1 implements _RandomAccessFileOps { _RandomAccessFileOpsImpl._(); @pragma("vm:external-name", "File_SetPointer") external void _setPointer(int pointer); @pragma("vm:external-name", "File_GetPointer") external int _getPointer(); @pragma("vm:external-name", "File_GetFD") external int get fd; @pragma("vm:external-name", "File_Close") external int close(); @pragma("vm:external-name", "File_ReadByte") external readByte(); @pragma("vm:external-name", "File_Read") external read(int bytes); @pragma("vm:external-name", "File_ReadInto") external readInto(List buffer, int start, int? end); @pragma("vm:external-name", "File_WriteByte") external writeByte(int value); @pragma("vm:external-name", "File_WriteFrom") external writeFrom(List buffer, int start, int? end); @pragma("vm:external-name", "File_Position") external position(); @pragma("vm:external-name", "File_SetPosition") external setPosition(int position); @pragma("vm:external-name", "File_Truncate") external truncate(int length); @pragma("vm:external-name", "File_Length") external length(); @pragma("vm:external-name", "File_Flush") external flush(); @pragma("vm:external-name", "File_Lock") external lock(int lock, int start, int end); } class _WatchedPath implements ffi.Finalizable { /// Path ID returned by [_FileSystemWatcher._watchPath]. /// /// Will remain valid until either [_unwatchPath] is called. final int pathId; final String path; final int events; /// Listeners subscribed to [FileSystemEvent] occuring at [path]. /// /// Once the last listener is unsubscribed the underlying watcher will /// stop monitoring changes at [path]. final List> listeners = >[]; bool isClosed = false; /// Source of [_NativeFSEvent] for this path. /// /// This subscription will be cancelled once the last listener is gone. /// /// Might be `null` if events for this path are delivered over multiplexed /// stream (see [_InotifyFileSystemWatcher]). StreamSubscription>? source; /// Finalizer associated with [_WatchedPath] instances. /// /// [_FileSystemWatcher._watchPathImpl] returns `pathId` values which behave /// slightly differently on different OSes. /// /// * On Mac OS X `pathId` values which are actually pointers to `Node` /// objects. These objects need to be released by calling /// [_FileSystemWatcher._unwatchPath] - otherwise they will leak. /// Attaching a [NativeFinalizer] ensures that even if Isolate exits /// abruptly via [Isolate.exit] we will still free these objects. /// * On Linux `pathId` is a _watch descriptor_ (returned by /// `inotify_add_watch`) associated with a specific inotify instance. /// Inotify instances are created by [_FileSystemWatcher._initWatcher] (see /// `inotify_init`) which returns a file descriptor. This file descriptor is /// wrapped in a [_NativeSocket] by /// [_FileSystemWatcher._eventsStreamFromSocket]. Socket takes ownership of /// the file descriptor and has a finalizer attached to itself. This /// finalizer will close the descriptor when socket is garbage collected. /// Thus there is no need to associate a separate finalizer with /// [_WatchedPath]. /// * Windows is a mixture of Linux and Mac OS X: `pathId` itself is a /// pointer to a socket-like `DirectoryWatchHandle`. It is wrapped in a /// socket by [_FileSystemWatcher._eventsStreamFromSocket] by we do not /// allow the socket to take full ownership of the `pathId` handle, because /// we want to guarantee that invariant that `pathId` remains valid /// until we explicitly call `_FileSystemWatcher._unwatchPath`. To ensure /// this we explicitly retain `DirectoryWatchHandle` before returning it /// from [_FileSystemWatcher._initWatcher]. This will keep the handle /// alive even after event handler is done with it. This however means that /// [_FileSystemWatcher._unwatchPath] must be called to release the handle. /// Thus we need to attach a finalizer to [_WatchedPath] to guarantee that. static final ffi.NativeFinalizer? finalizer = Platform.isMacOS || Platform.isWindows ? ffi.NativeFinalizer( ffi.Native.addressOf(_FileSystemWatcher._destroyWatch), ) : null; _WatchedPath(this.pathId, this.path, this.events) { finalizer?.attach(this, .fromAddress(pathId), detach: this); } FutureOr dispose() { assert(listeners.isEmpty); isClosed = true; finalizer?.detach(this); return source?.cancel(); } void add(FileSystemEvent event) { if (isClosed) { return; } if ((event.type & events) == 0) { return; } for (var listener in listeners) { listener.add(event); } } void close({Object? error}) { if (isClosed) { return; } isClosed = true; for (var listener in listeners) { if (error != null) { listener.addError(error); } listener.close(); } } /// Emit the given [_NativeFSEvent] to listeners. /// /// A single [_NativeFSEvent] is expanded into a sequence of appropriate /// [FileSystemEvent]. /// /// Note: this might modify [unmatchedMoves] - the caller is responsible for /// calling [flushUnmatchedMoves] once it emitted all events from a chunk /// of events. void addEvent(_NativeFSEvent event) { if (isClosed) { return; } final flags = event.flags; final fullPath = fullPathOf(event); final isDir = _NativeFSEvent.isDirectory(event, fullPath); if ((flags & FileSystemEvent.create) != 0) { add(FileSystemCreateEvent(fullPath, isDir)); } if ((flags & FileSystemEvent.modify) != 0) { add(FileSystemModifyEvent(fullPath, isDir, true)); } if ((flags & FileSystemEvent._modifyAttributes) != 0) { add(FileSystemModifyEvent(fullPath, isDir, false)); } if ((flags & FileSystemEvent.move) != 0) { // Use cookie to merge pairs of move from and move to events. final int cookie = event.cookie; if (cookie > 0) { if (unmatchedMoves.remove(cookie) case final linkedEvent?) { add(FileSystemMoveEvent(fullPathOf(linkedEvent), isDir, fullPath)); } else { unmatchedMoves[cookie] = event; } } else { addMove(event, fullPath, isDir); } } if ((flags & FileSystemEvent.delete) != 0) { add(FileSystemDeleteEvent(fullPath, false)); } if ((flags & FileSystemEvent._deleteSelf) != 0) { add(FileSystemDeleteEvent(fullPath, false)); // Emit all unmatched moves before emitting the stop event to avoid // loosing these events. flushUnmatchedMoves(); close(); } } /// Flush unmatched move events accumulated in [unmatchedMoves]. void flushUnmatchedMoves() { for (var move in unmatchedMoves.values) { final fullPathOfMove = fullPathOf(move); addMove( move, fullPathOfMove, _NativeFSEvent.isDirectory(move, fullPathOf(move)), ); } unmatchedMoves.clear(); } /// Most recently encountered move events by their [_NativeFSEvent.cookie]. /// /// When converting a chunk of _NativeFSEvent to corresponding FileSystemEvents /// we try to match and merge pairs of events which correspond to a single /// move operation (e.g. FILE_ACTION_RENAMED_{OLD|NEW}_NAME on Windows and /// IN_MOVED_{FROM|TO} on Linux). We use this as a temporary storage for /// matching. The caller feeding native events must call [flushUnmatchedMoves] /// at the end of the chunk to flush all unmatched moves. final Map unmatchedMoves = {}; String fullPathOf(_NativeFSEvent event) { assert(event.pathId == pathId); if (event.relativePath case final eventPath? when eventPath.isNotEmpty) { return '${path}${Platform.pathSeparator}${eventPath}'; } else { return path; } } void addMove(_NativeFSEvent event, String fullPath, bool isDir) { if ((event.flags & FileSystemEvent._movedTo) != 0) { add(FileSystemCreateEvent(fullPath, isDir)); } else { add(FileSystemDeleteEvent(fullPath, false)); } } } @patch abstract class _FileSystemWatcher { @patch static Stream _watch( String path, int events, bool recursive, ) { return _watcher._watchImpl(path, events, recursive); } Stream _watchImpl(String path, int events, bool recursive) { _WatchedPath? watchedPath; final stream = Stream.multi((controller) { _WatchedPath wp; try { wp = watchedPath ??= _watcher._startWatching(path, events, recursive); } on FileSystemException catch (e, st) { controller.addError(e, st); controller.close(); return; } if (wp.isClosed) { controller.addError( FileSystemException('Directory watcher is already closed', path), ); controller.close(); return; } wp.listeners.add(controller); controller.onCancel = () { wp.listeners.remove(controller); if (wp.listeners.isEmpty) { watchedPath = null; return _stopWatching(wp); } }; }); return stream; } Stream> _eventsStreamFromSocket( int socketId, int pathId, ) { final nativeSocket = _NativeSocket._watch(socketId); return _RawSocket(nativeSocket).map((event) { if (event == RawSocketEvent.read) { final result = <_NativeFSEvent>[]; int totalEvents; do { totalEvents = result.length; for (_NativeFSEvent? e in _readEvents(_watcherId, pathId)) { if (e == null) { break; } result.add(e); } } while (result.length > totalEvents); // Be sure to clear this manually, as the sockets are not read through // the _NativeSocket interface. nativeSocket.available = 0; return result; } return []; }); } /// Native ID associated with the watcher. /// /// Passed as a parameter to [_watchPathImpl] and other native functions. int get _watcherId => 0; /// Start watching the given path for the specified events. /// /// Returns [_WatchedPath] instances representing the watch. _WatchedPath _startWatching(String path, int events, bool recursive); /// Stop watching the given path. /// /// If this causes the watcher to free some native resources (e.g. because /// this was the last active filesystem watch) this function will return /// an instance of [Future] which will complete after native cleanup is /// complete. FutureOr _stopWatching(_WatchedPath wp) { _unwatchPath(_watcherId, wp.pathId); return wp.dispose(); } /// Wrapper over [_watchPathImpl] which takes care of converting /// [OSError] into [FileSystemException]. int _watchPath(String path, int events, bool recursive) { try { return _watchPathImpl( _watcherId, _Namespace._namespace, path, events, recursive, ); } on OSError catch (e) { throw FileSystemException._fromOSError(e, "Failed to watch path", path); } } /// Singleton [_FileSystemWatcher] which takes care of watching file system. static _FileSystemWatcher _watcher = () { if (isSupported) { if (Platform.isLinux || Platform.isAndroid) { return _InotifyFileSystemWatcher(); } if (Platform.isWindows) { return _Win32FileSystemWatcher(); } if (Platform.isMacOS) { return _FSEventStreamFileSystemWatcher(); } } throw FileSystemException( "File system watching is not supported on this platform", ); }(); @patch @pragma("vm:external-name", "FileSystemWatcher_IsSupported") external static bool get isSupported; @pragma("vm:external-name", "FileSystemWatcher_InitWatcher") external static int _initWatcher(); @pragma("vm:external-name", "FileSystemWatcher_WatchPath") external static int _watchPathImpl( int watcherId, _Namespace namespace, String path, int events, bool recursive, ); @pragma("vm:external-name", "FileSystemWatcher_UnwatchPath") external static void _unwatchPath(int watcherId, int pathId); /// Returns a list each element of which is [_NativeFSEvents] or `null`. /// /// After the first `null` only `null` entries will follow, in other words /// all non-`null` entries form the prefix of the list. @pragma("vm:external-name", "FileSystemWatcher_ReadEvents") external static List _readEvents(int watcherId, int pathId); @pragma("vm:external-name", "FileSystemWatcher_GetSocketId") external static int _getSocketId(int watcherId, int pathId); @ffi.Native)>( symbol: "FileSystemWatcher::DestroyWatch", ) external static void _destroyWatch(ffi.Pointer pathId); } /// A watcher that receives events for multiple `pathId` on a single channel. abstract class _MultiplexingFileSystemWatcher extends _FileSystemWatcher { /// Map of [_watchedPath] indexed by `pathId` values. final Map _watchedPaths = {}; /// Perform necessary initialization of the native state for the watcher. void _ensureWatcherIsRunning(); /// Shutdown the watcher when there is no actively watched paths. /// /// If shutdown requires asynchronous actions returns [Future] which will /// complete when shutdown is finished. FutureOr _stopWatcher(); @override _WatchedPath _startWatching(String path, int events, bool recursive) { _ensureWatcherIsRunning(); final pathId = super._watchPath(path, events, recursive); // On Linux inotify_add_watch will return an existing watch descriptor // for the inode if there is already one associated with it. Thus we // need accept the possibility that calling _watchPath twice will // return the same pathId. Other OSes do not reuse pathId values. assert(Platform.isLinux || !_watchedPaths.containsKey(pathId)); return _watchedPaths[pathId] ??= _WatchedPath(pathId, path, events); } @override Future _stopWatching(_WatchedPath wp) async { assert(_watchedPaths[wp.pathId] == wp); _watchedPaths.remove(wp.pathId); await super._stopWatching(wp); // If there are no more active watcher close inotify descriptor. if (_watchedPaths.isEmpty) { await _stopWatcher(); } } } class _InotifyFileSystemWatcher extends _MultiplexingFileSystemWatcher { int? _inotifyFd; StreamSubscription>? _inotifySubscription; @override int get _watcherId => _inotifyFd!; @override void _ensureWatcherIsRunning() { if (_inotifyFd != null) { return; } final inotifyFd = _inotifyFd = _FileSystemWatcher._initWatcher(); _inotifySubscription = _eventsStreamFromSocket( inotifyFd, 0, ).listen(_handleEvents); } void _handleEvents(List<_NativeFSEvent> events) { Set<_WatchedPath>? dirty; // Distribute events to corresponding _WatchedPath objects based on // pathId. for (_NativeFSEvent event in events) { if (_watchedPaths[event.pathId] case final watchedPath?) { watchedPath.addEvent(event); if (watchedPath.unmatchedMoves.isNotEmpty) { (dirty ??= {}).add(watchedPath); } } } if (dirty != null) { for (var watchedPath in dirty) { watchedPath.flushUnmatchedMoves(); } } } @override FutureOr _stopWatcher() { final subscription = _inotifySubscription; _inotifyFd = null; _inotifySubscription = null; return subscription?.cancel(); } } class _FSEventStreamFileSystemWatcher extends _MultiplexingFileSystemWatcher { final _port = RawReceivePort()..keepIsolateAlive = false; @override late final _watcherId = ffi.NativePort(_port.sendPort).nativePort; @override void _ensureWatcherIsRunning() { _port.keepIsolateAlive = true; _port.handler = _handleEvents; } @override FutureOr _stopWatcher() { _port.keepIsolateAlive = false; _port.handler = null; } void _handleEvents(List events) { // All events in a bundle have the same pathId, and we never get an empty // bundle. final pathId = (events[0] as _NativeFSEvent).pathId; if (_watchedPaths[pathId] case final watchedPath?) { for (_NativeFSEvent event in events) { watchedPath.addEvent(event); } watchedPath.flushUnmatchedMoves(); } } } class _Win32FileSystemWatcher extends _FileSystemWatcher { @override _WatchedPath _startWatching(String path, int events, bool recursive) { final watchedPath = _WatchedPath( _watchPath(path, events, recursive), path, events, ); watchedPath.source = _eventsStreamFromSocket( _FileSystemWatcher._getSocketId(0, watchedPath.pathId), watchedPath.pathId, ).listen( (events) { for (var e in events) { watchedPath.addEvent(e); } watchedPath.flushUnmatchedMoves(); }, onError: (error) { if (watchedPath.listeners.isNotEmpty) { watchedPath.close( error: FileSystemException( 'Directory watcher failed due to: $error', watchedPath.path, ), ); } }, onDone: () { if (watchedPath.listeners.isNotEmpty) { watchedPath.close( error: FileSystemException( 'Directory watcher closed unexpectedly', watchedPath.path, ), ); } }, cancelOnError: true, ); return watchedPath; } } extension type _NativeFSEvent(List _) { // See FileSystemWatcher::kEvent*Index constants. static const int flagsIndex = 0; static const int cookieIndex = 1; static const int pathIndex = 2; static const int pathIdIndex = 3; int get flags => this._[flagsIndex]; /// A unique identifier (32-bit unsigned integer) for matching related events. /// /// On Linux (inotify) associates unique cookie values with pairs of /// `IN_MOVED_FROM` and `IN_MOVED_TO` events. /// /// On Windows we set cookie to `1` on pairs of `FILE_ACTION_RENAMED_OLD_NAME` /// and `FILE_ACTION_RENAMED_NEW_NAME`. /// /// Not used on Mac OS X (because `FSEventStream` does not generate move /// events). int get cookie => this._[cookieIndex]; String? get relativePath => this._[pathIndex]; int get pathId => this._[pathIdIndex]; static bool isDirectory(_NativeFSEvent event, String fullPath) { if (Platform.isWindows) { // Windows does not get FileSystemEvent._isDir bit as part of the event // so we need to compute it by checking the file-system. We ignore links // when computing isDirectory. return FileSystemEntity._isDirectoryIgnoringLinksSync(fullPath); } else { return (event.flags & FileSystemEvent._isDir) != 0; } } } @pragma("vm:entry-point", "call") Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { return Uint8List.view(source.buffer, offsetInBytes, length); } ‚ÃNN< -D - 1 1C 1H -J 1N - 1 + 5L 3J 6$ 3J 6$ +L 0* 1:E& 1*1,,/+/<0!0=/2&/!-,+/0;?DI)A20JM04:O.J>FIF?JPQ(PINEILLPHNMJPPP0-B 6A$&& &"4KOL((?1319<:/D'EN* )1363JC./< NRMMKQ<6,$N= D93 3$ )9P@ O/$L $!)$$ 892*+ 'G /O($-L>?H$MKJ2)C,<  OO,4, *2@ ?(?&=&?@KM:>???9/DNK8FL"DL*! I>KIFFDI 6,%#E!IB % #F4 4H+A%5+ '+ "/!#O< E ### "$$N99-%); I&+ !B$'/ 4!,=$ 4!,;$  14#$"$'QG0Q*K)1)CNO%G :"P?dart:io-patch/file_patch.dartÀeÀh³—ìOorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/file_system_entity.dartÀ”É// Copyright (c) 2013, 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:io"; /// The type of an entity on the file system, /// such as a file, directory, or link. /// /// These constants are used by the [FileSystemEntity] class /// to indicate the object's type. final class FileSystemEntityType { static const file = const FileSystemEntityType._internal(0); static const directory = const FileSystemEntityType._internal(1); static const link = const FileSystemEntityType._internal(2); static const unixDomainSock = const FileSystemEntityType._internal(3); static const pipe = const FileSystemEntityType._internal(4); static const notFound = const FileSystemEntityType._internal(5); @Deprecated("Use notFound instead") static const NOT_FOUND = notFound; static const _typeList = const [ FileSystemEntityType.file, FileSystemEntityType.directory, FileSystemEntityType.link, FileSystemEntityType.unixDomainSock, FileSystemEntityType.pipe, FileSystemEntityType.notFound, ]; final int _type; const FileSystemEntityType._internal(this._type); static FileSystemEntityType _lookup(int type) => _typeList[type]; String toString() => const [ 'file', 'directory', 'link', 'unixDomainSock', 'pipe', 'notFound', ][_type]; } /// The result of calling the POSIX `stat()` function on a file system object. /// /// This is an immutable object, representing the snapshotted values returned /// by the `stat()` call. interface class FileStat { // These must agree with enum FileStat in file.h. static const _type = 0; static const _changedTime = 1; static const _modifiedTime = 2; static const _accessedTime = 3; static const _mode = 4; static const _size = 5; static final _epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true); static final _notFound = new FileStat._internal( _epoch, _epoch, _epoch, FileSystemEntityType.notFound, 0, -1, ); /// The time of the last change to the data or metadata of the file system /// object. /// /// On Windows platforms, this is instead the file creation time. final DateTime changed; /// The time of the last change to the data of the file system object. final DateTime modified; /// The time of the last access to the data of the file system object. /// /// On Windows platforms, this may have 1 day granularity, and be /// out of date by an hour. final DateTime accessed; /// The type of the underlying file system object. /// /// [FileSystemEntityType.notFound] if [stat] or [statSync] failed. final FileSystemEntityType type; /// The mode of the file system object. /// /// Permissions are encoded in the lower 16 bits of this number, and can be /// decoded using the [modeString] getter. final int mode; /// The size of the file system object. final int size; FileStat._internal( this.changed, this.modified, this.accessed, this.type, this.mode, this.size, ); external static _statSync(_Namespace namespace, String path); /// Calls the operating system's `stat()` function (or equivalent) on [path]. /// /// If [path] is a symbolic link then it is resolved and results for the /// resulting file are returned. /// /// Returns a [FileStat] object containing the data returned by `stat()`. /// If the call fails, returns a [FileStat] object with [FileStat.type] set to /// [FileSystemEntityType.notFound] and the other fields invalid. static FileStat statSync(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _statSyncInternal(path); } return overrides.statSync(path); } static FileStat _statSyncInternal(String path) { // Trailing path is not supported on Windows. if (Platform.isWindows) { path = FileSystemEntity._trimTrailingPathSeparators(path); } var data = _statSync(_Namespace._namespace, path); if (data is! Int64List) return FileStat._notFound; return FileStat._internal( DateTime.fromMillisecondsSinceEpoch(data[_changedTime]), DateTime.fromMillisecondsSinceEpoch(data[_modifiedTime]), DateTime.fromMillisecondsSinceEpoch(data[_accessedTime]), FileSystemEntityType._lookup(data[_type]), data[_mode], data[_size], ); } /// Asynchronously calls the operating system's `stat()` function (or /// equivalent) on [path]. /// /// If [path] is a symbolic link then it is resolved and results for the /// resulting file are returned. /// /// Returns a [Future] which completes with the same results as [statSync]. static Future stat(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _stat(path); } return overrides.stat(path); } static Future _stat(String path) { // Trailing path is not supported on Windows. if (Platform.isWindows) { path = FileSystemEntity._trimTrailingPathSeparators(path); } return _File._dispatchWithNamespace(_IOService.fileStat, [null, path]).then( (response) { if (response is List && response[0] != _successResponse) { return FileStat._notFound; } // Unwrap the real list from the "I'm not an error" wrapper. var data = (response as List)[1] as List; return FileStat._internal( DateTime.fromMillisecondsSinceEpoch(data[_changedTime] as int), DateTime.fromMillisecondsSinceEpoch(data[_modifiedTime] as int), DateTime.fromMillisecondsSinceEpoch(data[_accessedTime] as int), FileSystemEntityType._lookup(data[_type] as int), data[_mode] as int, data[_size] as int, ); }, ); } String toString() => """ FileStat: type $type changed $changed modified $modified accessed $accessed mode ${modeString()} size $size"""; /// The mode value as a human-readable string. /// /// The string is in the format "rwxrwxrwx", reflecting the user, group, and /// world permissions to read, write, and execute the file system object, with /// "-" replacing the letter for missing permissions. Extra permission bits /// may be represented by prepending "(suid)", "(guid)", and/or "(sticky)" to /// the mode string. String modeString() { var permissions = mode & 0xFFF; var codes = const ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx']; var result = []; if ((permissions & 0x800) != 0) result.add("(suid) "); if ((permissions & 0x400) != 0) result.add("(guid) "); if ((permissions & 0x200) != 0) result.add("(sticky) "); result ..add(codes[(permissions >> 6) & 0x7]) ..add(codes[(permissions >> 3) & 0x7]) ..add(codes[permissions & 0x7]); return result.join(); } } /// The common superclass of [File], [Directory], and [Link]. /// /// [FileSystemEntity] objects are returned from directory listing /// operations. To determine whether a [FileSystemEntity] is a [File], a /// [Directory], or a [Link] perform a type check: /// ```dart /// if (entity is File) (entity as File).readAsStringSync(); /// ``` /// You can also use the [type] or [typeSync] methods to determine /// the type of a file system object. /// /// Most methods in this class exist both in synchronous and asynchronous /// versions, for example, [exists] and [existsSync]. /// Unless you have a specific reason for using the synchronous version /// of a method, prefer the asynchronous version to avoid blocking your program. /// /// Here's the exists method in action: /// ```dart /// var isThere = await entity.exists(); /// print(isThere ? 'exists' : 'nonexistent'); /// ``` /// /// ## Other resources /// /// * The [Files and directories](https://dart.dev/guides/libraries/library-tour#files-and-directories) /// section of the library tour. /// /// * [Write Command-Line Apps](https://dart.dev/tutorials/server/cmdline), /// a tutorial about writing command-line apps, includes information about /// files and directories. abstract class FileSystemEntity { static const _backslashChar = 0x5c; static const _slashChar = 0x2f; static const _colonChar = 0x3a; String get _path; Uint8List get _rawPath; String get path; /// A [Uri] representing the file system entity's location. /// /// The returned URI's scheme is always "file" if the entity's [path] is /// absolute, otherwise the scheme will be empty and the URI relative. Uri get uri => new Uri.file(path); /// Checks whether the file system entity with this path exists. /// /// Returns a `Future` that completes with the result. /// /// Since [FileSystemEntity] is abstract, every [FileSystemEntity] object /// is actually an instance of one of the subclasses [File], /// [Directory], and [Link]. Calling [exists] on an instance of one /// of these subclasses checks whether the object exists in the file /// system object exists *and* is of the correct type (file, directory, /// or link). To check whether a path points to an object on the /// file system, regardless of the object's type, use the [type] /// static method. Future exists(); /// Synchronously checks whether the file system entity with this path /// exists. /// /// Since [FileSystemEntity] is abstract, every [FileSystemEntity] object /// is actually an instance of one of the subclasses [File], /// [Directory], and [Link]. Calling [existsSync] on an instance of /// one of these subclasses checks whether the object exists in the /// file system object exists and is of the correct type (file, /// directory, or link). To check whether a path points to an object /// on the file system, regardless of the object's type, use the /// [typeSync] static method. bool existsSync(); /// Renames this file system entity. /// /// Returns a `Future` that completes with a /// [FileSystemEntity] instance for the renamed file system entity. Future rename(String newPath); /// Synchronously renames this file system entity. /// /// Returns a [FileSystemEntity] instance for the renamed entity. FileSystemEntity renameSync(String newPath); /// Resolves the path of a file system object relative to the /// current working directory. /// /// Resolves all symbolic links on the path and resolves all `..` and `.` path /// segments. /// /// [resolveSymbolicLinks] uses the operating system's native /// file system API to resolve the path, using the `realpath` function /// on Linux and OS X, and the `GetFinalPathNameByHandle` function on /// Windows. If the path does not point to an existing file system object, /// `resolveSymbolicLinks` throws a `FileSystemException`. /// /// On Windows the `..` segments are resolved _before_ resolving the symbolic /// link, and on other platforms the symbolic links are _resolved to their /// target_ before applying a `..` that follows. /// /// To ensure the same behavior on all platforms resolve `..` segments before /// calling `resolveSymbolicLinks`. One way of doing this is with the [Uri] /// class: /// ```dart /// var path = Uri.parse('.').resolveUri(Uri.file(input)).toFilePath(); /// if (path == '') path = '.'; /// var resolved = await File(path).resolveSymbolicLinks(); /// print(resolved); /// ``` /// since `Uri.resolve` removes `..` segments. This will result in the Windows /// behavior. /// /// On Windows, attempting to resolve a symbolic link where the link type /// does not match the type of the resolved file system object will cause the /// Future to complete with a [PathAccessException] error. Future resolveSymbolicLinks() { return _File._dispatchWithNamespace(_IOService.fileResolveSymbolicLinks, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot resolve symbolic links", path); return response as String; }); } /// Resolves the path of a file system object relative to the /// current working directory. /// /// Resolves all symbolic links on the path and resolves all `..` and `.` path /// segments. /// /// [resolveSymbolicLinksSync] uses the operating system's native /// file system API to resolve the path, using the `realpath` function /// on linux and OS X, and the `GetFinalPathNameByHandle` function on /// Windows. If the path does not point to an existing file system object, /// `resolveSymbolicLinksSync` throws a `FileSystemException`. /// /// On Windows the `..` segments are resolved _before_ resolving the symbolic /// link, and on other platforms the symbolic links are _resolved to their /// target_ before applying a `..` that follows. /// /// To ensure the same behavior on all platforms resolve `..` segments before /// calling `resolveSymbolicLinksSync`. One way of doing this is with the [Uri] /// class: /// ```dart /// var path = Uri.parse('.').resolveUri(Uri.file(input)).toFilePath(); /// if (path == '') path = '.'; /// var resolved = File(path).resolveSymbolicLinksSync(); /// print(resolved); /// ``` /// since `Uri.resolve` removes `..` segments. This will result in the Windows /// behavior. /// /// On Windows, a symbolic link is created as either a file link or a /// directory link. Attempting to resolve such a symbolic link requires the /// link type to match the type of the file system object that it points to, /// otherwise it throws a [PathAccessException]. String resolveSymbolicLinksSync() { var result = _resolveSymbolicLinks(_Namespace._namespace, _rawPath); _throwIfError(result, "Cannot resolve symbolic links", path); return result; } /// Calls the operating system's `stat()` function on [path]. /// /// Identical to `FileStat.stat(this.path)`. /// /// Returns a `Future` object containing the data returned by /// `stat()`. /// /// If [path] is a symbolic link then it is resolved and results for the /// resulting file are returned. /// /// If the call fails, completes the future with a [FileStat] object /// with `.type` set to [FileSystemEntityType.notFound] and the other fields /// invalid. Future stat() => FileStat.stat(path); /// Synchronously calls the operating system's `stat()` function on [path]. /// /// Identical to `FileStat.statSync(this.path)`. /// /// Returns a [FileStat] object containing the data returned by `stat()`. /// /// If [path] is a symbolic link then it is resolved and results for the /// resulting file are returned. /// /// If the call fails, returns a [FileStat] object with `.type` set to /// [FileSystemEntityType.notFound] and the other fields invalid. FileStat statSync() => FileStat.statSync(path); /// Deletes this [FileSystemEntity]. /// /// The exact details vary according to the [FileSystemEntity]: /// /// * [Directory.delete] /// * [File.delete] /// * [Link.delete] Future delete({bool recursive = false}) => _delete(recursive: recursive); /// Synchronously deletes this [FileSystemEntity]. /// /// The exact details vary according to the [FileSystemEntity]: /// /// * [Directory.deleteSync] /// * [File.deleteSync] /// * [Link.deleteSync] void deleteSync({bool recursive = false}) => _deleteSync(recursive: recursive); /// Start watching the [FileSystemEntity] for changes. /// /// The implementation uses platform-dependent event-based APIs for receiving /// file-system notifications, thus behavior depends on the platform. /// /// * `Windows`: Uses `ReadDirectoryChangesW`. The implementation only /// supports watching directories. Recursive watching is supported. /// * `Linux`: Uses `inotify`. The implementation supports watching both /// files and directories. Recursive watching is not supported. /// Note: When watching files directly, delete events might not happen /// as expected. /// * `OS X`: Uses the /// [File System Events API](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/TechnologyOverview/TechnologyOverview.html). /// The implementation supports watching both files and directories. /// Recursive watching is supported. /// This API has several limitations: /// /// * Changes that occurred shortly *before* the [watch] method was /// called may still appear in the [Stream]. /// * Changes that occur in a short period of time may arrive /// out-of-order. /// * Multiple changes made in a single directory may be coalesced into /// a single `FileSystemEvent`. /// /// The system will start listening for events once the returned [Stream] is /// being listened to, not when the call to [watch] is issued. /// /// The returned value is an endless broadcast [Stream], that only stops when /// one of the following happens: /// /// * The [Stream] is canceled, e.g. by calling `cancel` on the /// [StreamSubscription]. /// * The [FileSystemEntity] being watched is deleted. /// * System Watcher exits unexpectedly. e.g. On `Windows` this happens when /// buffer that receive events from `ReadDirectoryChangesW` overflows. /// /// Use `events` to specify what events to listen for. The constants in /// [FileSystemEvent] can be or'ed together to mix events. Default is /// [FileSystemEvent.all]. /// /// A move event may be reported as separate delete and create events. Stream watch({ int events = FileSystemEvent.all, bool recursive = false, }) { // FIXME(bkonyi): find a way to do this using the raw path. final String trimmedPath = _trimTrailingPathSeparators(path); final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _FileSystemWatcher._watch(trimmedPath, events, recursive); } return overrides.fsWatch(trimmedPath, events, recursive); } Future _delete({bool recursive = false}); void _deleteSync({bool recursive = false}); static Future _identical(String path1, String path2) { return _File._dispatchWithNamespace(_IOService.fileIdentical, [ null, path1, path2, ]).then((response) { _checkForErrorResponse( response, "Error in FileSystemEntity.identical($path1, $path2)", "", ); return response as bool; }); } /// Checks whether two paths refer to the same object in the /// file system. /// /// Returns a `Future` that completes with the result. /// /// Comparing a link to its target returns `false`, as does comparing two links /// that point to the same target. To check the target of a link, use /// Link.target explicitly to fetch it. Directory links appearing /// inside a path are followed, though, to find the file system object. /// /// Completes the returned Future with an error if one of the paths points /// to an object that does not exist. static Future identical(String path1, String path2) { IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _identical(path1, path2); } return overrides.fseIdentical(path1, path2); } static final RegExp _absoluteWindowsPathPattern = new RegExp( r'^(?:\\\\|[a-zA-Z]:[/\\])', ); /// Whether this object's path is absolute. /// /// An absolute path is independent of the current working /// directory ([Directory.current]). /// A non-absolute path must be interpreted relative to /// the current working directory. /// /// On Windows, a path is absolute if it starts with `\\` /// (two backslashes or representing a UNC path) or with a drive letter /// between `a` and `z` (upper or lower case) followed by `:\` or `:/`. /// This makes, for example, `\file.ext` a non-absolute path /// because it depends on the current drive letter. /// /// On non-Windows, a path is absolute if it starts with `/`. /// /// If the path is not absolute, use [absolute] to get an entity /// with an absolute path referencing the same object in the file system, /// if possible. bool get isAbsolute => _isAbsolute(path); static bool _isAbsolute(String path) { if (Platform.isWindows) { return path.startsWith(_absoluteWindowsPathPattern); } else { return path.startsWith('/'); } } /// A [FileSystemEntity] whose path is the absolute path of [path]. /// /// The type of the returned instance is the same as the type of /// this entity. /// /// A file system entity with an already absolute path /// (as reported by [isAbsolute]) is returned directly. /// For a non-absolute path, the returned entity is absolute ([isAbsolute]) /// *if possible*, but still refers to the same file system object. FileSystemEntity get absolute; String get _absolutePath { if (isAbsolute) return path; if (Platform.isWindows) return _absoluteWindowsPath(path); String current = Directory.current.path; if (current.endsWith('/')) { return '$current$path'; } else { return '$current${Platform.pathSeparator}$path'; } } /// The ASCII code of the Windows drive letter of [path], if any. /// /// Returns the ASCII code of the upper-cased drive letter of /// the path of [path], if it has a drive letter (starts with `[a-zA-z]:`), /// or `-1` if it has no drive letter. static int _windowsDriveLetter(String path) { if (path.isEmpty || !path.startsWith(':', 1)) return -1; var first = path.codeUnitAt(0) & ~0x20; if (first >= 0x41 && first <= 0x5b) return first; return -1; } /// The relative [path] converted to an absolute path. static String _absoluteWindowsPath(String path) { assert(Platform.isWindows); assert(!_isAbsolute(path)); // Could perhaps use something like // https://docs.microsoft.com/en-us/windows/win32/api/pathcch/nf-pathcch-pathalloccombine var current = Directory.current.path; if (path.startsWith(r'\')) { assert(!path.startsWith(r'\', 1)); // Absolute path, no drive letter. var currentDrive = _windowsDriveLetter(current); if (currentDrive >= 0) { return '${current[0]}:$path'; } // If `current` is a UNC path \\server\share[...], // we make the absolute path relative to the share. // Also works with `\\?\c:\` paths. if (current.startsWith(r'\\')) { var serverEnd = current.indexOf(r'\', 2); if (serverEnd >= 0) { // We may want to recognize UNC paths of the form: // \\?\UNC\Server\share\... // specially, and be relative to the *share* not to UNC\. var shareEnd = current.indexOf(r'\', serverEnd + 1); if (shareEnd < 0) shareEnd = current.length; return '${current.substring(0, shareEnd)}$path'; } } // If `current` is not in the drive-letter:path format, // or not \\server\share[\path], // we ignore it and return a relative path. return path; } var entityDrive = _windowsDriveLetter(path); if (entityDrive >= 0) { if (entityDrive != _windowsDriveLetter(current)) { // Need to resolve relative to current directory of the drive. // Windows remembers the last CWD of each drive. // We currently don't have that information, // so we assume the root of that drive. return '${path[0]}:\\$path'; } /// A `c:relative\path` path on the same drive as `current`. path = path.substring(2); assert(!path.startsWith(r'\\')); } if (current.endsWith(r'\') || current.endsWith('/')) { return '$current$path'; } return '$current\\$path'; } static bool _identicalSync(String path1, String path2) { var result = _identicalNative(_Namespace._namespace, path1, path2); _throwIfError(result, 'Error in FileSystemEntity.identicalSync'); return result; } /// Synchronously checks whether two paths refer to the same object in the /// file system. /// /// Comparing a link to its target returns `false`, as does comparing two links /// that point to the same target. To check the target of a link, use /// Link.target explicitly to fetch it. Directory links appearing /// inside a path are followed, though, to find the file system object. /// /// Throws an error if one of the paths points to an object that does not /// exist. static bool identicalSync(String path1, String path2) { IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _identicalSync(path1, path2); } return overrides.fseIdenticalSync(path1, path2); } /// Test if [watch] is supported on the current system. /// /// OS X 10.6 and below is not supported. static bool get isWatchSupported { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _FileSystemWatcher.isSupported; } return overrides.fsWatchIsSupported(); } // The native methods which determine type of the FileSystemEntity require // that the buffer provided is null terminated. static Uint8List _toUtf8Array(String s) => _toNullTerminatedUtf8Array(utf8.encode(s)); static Uint8List _toNullTerminatedUtf8Array(Uint8List l) { if (l.isEmpty || (l.isNotEmpty && l.last != 0)) { final tmp = new Uint8List(l.length + 1); tmp.setRange(0, l.length, l); return tmp; } else { return l; } } static String _toStringFromUtf8Array(Uint8List l) { Uint8List nonNullTerminated = l; if (l.last == 0) { nonNullTerminated = new Uint8List.view( l.buffer, l.offsetInBytes, l.length - 1, ); } return utf8.decode(nonNullTerminated, allowMalformed: true); } /// Finds the type of file system object that a path points to. /// /// Returns a `Future` that completes with the same /// results as [typeSync]. static Future type( String path, { bool followLinks = true, }) { return _getType(_toUtf8Array(path), followLinks); } /// Synchronously finds the type of file system object that a path points to. /// /// Returns [FileSystemEntityType.notFound] if [path] does not point to a file /// system object or if any other error occurs in looking up the path. /// /// If [path] points to a link and [followLinks] is `true` then the result /// will be for the file system object that the link points to. If that /// object does not exist then the result will be /// [FileSystemEntityType.notFound]. If [path] points to a link and /// [followLinks] is `false` then the result will be /// [FileSystemEntityType.link]. static FileSystemEntityType typeSync(String path, {bool followLinks = true}) { return _getTypeSync(_toUtf8Array(path), followLinks); } /// Whether [path] refers to a link. /// /// Checks whether `type(path, followLinks: false)` /// returns [FileSystemEntityType.link]. static Future isLink(String path) => _isLinkRaw(_toUtf8Array(path)); static Future _isLinkRaw(Uint8List rawPath) => _getType( rawPath, false, ).then((type) => (type == FileSystemEntityType.link)); /// Whether [path] refers to a file. /// /// Checks whether `type(path)` returns [FileSystemEntityType.file]. static Future isFile(String path) => _getType( _toUtf8Array(path), true, ).then((type) => (type == FileSystemEntityType.file)); /// Whether [path] refers to a directory. /// /// Checks whether `type(path)` returns [FileSystemEntityType.directory]. static Future isDirectory(String path) => _getType( _toUtf8Array(path), true, ).then((type) => (type == FileSystemEntityType.directory)); /// Synchronously checks whether [path] refers to a link. /// /// Checks whether `typeSync(path, followLinks: false)` returns /// [FileSystemEntityType.link]. static bool isLinkSync(String path) => _isLinkRawSync(_toUtf8Array(path)); static bool _isLinkRawSync(rawPath) => (_getTypeSync(rawPath, false) == FileSystemEntityType.link); /// Synchronously checks whether [path] refers to a file. /// /// Checks whether `typeSync(path)` returns /// [FileSystemEntityType.file]. static bool isFileSync(String path) => (_getTypeSync(_toUtf8Array(path), true) == FileSystemEntityType.file); /// Synchronously checks whether [path] refers to a directory. /// /// Checks whether `typeSync(path)` returns /// [FileSystemEntityType.directory]. static bool isDirectorySync(String path) => (_getTypeSync(_toUtf8Array(path), true) == FileSystemEntityType.directory); static bool _isDirectoryIgnoringLinksSync(String path) => (_getTypeSync(_toUtf8Array(path), false) == FileSystemEntityType.directory); external static _getTypeNative( _Namespace namespace, Uint8List rawPath, bool followLinks, ); external static _identicalNative( _Namespace namespace, String path1, String path2, ); external static _resolveSymbolicLinks(_Namespace namespace, Uint8List path); // Finds the next-to-last component when dividing at path separators. static final RegExp _parentRegExp = Platform.isWindows ? new RegExp(r'[^/\\][/\\]+[^/\\]') : new RegExp(r'[^/]/+[^/]'); /// The parent path of a path. /// /// Finds the final path component of a path, using the platform's /// path separator to split the path, and returns the prefix up to /// that part. /// /// Will not remove the root component of a Windows path, like "C:\\" or /// "\\\\server_name\\". Includes a trailing path separator in the last /// part of [path], and leaves no trailing path separator. static String parentOf(String path) { int rootEnd = -1; if (Platform.isWindows) { if (path.startsWith(_absoluteWindowsPathPattern)) { // Root ends at first / or \ after the first two characters. rootEnd = path.indexOf(new RegExp(r'[/\\]'), 2); if (rootEnd == -1) return path; } else if (path.startsWith('\\') || path.startsWith('/')) { rootEnd = 0; } } else if (path.startsWith('/')) { rootEnd = 0; } // Ignore trailing slashes. // All non-trivial cases have separators between two non-separators. int pos = path.lastIndexOf(_parentRegExp); if (pos > rootEnd) { return path.substring(0, pos + 1); } else if (rootEnd > -1) { return path.substring(0, rootEnd + 1); } else { return '.'; } } /// The parent directory of this entity. Directory get parent => new Directory(parentOf(path)); static FileSystemEntityType _getTypeSyncHelper( Uint8List rawPath, bool followLinks, ) { var result = _getTypeNative(_Namespace._namespace, rawPath, followLinks); _throwIfError(result, 'Error getting type of FileSystemEntity'); return FileSystemEntityType._lookup(result); } static FileSystemEntityType _getTypeSync( Uint8List rawPath, bool followLinks, ) { IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _getTypeSyncHelper(rawPath, followLinks); } return overrides.fseGetTypeSync( _toStringFromUtf8Array(rawPath), followLinks, ); } static Future _getTypeRequest( Uint8List rawPath, bool followLinks, ) { return _File._dispatchWithNamespace(_IOService.fileType, [ null, rawPath, followLinks, ]).then((response) { _checkForErrorResponse( response, "Error getting type", utf8.decode(rawPath, allowMalformed: true), ); return FileSystemEntityType._lookup(response as int); }); } static Future _getType( Uint8List rawPath, bool followLinks, ) { IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return _getTypeRequest(rawPath, followLinks); } return overrides.fseGetType(_toStringFromUtf8Array(rawPath), followLinks); } static _throwIfError(Object result, String msg, [String? path]) { if (result is OSError) { throw new FileSystemException(msg, path, result); } else if (result is ArgumentError) { throw result; } } // TODO(bkonyi): find a way to do this with raw paths. static String _trimTrailingPathSeparators(String path) { if (Platform.isWindows) { // "C:" and "C:\" are semantically different on Windows and only "C:\" is an // absolute path. // See https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats final minimumCharToNotTrim = _isAbsolute(path) ? 3 : 1; while (path.length > minimumCharToNotTrim && (path.endsWith(Platform.pathSeparator) || path.endsWith('/'))) { path = path.substring(0, path.length - 1); } } else { while (path.length > 1 && path.endsWith(Platform.pathSeparator)) { path = path.substring(0, path.length - 1); } } return path; } // TODO(bkonyi): find a way to do this with raw paths. static String _ensureTrailingPathSeparators(String path) { if (path.isEmpty) path = '.'; if (Platform.isWindows) { while (!path.endsWith(Platform.pathSeparator) && !path.endsWith('/')) { path = "$path${Platform.pathSeparator}"; } } else { while (!path.endsWith(Platform.pathSeparator)) { path = "$path${Platform.pathSeparator}"; } } return path; } } /// Base event class emitted by [FileSystemEntity.watch]. sealed class FileSystemEvent { /// Bitfield for [FileSystemEntity.watch], to enable [FileSystemCreateEvent]s. static const int create = 1 << 0; /// Bitfield for [FileSystemEntity.watch], to enable [FileSystemModifyEvent]s. static const int modify = 1 << 1; /// Bitfield for [FileSystemEntity.watch], to enable [FileSystemDeleteEvent]s. static const int delete = 1 << 2; /// Bitfield for [FileSystemEntity.watch], to enable [FileSystemMoveEvent]s. static const int move = 1 << 3; /// Bitfield for [FileSystemEntity.watch], for enabling all of [create], /// [modify], [delete] and [move]. static const int all = create | modify | delete | move; static const int _modifyAttributes = 1 << 4; static const int _deleteSelf = 1 << 5; static const int _isDir = 1 << 6; static const int _movedTo = 1 << 7; /// The type of event. See [FileSystemEvent] for a list of events. final int type; /// The path that triggered the event. /// /// Depending on the platform and the [FileSystemEntity], the path may be /// relative. final String path; /// Whether the event target is a directory. /// /// The value will always be `false` for [FileSystemDeleteEvent]. /// /// On Windows `isDirectory` is computed by checking the file system entity /// type after the event, which means it can be incorrect. It can be incorrectly /// `false` for a directory create, modify or move event if that directory was /// deleted soon after the event occurred. And, it can be incorrectly `true` /// for a newly created link to a directory. final bool isDirectory; FileSystemEvent._(this.type, this.path, this.isDirectory); } /// File system event for newly created file system objects. final class FileSystemCreateEvent extends FileSystemEvent { /// Constructs a new [FileSystemCreateEvent]. FileSystemCreateEvent(String path, bool isDirectory) : super._(FileSystemEvent.create, path, isDirectory); String toString() => "FileSystemCreateEvent('$path', isDirectory=$isDirectory)"; } /// File system event for modifications of file system objects. final class FileSystemModifyEvent extends FileSystemEvent { /// If the content was changed and not only the attributes, [contentChanged] /// is `true`. final bool contentChanged; /// Constructs a new [FileSystemModifyEvent]. FileSystemModifyEvent(String path, bool isDirectory, this.contentChanged) : super._(FileSystemEvent.modify, path, isDirectory); String toString() => "FileSystemModifyEvent('$path', isDirectory=$isDirectory, " "contentChanged=$contentChanged)"; } /// File system event for deletion of file system objects. final class FileSystemDeleteEvent extends FileSystemEvent { /// Constructs a new [FileSystemDeleteEvent]. FileSystemDeleteEvent(String path, bool isDirectory) : super._(FileSystemEvent.delete, path, false); String toString() => "FileSystemDeleteEvent('$path')"; /// Whether the file system object was a directory. /// /// The value will always be `false` for [FileSystemDeleteEvent]. @Deprecated('always false for FileSystemDeleteEvent') bool get isDirectory => false; } /// File system event for moving of file system objects. final class FileSystemMoveEvent extends FileSystemEvent { /// The destination path of the file being moved. /// /// The destination is `null` if the underlying implementation /// is unable to identify the destination of the moved file. /// /// The source path is available as [path]. final String? destination; /// Constructs a new [FileSystemMoveEvent]. FileSystemMoveEvent(String path, bool isDirectory, this.destination) : super._(FileSystemEvent.move, path, isDirectory); String toString() => "FileSystemMoveEvent('$path', " "isDirectory=$isDirectory, destination=$destination)"; } abstract class _FileSystemWatcher { external static Stream _watch( String path, int events, bool recursive, ); external static bool get isSupported; } „NN<.(=##?D?I?C&%#$)#4D    ON4!""M3 #MDIID5F#*N-*@PK#LQD*8&%32A77?@@1HK#N.8!/2AQL% E;#JKK<  1OQNP$P;;= --'>CI3 =C&J6HQ( )/h#LM"&"">KI%C?L?FGJCCIL?GFBHC 'AF35D/@!Q@IHM=PM3PN J"> QLP=*O O!@!QDIHMAPM3PR J"< QHNO3&IB@/JK#GO2N3LK#ID2'B?%5B!/)9PHKJMFM€¯K+,J5DN(OAP$D!;QMJHI"&@B8H>>.?D ? ??RIEJM(>2'1@!.=':%'219G950%C ';;HFMRIEJL :2+5:,%8-+M2-2=6/$ 6%. ABL,6PQIMJ4F7#Q:'6+MA 9'G7 9,L< ><B#M)C<.#)MA.(.1'<2'"$OH9*#!EEKJ=(:E9(B' I/)- +92NE1,27%'7? 4 <024OD8*9;ST>3K3 I39="N1 71:Q$Q$Q$O"K%:/)$&E)L/DNSQO/==<07:B@ _startTime + _sw.elapsedMicroseconds ~/ 1000; _IOResourceInfo(this.type) : id = _IOResourceInfo.getNextID(); /// Get the full set of values for a specific implementation. This is normally /// looked up based on an id from a referenceValueMap. Map get fullValueMap; /// The reference map, used to return a list of values, e.g., getting /// all open sockets. The structure of this is shared among all subclasses. Map get referenceValueMap => { // The type for a reference object is prefixed with @ in observatory. 'type': '@$type', 'id': id, 'name': name, }; static int getNextID() => _count++; } abstract class _ReadWriteResourceInfo extends _IOResourceInfo { int readBytes; int writeBytes; int readCount; int writeCount; int lastReadTime; int lastWriteTime; // Not all call sites use this. In some cases, e.g., a socket, a read does // not always mean that we actually read some bytes (we may do a read to see // if there are some bytes available). void addRead(int bytes) { readBytes += bytes; readCount++; lastReadTime = _IOResourceInfo.timestamp; } // In cases where we read but did not necessarily get any bytes, use this to // update the readCount and timestamp. Manually update totalRead if any bytes // where actually read. void didRead() { addRead(0); } void addWrite(int bytes) { writeBytes += bytes; writeCount++; lastWriteTime = _IOResourceInfo.timestamp; } _ReadWriteResourceInfo(String type) : readBytes = 0, writeBytes = 0, readCount = 0, writeCount = 0, lastReadTime = 0, lastWriteTime = 0, super(type); Map get fullValueMap => { 'type': type, 'id': id, 'name': name, 'readBytes': readBytes, 'writeBytes': writeBytes, 'readCount': readCount, 'writeCount': writeCount, 'lastReadTime': lastReadTime, 'lastWriteTime': lastWriteTime, }; } class _FileResourceInfo extends _ReadWriteResourceInfo { static const String _type = 'OpenFile'; final RandomAccessFile file; static Map openFiles = {}; _FileResourceInfo(this.file) : super(_type) { fileOpened(this); } static fileOpened(_FileResourceInfo info) { assert(!openFiles.containsKey(info.id)); openFiles[info.id] = info; } static fileClosed(_FileResourceInfo info) { assert(openFiles.containsKey(info.id)); openFiles.remove(info.id); } static Iterable> getOpenFilesList() { return List.from(openFiles.values.map((e) => e.referenceValueMap)); } static Future getOpenFiles( String function, Map params, ) { assert(function == 'ext.dart.io.getOpenFiles'); final data = {'type': 'OpenFileList', 'files': getOpenFilesList()}; final jsonValue = json.encode(data); return Future.value(ServiceExtensionResponse.result(jsonValue)); } Map get fileInfoMap => fullValueMap; static Future getOpenFileInfoMapByID( String function, Map params, ) { final id = int.parse(params['id']!); final result = openFiles.containsKey(id) ? openFiles[id]!.fileInfoMap : {}; final jsonValue = json.encode(result); return Future.value(ServiceExtensionResponse.result(jsonValue)); } String get name => file.path; } abstract class _Process implements Process { abstract String _path; abstract List _arguments; abstract String? _workingDirectory; } class _SpawnedProcessResourceInfo extends _IOResourceInfo { static const String _type = 'SpawnedProcess'; final _Process process; final int startedAt; static Map startedProcesses = Map(); _SpawnedProcessResourceInfo(this.process) : startedAt = _IOResourceInfo.timestamp, super(_type) { processStarted(this); } String get name => process._path; void stopped() => processStopped(this); Map get fullValueMap => { 'type': type, 'id': id, 'name': name, 'pid': process.pid, 'startedAt': startedAt, 'arguments': process._arguments, 'workingDirectory': process._workingDirectory == null ? '.' : process._workingDirectory, }; static processStarted(_SpawnedProcessResourceInfo info) { assert(!startedProcesses.containsKey(info.id)); startedProcesses[info.id] = info; } static processStopped(_SpawnedProcessResourceInfo info) { assert(startedProcesses.containsKey(info.id)); startedProcesses.remove(info.id); } static Iterable> getStartedProcessesList() => List.from(startedProcesses.values.map((e) => e.referenceValueMap)); static Future getStartedProcesses( String function, Map params, ) { assert(function == 'ext.dart.io.getSpawnedProcesses'); final data = { 'type': 'SpawnedProcessList', 'processes': getStartedProcessesList(), }; final jsonValue = json.encode(data); return Future.value(ServiceExtensionResponse.result(jsonValue)); } static Future getProcessInfoMapById( String function, Map params, ) { final id = int.parse(params['id']!); final result = startedProcesses.containsKey(id) ? startedProcesses[id]!.fullValueMap : {}; final jsonValue = json.encode(result); return Future.value(ServiceExtensionResponse.result(jsonValue)); } } €ÕNN<!5CLAQ9)HN2J&@MO).OP/&-"$9*50.-.,=H8 4H)E8B )P+E -$&<0B/,-$*-%:%<4&<3&EJ? ;$.)EA )4-+Edart:io/io_resource_info.dartDorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/io_sink.dart¨ý// Copyright (c) 2013, 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:io"; /// A combined byte and text output. /// /// An [IOSink] combines a [StreamSink] of bytes with a [StringSink], /// and allows easy output of both bytes and text. /// /// An `IOSink` is intended for writing bytes. /// Strings written through [write] or [writeCharCode] will be converted /// to bytes using [encoding]. /// Integer data added using [add] or [addStream] will be treated as byte data, /// and will be truncated to unsigned 8-bit values as by using [int.toUnsigned]. /// No guarantees are given for when such a conversion happens, since it /// depends on the implementation behind the sink. /// /// Writing text ([write]) and adding bytes ([add]) may be interleaved freely. /// /// While a stream is being added using [addStream], any further attempts /// to add or write to the [IOSink] will fail until the [addStream] completes. /// /// It is an error to add data to the [IOSink] after the sink is closed. abstract interface class IOSink implements StreamSink>, StringSink { /// Create an [IOSink] that outputs to a [target] [StreamConsumer] of bytes. /// /// Text written to [StreamSink] methods is encoded to bytes using [encoding] /// before being output on [target]. factory IOSink( StreamConsumer> target, { Encoding encoding = utf8, }) => _IOSinkImpl(target, encoding); /// The [Encoding] used when writing strings. /// /// Depending on the underlying consumer, this property might be mutable. late Encoding encoding; /// Adds byte [data] to the target consumer, ignoring [encoding]. /// /// The [encoding] does not apply to this method, and the [data] list is passed /// directly to the target consumer as a stream event. /// /// This method must not be called when a stream is currently being added /// using [addStream]. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. /// /// The data list should not be modified after it has been passed to `add` /// because it is not defined whether the target consumer will receive the /// list in the original or modified state. /// /// Individual values in [data] which are not in the range 0 .. 255 will be /// truncated to their low eight bits, as if by [int.toUnsigned], before being /// used. void add(List data); /// Converts [object] to a String by invoking [Object.toString] and /// [add]s the encoding of the result to the target consumer. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. void write(Object? object); /// Iterates over the given [objects] and [write]s them in sequence. /// /// If [separator] is provided, a `write` with the `separator` is performed /// between any two elements of objects. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. void writeAll(Iterable objects, [String separator = ""]); /// Converts [object] to a String by invoking [Object.toString] and /// writes the result to `this`, followed by a newline. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. void writeln([Object? object = ""]); /// Writes the character of [charCode]. /// /// This method is equivalent to `write(String.fromCharCode(charCode))`. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. void writeCharCode(int charCode); /// Passes the error to the target consumer as an error event. /// /// This method must not be called when a stream is currently being added /// using [addStream]. /// /// This operation is non-blocking. See [flush] or [done] for how to get any /// errors generated by this call. void addError(error, [StackTrace? stackTrace]); /// Adds all elements of the given [stream]. /// /// Returns a [Future] that completes when /// all elements of the given [stream] have been added. /// /// If the stream contains an error, the `addStream` ends at the error, /// and the returned future completes with that error. /// /// This method must not be called when a stream is currently being added /// using this method. /// /// Individual values in the lists emitted by [stream] which are not in the /// range 0 .. 255 will be truncated to their low eight bits, as if by /// [int.toUnsigned], before being used. Future addStream(Stream> stream); /// Returns a [Future] that completes once all buffered data is accepted by the /// underlying [StreamConsumer]. /// /// This method must not be called while an [addStream] is incomplete. /// /// NOTE: This is not necessarily the same as the data being flushed by the /// operating system. Future flush(); /// Close the target consumer. /// /// NOTE: Writes to the [IOSink] may be buffered, and may not be flushed by /// a call to `close()`. To flush all buffered writes, call `flush()` before /// calling `close()`. Future close(); /// A future that will complete when the consumer closes, or when an /// error occurs. /// /// This future is identical to the future returned by [close]. Future get done; } class _StreamSinkImpl implements StreamSink { final StreamConsumer _target; final Completer _doneCompleter = Completer(); StreamController? _controllerInstance; Completer? _controllerCompleter; bool _isClosed = false; bool _isBound = false; bool _hasError = false; _StreamSinkImpl(this._target); void add(T data) { if (_isClosed) { throw StateError("StreamSink is closed"); } _controller.add(data); } void addError(error, [StackTrace? stackTrace]) { if (_isClosed) { throw StateError("StreamSink is closed"); } _controller.addError(error, stackTrace); } Future addStream(Stream stream) { if (_isBound) { throw StateError("StreamSink is already bound to a stream"); } if (_hasError) return done; _isBound = true; var future = _controllerCompleter == null ? _target.addStream(stream) : _controllerCompleter!.future.then((_) => _target.addStream(stream)); _controllerInstance?.close(); // Wait for any pending events in [_controller] to be dispatched before // adding [stream]. return future.whenComplete(() { _isBound = false; }); } Future flush() { if (_isBound) { throw StateError("StreamSink is bound to a stream"); } if (_controllerInstance == null) return Future.value(this); // Adding an empty stream-controller will return a future that will complete // when all data is done. _isBound = true; var future = _controllerCompleter!.future; _controllerInstance!.close(); return future.whenComplete(() { _isBound = false; }); } Future close() { if (_isBound) { throw StateError("StreamSink is bound to a stream"); } if (!_isClosed) { _isClosed = true; if (_controllerInstance != null) { _controllerInstance!.close(); } else { _closeTarget(); } } return done; } void _closeTarget() { _target.close().then(_completeDoneValue, onError: _completeDoneError); } Future get done => _doneCompleter.future; void _completeDoneValue(value) { if (!_doneCompleter.isCompleted) { _doneCompleter.complete(value); } } void _completeDoneError(error, StackTrace? stackTrace) { if (!_doneCompleter.isCompleted) { _hasError = true; _doneCompleter.completeError(error, stackTrace); } } StreamController get _controller { if (_isBound) { throw StateError("StreamSink is bound to a stream"); } if (_isClosed) { throw StateError("StreamSink is closed"); } if (_controllerInstance == null) { _controllerInstance = StreamController(sync: true); _controllerCompleter = Completer(); _target .addStream(_controller.stream) .then( (_) { if (_isBound) { // A new stream takes over - forward values to that stream. _controllerCompleter!.complete(this); _controllerCompleter = null; _controllerInstance = null; } else { // No new stream, .close was called. Close _target. _closeTarget(); } }, onError: (error, stackTrace) { if (_isBound) { // A new stream takes over - forward errors to that stream. _controllerCompleter!.completeError(error, stackTrace); _controllerCompleter = null; _controllerInstance = null; } else { // No new stream. No need to close target, as it has already // failed. _completeDoneError(error, stackTrace); } }, ); } return _controllerInstance!; } } class _IOSinkImpl extends _StreamSinkImpl> implements IOSink { Encoding _encoding; bool _encodingMutable = true; _IOSinkImpl(StreamConsumer> target, this._encoding) : super(target); Encoding get encoding => _encoding; void set encoding(Encoding value) { if (!_encodingMutable) { throw StateError("IOSink encoding is not mutable"); } _encoding = value; } void write(Object? obj) { String string = '$obj'; if (string.isEmpty) return; add(_encoding.encode(string)); } void writeAll(Iterable objects, [String separator = ""]) { Iterator iterator = objects.iterator; if (!iterator.moveNext()) return; if (separator.isEmpty) { do { write(iterator.current); } while (iterator.moveNext()); } else { write(iterator.current); while (iterator.moveNext()) { write(separator); write(iterator.current); } } } void writeln([Object? object = ""]) { add(_encoding.encode('$object\n')); } void writeCharCode(int charCode) { write(String.fromCharCode(charCode)); } } �ANN<%F3/IPQI3OJOIOOP'('0LDR9LO%MM.NQ F@O%GN+O%<F:O%'*KO%$ALO%2/-:J9LNI+.R#IN!NOGB4#0,#!030-'C .$O"L$;@Q/"$;)&K,#'&;'7(;0'=*)L6-,D +LH-,M7 !I Q&&: #=*& !% $!((%*dart:io/io_sink.dartGorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/io_service.dartˆZ// Copyright (c) 2013, 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:io"; class _IOService { // This list must be kept in sync with the list in runtime/bin/io_service.h static const int fileExists = 0; static const int fileCreate = 1; static const int fileDelete = 2; static const int fileRename = 3; static const int fileCopy = 4; static const int fileOpen = 5; static const int fileResolveSymbolicLinks = 6; static const int fileClose = 7; static const int filePosition = 8; static const int fileSetPosition = 9; static const int fileTruncate = 10; static const int fileLength = 11; static const int fileLengthFromPath = 12; static const int fileLastAccessed = 13; static const int fileSetLastAccessed = 14; static const int fileLastModified = 15; static const int fileSetLastModified = 16; static const int fileFlush = 17; static const int fileReadByte = 18; static const int fileWriteByte = 19; static const int fileRead = 20; static const int fileReadInto = 21; static const int fileWriteFrom = 22; static const int fileCreateLink = 23; static const int fileDeleteLink = 24; static const int fileRenameLink = 25; static const int fileLinkTarget = 26; static const int fileType = 27; static const int fileIdentical = 28; static const int fileStat = 29; static const int fileLock = 30; static const int fileCreatePipe = 31; static const int socketLookup = 32; static const int socketListInterfaces = 33; static const int socketReverseLookup = 34; static const int directoryCreate = 35; static const int directoryDelete = 36; static const int directoryExists = 37; static const int directoryCreateTemp = 38; static const int directoryListStart = 39; static const int directoryListNext = 40; static const int directoryListStop = 41; static const int directoryRename = 42; static const int sslProcessFilter = 43; external static Future _dispatch(int request, List data); } 9NN<N####!!1"%(&$,*-*-#&'"&'(((("'""(&.-)))-,++)*Edart:io/io_service.dart[org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/io_service_patch.dart†Í// Copyright (c) 2013, 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 "common_patch.dart"; @pragma("vm:external-name", "IOService_NewServicePort") external SendPort _newServicePort(); @patch class _IOService { static final SendPort _port = _newServicePort(); static final RawReceivePort _receivePort = RawReceivePort(( List data, ) { assert(data.length == 2); _forwardResponse(data[0] as int, data[1]); }, 'IO Service'); static HashMap _messageMap = HashMap(); static int _id = 0; @patch static Future _dispatch(int request, List data) { int id; do { id = _getNextId(); } while (_messageMap.containsKey(id)); final Completer completer = Completer(); try { if (_messageMap.isEmpty) { // This is the first outgoing request after being in an idle state, // make sure to mark [_receivePort] alive. _receivePort.keepIsolateAlive = true; } _messageMap[id] = completer; _port.send([id, _receivePort.sendPort, request, data]); } catch (error) { _forwardResponse(id, error); } return completer.future; } static void _forwardResponse(int id, Object? response) { _messageMap.remove(id)!.complete(response); if (_messageMap.isEmpty) { // Last pending response received. We are entering idle state so // mark _receivePort inactive. _id = 0; _receivePort.keepIsolateAlive = false; } } static int _getNextId() { if (_id == 0x7FFFFFFF) _id = 0; return _id++; } } <NN<8%3>/J = +- !L3.#G#;0G%-$#dart:io-patch/io_service_patch.dartÀeÀh³Aorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/link.dartµ¯// Copyright (c) 2013, 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:io"; /// References to filesystem links. @pragma("vm:entry-point") abstract interface class Link implements FileSystemEntity { /// Creates a Link object. @pragma("vm:entry-point") factory Link(String path) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return new _Link(path); } return overrides.createLink(path); } @pragma("vm:entry-point") factory Link.fromRawPath(Uint8List rawPath) { // TODO(bkonyi): handle overrides return new _Link.fromRawPath(rawPath); } /// Creates a [Link] object. /// /// If [path] is a relative path, it will be interpreted relative to the /// current working directory (see [Directory.current]), when used. /// /// If [path] is an absolute path, it will be immune to changes to the /// current working directory. factory Link.fromUri(Uri uri) => new Link(uri.toFilePath()); /// Creates a symbolic link in the file system. /// /// The created link will point to the path at [target], whether that path /// exists or not. /// /// Returns a `Future` that completes with /// the link when it has been created. If the link path already exists, /// the future will complete with an error. /// /// If [recursive] is `false`, the default, the link is created /// only if all directories in its path exist. /// If [recursive] is `true`, all non-existing parent paths /// are created first. The directories in the path of [target] are /// not affected, unless they are also in [path]. /// /// On the Windows platform, this call will create a true symbolic link /// instead of a junction. Windows treats links to files and links to /// directories as different and non-interchangable kinds of links. /// Each link is either a file-link or a directory-link, and the type is /// chosen when the link is created, and the link then counts as either a /// file or directory for most purposes. Different Win32 API calls are /// used to manipulate each. For example, the `DeleteFile` function is /// used to delete links to files, and `RemoveDirectory` must be used to /// delete links to directories. /// /// The created Windows symbolic link will match the type of the [target], /// if [target] exists, otherwise a file-link is created. The type of the /// created link will not change if [target] is later replaced by something /// of a different type, but then the link will not be resolvable by /// [resolveSymbolicLinks]. /// /// In order to create a symbolic link on Windows, Dart must be run in /// Administrator mode or the system must have Developer Mode enabled, /// otherwise a [FileSystemException] will be raised with /// `ERROR_PRIVILEGE_NOT_HELD` set as the errno when this call is made. /// /// On other platforms, the POSIX `symlink()` call is used to make a symbolic /// link containing the string [target]. If [target] is a relative path, /// it will be interpreted relative to the directory containing the link. Future create(String target, {bool recursive = false}); /// Creates a symbolic link in the file system. /// /// The created link will point to the path at [target], whether that path /// exists or not. /// /// If the link path already exists, an exception will be thrown. /// /// If [recursive] is `false`, the default, the link is created only if all /// directories in its path exist. If [recursive] is `true`, all /// non-existing parent paths are created first. The directories in /// the path of [target] are not affected, unless they are also in [path]. /// /// On the Windows platform, this call will create a true symbolic link /// instead of a junction. Windows treats links to files and links to /// directories as different and non-interchangable kinds of links. /// Each link is either a file-link or a directory-link, and the type is /// chosen when the link is created, and the link then counts as either a /// file or directory for most purposes. Different Win32 API calls are /// used to manipulate each. For example, the `DeleteFile` function is /// used to delete links to files, and `RemoveDirectory` must be used to /// delete links to directories. /// /// The created Windows symbolic link will match the type of the [target], /// if [target] exists, otherwise a file-link is created. The type of the /// created link will not change if [target] is later replaced by something /// of a different type, but then the link will not be resolvable by /// [resolveSymbolicLinks]. /// /// In order to create a symbolic link on Windows, Dart must be run in /// Administrator mode or the system must have Developer Mode enabled, /// otherwise a [FileSystemException] will be raised with /// `ERROR_PRIVILEGE_NOT_HELD` set as the errno when this call is made. /// /// On other platforms, the POSIX `symlink()` call is used to make a symbolic /// link containing the string [target]. If [target] is a relative path, /// it will be interpreted relative to the directory containing the link. void createSync(String target, {bool recursive = false}); /// Synchronously updates an existing link. /// /// Deletes the existing link at [path] and uses [createSync] to create a new /// link to [target]. Throws [PathNotFoundException] if the original link /// does not exist or any [FileSystemException] that [deleteSync] or /// [createSync] can throw. void updateSync(String target); /// Updates an existing link. /// /// Deletes the existing link at [path] and creates a new link to [target], /// using [create]. /// /// Returns a future which completes with this `Link` if successful, /// and with a [PathNotFoundException] if there is no existing link at [path], /// or with any [FileSystemException] that [delete] or [create] can throw. Future update(String target); Future resolveSymbolicLinks(); String resolveSymbolicLinksSync(); /// Renames this link. /// /// Returns a `Future` that completes with a [Link] /// for the renamed link. /// /// If [newPath] identifies an existing file or link, that entity is removed /// first. If [newPath] identifies an existing directory then the future /// completes with a [FileSystemException]. Future rename(String newPath); /// Synchronously renames this link. /// /// Returns a [Link] instance for the renamed link. /// /// If [newPath] identifies an existing file or link, that entity is removed /// first. If [newPath] identifies an existing directory then /// [FileSystemException] is thrown. Link renameSync(String newPath); /// Deletes this [Link]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a link then that path is deleted. Otherwise, /// [delete] completes with a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [Link] cannot be deleted, then [delete] completes with a /// [FileSystemException]. Future delete({bool recursive = false}); /// Synchronously deletes this [Link]. /// /// If [recursive] is `false`: /// /// * If [path] corresponds to a link then that path is deleted. Otherwise, /// [delete] throws a [FileSystemException]. /// /// If [recursive] is `true`: /// /// * The [FileSystemEntity] at [path] is deleted regardless of type. If /// [path] corresponds to a file or link, then that file or link is /// deleted. If [path] corresponds to a directory, then it and all /// sub-directories and files in those directories are deleted. Links /// are not followed when deleting recursively. Only the link is deleted, /// not its target. This behavior allows [delete] to be used to /// unconditionally delete any file system object. /// /// If this [Link] cannot be deleted, then [delete] throws a /// [FileSystemException]. void deleteSync({bool recursive = false}); /// A [Link] instance whose path is the absolute path to this [Link]. /// /// The absolute path is computed by prefixing /// a relative path with the current working directory, or returning /// an absolute path unchanged. Link get absolute; /// Gets the target of the link. /// /// Returns a future that completes with the path to the target. /// /// If the returned target is a relative path, it is relative to the /// directory containing the link. /// /// If the link does not exist, or is not a link, the future completes with /// a [FileSystemException]. Future target(); /// Synchronously gets the target of the link. /// /// Returns the path to the target. /// /// If the returned target is a relative path, it is relative to the /// directory containing the link. /// /// If the link does not exist, or is not a link, /// throws a [FileSystemException]. String targetSync(); } class _Link extends FileSystemEntity implements Link { final String _path; final Uint8List _rawPath; _Link(String path) : _path = path, _rawPath = FileSystemEntity._toUtf8Array(path); _Link.fromRawPath(Uint8List rawPath) : _rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawPath), _path = FileSystemEntity._toStringFromUtf8Array(rawPath); String get path => _path; String toString() => "Link: '$path'"; Future exists() => FileSystemEntity._isLinkRaw(_rawPath); bool existsSync() => FileSystemEntity._isLinkRawSync(_rawPath); Link get absolute => isAbsolute ? this : _Link(_absolutePath); Future create(String target, {bool recursive = false}) { var result = recursive ? parent.create(recursive: true) : new Future.value(null); return result .then( (_) => _File._dispatchWithNamespace(_IOService.fileCreateLink, [ null, _rawPath, target, ]), ) .then((response) { _checkForErrorResponse( response, "Cannot create link to target '$target'", path, ); return this; }); } void createSync(String target, {bool recursive = false}) { if (recursive) { parent.createSync(recursive: true); } var result = _File._createLink(_Namespace._namespace, _rawPath, target); throwIfError(result, "Cannot create link", path); } void updateSync(String target) { // TODO(12414): Replace with atomic update, where supported by platform. // Atomically changing a link can be done by creating the new link, with // a different name, and using the rename() posix call to move it to // the old name atomically. deleteSync(); createSync(target); } Future update(String target) { // TODO(12414): Replace with atomic update, where supported by platform. // Atomically changing a link can be done by creating the new link, with // a different name, and using the rename() posix call to move it to // the old name atomically. return delete().then((_) => create(target)); } Future _delete({bool recursive = false}) { if (recursive) { return new Directory.fromRawPath( _rawPath, ).delete(recursive: true).then((_) => this); } return _File._dispatchWithNamespace(_IOService.fileDeleteLink, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot delete link", path); return this; }); } void _deleteSync({bool recursive = false}) { if (recursive) { return new Directory.fromRawPath(_rawPath).deleteSync(recursive: true); } var result = _File._deleteLinkNative(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot delete link", path); } Future rename(String newPath) { return _File._dispatchWithNamespace(_IOService.fileRenameLink, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse( response, "Cannot rename link to '$newPath'", path, ); return new Link(newPath); }); } Link renameSync(String newPath) { var result = _File._renameLink(_Namespace._namespace, _rawPath, newPath); throwIfError(result, "Cannot rename link '$path' to '$newPath'"); return new Link(newPath); } Future target() { return _File._dispatchWithNamespace(_IOService.fileLinkTarget, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot get target of link", path); return response as String; }); } String targetSync() { var result = _File._linkTarget(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot read link", path); return result; } static throwIfError(Object? result, String msg, [String path = ""]) { if (result is OSError) { throw FileSystemException._fromOSError(result, msg, path); } } } �kNN<$<8'0&+KFI!?2M3J.B1>E4JHFKLIIK#MLNGII _getPointer(_namespace); } @patch @pragma("vm:entry-point") class _Namespace { @patch @pragma("vm:entry-point", "call") static void _setupNamespace(namespace) { _NamespaceImpl._setupNamespace(namespace); } @patch static _Namespace get _namespace => _NamespaceImpl._namespace; @patch static int get _namespacePointer => _NamespaceImpl._namespacePointer; } 6NN<;2G6=6%I@2+?)$L/E? $+/ A H"dart:io-patch/namespace_patch.dartÀeÀh³Norg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/network_profiling.dart¯+// Copyright (c) 2019, 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:io"; // TODO(bkonyi): refactor into io_resource_info.dart const int _versionMajor = 4; const int _versionMinor = 0; const String _tcpSocket = 'tcp'; const String _udpSocket = 'udp'; int _nextSocketId = 0; /// Creates a Map conforming to the `HttpProfileRequest` type defined in the /// dart:io service extension spec from an element of dart:developer's /// `_developerProfilingData`. Map _createHttpProfileRequestFromProfileMap( Map requestProfile, { required bool ref, }) { final responseData = requestProfile['responseData'] as Map?; return { 'type': '${ref ? '@' : ''}HttpProfileRequest', 'id': requestProfile['id']!, 'isolateId': requestProfile['isolateId']!, 'method': requestProfile['requestMethod']!, 'uri': requestProfile['requestUri']!, 'events': requestProfile['events']!, 'startTime': requestProfile['requestStartTimestamp']!, if (requestProfile['requestEndTimestamp'] != null) 'endTime': requestProfile['requestEndTimestamp']!, 'request': requestProfile['requestData']!, if (responseData != null) 'response': responseData, if (!ref && requestProfile['requestEndTimestamp'] != null) 'requestBody': requestProfile['requestBodyBytes']!, if (!ref && responseData?['endTime'] != null) 'responseBody': requestProfile['responseBodyBytes']!, }; } @pragma('vm:entry-point', !bool.fromEnvironment("dart.vm.product")) abstract class _NetworkProfiling { // Http relative RPCs static const _kHttpEnableTimelineLogging = 'ext.dart.io.httpEnableTimelineLogging'; static const _kGetHttpProfileRPC = 'ext.dart.io.getHttpProfile'; static const _kGetHttpProfileRequestRPC = 'ext.dart.io.getHttpProfileRequest'; static const _kClearHttpProfileRPC = 'ext.dart.io.clearHttpProfile'; // Socket relative RPCs static const _kClearSocketProfileRPC = 'ext.dart.io.clearSocketProfile'; static const _kGetSocketProfileRPC = 'ext.dart.io.getSocketProfile'; static const _kSocketProfilingEnabledRPC = 'ext.dart.io.socketProfilingEnabled'; // TODO(zichangguo): This version number represents the version of service // extension of dart:io. Consider moving this out of web profiler class, // if more methods added to dart:io, static const _kGetVersionRPC = 'ext.dart.io.getVersion'; @pragma('vm:entry-point', !bool.fromEnvironment("dart.vm.product")) static void _registerServiceExtension() { registerExtension(_kHttpEnableTimelineLogging, _serviceExtensionHandler); registerExtension(_kGetSocketProfileRPC, _serviceExtensionHandler); registerExtension(_kSocketProfilingEnabledRPC, _serviceExtensionHandler); registerExtension(_kClearSocketProfileRPC, _serviceExtensionHandler); registerExtension(_kGetVersionRPC, _serviceExtensionHandler); registerExtension(_kGetHttpProfileRPC, _serviceExtensionHandler); registerExtension(_kGetHttpProfileRequestRPC, _serviceExtensionHandler); registerExtension(_kClearHttpProfileRPC, _serviceExtensionHandler); } // Note this function only returns a `Future` because that is required by the // signature of `registerExtension`. We might be able to change the signature // of ServiceExtensionHandler to use `FutureOr` instead of `Future`. static Future _serviceExtensionHandler( String method, Map parameters, ) async { try { String responseJson; switch (method) { case _kHttpEnableTimelineLogging: if (parameters.containsKey('enabled')) { _setHttpEnableTimelineLogging(parameters); } responseJson = _getHttpEnableTimelineLogging(); break; case _kGetHttpProfileRPC: final updatedSince = parameters.containsKey('updatedSince') ? int.tryParse(parameters['updatedSince']!) : null; responseJson = json.encode({ 'type': 'HttpProfile', 'timestamp': DateTime.now().microsecondsSinceEpoch, 'requests': [ ...HttpProfiler.serializeHttpProfileRequests(updatedSince), ...getHttpClientProfilingData() .where( (Map p) => updatedSince == null || (p['_lastUpdateTime'] as int) >= updatedSince, ) .map( (p) => _createHttpProfileRequestFromProfileMap(p, ref: true), ), ], }); break; case _kGetHttpProfileRequestRPC: responseJson = _getHttpProfileRequest(parameters); break; case _kClearHttpProfileRPC: HttpProfiler.clear(); responseJson = _success(); break; case _kGetSocketProfileRPC: responseJson = _SocketProfile.toJson(); break; case _kSocketProfilingEnabledRPC: responseJson = _socketProfilingEnabled(parameters); break; case _kClearSocketProfileRPC: responseJson = _SocketProfile.clear(); break; case _kGetVersionRPC: responseJson = getVersion(); break; default: return ServiceExtensionResponse.error( ServiceExtensionResponse.extensionError, 'Method $method does not exist', ); } return ServiceExtensionResponse.result(responseJson); } catch (errorMessage) { return ServiceExtensionResponse.error( ServiceExtensionResponse.invalidParams, errorMessage.toString(), ); } } static String getVersion() => json.encode({ 'type': 'Version', 'major': _versionMajor, 'minor': _versionMinor, }); } String _success() => json.encode({'type': 'Success'}); String _invalidArgument(String argument, Object value) => "Value for parameter '$argument' is not valid: $value"; String _missingArgument(String argument) => "Parameter '$argument' is required"; String _getHttpEnableTimelineLogging() => json.encode({ 'type': 'HttpTimelineLoggingState', 'enabled': HttpClient.enableTimelineLogging, }); String _setHttpEnableTimelineLogging(Map parameters) { const String kEnabled = 'enabled'; if (!parameters.containsKey(kEnabled)) { throw _missingArgument(kEnabled); } final enable = parameters[kEnabled]!.toLowerCase(); if (enable != 'true' && enable != 'false') { throw _invalidArgument(kEnabled, enable); } HttpClient.enableTimelineLogging = enable == 'true'; return _success(); } String _getHttpProfileRequest(Map parameters) { final id = parameters['id']; if (id == null) { throw _missingArgument('id'); } final Map? request; if (id.startsWith('from_package/')) { final profileMap = getHttpClientProfilingData().elementAtOrNull( int.parse(id.substring('from_package/'.length)) - 1, ); request = profileMap == null ? null : _createHttpProfileRequestFromProfileMap( profileMap.cast(), ref: false, ); } else { request = HttpProfiler.getHttpProfileRequest(id)?.toJson(ref: false); } if (request == null) { throw "Unable to find request with id: '$id'"; } return json.encode(request); } String _socketProfilingEnabled(Map parameters) { const String kEnabled = 'enabled'; if (parameters.containsKey(kEnabled)) { final enable = parameters[kEnabled]!.toLowerCase(); if (enable != 'true' && enable != 'false') { throw _invalidArgument(kEnabled, enable); } enable == 'true' ? _SocketProfile.start() : _SocketProfile.pause(); } return json.encode({ 'type': 'SocketProfilingState', 'enabled': _SocketProfile.enableSocketProfiling, }); } abstract class _SocketProfile { static const _kType = 'SocketProfile'; static set enableSocketProfiling(bool enabled) { if (enabled != _enableSocketProfiling) { postEvent('SocketProfilingStateChange', { 'isolateId': Service.getIsolateID(Isolate.current), 'enabled': enabled, }); _enableSocketProfiling = enabled; } } static bool get enableSocketProfiling => _enableSocketProfiling; static bool _enableSocketProfiling = false; static Map _idToSocketStatistic = {}; static String toJson() => json.encode({ 'type': _kType, 'sockets': _idToSocketStatistic.values.map((f) => f.toMap()).toList(), }); static void collectNewSocket( int id, String type, InternetAddress addr, int port, ) { if (!_enableSocketProfiling) { return; } final address = (addr.type == InternetAddress.anyIPv6 || addr.type == InternetAddress.loopbackIPv6) ? '[${addr.address}]' : addr.address; assert(!_idToSocketStatistic.containsKey(id)); _idToSocketStatistic[id] = _SocketStatistic( id, startTime: Timeline.now, socketType: type, address: address, port: port, ); } static void collectStatistic( int id, _SocketProfileType type, [ Object? object, ]) { if (!_enableSocketProfiling) { return; } // Skip any socket that started before `_enableSocketProfiling` was turned // on. final stats = _idToSocketStatistic[id]; if (stats == null) return; switch (type) { case _SocketProfileType.endTime: stats.endTime = Timeline.now; break; case _SocketProfileType.readBytes: if (object == null) return; stats.readBytes += object as int; stats.lastReadTime = Timeline.now; break; case _SocketProfileType.writeBytes: if (object == null) return; stats.writeBytes += object as int; stats.lastWriteTime = Timeline.now; break; case _SocketProfileType.startTime: case _SocketProfileType.socketType: case _SocketProfileType.address: case _SocketProfileType.port: throw ArgumentError( 'The "${type}" type can only be set on initialization', ); } } static String start() { enableSocketProfiling = true; return _success(); } static String pause() { enableSocketProfiling = false; return _success(); } // clear the storage if _idToSocketStatistic has been initialized. static String clear() { _idToSocketStatistic.clear(); return _success(); } } /// The [_SocketProfileType] is used as a parameter for /// [_SocketProfile.collectStatistic] to determine the type of statistic. enum _SocketProfileType { startTime, endTime, address, port, socketType, readBytes, writeBytes, } /// Socket statistic class _SocketStatistic { final int id; final int startTime; int? endTime; final String address; final int port; final String socketType; int readBytes = 0; int writeBytes = 0; int? lastWriteTime; int? lastReadTime; _SocketStatistic( this.id, { required this.startTime, required this.socketType, required this.address, required this.port, }); Map toMap() { final map = { 'id': id.toString(), 'startTime': startTime, 'address': address, 'port': port, 'socketType': socketType, }; // TODO(srawlins): Replace with null-aware elements. _setIfNotNull(map, 'endTime', endTime); _setIfNotNull(map, 'readBytes', readBytes); _setIfNotNull(map, 'writeBytes', writeBytes); _setIfNotNull(map, 'lastWriteTime', lastWriteTime); _setIfNotNull(map, 'lastReadTime', lastReadTime); return map; } void _setIfNotNull(Map json, String key, Object? value) { if (value == null) return; json[key] = value; } } �wMN<5!!MG>)P 3!/0*);79/8?:2<D#-/CQGKG-,MK';F,NHNJBFMHPPGD$ *37 :"F:'#@J.00GO)=$ %$2*>&1'15- <-0! .7:<Q8&/G%+&6/.7@"'(E;!3/ J3A%*810H$5 )3-0< (C.?*K #1731  #O ,'&)$*+*$+,)*'$B "#E"8J   " 9,0286Ldart:io/network_profiling.dartÀeÀh³ÀE/ÀfdForg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/overrides.dartÀKG// Copyright (c) 2017, 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:io"; final _ioOverridesToken = Object(); /// Facilities for overriding various APIs of `dart:io` with mock /// implementations. /// /// This abstract base class should be extended with overrides for the /// operations needed to construct mocks. The implementations in this base class /// default to the actual `dart:io` implementation. For example: /// /// ```dart /// class MyDirectory implements Directory { /// ... /// // An implementation of the Directory interface /// ... /// } /// /// void main() { /// IOOverrides.runZoned(() { /// ... /// // Operations will use MyDirectory instead of dart:io's Directory /// // implementation whenever Directory is used. /// ... /// }, createDirectory: (String path) => new MyDirectory(path)); /// } /// ``` abstract base class IOOverrides { static IOOverrides? _global; static IOOverrides? get current { return Zone.current[_ioOverridesToken] ?? _global; } /// The [IOOverrides] to use in the root [Zone]. /// /// These are the [IOOverrides] that will be used in the root [Zone], and in /// [Zone]'s that do not set [IOOverrides] and whose ancestors up to the root /// [Zone] also do not set [IOOverrides]. static set global(IOOverrides? overrides) { _global = overrides; } /// Runs [body] in a fresh [Zone] using the provided overrides. /// /// See the documentation on the corresponding methods of [IOOverrides] for /// information about what the optional arguments do. static R runZoned( R body(), { // Directory Directory Function(String)? createDirectory, Directory Function()? getCurrentDirectory, void Function(String)? setCurrentDirectory, Directory Function()? getSystemTempDirectory, // File File Function(String)? createFile, // FileStat Future Function(String)? stat, FileStat Function(String)? statSync, // FileSystemEntity Future Function(String, String)? fseIdentical, bool Function(String, String)? fseIdenticalSync, Future Function(String, bool)? fseGetType, FileSystemEntityType Function(String, bool)? fseGetTypeSync, // _FileSystemWatcher Stream Function(String, int, bool)? fsWatch, bool Function()? fsWatchIsSupported, // Link Link Function(String)? createLink, // Socket Future Function( dynamic, int, { dynamic sourceAddress, int sourcePort, Duration? timeout, })? socketConnect, Future> Function( dynamic, int, { dynamic sourceAddress, int sourcePort, })? socketStartConnect, // ServerSocket Future Function( dynamic, int, { int backlog, bool v6Only, bool shared, })? serverSocketBind, // Standard Streams Stdin Function()? stdin, Stdout Function()? stdout, Stdout Function()? stderr, // Process exit @Since("3.10") Never Function(int)? exit, }) { // Avoid building chains of override scopes. Just copy outer scope's // functions and `_previous`. var current = IOOverrides.current; _IOOverridesScope? currentScope; if (current is _IOOverridesScope) { currentScope = current; current = currentScope._previous; } IOOverrides overrides = _IOOverridesScope( current, // Directory createDirectory ?? currentScope?._createDirectory, getCurrentDirectory ?? currentScope?._getCurrentDirectory, setCurrentDirectory ?? currentScope?._setCurrentDirectory, getSystemTempDirectory ?? currentScope?._getSystemTempDirectory, // File createFile ?? currentScope?._createFile, // FileStat stat ?? currentScope?._stat, statSync ?? currentScope?._statSync, // FileSystemEntity fseIdentical ?? currentScope?._fseIdentical, fseIdenticalSync ?? currentScope?._fseIdenticalSync, fseGetType ?? currentScope?._fseGetType, fseGetTypeSync ?? currentScope?._fseGetTypeSync, // _FileSystemWatcher fsWatch ?? currentScope?._fsWatch, fsWatchIsSupported ?? currentScope?._fsWatchIsSupported, // Link createLink ?? currentScope?._createLink, // Socket socketConnect ?? currentScope?._socketConnect, socketStartConnect ?? currentScope?._socketStartConnect, // ServerSocket serverSocketBind ?? currentScope?._serverSocketBind, // Standard streams stdin ?? currentScope?._stdin, stdout ?? currentScope?._stdout, stderr ?? currentScope?._stderr, // Process exit exit ?? currentScope?._exit, ); return dart_async.runZoned( body, zoneValues: {_ioOverridesToken: overrides}, ); } /// Runs [body] in a fresh [Zone] using the overrides found in [overrides]. /// /// Note that [overrides] should be an instance of a class that extends /// [IOOverrides]. static R runWithIOOverrides(R body(), IOOverrides overrides) { return dart_async.runZoned( body, zoneValues: {_ioOverridesToken: overrides}, ); } // Directory /// Creates a new [Directory] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new Directory()` and `new Directory.fromUri()`. Directory createDirectory(String path) => _Directory(path); /// Returns the current working directory. /// /// When this override is installed, this function overrides the behavior of /// the static getter `Directory.current` Directory getCurrentDirectory() => _Directory.current; /// Sets the current working directory to be [path]. /// /// When this override is installed, this function overrides the behavior of /// the setter `Directory.current`. void setCurrentDirectory(String path) { _Directory.current = path; } /// Returns the system temporary directory. /// /// When this override is installed, this function overrides the behavior of /// `Directory.systemTemp`. Directory getSystemTempDirectory() => _Directory.systemTemp; // File /// Creates a new [File] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new File()` and `new File.fromUri()`. File createFile(String path) => _File(path); // FileStat /// Asynchronously returns [FileStat] information for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileStat.stat()`. Future stat(String path) { return FileStat._stat(path); } /// Returns [FileStat] information for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileStat.statSync()`. FileStat statSync(String path) { return FileStat._statSyncInternal(path); } // FileSystemEntity /// Asynchronously returns `true` if [path1] and [path2] are paths to the /// same file system object. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.identical`. Future fseIdentical(String path1, String path2) { return FileSystemEntity._identical(path1, path2); } /// Returns `true` if [path1] and [path2] are paths to the /// same file system object. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.identicalSync`. bool fseIdenticalSync(String path1, String path2) { return FileSystemEntity._identicalSync(path1, path2); } /// Asynchronously returns the [FileSystemEntityType] for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.type`. Future fseGetType(String path, bool followLinks) { return FileSystemEntity._getTypeRequest(utf8.encode(path), followLinks); } /// Returns the [FileSystemEntityType] for [path]. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.typeSync`. FileSystemEntityType fseGetTypeSync(String path, bool followLinks) { return FileSystemEntity._getTypeSyncHelper(utf8.encode(path), followLinks); } // _FileSystemWatcher /// Returns a [Stream] of [FileSystemEvent]s. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.watch()`. Stream fsWatch(String path, int events, bool recursive) { return _FileSystemWatcher._watch(path, events, recursive); } /// Returns `true` when [FileSystemEntity.watch] is supported. /// /// When this override is installed, this function overrides the behavior of /// `FileSystemEntity.isWatchSupported`. bool fsWatchIsSupported() => _FileSystemWatcher.isSupported; // Link /// Returns a new [Link] object for the given [path]. /// /// When this override is installed, this function overrides the behavior of /// `new Link()` and `new Link.fromUri()`. Link createLink(String path) => _Link(path); // Socket /// Asynchronously returns a [Socket] connected to the given host and port. /// /// When this override is installed, this function overrides the behavior of /// `Socket.connect(...)`. Future socketConnect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }) { return Socket._connect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); } /// Asynchronously returns a [ConnectionTask] that connects to the given host /// and port when successful. /// /// When this override is installed, this function overrides the behavior of /// `Socket.startConnect(...)`. Future> socketStartConnect( host, int port, { sourceAddress, int sourcePort = 0, }) { return Socket._startConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); } // ServerSocket /// Asynchronously returns a [ServerSocket] that connects to the given address /// and port when successful. /// /// When this override is installed, this function overrides the behavior of /// `ServerSocket.bind(...)`. Future serverSocketBind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) { return ServerSocket._bind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); } // Process exit /// Exit the Dart VM process immediately with the given exit code. /// /// When this override is installed, this function overrides the behavior of /// `exit(...)`. @Since("3.10") Never exit(int code) { if (!_EmbedderConfig._mayExit) { throw new UnsupportedError( "This embedder disallows calling dart:io's exit()", ); } _ProcessUtils._exit(code); } // Standard streams /// The standard input stream of data read by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stdin` getter. Stdin get stdin { return _stdin; } /// The standard output stream of data written by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stdout` getter. Stdout get stdout { return _stdout; } /// The standard output stream of errors written by this program. /// /// When this override is installed, this getter overrides the behavior of /// the top-level `stderr` getter. Stdout get stderr { return _stderr; } } final class _IOOverridesScope extends IOOverrides { final IOOverrides? _previous; // Directory final Directory Function(String)? _createDirectory; final Directory Function()? _getCurrentDirectory; final void Function(String)? _setCurrentDirectory; final Directory Function()? _getSystemTempDirectory; // File final File Function(String)? _createFile; // FileStat final Future Function(String)? _stat; final FileStat Function(String)? _statSync; // FileSystemEntity final Future Function(String, String)? _fseIdentical; final bool Function(String, String)? _fseIdenticalSync; final Future Function(String, bool)? _fseGetType; final FileSystemEntityType Function(String, bool)? _fseGetTypeSync; // _FileSystemWatcher final Stream Function(String, int, bool)? _fsWatch; final bool Function()? _fsWatchIsSupported; // Link final Link Function(String)? _createLink; // Socket final Future Function( dynamic, int, { dynamic sourceAddress, int sourcePort, Duration? timeout, })? _socketConnect; final Future> Function( dynamic, int, { dynamic sourceAddress, int sourcePort, })? _socketStartConnect; // ServerSocket final Future Function( dynamic, int, { int backlog, bool v6Only, bool shared, })? _serverSocketBind; // Standard streams final Stdin Function()? _stdin; final Stdout Function()? _stdout; final Stdout Function()? _stderr; // Process exit final Never Function(int)? _exit; _IOOverridesScope( this._previous, // Directory this._createDirectory, this._getCurrentDirectory, this._setCurrentDirectory, this._getSystemTempDirectory, // File this._createFile, // FileStat this._stat, this._statSync, // FileSystemEntity this._fseIdentical, this._fseIdenticalSync, this._fseGetType, this._fseGetTypeSync, // _FileSystemWatcher this._fsWatch, this._fsWatchIsSupported, // Link this._createLink, // Socket this._socketConnect, this._socketStartConnect, // ServerSocket this._serverSocketBind, // Standard streams this._stdin, this._stdout, this._stderr, // Process exit this._exit, ); // Directory @override Directory createDirectory(String path) => _createDirectory?.call(path) ?? _previous?.createDirectory(path) ?? super.createDirectory(path); @override Directory getCurrentDirectory() => _getCurrentDirectory?.call() ?? _previous?.getCurrentDirectory() ?? super.getCurrentDirectory(); @override void setCurrentDirectory(String path) { var setter = _setCurrentDirectory; if (setter != null) { setter(path); } else { super.setCurrentDirectory(path); } } @override Directory getSystemTempDirectory() => _getSystemTempDirectory?.call() ?? _previous?.getSystemTempDirectory() ?? super.getSystemTempDirectory(); // File @override File createFile(String path) => _createFile?.call(path) ?? _previous?.createFile(path) ?? super.createFile(path); // FileStat @override Future stat(String path) => _stat?.call(path) ?? _previous?.stat(path) ?? super.stat(path); @override FileStat statSync(String path) => _statSync?.call(path) ?? _previous?.statSync(path) ?? super.statSync(path); // FileSystemEntity @override Future fseIdentical(String path1, String path2) => _fseIdentical?.call(path1, path2) ?? _previous?.fseIdentical(path1, path2) ?? super.fseIdentical(path1, path2); @override bool fseIdenticalSync(String path1, String path2) => _fseIdenticalSync?.call(path1, path2) ?? _previous?.fseIdenticalSync(path1, path2) ?? super.fseIdenticalSync(path1, path2); @override Future fseGetType(String path, bool followLinks) => _fseGetType?.call(path, followLinks) ?? _previous?.fseGetType(path, followLinks) ?? super.fseGetType(path, followLinks); @override FileSystemEntityType fseGetTypeSync(String path, bool followLinks) => _fseGetTypeSync?.call(path, followLinks) ?? _previous?.fseGetTypeSync(path, followLinks) ?? super.fseGetTypeSync(path, followLinks); // _FileSystemWatcher @override Stream fsWatch(String path, int events, bool recursive) => _fsWatch?.call(path, events, recursive) ?? _previous?.fsWatch(path, events, recursive) ?? super.fsWatch(path, events, recursive); @override bool fsWatchIsSupported() => _fsWatchIsSupported?.call() ?? _previous?.fsWatchIsSupported() ?? super.fsWatchIsSupported(); // Link @override Link createLink(String path) => _createLink?.call(path) ?? _previous?.createLink(path) ?? super.createLink(path); // Socket @override Future socketConnect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }) => _socketConnect?.call( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ) ?? _previous?.socketConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ) ?? super.socketConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); @override Future> socketStartConnect( host, int port, { sourceAddress, int sourcePort = 0, }) => _socketStartConnect?.call( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ) ?? _previous?.socketStartConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ) ?? super.socketStartConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); // ServerSocket @override Future serverSocketBind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) => _serverSocketBind?.call( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ) ?? _previous?.serverSocketBind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ) ?? super.serverSocketBind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); // Process exit @override Never exit(int code) => _exit?.call(code) ?? _previous?.exit.call(code) ?? super.exit(code); // Standard streams @override Stdin get stdin => _stdin?.call() ?? _previous?.stdin ?? super.stdin; @override Stdout get stdout => _stdout?.call() ?? _previous?.stdout ?? super.stdout; @override Stdout get stderr => _stderr?.call() ?? _previous?.stderr ?? super.stderr; } ‚ÅNN<$BGQA - 6  J6 C"$73OP,.BN81/02 '-)95EAB) ' - # .I"'%((/9AAG/#+3;/7)?/5?;%''## 2NJD# 2=O7>-O,97O&*.O? 8O-/@O'!1O#-LO$:6=O(6:DOKM5O#GP0O"M?AO+? 8O-/ NO  $P O"5 ! $Q O )  EO%"< >M$BM%DM%4 6457 ,2.>:JFG. , ! 1 ' "$$$"   ,&*# %&*# *' ' ()-& "!% (F $# ;+/( 7/3, L.2+ H26/ N15. %)" "!% &  &  &   5 !& %& &  )  #   K H M Mdart:io/overrides.dartœœÀh³Eorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/platform.dart¦// 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:io"; /// Information about the environment in which the current program is running. /// /// Platform provides information such as the operating system, /// the hostname of the computer, the value of environment variables, /// the path to the running program, /// and other global properties of the program being run. /// /// ## Get the URI of the current Dart script /// /// Use the [script] getter to get the URI to the currently running /// Dart script. /// ```dart /// import 'dart:io' show Platform; /// /// void main() { /// // Get the URI of the script being run. /// var uri = Platform.script; /// // Convert the URI to a path. /// var path = uri.toFilePath(); /// } /// ``` /// ## Get the value of an environment variable /// /// The [environment] getter returns a the names and values of environment /// variables in a [Map] that contains key-value pairs of strings. The Map is /// unmodifiable. This sample shows how to get the value of the `PATH` /// environment variable. /// ```dart /// import 'dart:io' show Platform; /// /// void main() { /// Map envVars = Platform.environment; /// print(envVars['PATH']); /// } /// ``` /// ## Determine the OS /// /// You can get the name of the operating system as a string with the /// [operatingSystem] getter. You can also use one of the static boolean /// getters: [isMacOS], [isLinux], [isWindows], etc. /// ```dart /// import 'dart:io' show Platform; /// /// void main() { /// // Get the operating system as a string. /// String os = Platform.operatingSystem; /// // Or, use a predicate getter. /// if (Platform.isMacOS) { /// print('is a Mac'); /// } else { /// print('is not a Mac'); /// } /// } /// ``` abstract final class Platform { /// The number of individual execution units of the machine. static final numberOfProcessors = _Platform.numberOfProcessors; /// The path separator used by the operating system to separate /// components in file paths. @pragma("vm:platform-const") static final pathSeparator = _Platform.pathSeparator; /// A string representing the operating system or platform. /// /// Possible values include: /// * "android" /// * "fuchsia" /// * "ios" /// * "linux" /// * "macos" /// * "windows" /// /// Note that this list may change over time so platform-specific logic /// should be guarded by the appropriate Boolean getter e.g. [isMacOS]. @pragma("vm:platform-const") @pragma("vm:shared") static final operatingSystem = _Platform.operatingSystem; /// A string representing the version of the operating system or platform. /// /// The format of this string will vary by operating system, platform and /// version and is not suitable for parsing. For example: /// "Linux 5.11.0-1018-gcp #20~20.04.2-Ubuntu SMP Fri Sep 3 01:01:37 UTC 2021" /// "Version 14.5 (Build 18E182)" /// '"Windows 10 Pro" 10.0 (Build 19043)' @pragma("vm:shared") static final operatingSystemVersion = _Platform.operatingSystemVersion; /// The local hostname for the system. /// /// For example: /// "mycomputer.corp.example.com" /// "mycomputer" /// /// Uses the platform /// [`gethostname`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html) /// implementation. @pragma("vm:shared") static final localHostname = _Platform.localHostname; /// The version of the current Dart runtime. /// /// The value is a [semantic versioning](http://semver.org) /// string representing the version of the current Dart runtime, /// possibly followed by whitespace and other version and /// build details. @pragma("vm:shared") static final version = _Platform.version; /// Get the name of the current locale. /// /// The result usually consists of /// - a language (e.g., "en"), or /// - a language and country code (e.g. "en_US", "de_AT"), or /// - a language, country code and character set (e.g. "en_US.UTF-8"). /// /// On macOS and iOS, the locale is taken from CFLocaleGetIdentifier. /// /// On Linux and Fuchsia, the locale is taken from the "LANG" environment /// variable, which may be set to any value. For example: /// ```shell /// LANG=kitten dart myfile.dart # localeName is "kitten" /// ``` /// /// On Android, the value will not change while the application is running, /// even if the user adjusts their language settings. /// /// See https://en.wikipedia.org/wiki/Locale_(computer_software) static String get localeName => _Platform.localeName(); /// Whether the operating system is a version of /// [Linux](https://en.wikipedia.org/wiki/Linux). /// /// This value is `false` if the operating system is a specialized /// version of Linux that identifies itself by a different name, /// for example Android (see [isAndroid]). @pragma("vm:platform-const") @pragma("vm:shared") static final bool isLinux = (operatingSystem == "linux"); /// Whether the operating system is a version of /// [macOS](https://en.wikipedia.org/wiki/MacOS). @pragma("vm:platform-const") @pragma("vm:shared") static final bool isMacOS = (operatingSystem == "macos"); /// Whether the operating system is a version of /// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows). @pragma("vm:platform-const") @pragma("vm:shared") static bool isWindows = (operatingSystem == "windows"); /// Whether the operating system is a version of /// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29). @pragma("vm:platform-const") @pragma("vm:shared") static final bool isAndroid = (operatingSystem == "android"); /// Whether the operating system is a version of /// [iOS](https://en.wikipedia.org/wiki/IOS). @pragma("vm:platform-const") @pragma("vm:shared") static final bool isIOS = (operatingSystem == "ios"); /// Whether the operating system is a version of /// [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia). @pragma("vm:platform-const") @pragma("vm:shared") static final bool isFuchsia = (operatingSystem == "fuchsia"); /// The environment for this process as a map from string key to string value. /// /// The map is unmodifiable, /// and its content is retrieved from the operating system on its first use. /// /// Environment variables on Windows are case-insensitive, /// so on Windows the map is case-insensitive and will convert /// all keys to upper case. /// On other platforms, keys can be distinguished by case. static Map get environment => _Platform.environment; /// The path of the executable used to run the script in this isolate. /// Usually `dart` when running on the Dart VM or the /// compiled script name (`script_name.exe`). /// /// The literal path used to identify the executable. /// This path might be relative or just be a name from which the executable /// was found by searching the system path. /// /// Use [resolvedExecutable] to get an absolute path to the executable. static String get executable => _Platform.executable; /// The path of the executable used to run the script in this /// isolate after it has been resolved by the OS. /// /// This is the absolute path, with all symlinks resolved, to the /// executable used to run the script. /// /// See [executable] for the unresolved version. static String get resolvedExecutable => _Platform.resolvedExecutable; /// The absolute URI of the script being run in this isolate. /// /// If the script argument on the command line is relative, /// it is resolved to an absolute URI before fetching the script, and /// that absolute URI is returned. /// /// URI resolution only does string manipulation on the script path, and this /// may be different from the file system's path resolution behavior. For /// example, a symbolic link immediately followed by '..' will not be /// looked up. /// /// If a compiled Dart script is being executed the URI to the compiled /// script is returned, for example, `file:///full/path/to/script_name.exe`. /// /// If running on the Dart VM the URI to the running Dart script is returned, /// for example, `file:///full/path/to/script_name.dart`. /// /// If the executable environment does not support [script], /// the URI is empty. static Uri get script => _Platform.script; /// The flags passed to the executable used to run the script in this isolate. /// /// These are the command-line flags to the executable that precedes /// the script name. /// Provides a new list every time the value is read. static List get executableArguments => _Platform.executableArguments; /// The `--packages` flag passed to the executable used to run the script /// in this isolate. /// /// If present, it specifies a file describing how Dart packages are looked up. /// /// Is `null` if there is no `--packages` flag. static String? get packageConfig => _Platform.packageConfig; /// The current operating system's default line terminator. /// /// The default character sequence that the operating system /// uses to separate or terminate text lines. /// /// The line terminator is currently the single line-feed character, /// U+000A or `"\n"`, on all supported operating systems except Windows, /// which uses the carriage-return + line-feed sequence, U+000D U+000A or /// `"\r\n"` @pragma("vm:platform-const") static String get lineTerminator => isWindows ? '\r\n' : '\n'; } �NN<O@F%:.D $.!$#0KNG $:FI5 $/,% ?BB 8>JJ<MLC<,*%%AJHL<= N8C:34EC-<34<3L:3O@3083?@QO=A=GI808N.J8@4D)3H@>H%PLHJOP<?-QG8PLR2?>?0GKLAdart:io/platform.dartÀeÀh³Jorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/platform_impl.dart—”// 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:io"; class _Platform { external static int _numberOfProcessors(); external static String _pathSeparator(); external static String _operatingSystem(); external static _operatingSystemVersion(); external static _localHostname(); external static _executable(); external static _resolvedExecutable(); /// Retrieve the entries of the process environment. /// /// The result is an [Iterable] of strings, where each string represents /// an environment entry. /// /// Environment entries should be strings containing /// a non-empty name and a value separated by a '=' character. /// The name does not contain a '=' character, /// so the name is everything up to the first '=' character. /// Values are everything after the first '=' character. /// A value may contain further '=' characters, and it may be empty. /// /// Returns an [OSError] if retrieving the environment fails. external static _environment(); external static List _executableArguments(); external static String? _packageConfig(); external static String _version(); external static String _localeName(); external static Uri _script(); @pragma("vm:shared") static String executable = _executable(); @pragma("vm:shared") static String resolvedExecutable = _resolvedExecutable(); @pragma("vm:shared") static String? packageConfig = _packageConfig(); @pragma("vm:entry-point") static String Function()? _localeClosure; static String localeName() { final result = (_localeClosure == null) ? _localeName() : _localeClosure!(); if (result is OSError) { throw result; } return result; } // Cache the OS environment. This can be an OSError instance if // retrieving the environment failed. static var /*OSError?|Map?*/ _environmentCache; static int get numberOfProcessors => _numberOfProcessors(); static String get pathSeparator => _pathSeparator(); static String get operatingSystem => _operatingSystem(); static Uri get script => _script(); static String? _cachedOSVersion; static String get operatingSystemVersion { if (_cachedOSVersion == null) { var result = _operatingSystemVersion(); if (result is OSError) { throw result; } _cachedOSVersion = result; } return _cachedOSVersion!; } static String get localHostname { var result = _localHostname(); if (result is OSError) { throw result; } return result; } static List get executableArguments => _executableArguments(); static Map get environment { if (_environmentCache == null) { var env = _environment(); if (env is Iterable) { var result = Platform.isWindows ? _CaseInsensitiveStringMap() : Map(); for (var environmentEntry in env) { if (environmentEntry == null) { continue; } // TODO(kallentu): [_environment()] emits Iterable which is // why the cast and check is needed. Every element is a String, // however, so refactor [_environment()] at some point to emit // Iterables instead. var text = environmentEntry as String; // The Strings returned by [_environment()] are expected to be // valid environment entries, but exceptions have been seen // (e.g., an entry of just '=' has been seen on OS/X). // Invalid entries (lines without a '=' or with an empty name) // are discarded. var equalsIndex = text.indexOf('='); if (equalsIndex > 0) { result[text.substring(0, equalsIndex)] = text.substring( equalsIndex + 1, ); } } _environmentCache = UnmodifiableMapView(result); } else { _environmentCache = env; } } if (_environmentCache is OSError) { throw _environmentCache; } else { return _environmentCache!; } } static String get version => _version(); } // Environment variables are case-insensitive on Windows. In order // to reflect that we use a case-insensitive string map on Windows. class _CaseInsensitiveStringMap extends MapBase { final Map _map = Map(); bool containsKey(Object? key) => key is String && _map.containsKey(key.toUpperCase()); bool containsValue(Object? value) => _map.containsValue(value); V? operator [](Object? key) => key is String ? _map[key.toUpperCase()] : null; void operator []=(String key, V value) { _map[key.toUpperCase()] = value; } V putIfAbsent(String key, V ifAbsent()) { return _map.putIfAbsent(key.toUpperCase(), ifAbsent); } void addAll(Map other) { other.forEach((key, value) => this[key.toUpperCase()] = value); } V? remove(Object? key) => key is String ? _map.remove(key.toUpperCase()) : null; void clear() { _map.clear(); } void forEach(void f(String key, V value)) { _map.forEach(f); } Iterable get keys => _map.keys; Iterable get values => _map.values; int get length => _map.length; bool get isEmpty => _map.isEmpty; bool get isNotEmpty => _map.isNotEmpty; Iterable> get entries => _map.entries; Map map(MapEntry transform(String key, V value)) => _map.map(transform); V update(String key, V update(V value), {V ifAbsent()?}) => _map.update(key.toUpperCase(), update, ifAbsent: ifAbsent); void updateAll(V update(String key, V value)) { _map.updateAll(update); } void removeWhere(bool test(String key, V value)) { _map.removeWhere(test); } String toString() => _map.toString(); } €¹NN<-+--$!)7K7A1?;G@"7,%(!,<3,QB(A>7;&#-$.!$#I/% &(2%,* OJI(1IFAI/!E I!( !+CD@0#B25(dart:io/platform_impl.dartÀeÀh³Yorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/platform_patch.dartˆŠ// 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 "common_patch.dart"; @patch @pragma("vm:entry-point") class _Platform { @patch @pragma("vm:external-name", "Platform_NumberOfProcessors") external static int _numberOfProcessors(); @patch @pragma("vm:external-name", "Platform_PathSeparator") external static String _pathSeparator(); @patch @pragma("vm:external-name", "Platform_OperatingSystem") external static String _operatingSystem(); @patch @pragma("vm:external-name", "Platform_OperatingSystemVersion") external static _operatingSystemVersion(); @patch @pragma("vm:external-name", "Platform_LocalHostname") external static _localHostname(); @patch @pragma("vm:external-name", "Platform_ExecutableName") external static _executable(); @patch @pragma("vm:external-name", "Platform_ResolvedExecutableName") external static _resolvedExecutable(); @patch @pragma("vm:external-name", "Platform_Environment") external static _environment(); @patch @pragma("vm:external-name", "Platform_ExecutableArguments") external static List _executableArguments(); @patch @pragma("vm:external-name", "Platform_GetVersion") external static String _version(); @patch @pragma("vm:external-name", "Platform_LocaleName") external static String _localeName(); @patch static String? _packageConfig() => VMLibraryHooks.packageConfigString; @patch static Uri _script() => VMLibraryHooks.platformScript!; // This script singleton is written to by the embedder if applicable. @pragma("vm:entry-point") static void set _nativeScript(String path) { VMLibraryHooks.platformScript = (() { if (path.startsWith('http:') || path.startsWith('https:') || path.startsWith('package:') || path.startsWith('dart:') || path.startsWith('data:') || path.startsWith('file:')) { return Uri.parse(path); } else { return Uri.base.resolveUri(Uri.file(path)); } }); } } ENN< =- 8+ :- A- 8$ 9! A) 6" >7 5% 5( I :H/*&')&&& 4!dart:io-patch/platform_patch.dartÀeÀh³Dorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/process.dartÀn// Copyright (c) 2014, 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:io"; // TODO(ager): The only reason for this class is that we // cannot patch a top-level at this point. class _ProcessUtils { external static Never _exit(int status); external static void _setExitCode(int status); external static int _getExitCode(); external static void _sleep(int millis); external static int _pid(Process? process); external static Stream _watchSignal(ProcessSignal signal); } /// Exit the Dart VM process immediately with the given exit code. /// /// This does not wait for any asynchronous operations to terminate nor execute /// `finally` blocks. Using [exit] is therefore very likely to lose data. /// /// Child processes are not explicitly terminated (but they may terminate /// themselves when they detect that their parent has exited). /// /// While debugging, the VM will not respect the `--pause-isolates-on-exit` /// flag if [exit] is called as invoking this method causes the Dart VM /// process to shutdown immediately. To properly break on exit, consider /// calling [debugger] from `dart:developer` or [Isolate.pause] from /// `dart:isolate` on [Isolate.current] to pause the isolate before /// invoking [exit]. /// /// The handling of exit codes is platform specific. /// /// On Linux and OS X an exit code for normal termination will always /// be in the range `[0..255]`. If an exit code outside this range is /// set the actual exit code will be the lower 8 bits masked off and /// treated as an unsigned value. E.g. using an exit code of -1 will /// result in an actual exit code of 255 being reported. /// /// On Windows the exit code can be set to any 32-bit value. However /// some of these values are reserved for reporting system errors like /// crashes. /// /// Besides this the Dart executable itself uses an exit code of `254` /// for reporting compile time errors and an exit code of `255` for /// reporting runtime error (unhandled exception). /// /// Due to these facts it is recommended to only use exit codes in the /// range \[0..127\] for communicating the result of running a Dart /// program to the surrounding environment. This will avoid any /// cross-platform issues. Never exit(int code) { IOOverrides? overrides = IOOverrides.current; if (overrides == null) { if (!_EmbedderConfig._mayExit) { throw new UnsupportedError( "This embedder disallows calling dart:io's exit()", ); } _ProcessUtils._exit(code); } overrides.exit(code); } /// Set the global exit code for the Dart VM. /// /// The exit code is global for the Dart VM and the last assignment to /// exitCode from any isolate determines the exit code of the Dart VM /// on normal termination. /// /// Default value is `0`. /// /// See [exit] for more information on how to chose a value for the /// exit code. void set exitCode(int code) { _ProcessUtils._setExitCode(code); } /// Get the global exit code for the Dart VM. /// /// The exit code is global for the Dart VM and the last assignment to /// exitCode from any isolate determines the exit code of the Dart VM /// on normal termination. /// /// See [exit] for more information on how to chose a value for the /// exit code. int get exitCode => _ProcessUtils._getExitCode(); /// Sleep for the duration specified in [duration]. /// /// Use this with care, as no asynchronous operations can be processed /// in a isolate while it is blocked in a [sleep] call. /// ```dart /// var duration = const Duration(seconds: 5); /// print('Start sleeping'); /// sleep(duration); /// print('5 seconds has passed'); /// ``` void sleep(Duration duration) { int milliseconds = duration.inMilliseconds; if (milliseconds < 0) { throw new ArgumentError("sleep: duration cannot be negative"); } _ProcessUtils._sleep(milliseconds); } /// Returns the PID of the current process. int get pid => _ProcessUtils._pid(null); /// Methods for retrieving information about the current process. abstract final class ProcessInfo { /// The current resident set size of memory for the process, in bytes. /// /// Note that the meaning of this field is platform dependent. For example, /// some memory accounted for here may be shared with other processes, or if /// the same page is mapped into a process's address space, it may be counted /// twice. external static int get currentRss; /// The high-watermark in bytes for the resident set size of memory for the /// process. /// /// Note that the meaning of this field is platform dependent. For example, /// some memory accounted for here may be shared with other processes, or if /// the same page is mapped into a process's address space, it may be counted /// twice. external static int get maxRss; } /// Modes for running a new process. final class ProcessStartMode { /// Normal child process. static const normal = const ProcessStartMode._internal(0); /// Stdio handles are inherited by the child process. static const inheritStdio = const ProcessStartMode._internal(1); /// Detached child process with no open communication channel. static const detached = const ProcessStartMode._internal(2); /// Detached child process with stdin, stdout and stderr still open /// for communication with the child. static const detachedWithStdio = const ProcessStartMode._internal(3); static List get values => const [ normal, inheritStdio, detached, detachedWithStdio, ]; String toString() => const ["normal", "inheritStdio", "detached", "detachedWithStdio"][_mode]; final int _mode; const ProcessStartMode._internal(this._mode); } /// The means to execute a program. /// /// Use the static [start] and [run] methods to start a new process. /// The run method executes the process non-interactively to completion. /// In contrast, the start method allows your code to interact with the /// running process. /// /// ## Start a process with the run method /// /// The following code sample uses the run method to create a process /// that runs the UNIX command `ls`, which lists the contents of a directory. /// The run method completes with a [ProcessResult] object when the process /// terminates. This provides access to the output and exit code from the /// process. The run method does not return a `Process` object; /// this prevents your code from interacting with the running process. /// ```dart /// import 'dart:io'; /// /// main() async { /// // List all files in the current directory in UNIX-like systems. /// var result = await Process.run('ls', ['-l']); /// print(result.stdout); /// } /// ``` /// ## Start a process with the start method /// /// The following example uses start to create the process. /// The start method returns a [Future] for a `Process` object. /// When the future completes the process is started and /// your code can interact with the process: /// writing to stdin, listening to stdout, and so on. /// /// The following sample starts the UNIX `cat` utility, which when given no /// command-line arguments, echos its input. /// The program writes to the process's standard input stream /// and prints data from its standard output stream. /// ```dart /// import 'dart:io'; /// import 'dart:convert'; /// /// main() async { /// var process = await Process.start('cat', []); /// process.stdout /// .transform(utf8.decoder) /// .forEach(print); /// process.stdin.writeln('Hello, world!'); /// process.stdin.writeln('Hello, galaxy!'); /// process.stdin.writeln('Hello, universe!'); /// } /// ``` /// ## Standard I/O streams /// /// As seen in the previous code sample, you can interact with the `Process`'s /// standard output stream through the getter [stdout], /// and you can interact with the `Process`'s standard input stream through /// the getter [stdin]. /// In addition, `Process` provides a getter [stderr] for using the `Process`'s /// standard error stream. /// /// A `Process`'s streams are distinct from the top-level streams /// for the current program. /// /// **NOTE:** /// `stdin`, `stdout`, and `stderr` are implemented using pipes between /// the parent process and the spawned subprocess. These pipes have limited /// capacity. If the subprocess writes to stderr or stdout in excess of that /// limit without the output being read, the subprocess blocks waiting for /// the pipe buffer to accept more data. For example: /// /// ```dart /// import 'dart:io'; /// /// main() async { /// var process = await Process.start('cat', ['largefile.txt']); /// // The following await statement will never complete because the /// // subprocess never exits since it is blocked waiting for its /// // stdout to be read. /// await process.stderr.forEach(print); /// } /// ``` /// /// ## Exit codes /// /// Call the [exitCode] method to get the exit code of the process. /// The exit code indicates whether the program terminated successfully /// (usually indicated with an exit code of 0) or with an error. /// /// If the start method is used, the [exitCode] is available through a future /// on the `Process` object (as shown in the example below). /// If the run method is used, the [exitCode] is available /// through a getter on the [ProcessResult] instance. /// ```dart /// import 'dart:io'; /// /// main() async { /// var process = await Process.start('ls', ['-l']); /// var exitCode = await process.exitCode; /// print('exit code: $exitCode'); /// } /// ``` abstract interface class Process { /// A `Future` which completes with the exit code of the process /// when the process completes. /// /// The exit code is not available for processes running with /// [ProcessStartMode.detached] or [ProcessStartMode.detachedWithStdio] and /// the getter will throw [StateError] if it is used. /// /// The handling of exit codes is platform specific. /// /// On Linux and OS X a normal exit code will be a positive value in /// the range `[0..255]`. If the process was terminated due to a signal /// the exit code will be a negative value in the range `[-255..-1]`, /// where the absolute value of the exit code is the signal /// number. For example, if a process crashes due to a segmentation /// violation the exit code will be -11, as the signal SIGSEGV has the /// number 11. /// /// On Windows a process can report any 32-bit value as an exit /// code. When returning the exit code this exit code is turned into /// a signed value. Some special values are used to report /// termination due to some system event. E.g. if a process crashes /// due to an access violation the 32-bit exit code is `0xc0000005`, /// which will be returned as the negative number `-1073741819`. To /// get the original 32-bit value use `(0x100000000 + exitCode) & /// 0xffffffff`. /// /// There is no guarantee that [stdout] and [stderr] have finished reporting /// the buffered output of the process when the returned future completes. /// To be sure that all output is captured, /// wait for the done event on the streams. Future get exitCode; /// Starts a process running the [executable] with the specified /// [arguments]. /// /// Returns a `Future` that completes with a /// [Process] instance when the process has been successfully /// started. That [Process] object can be used to interact with the /// process. If the process cannot be started the returned [Future] /// completes with an exception. /// /// Using an absolute path for [executable] is recommended since resolving /// the [executable] path is platform-specific. On Windows, both any `PATH` /// set in the [environment] map parameter and the path set in /// [workingDirectory] parameter are ignored for the purposes of resolving /// the [executable] path. /// /// Use [workingDirectory] to set the working directory for the process. Note /// that the change of directory occurs before executing the process on some /// platforms, which may have impact when using relative paths for the /// executable and the arguments. /// /// Use [environment] to set the environment variables for the process. If not /// set the environment of the parent process is inherited. Currently, only /// US-ASCII environment variables are supported and errors are likely to occur /// if an environment variable with code-points outside the US-ASCII range is /// passed in. /// /// If [includeParentEnvironment] is `true`, the process's environment will /// include the parent process's environment, with [environment] taking /// precedence. Default is `true`. /// /// If [runInShell] is `true`, the process will be spawned through a system /// shell. On Linux and OS X, `/bin/sh` is used, while /// `%WINDIR%\system32\cmd.exe` is used on Windows. /// /// **NOTE**: On Windows, if [executable] is a batch file /// ('*.bat' or '*.cmd'), it may be launched by the operating system in a /// system shell regardless of the value of [runInShell]. This could result in /// arguments being parsed according to shell rules. For example: /// /// ``` /// void main() async { /// // Will launch notepad. /// Process.start('test.bat', ['test¬epad.exe']); /// } /// ``` /// /// Users must read all data coming on the [stdout] and [stderr] /// streams of processes started with `Process.start`. If the user /// does not read all data on the streams the underlying system /// resources will not be released since there is still pending data. /// /// The following code uses `Process.start` to grep for `main` in the /// file `test.dart` on Linux. /// ```dart /// var process = await Process.start('grep', ['-i', 'main', 'test.dart']); /// stdout.addStream(process.stdout); /// stderr.addStream(process.stderr); /// ``` /// If [mode] is [ProcessStartMode.normal] (the default) a child /// process will be started with `stdin`, `stdout` and `stderr` /// connected to its parent. The parent process will not exit so long as the /// child is running, unless [exit] is called by the parent. If [exit] is /// called by the parent then the parent will be terminated but the child /// will continue running. /// /// If `mode` is [ProcessStartMode.detached] a detached process will /// be created. A detached process has no connection to its parent, /// and can keep running on its own when the parent dies. The only /// information available from a detached process is its `pid`. There /// is no connection to its `stdin`, `stdout` or `stderr`, nor will /// the process' exit code become available when it terminates. /// /// If `mode` is [ProcessStartMode.detachedWithStdio] a detached /// process will be created where the `stdin`, `stdout` and `stderr` /// are connected. The creator can communicate with the child through /// these. The detached process will keep running even if these /// communication channels are closed or the parent dies. The process' /// exit code will not become available when it terminated. /// /// The default value for `mode` is `ProcessStartMode.normal`. external static Future start( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, ProcessStartMode mode = ProcessStartMode.normal, }); /// Starts a process and runs it non-interactively to completion. The /// process run is [executable] with the specified [arguments]. /// /// Using an absolute path for [executable] is recommended since resolving /// the [executable] path is platform-specific. On Windows, both any `PATH` /// set in the [environment] map parameter and the path set in /// [workingDirectory] parameter are ignored for the purposes of resolving /// the [executable] path. /// /// Use [workingDirectory] to set the working directory for the process. Note /// that the change of directory occurs before executing the process on some /// platforms, which may have impact when using relative paths for the /// executable and the arguments. /// /// Use [environment] to set the environment variables for the process. If not /// set the environment of the parent process is inherited. Currently, only /// US-ASCII environment variables are supported and errors are likely to occur /// if an environment variable with code-points outside the US-ASCII range is /// passed in. /// /// If [includeParentEnvironment] is `true`, the process's environment will /// include the parent process's environment, with [environment] taking /// precedence. Default is `true`. /// /// If [runInShell] is true, the process will be spawned through a system /// shell. On Linux and OS X, `/bin/sh` is used, while /// `%WINDIR%\system32\cmd.exe` is used on Windows. /// /// **NOTE**: On Windows, if [executable] is a batch file /// ('*.bat' or '*.cmd'), it may be launched by the operating system in a /// system shell regardless of the value of [runInShell]. This could result in /// arguments being parsed according to shell rules. For example: /// /// ``` /// void main() async { /// // Will launch notepad. /// await Process.run('test.bat', ['test¬epad.exe']); /// } /// ``` /// /// The encoding used for decoding `stdout` and `stderr` into text is /// controlled through [stdoutEncoding] and [stderrEncoding]. The /// default encoding is [systemEncoding]. If `null` is used no /// decoding will happen and the [ProcessResult] will hold binary /// data. /// /// Returns a `Future` that completes with the /// result of running the process, i.e., exit code, standard out and /// standard error. /// /// The following code uses `Process.run` to grep for `main` in the /// file `test.dart` on Linux. /// ```dart /// var result = await Process.run('grep', ['-i', 'main', 'test.dart']); /// stdout.write(result.stdout); /// stderr.write(result.stderr); /// ``` external static Future run( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, Encoding? stdoutEncoding = systemEncoding, Encoding? stderrEncoding = systemEncoding, }); /// Starts a process and runs it to completion. This is a synchronous /// call and will block until the child process terminates. /// /// The arguments are the same as for [Process.run]. /// /// Returns a [ProcessResult] with the result of running the process, /// i.e., exit code, standard out and standard error. external static ProcessResult runSync( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, Encoding? stdoutEncoding = systemEncoding, Encoding? stderrEncoding = systemEncoding, }); /// Kills the process with id [pid]. /// /// Where possible, sends the [signal] to the process with id /// [pid]. This includes Linux and OS X. The default signal is /// [ProcessSignal.sigterm] which will normally terminate the /// process. /// /// On platforms without signal support, including Windows, the call /// just terminates the process with id [pid] in a platform specific /// way, and the [signal] parameter is ignored. /// /// Returns `true` if the signal is successfully delivered to the /// process. Otherwise the signal could not be sent, usually meaning /// that the process is already dead. external static bool killPid( int pid, [ ProcessSignal signal = ProcessSignal.sigterm, ]); /// The standard output stream of the process as a `Stream`. /// /// **NOTE:** /// `stdin`, `stdout`, and `stderr` are implemented using pipes between /// the parent process and the spawned subprocess. These pipes have limited /// capacity. If the subprocess writes to stderr or stdout in excess of that /// limit without the output being read, the subprocess blocks waiting for /// the pipe buffer to accept more data. For example: /// /// ```dart /// import 'dart:io'; /// /// main() async { /// var process = await Process.start('cat', ['largefile.txt']); /// // The following await statement will never complete because the /// // subprocess never exits since it is blocked waiting for its /// // stdout to be read. /// await process.stderr.forEach(print); /// } /// ``` Stream> get stdout; /// The standard error stream of the process as a `Stream`. /// /// **NOTE:** /// `stdin`, `stdout`, and `stderr` are implemented using pipes between /// the parent process and the spawned subprocess. These pipes have limited /// capacity. If the subprocess writes to stderr or stdout in excess of that /// limit without the output being read, the subprocess blocks waiting for /// the pipe buffer to accept more data. For example: /// /// ```dart /// import 'dart:io'; /// /// main() async { /// var process = await Process.start('cat', ['largefile.txt']); /// // The following await statement will never complete because the /// // subprocess never exits since it is blocked waiting for its /// // stdout to be read. /// await process.stderr.forEach(print); /// } /// ``` Stream> get stderr; /// The standard input stream of the process as an [IOSink]. IOSink get stdin; /// The process id of the process. int get pid; /// Kills the process. /// /// Where possible, sends the [signal] to the process. This includes /// Linux and OS X. The default signal is [ProcessSignal.sigterm] /// which will normally terminate the process. /// /// On platforms without signal support, including Windows, the call /// just terminates the process in a platform specific way, and the /// [signal] parameter is ignored. /// /// Returns `true` if the signal is successfully delivered to the /// process. Otherwise the signal could not be sent, usually meaning /// that the process is already dead. bool kill([ProcessSignal signal = ProcessSignal.sigterm]); } /// The result of running a non-interactive /// process started with [Process.run] or [Process.runSync]. final class ProcessResult { /// Exit code for the process. /// /// See [Process.exitCode] for more information in the exit code /// value. final int exitCode; /// Standard output from the process. /// /// The value used for the `stdoutEncoding` argument to [Process.run] /// determines the type. If `null` was used, this value is of type /// [Uint8List] otherwise it is of type `String`. final stdout; /// Standard error from the process. /// /// The value used for the `stderrEncoding` argument to [Process.run] /// determines the type. If `null` was used, this value is of type /// [Uint8List] otherwise it is of type [String]. final stderr; /// Process id of the process. final int pid; ProcessResult(this.pid, this.exitCode, this.stdout, this.stderr); } /// On Posix systems, [ProcessSignal] is used to send a specific signal /// to a child process, see `Process.kill`. /// /// Some [ProcessSignal]s can also be watched, as a way to intercept the default /// signal handler and implement another. See [ProcessSignal.watch] for more /// information. interface class ProcessSignal { static const ProcessSignal sighup = const ProcessSignal._(1, "SIGHUP"); static const ProcessSignal sigint = const ProcessSignal._(2, "SIGINT"); static const ProcessSignal sigquit = const ProcessSignal._(3, "SIGQUIT"); static const ProcessSignal sigill = const ProcessSignal._(4, "SIGILL"); static const ProcessSignal sigtrap = const ProcessSignal._(5, "SIGTRAP"); static const ProcessSignal sigabrt = const ProcessSignal._(6, "SIGABRT"); static const ProcessSignal sigbus = const ProcessSignal._(7, "SIGBUS"); static const ProcessSignal sigfpe = const ProcessSignal._(8, "SIGFPE"); static const ProcessSignal sigkill = const ProcessSignal._(9, "SIGKILL"); static const ProcessSignal sigusr1 = const ProcessSignal._(10, "SIGUSR1"); static const ProcessSignal sigsegv = const ProcessSignal._(11, "SIGSEGV"); static const ProcessSignal sigusr2 = const ProcessSignal._(12, "SIGUSR2"); static const ProcessSignal sigpipe = const ProcessSignal._(13, "SIGPIPE"); static const ProcessSignal sigalrm = const ProcessSignal._(14, "SIGALRM"); static const ProcessSignal sigterm = const ProcessSignal._(15, "SIGTERM"); static const ProcessSignal sigchld = const ProcessSignal._(17, "SIGCHLD"); static const ProcessSignal sigcont = const ProcessSignal._(18, "SIGCONT"); static const ProcessSignal sigstop = const ProcessSignal._(19, "SIGSTOP"); static const ProcessSignal sigtstp = const ProcessSignal._(20, "SIGTSTP"); static const ProcessSignal sigttin = const ProcessSignal._(21, "SIGTTIN"); static const ProcessSignal sigttou = const ProcessSignal._(22, "SIGTTOU"); static const ProcessSignal sigurg = const ProcessSignal._(23, "SIGURG"); static const ProcessSignal sigxcpu = const ProcessSignal._(24, "SIGXCPU"); static const ProcessSignal sigxfsz = const ProcessSignal._(25, "SIGXFSZ"); static const ProcessSignal sigvtalrm = const ProcessSignal._(26, "SIGVTALRM"); static const ProcessSignal sigprof = const ProcessSignal._(27, "SIGPROF"); static const ProcessSignal sigwinch = const ProcessSignal._(28, "SIGWINCH"); static const ProcessSignal sigpoll = const ProcessSignal._(29, "SIGPOLL"); static const ProcessSignal sigsys = const ProcessSignal._(31, "SIGSYS"); /// The numeric constant for the signal e.g. [ProcessSignal.signalNumber] /// will be 1 for [ProcessSignal.sighup] on most platforms. final int signalNumber; /// The POSIX-standardized name of the signal e.g. [ProcessSignal.name] will /// be "SIGHUP" for [ProcessSignal.sighup]. final String name; const ProcessSignal._(this.signalNumber, this.name); String toString() => name; /// Watch for process signals. /// /// The following [ProcessSignal]s can be listened to: /// /// * [ProcessSignal.sighup]. /// * [ProcessSignal.sigint]. Signal sent by e.g. CTRL-C. /// * [ProcessSignal.sigterm]. Not available on Windows. /// * [ProcessSignal.sigusr1]. Not available on Windows. /// * [ProcessSignal.sigusr2]. Not available on Windows. /// * [ProcessSignal.sigwinch]. Not available on Windows. /// /// Other signals are disallowed, as they may be used by the VM. /// /// A signal can be watched multiple times, from multiple isolates, where all /// callbacks are invoked when signaled, in no specific order. Stream watch() => _ProcessUtils._watchSignal(this); } class SignalException implements IOException { final String message; final osError; const SignalException(this.message, [this.osError]); String toString() { var msg = ""; if (osError != null) { msg = ", osError: $osError"; } return "SignalException: $message$msg"; } } class ProcessException implements IOException { /// The executable provided for the process. final String executable; /// The arguments provided for the process. final List arguments; /// The system message for the process exception, if any. /// /// The empty string if no message was available. final String message; /// The OS error code for the process exception, if any. /// /// The value is zero if no OS error code was available. final int errorCode; const ProcessException( this.executable, this.arguments, [ this.message = "", this.errorCode = 0, ]); String toString() { var args = arguments.join(' '); return "ProcessException: $message\n Command: $executable $args"; } } ‚µNN<9++1&+.LCPJJ?LHIED5FFEE9EG GD3GD@0%"< .GFD$.GFD24G8 /# .C&,)B#INOP &NNOP "%=8CA?F(HH P0$EIH+FNLJ@G G4-<@9-6L->5 4#./1O8LPBHLMK6 CGD+DHAN=;6 7-%#C"@N87GJH>FIBG=FGFDOM..C8@FF#MNAMPOI$QNRPNJ%N96A)&*5HBMNAMPOI$QNRPNJ%L967H8)&*//'@A@GG2DG( 2?JNOM8EIF- >JNOM8EIF- ?%GD1GF%DG(=,=!C (HE4'HE4!DH,QM JJLJLLJJLMMMMMMMMMMMMKMMQMOMKL>O.7!9">===>CPAE/7#,0/. <4;;$Gdart:io/process.dartÀFÊÀh³ÀFgXorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/process_patch.dartÀI«// Copyright (c) 2013, 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 "common_patch.dart"; @patch class _WindowsCodePageDecoder { @patch @pragma("vm:external-name", "SystemEncodingToString") external static String _decodeBytes(List bytes); } @patch class _WindowsCodePageEncoder { @patch @pragma("vm:external-name", "StringToSystemEncoding") external static List _encodeString(String string); } @patch class Process { @patch static Future start( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, ProcessStartMode mode = ProcessStartMode.normal, }) { _ProcessImpl process = _ProcessImpl( executable, arguments, workingDirectory, environment, includeParentEnvironment, runInShell, mode, ); return process._start(); } @patch static Future run( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, Encoding? stdoutEncoding = systemEncoding, Encoding? stderrEncoding = systemEncoding, }) { return _runNonInteractiveProcess( executable, arguments, workingDirectory, environment, includeParentEnvironment, runInShell, stdoutEncoding, stderrEncoding, ); } @patch static ProcessResult runSync( String executable, List arguments, { String? workingDirectory, Map? environment, bool includeParentEnvironment = true, bool runInShell = false, Encoding? stdoutEncoding = systemEncoding, Encoding? stderrEncoding = systemEncoding, }) { return _runNonInteractiveProcessSync( executable, arguments, workingDirectory, environment, includeParentEnvironment, runInShell, stdoutEncoding, stderrEncoding, ); } @patch static bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) { return _ProcessUtils._killPid(pid, signal.signalNumber); } } List<_SignalController?> _signalControllers = List.filled(32, null); class _SignalController { final ProcessSignal signal; final _controller = StreamController.broadcast(); var _id; _SignalController(this.signal) { _controller ..onListen = _listen ..onCancel = _cancel; } Stream get stream => _controller.stream; void _listen() { var id = _setSignalHandler(signal.signalNumber); if (id is! int) { _controller.addError(SignalException("Failed to listen for $signal", id)); return; } _id = id; var socket = _RawSocket(_NativeSocket._watchSignal(id)); socket.listen((event) { if (event == RawSocketEvent.read) { var bytes = socket.read()!; for (int i = 0; i < bytes.length; i++) { _controller.add(signal); } } }); } void _cancel() { if (_id != null) { _clearSignalHandler(signal.signalNumber); _id = null; } } @pragma("vm:external-name", "Process_SetSignalHandler") external static _setSignalHandler(int signal); @pragma("vm:external-name", "Process_ClearSignalHandler") external static void _clearSignalHandler(int signal); } @pragma("vm:entry-point", "call") Function _getWatchSignalInternal() => _ProcessUtils._watchSignalInternal; @patch class _ProcessUtils { @patch @pragma("vm:external-name", "Process_Exit") external static Never _exit(int status); @patch @pragma("vm:external-name", "Process_SetExitCode") external static void _setExitCode(int status); @patch @pragma("vm:external-name", "Process_GetExitCode") external static int _getExitCode(); @patch @pragma("vm:external-name", "Process_Sleep") external static void _sleep(int millis); @patch @pragma("vm:external-name", "Process_Pid") external static int _pid(Process? process); @pragma("vm:external-name", "Process_KillPid") external static bool _killPid(int pid, int signal); @patch static Stream _watchSignal(ProcessSignal signal) { if (signal != ProcessSignal.sighup && signal != ProcessSignal.sigint && signal != ProcessSignal.sigterm && (Platform.isWindows || (signal != ProcessSignal.sigusr1 && signal != ProcessSignal.sigusr2 && signal != ProcessSignal.sigwinch))) { throw SignalException("Listening for signal $signal is not supported"); } return _watchSignalInternal(signal); } static Stream _watchSignalInternal(ProcessSignal signal) { if (_signalControllers[signal.signalNumber] == null) { _signalControllers[signal.signalNumber] = _SignalController(signal); } return _signalControllers[signal.signalNumber]!.stream; } } @patch class ProcessInfo { @patch static int get maxRss { var result = _maxRss(); if (result is OSError) { throw result; } return result; } @patch static int get currentRss { var result = _currentRss(); if (result is OSError) { throw result; } return result; } @pragma("vm:external-name", "ProcessInfo_MaxRSS") external static _maxRss(); @pragma("vm:external-name", "ProcessInfo_CurrentRSS") external static _currentRss(); } @pragma("vm:entry-point") class _ProcessStartStatus { @pragma("vm:entry-point", "set") int? _errorCode; // Set to OS error code if process start failed. @pragma("vm:entry-point", "set") String? _errorMessage; // Set to OS error message if process start failed. } // The NativeFieldWrapperClass1 can not be used with a mixin, due to missing // implicit constructor. base class _ProcessImplNativeWrapper extends NativeFieldWrapperClass1 {} base class _ProcessImpl extends _ProcessImplNativeWrapper implements _Process { static bool connectedResourceHandler = false; _ProcessImpl( String path, List arguments, this._workingDirectory, Map? environment, bool includeParentEnvironment, bool runInShell, this._mode, ) : super() { if (!const bool.fromEnvironment("dart.vm.product") && !connectedResourceHandler) { registerExtension( 'ext.dart.io.getSpawnedProcesses', _SpawnedProcessResourceInfo.getStartedProcesses, ); registerExtension( 'ext.dart.io.getSpawnedProcessById', _SpawnedProcessResourceInfo.getProcessInfoMapById, ); connectedResourceHandler = true; } if (runInShell) { arguments = _getShellArguments(path, arguments); path = _getShellCommand(); } if (Platform.isWindows && path.contains(' ') && !path.contains('"')) { // Escape paths that may contain spaces // Bug: https://github.com/dart-lang/sdk/issues/37751 _path = '"$path"'; } else { _path = path; } _arguments = [ for (int i = 0; i < arguments.length; i++) Platform.isWindows ? _windowsArgumentEscape(arguments[i]) : arguments[i], ]; _environment = []; // Ensure that we have a non-null environment. environment ??= const {}; environment.forEach((key, value) { _environment.add('$key=$value'); }); if (includeParentEnvironment) { Platform.environment.forEach((key, value) { // Do not override keys already set as part of environment. if (!environment!.containsKey(key)) { _environment.add('$key=$value'); } }); } if (_modeHasStdio(_mode)) { // stdin going to process. _stdin = _StdSink(_Socket._writePipe().._owner = this); // stdout coming from process. _stdout = _StdStream(_Socket._readPipe().._owner = this); // stderr coming from process. _stderr = _StdStream(_Socket._readPipe().._owner = this); } if (_modeIsAttached(_mode)) { _exitHandler = _Socket._readPipe(); } } _NativeSocket get _stdinNativeSocket => (_stdin!._sink as _Socket)._nativeSocket; _NativeSocket get _stdoutNativeSocket => (_stdout!._stream as _Socket)._nativeSocket; _NativeSocket get _stderrNativeSocket => (_stderr!._stream as _Socket)._nativeSocket; static bool _modeIsAttached(ProcessStartMode mode) { return (mode == ProcessStartMode.normal) || (mode == ProcessStartMode.inheritStdio); } static bool _modeHasStdio(ProcessStartMode mode) { return (mode == ProcessStartMode.normal) || (mode == ProcessStartMode.detachedWithStdio); } static String _getShellCommand() { if (Platform.isWindows) { return 'cmd.exe'; } if (Platform.isAndroid) { return '/system/bin/sh'; } return '/bin/sh'; } static List _getShellArguments( String executable, List arguments, ) { List shellArguments = []; if (Platform.isWindows) { shellArguments.add('/c'); shellArguments.add(executable); for (var arg in arguments) { shellArguments.add(arg); } } else { var commandLine = StringBuffer(); executable = executable.replaceAll("'", "'\"'\"'"); commandLine.write("'$executable'"); shellArguments.add("-c"); for (var arg in arguments) { arg = arg.replaceAll("'", "'\"'\"'"); commandLine.write(" '$arg'"); } shellArguments.add(commandLine.toString()); } return shellArguments; } String _windowsArgumentEscape(String argument) { if (argument.isEmpty) { return '""'; } var result = argument; if (argument.contains('\t') || argument.contains(' ') || argument.contains('"')) { // Produce something that the C runtime on Windows will parse // back as this string. // Replace any number of '\' followed by '"' with // twice as many '\' followed by '\"'. var backslash = '\\'.codeUnitAt(0); var sb = StringBuffer(); var nextPos = 0; var quotePos = argument.indexOf('"', nextPos); while (quotePos != -1) { var numBackslash = 0; var pos = quotePos - 1; while (pos >= 0 && argument.codeUnitAt(pos) == backslash) { numBackslash++; pos--; } sb.write(argument.substring(nextPos, quotePos - numBackslash)); for (var i = 0; i < numBackslash; i++) { sb.write(r'\\'); } sb.write(r'\"'); nextPos = quotePos + 1; quotePos = argument.indexOf('"', nextPos); } sb.write(argument.substring(nextPos, argument.length)); result = sb.toString(); // Add '"' at the beginning and end and replace all '\' at // the end with two '\'. sb = StringBuffer('"'); sb.write(result); nextPos = argument.length - 1; while (argument.codeUnitAt(nextPos) == backslash) { sb.write('\\'); nextPos--; } sb.write('"'); result = sb.toString(); } return result; } int _intFromBytes(List bytes, int offset) { return (bytes[offset] + (bytes[offset + 1] << 8) + (bytes[offset + 2] << 16) + (bytes[offset + 3] << 24)); } Future _start() { var completer = Completer(); var stackTrace = StackTrace.current; if (_modeIsAttached(_mode)) { _exitCode = Completer(); } // TODO(ager): Make the actual process starting really async instead of // simulating it with a timer. Timer.run(() { var status = _ProcessStartStatus(); bool success = _startNative( _Namespace._namespace, _path, _arguments, _workingDirectory, _environment, _mode._mode, _modeHasStdio(_mode) ? _stdinNativeSocket : null, _modeHasStdio(_mode) ? _stdoutNativeSocket : null, _modeHasStdio(_mode) ? _stderrNativeSocket : null, _modeIsAttached(_mode) ? _exitHandler._nativeSocket : null, status, ); if (!success) { completer.completeError( ProcessException( _path, _arguments, status._errorMessage!, status._errorCode!, ), stackTrace, ); return; } _started = true; final resourceInfo = _SpawnedProcessResourceInfo(this); // Setup an exit handler to handle internal cleanup and possible // callback when a process terminates. if (_modeIsAttached(_mode)) { int exitDataRead = 0; final int EXIT_DATA_SIZE = 8; List exitDataBuffer = List.filled(EXIT_DATA_SIZE, 0); _exitHandler.listen((data) { int exitCode(List ints) { var code = _intFromBytes(ints, 0); var negative = _intFromBytes(ints, 4); assert(negative == 0 || negative == 1); return (negative == 0) ? code : -code; } void handleExit() { _ended = true; _exitCode!.complete(exitCode(exitDataBuffer)); // Kill stdin, helping hand if the user forgot to do it. if (_modeHasStdio(_mode)) { (_stdin!._sink as _Socket).destroy(); } resourceInfo.stopped(); } exitDataBuffer.setRange( exitDataRead, exitDataRead + data.length, data, ); exitDataRead += data.length; if (exitDataRead == EXIT_DATA_SIZE) { handleExit(); } }); } completer.complete(this); }); return completer.future; } ProcessResult _runAndWait( Encoding? stdoutEncoding, Encoding? stderrEncoding, ) { var status = _ProcessStartStatus(); _exitCode = Completer(); bool success = _startNative( _Namespace._namespace, _path, _arguments, _workingDirectory, _environment, ProcessStartMode.normal._mode, _stdinNativeSocket, _stdoutNativeSocket, _stderrNativeSocket, _exitHandler._nativeSocket, status, ); if (!success) { throw ProcessException( _path, _arguments, status._errorMessage!, status._errorCode!, ); } final resourceInfo = _SpawnedProcessResourceInfo(this); var result = _wait( _stdinNativeSocket, _stdoutNativeSocket, _stderrNativeSocket, _exitHandler._nativeSocket, ); getOutput(output, encoding) { if (encoding == null) return output; return encoding.decode(output); } resourceInfo.stopped(); return ProcessResult( result[0], result[1], getOutput(result[2], stdoutEncoding), getOutput(result[3], stderrEncoding), ); } @pragma("vm:external-name", "Process_Start") external bool _startNative( _Namespace namespace, String path, List arguments, String? workingDirectory, List environment, int mode, _NativeSocket? stdin, _NativeSocket? stdout, _NativeSocket? stderr, _NativeSocket? exitHandler, _ProcessStartStatus status, ); @pragma("vm:external-name", "Process_Wait") external _wait( _NativeSocket? stdin, _NativeSocket? stdout, _NativeSocket? stderr, _NativeSocket exitHandler, ); Stream> get stdout => _stdout ?? (throw StateError("stdio is not connected")); Stream> get stderr => _stderr ?? (throw StateError("stdio is not connected")); IOSink get stdin => _stdin ?? (throw StateError("stdio is not connected")); Future get exitCode => _exitCode?.future ?? (throw StateError("Process is detached")); bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { assert(_started); if (_ended) return false; return _ProcessUtils._killPid(pid, signal.signalNumber); } int get pid => _ProcessUtils._pid(this); late String _path; late List _arguments; String? _workingDirectory; late List _environment; final ProcessStartMode _mode; // Private methods of Socket are used by _in, _out, and _err. _StdSink? _stdin; _StdStream? _stdout; _StdStream? _stderr; late _Socket _exitHandler; bool _ended = false; bool _started = false; Completer? _exitCode; } // _NonInteractiveProcess is a wrapper around an interactive process // that buffers output so it can be delivered when the process exits. // _NonInteractiveProcess is used to implement the Process.run // method. Future _runNonInteractiveProcess( String path, List arguments, String? workingDirectory, Map? environment, bool includeParentEnvironment, bool runInShell, Encoding? stdoutEncoding, Encoding? stderrEncoding, ) { // Start the underlying process. return Process.start( path, arguments, workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: includeParentEnvironment, runInShell: runInShell, ).then((Process p) { int pid = p.pid; // Make sure the process stdin is closed. p.stdin.close(); // Setup stdout and stderr handling. Future foldStream(Stream> stream, Encoding? encoding) { if (encoding == null) { return stream .fold( BytesBuilder(), (builder, data) => builder..add(data), ) .then((builder) => builder.takeBytes()); } else { return stream .transform(encoding.decoder) .fold(StringBuffer(), (buf, data) { buf.write(data); return buf; }) .then((sb) => sb.toString()); } } Future stdout = foldStream(p.stdout, stdoutEncoding); Future stderr = foldStream(p.stderr, stderrEncoding); return Future.wait([p.exitCode, stdout, stderr]).then((result) { return ProcessResult(pid, result[0], result[1], result[2]); }); }); } ProcessResult _runNonInteractiveProcessSync( String executable, List arguments, String? workingDirectory, Map? environment, bool includeParentEnvironment, bool runInShell, Encoding? stdoutEncoding, Encoding? stderrEncoding, ) { var process = _ProcessImpl( executable, arguments, workingDirectory, environment, includeParentEnvironment, runInShell, ProcessStartMode.normal, ); return process._runAndWait(stdoutEncoding, stderrEncoding); } ‚¥NN< 88 8: &*5)   $&*//&  &*//*  Q=EC #:5Q=*$1# 0:1<8"J .+ 51 5& /+ -.16 D**+036N)L;K<   48!#D#MMIP0&#:%+9 -; '7!K.< 133''$2D.+  !>%@%@"**0+3+3701506%*& &#! (:* #.&23#""D8-*5 D H1  3>A%:3#$$*)"$L#*#:;;D !#  >G-$&H%)/343 ;E(4$ #( '0  ("! %" <""+&,,/ ."?"?NF>=+ " @EF? 1$!# (8.)F!55)>*::EB-$!> dart:io-patch/process_patch.dartÀeÀh³Qorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/secure_server_socket.dartªe// 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:io"; /// A server socket, providing a stream of high-level [Socket]s. /// /// See [SecureSocket] for more info. class SecureServerSocket extends Stream implements ServerSocketBase { final RawSecureServerSocket _socket; SecureServerSocket._(this._socket); /// Listens on a given address and port. /// /// When the returned future completes, the server socket is bound /// to the given [address] and [port] and has started listening on it. /// /// The [address] can either be a [String] or an /// [InternetAddress]. If [address] is a [String], [bind] will /// perform a [InternetAddress.lookup] and use the first value in the /// list. To listen on the loopback adapter, which will allow only /// incoming connections from the local host, use the value /// [InternetAddress.loopbackIPv4] or /// [InternetAddress.loopbackIPv6]. To allow for incoming /// connection from the network use either one of the values /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to /// bind to all interfaces or the IP address of a specific interface. /// /// If [port] has the value `0`, an ephemeral port will be chosen by /// the system. The actual port used can be retrieved using the /// [port] getter. /// /// The optional argument [backlog] can be used to specify the listen /// backlog for the underlying OS listen setup. If [backlog] has the /// value of `0` (the default) a reasonable value will be chosen by /// the system. /// /// Incoming client connections are promoted to secure connections, using /// the server certificate and key set in [context]. /// /// The [address] must be given as a numeric address, not a host name. /// /// To request or require that clients authenticate by providing an SSL (TLS) /// client certificate, set the optional parameter [requestClientCertificate] /// or [requireClientCertificate] to true. Requiring a certificate implies /// requesting a certificate, so setting both is redundant. /// To check whether a client certificate was received, check /// [SecureSocket.peerCertificate] after connecting. If no certificate /// was received, the result will be null. /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with /// clients. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [SecureSocket.selectedProtocol]. /// /// The optional argument [shared] specifies whether additional /// [SecureServerSocket] objects can bind to the same combination of [address], /// [port] and [v6Only]. If [shared] is `true` and more [SecureServerSocket]s /// from this isolate or other isolates are bound to the same port, then the /// incoming connections will be distributed among all the bound /// `SecureServerSocket`s. Connections can be distributed over multiple /// isolates this way. static Future bind( address, int port, SecurityContext? context, { int backlog = 0, bool v6Only = false, bool requestClientCertificate = false, bool requireClientCertificate = false, List? supportedProtocols, bool shared = false, }) { return RawSecureServerSocket.bind( address, port, context, backlog: backlog, v6Only: v6Only, requestClientCertificate: requestClientCertificate, requireClientCertificate: requireClientCertificate, supportedProtocols: supportedProtocols, shared: shared, ).then((serverSocket) => new SecureServerSocket._(serverSocket)); } StreamSubscription listen( void onData(SecureSocket socket)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _socket .map((rawSocket) => new SecureSocket._(rawSocket)) .listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } /// The port used by this socket. int get port => _socket.port; /// The address used by this socket. InternetAddress get address => _socket.address; /// Closes this socket. /// /// The returned future completes when the socket /// is fully closed and is no longer bound. Future close() => _socket.close().then((_) => this); void set _owner(owner) { _socket._owner = owner; } } /// A server socket providing a stream of low-level [RawSecureSocket]s. /// /// See [RawSecureSocket] for more info. class RawSecureServerSocket extends Stream { final RawServerSocket _socket; late StreamController _controller; StreamSubscription? _subscription; final SecurityContext? _context; final bool requestClientCertificate; final bool requireClientCertificate; final List? supportedProtocols; bool _closed = false; RawSecureServerSocket._( this._socket, this._context, this.requestClientCertificate, this.requireClientCertificate, this.supportedProtocols, ) { _controller = new StreamController( sync: true, onListen: _start, onPause: _pause, onResume: _resume, onCancel: close, ); } /// Listens on a provided address and port. /// /// When the returned future completes, the server socket is bound /// to the given [address] and [port] and has started listening on it. /// /// The [address] can either be a [String] or an /// [InternetAddress]. If [address] is a [String], [bind] will /// perform a [InternetAddress.lookup] and use the first value in the /// list. To listen on the loopback adapter, which will allow only /// incoming connections from the local host, use the value /// [InternetAddress.loopbackIPv4] or /// [InternetAddress.loopbackIPv6]. To allow for incoming /// connection from the network use either one of the values /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to /// bind to all interfaces or the IP address of a specific interface. /// /// If [port] has the value `0` an ephemeral port will be chosen by /// the system. The actual port used can be retrieved using the /// [port] getter. /// /// The optional argument [backlog] can be used to specify the listen /// backlog for the underlying OS listen setup. If [backlog] has the /// value of `0` (the default) a reasonable value will be chosen by /// the system. /// /// Incoming client connections are promoted to secure connections, /// using the server certificate and key set in [context]. /// /// [address] must be given as a numeric address, not a host name. /// /// To request or require that clients authenticate by providing an SSL (TLS) /// client certificate, set the optional parameters requestClientCertificate or /// requireClientCertificate to true. Require implies request, so one doesn't /// need to specify both. To check whether a client certificate was received, /// check SecureSocket.peerCertificate after connecting. If no certificate /// was received, the result will be null. /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with /// clients. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [RawSecureSocket.selectedProtocol]. /// /// The optional argument [shared] specifies whether additional /// [RawSecureServerSocket] objects can bind to the same combination of /// [address], [port] and [v6Only]. If [shared] is `true` and more /// [RawSecureServerSocket]s from this isolate or other isolates are bound to /// the port, then the incoming connections will be distributed among all the /// bound [RawSecureServerSocket]s. Connections can be distributed over /// multiple isolates this way. static Future bind( address, int port, SecurityContext? context, { int backlog = 0, bool v6Only = false, bool requestClientCertificate = false, bool requireClientCertificate = false, List? supportedProtocols, bool shared = false, }) { return RawServerSocket.bind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ).then( (serverSocket) => new RawSecureServerSocket._( serverSocket, context, requestClientCertificate, requireClientCertificate, supportedProtocols, ), ); } StreamSubscription listen( void onData(RawSecureSocket s)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } /// The port used by this socket. int get port => _socket.port; /// The address used by this socket. InternetAddress get address => _socket.address; /// Closes this socket. /// /// The returned future completes when the socket /// is fully closed and is no longer bound. Future close() { _closed = true; return _socket.close().then((_) => this); } void _onData(RawSocket connection) { var remotePort; try { remotePort = connection.remotePort; } catch (e) { // If connection is already closed, remotePort throws an exception. // Do nothing - connection is closed. return; } _RawSecureSocket.connect( connection.address, remotePort, true, connection, context: _context, requestClientCertificate: requestClientCertificate, requireClientCertificate: requireClientCertificate, supportedProtocols: supportedProtocols, ) .then((RawSecureSocket secureConnection) { if (_closed) { secureConnection.close(); } else { _controller.add(secureConnection); } }) .catchError((e, s) { if (!_closed) { _controller.addError(e, s); } }); } void _pause() { _subscription!.pause(); } void _resume() { _subscription!.resume(); } void _start() { _subscription = _socket.listen( _onData, onError: _controller.addError, onDone: _controller.close, ); } void set _owner(owner) { (_socket as _RawSocketBase)._owner = owner; } } �8NN<A&60'&+EI3AHE>(@J-KLN;BRQOCJ*  ++&' ::.F+);( $ '24.KH)>!60#''*##9.EI3AHE>(BJFPPJ"-  ++&!  5"" .'&$$ '24.*.' *J,>>2 3&/ ( $%!0!dart:io/secure_server_socket.dartJorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/secure_socket.dartÀÄÛ// Copyright (c) 2013, 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:io"; /// A TCP socket using TLS and SSL. /// /// See [Socket] for more information. abstract interface class SecureSocket implements Socket { external factory SecureSocket._(RawSecureSocket rawSocket); /// Constructs a new secure client socket and connects it to the given /// [host] on port [port]. /// /// The returned Future will complete with a /// [SecureSocket] that is connected and ready for subscription. /// /// The certificate provided by the server is checked /// using the trusted certificates set in the SecurityContext object. /// The default SecurityContext object contains a built-in set of trusted /// root certificates for well-known certificate authorities. /// /// [onBadCertificate] is an optional handler for unverifiable certificates. /// The handler receives the [X509Certificate], and can inspect it and /// decide (or let the user decide) whether to accept /// the connection or not. The handler should return true /// to continue the [SecureSocket] connection. /// /// [keyLog] is an optional callback that will be called when new TLS keys /// are exchanged with the server. [keyLog] will receive one line of text in /// [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) /// for each call. Writing these lines to a file will allow tools (such as /// [Wireshark](https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption)) /// to decrypt content sent through this socket. This is meant to allow /// network-level debugging of secure sockets and should not be used in /// production code. For example: /// ```dart /// final log = File('keylog.txt'); /// final socket = await SecureSocket.connect('www.example.com', 443, /// keyLog: (line) => log.writeAsStringSync(line, mode: FileMode.append)); /// ``` /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with the /// server. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [SecureSocket.selectedProtocol]. /// /// The argument [timeout] is used to specify the maximum allowed time to wait /// for a connection to be established. If [timeout] is longer than the system /// level timeout duration, a timeout may occur sooner than specified in /// [timeout]. On timeout, a [SocketException] is thrown and all ongoing /// connection attempts to [host] are cancelled. static Future connect( host, int port, { SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, Duration? timeout, }) { return RawSecureSocket.connect( host, port, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, timeout: timeout, ).then((rawSocket) => SecureSocket._(rawSocket)); } /// Like [connect], but returns a [Future] that completes with a /// [ConnectionTask] that can be cancelled if the [SecureSocket] is no /// longer needed. static Future> startConnect( host, int port, { SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, }) { return RawSecureSocket.startConnect( host, port, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, ).then((rawState) { Future socket = rawState.socket.then( (rawSocket) => SecureSocket._(rawSocket), ); return ConnectionTask._(socket, rawState._onCancel); }); } /// Initiates TLS on an existing connection. /// /// Takes an already connected [socket] and starts client side TLS /// handshake to make the communication secure. When the returned /// future completes the [SecureSocket] has completed the TLS /// handshake. Using this function requires that the other end of the /// connection is prepared for TLS handshake. /// /// If the [socket] already has a subscription, this subscription /// will no longer receive and events. In most cases calling /// [StreamSubscription.pause] on this subscription before /// starting TLS handshake is the right thing to do. /// /// The given [socket] is closed and may not be used anymore. /// /// If the [host] argument is passed it will be used as the host name /// for the TLS handshake. If [host] is not passed the host name from /// the [socket] will be used. The [host] can be either a [String] or /// an [InternetAddress]. /// /// [onBadCertificate] is an optional handler for unverifiable certificates. /// The handler receives the [X509Certificate], and can inspect it and /// decide (or let the user decide) whether to accept /// the connection or not. The handler should return true /// to continue the [SecureSocket] connection. /// /// [keyLog] is an optional callback that will be called when new TLS keys /// are exchanged with the server. [keyLog] will receive one line of text in /// [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) /// for each call. Writing these lines to a file will allow tools (such as /// [Wireshark](https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption)) /// to decrypt content sent through this socket. This is meant to allow /// network-level debugging of secure sockets and should not be used in /// production code. For example: /// ```dart /// final log = File('keylog.txt'); /// final socket = await SecureSocket.connect('www.example.com', 443, /// keyLog: (line) => log.writeAsStringSync(line, mode: FileMode.append)); /// ``` /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with the /// server. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [SecureSocket.selectedProtocol]. /// /// Calling this function will _not_ cause a DNS host lookup. If the /// [host] passed is a [String], the [InternetAddress] for the /// resulting [SecureSocket] will have the passed in [host] as its /// host value and the internet address of the already connected /// socket as its address value. /// /// See [connect] for more information on the arguments. static Future secure( Socket socket, { host, SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, }) { return socket ._detachRaw() .then((detachedRaw) { return RawSecureSocket.secure( detachedRaw[0] as RawSocket, subscription: detachedRaw[1] as StreamSubscription?, host: host, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, ); }) .then((raw) => SecureSocket._(raw)); } /// Initiates TLS on an existing server connection. /// /// Takes an already connected [socket] and starts server side TLS /// handshake to make the communication secure. When the returned /// future completes the [SecureSocket] has completed the TLS /// handshake. Using this function requires that the other end of the /// connection is going to start the TLS handshake. /// /// If the [socket] already has a subscription, this subscription /// will no longer receive and events. In most cases calling /// [StreamSubscription.pause] on this subscription /// before starting TLS handshake is the right thing to do. /// /// If some of the data of the TLS handshake has already been read /// from the socket this data can be passed in the [bufferedData] /// parameter. This data will be processed before any other data /// available on the socket. /// /// See [SecureServerSocket.bind] for more information on the /// arguments. static Future secureServer( Socket socket, SecurityContext? context, { List? bufferedData, bool requestClientCertificate = false, bool requireClientCertificate = false, List? supportedProtocols, }) { return socket ._detachRaw() .then((detachedRaw) { return RawSecureSocket.secureServer( detachedRaw[0] as RawSocket, context, subscription: detachedRaw[1] as StreamSubscription?, bufferedData: bufferedData, requestClientCertificate: requestClientCertificate, requireClientCertificate: requireClientCertificate, supportedProtocols: supportedProtocols, ); }) .then((raw) => SecureSocket._(raw)); } /// The peer certificate for a connected SecureSocket. /// /// If this [SecureSocket] is the server end of a secure socket connection, /// [peerCertificate] will return the client certificate, or `null` if no /// client certificate was received. If this socket is the client end, /// [peerCertificate] will return the server's certificate. X509Certificate? get peerCertificate; /// The protocol which was selected during ALPN protocol negotiation. /// /// Returns `null` if one of the peers does not have support for ALPN, did not /// specify a list of supported ALPN protocols or there was no common /// protocol between client and server. String? get selectedProtocol; /// Does nothing. /// /// The original intent was to allow TLS renegotiation of existing secure /// connections. @Deprecated("Not implemented") void renegotiate({ bool useSessionCache = true, bool requestClientCertificate = false, bool requireClientCertificate = false, }); } /// `RawSecureSocket` provides a secure (SSL or TLS) network connection. /// /// Client connections to a server are provided by calling /// RawSecureSocket.connect. A secure server, created with /// [RawSecureServerSocket], also returns `RawSecureSocket` objects representing /// the server end of a secure connection. /// The certificate provided by the server is checked /// using the trusted certificates set in the [SecurityContext] object. /// The default [SecurityContext] object contains a built-in set of trusted /// root certificates for well-known certificate authorities. /// /// See [RawSocket] for more information. abstract interface class RawSecureSocket implements RawSocket { /// Constructs a new secure client socket and connect it to the given /// host on the given port. /// /// The returned [Future] is completed with the /// [RawSecureSocket] when it is connected and ready for subscription. /// /// The certificate provided by the server is checked using the trusted /// certificates set in the SecurityContext object If a certificate and key are /// set on the client, using [SecurityContext.useCertificateChain] and /// [SecurityContext.usePrivateKey], and the server asks for a client /// certificate, then that client certificate is sent to the server. /// /// [onBadCertificate] is an optional handler for unverifiable certificates. /// The handler receives the [X509Certificate], and can inspect it and /// decide (or let the user decide) whether to accept /// the connection or not. The handler should return true /// to continue the [RawSecureSocket] connection. /// /// [onBadCertificate] is an optional handler for unverifiable certificates. /// The handler receives the [X509Certificate], and can inspect it and /// decide (or let the user decide) whether to accept /// the connection or not. The handler should return true /// to continue the [SecureSocket] connection. /// /// [keyLog] is an optional callback that will be called when new TLS keys /// are exchanged with the server. [keyLog] will receive one line of text in /// [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) /// for each call. Writing these lines to a file will allow tools (such as /// [Wireshark](https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption)) /// to decrypt content sent through this socket. This is meant to allow /// network-level debugging of secure sockets and should not be used in /// production code. For example: /// ```dart /// final log = File('keylog.txt'); /// final socket = await SecureSocket.connect('www.example.com', 443, /// keyLog: (line) => log.writeAsStringSync(line, mode: FileMode.append)); /// ``` /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with the /// server. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [RawSecureSocket.selectedProtocol]. static Future connect( host, int port, { SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, Duration? timeout, }) { _RawSecureSocket._verifyFields(host, port, false, false); return RawSocket.connect(host, port, timeout: timeout).then((socket) { return secure( socket, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, ); }); } /// Like [connect], but returns a [Future] that completes with a /// [ConnectionTask] that can be cancelled if the [RawSecureSocket] is no /// longer needed. static Future> startConnect( host, int port, { SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, }) { return RawSocket.startConnect(host, port).then(( ConnectionTask rawState, ) { Future socket = rawState.socket.then((rawSocket) { return secure( rawSocket, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, ); }); return ConnectionTask._(socket, rawState._onCancel); }); } /// Initiates TLS on an existing connection. /// /// Takes an already connected [socket] and starts client side TLS /// handshake to make the communication secure. When the returned /// future completes the [RawSecureSocket] has completed the TLS /// handshake. Using this function requires that the other end of the /// connection is prepared for TLS handshake. /// /// If the [socket] already has a subscription, pass the existing /// subscription in the [subscription] parameter. The [secure] /// operation will take over the subscription by replacing the /// handlers with it own secure processing. The caller must not touch /// this subscription anymore. Passing a paused subscription is an /// error. /// /// If the [host] argument is passed it will be used as the host name /// for the TLS handshake. If [host] is not passed the host name from /// the [socket] will be used. The [host] can be either a [String] or /// an [InternetAddress]. /// /// [onBadCertificate] is an optional handler for unverifiable certificates. /// The handler receives the [X509Certificate], and can inspect it and /// decide (or let the user decide) whether to accept /// the connection or not. The handler should return true /// to continue the [SecureSocket] connection. /// /// [keyLog] is an optional callback that will be called when new TLS keys /// are exchanged with the server. [keyLog] will receive one line of text in /// [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) /// for each call. Writing these lines to a file will allow tools (such as /// [Wireshark](https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption)) /// to decrypt content sent through this socket. This is meant to allow /// network-level debugging of secure sockets and should not be used in /// production code. For example: /// ```dart /// final log = File('keylog.txt'); /// final socket = await SecureSocket.connect('www.example.com', 443, /// keyLog: (line) => log.writeAsStringSync(line, mode: FileMode.append)); /// ``` /// /// [supportedProtocols] is an optional list of protocols (in decreasing /// order of preference) to use during the ALPN protocol negotiation with the /// server. Example values are "http/1.1" or "h2". The selected protocol /// can be obtained via [SecureSocket.selectedProtocol]. /// /// Calling this function will _not_ cause a DNS host lookup. If the /// [host] passed is a [String] the [InternetAddress] for the /// resulting [SecureSocket] will have this passed in [host] as its /// host value and the internet address of the already connected /// socket as its address value. /// /// See [connect] for more information on the arguments. static Future secure( RawSocket socket, { StreamSubscription? subscription, host, SecurityContext? context, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, }) { socket.readEventsEnabled = false; socket.writeEventsEnabled = false; return _RawSecureSocket.connect( host != null ? host : socket.address.host, socket.port, false, socket, subscription: subscription, context: context, onBadCertificate: onBadCertificate, keyLog: keyLog, supportedProtocols: supportedProtocols, ); } /// Initiates TLS on an existing server connection. /// /// Takes an already connected [socket] and starts server side TLS /// handshake to make the communication secure. When the returned /// future completes the [RawSecureSocket] has completed the TLS /// handshake. Using this function requires that the other end of the /// connection is going to start the TLS handshake. /// /// If the [socket] already has a subscription, pass the existing /// subscription in the [subscription] parameter. The [secureServer] /// operation will take over the subscription by replacing the /// handlers with it own secure processing. The caller must not touch /// this subscription anymore. Passing a paused subscription is an /// error. /// /// If some of the data of the TLS handshake has already been read /// from the socket this data can be passed in the [bufferedData] /// parameter. This data will be processed before any other data /// available on the socket. /// /// See [RawSecureServerSocket.bind] for more information on the /// arguments. static Future secureServer( RawSocket socket, SecurityContext? context, { StreamSubscription? subscription, List? bufferedData, bool requestClientCertificate = false, bool requireClientCertificate = false, List? supportedProtocols, }) { socket.readEventsEnabled = false; socket.writeEventsEnabled = false; return _RawSecureSocket.connect( socket.address, socket.remotePort, true, socket, context: context, subscription: subscription, bufferedData: bufferedData, requestClientCertificate: requestClientCertificate, requireClientCertificate: requireClientCertificate, supportedProtocols: supportedProtocols, ); } /// Does nothing. /// /// The original intent was to allow TLS renegotiation of existing secure /// connections. @Deprecated("Not implemented") void renegotiate({ bool useSessionCache = true, bool requestClientCertificate = false, bool requireClientCertificate = false, }); /// Get the peer certificate for a connected RawSecureSocket. If this /// RawSecureSocket is the server end of a secure socket connection, /// [peerCertificate] will return the client certificate, or null, if no /// client certificate was received. If it is the client end, /// [peerCertificate] will return the server's certificate. X509Certificate? get peerCertificate; /// The protocol which was selected during protocol negotiation. /// /// Returns null if one of the peers does not have support for ALPN, did not /// specify a list of supported ALPN protocols or there was no common /// protocol between client and server. String? get selectedProtocol; } /// X509Certificate represents an SSL certificate, with accessors to /// get the fields of the certificate. @pragma("vm:entry-point") abstract interface class X509Certificate { @pragma("vm:entry-point") external factory X509Certificate._(); /// The DER encoded bytes of the certificate. Uint8List get der; /// The PEM encoded String of the certificate. String get pem; /// The SHA1 hash of the certificate. Uint8List get sha1; String get subject; String get issuer; DateTime get startValidity; DateTime get endValidity; } class _FilterStatus { bool progress = false; // The filter read or wrote data to the buffers. bool readEmpty = true; // The read buffers and decryption filter are empty. bool writeEmpty = true; // The write buffers and encryption filter are empty. // These are set if a buffer changes state from empty or full. bool readPlaintextNoLongerEmpty = false; bool writePlaintextNoLongerFull = false; bool readEncryptedNoLongerFull = false; bool writeEncryptedNoLongerEmpty = false; _FilterStatus(); } // Interface used by [RawSecureServerSocket] and [_RawSecureSocket] that exposes // members of [_NativeSocket]. abstract interface class _RawSocketBase { bool get _closedReadEventSent; void set _owner(owner); } class _RawSecureSocket extends Stream implements RawSecureSocket, _RawSocketBase { // Status states static const int handshakeStatus = 201; static const int connectedStatus = 202; static const int closedStatus = 203; // Buffer identifiers. // These must agree with those in the native C++ implementation. static const int readPlaintextId = 0; static const int writePlaintextId = 1; static const int readEncryptedId = 2; static const int writeEncryptedId = 3; static const int bufferCount = 4; // Is a buffer identifier for an encrypted buffer? static bool _isBufferEncrypted(int identifier) => identifier >= readEncryptedId; final RawSocket _socket; final Completer<_RawSecureSocket> _handshakeComplete = Completer<_RawSecureSocket>(); final _controller = StreamController(sync: true); late final StreamSubscription _socketSubscription; List? _bufferedData; int _bufferedDataIndex = 0; final InternetAddress address; final bool isServer; final SecurityContext context; final bool requestClientCertificate; final bool requireClientCertificate; final bool Function(X509Certificate certificate)? onBadCertificate; final void Function(String line)? keyLog; ReceivePort? keyLogPort; var _status = handshakeStatus; bool _writeEventsEnabled = true; bool _readEventsEnabled = true; int _pauseCount = 0; bool _pendingReadEvent = false; bool _socketClosedRead = false; // The network socket is closed for reading. bool _socketClosedWrite = false; // The network socket is closed for writing. bool _closedRead = false; // The secure socket has fired an onClosed event. bool _closedWrite = false; // The secure socket has been closed for writing. // The network socket is gone. Completer _closeCompleter = Completer(); _FilterStatus _filterStatus = _FilterStatus(); bool _connectPending = true; bool _filterPending = false; bool _filterActive = false; _SecureFilter? _secureFilter = _SecureFilter._(); String? _selectedProtocol; static Future<_RawSecureSocket> connect( dynamic /*String|InternetAddress*/ host, int requestedPort, bool isServer, RawSocket socket, { SecurityContext? context, StreamSubscription? subscription, List? bufferedData, bool requestClientCertificate = false, bool requireClientCertificate = false, bool onBadCertificate(X509Certificate certificate)?, void keyLog(String line)?, List? supportedProtocols, }) { _verifyFields( host, requestedPort, requestClientCertificate, requireClientCertificate, ); if (host is InternetAddress) host = host.host; InternetAddress address = socket.address; if (host != null) { address = InternetAddress._cloneWithNewHost(address, host); } return _RawSecureSocket( address, requestedPort, isServer, context ?? SecurityContext.defaultContext, socket, subscription, bufferedData, requestClientCertificate, requireClientCertificate, onBadCertificate, keyLog, supportedProtocols, )._handshakeComplete.future; } _RawSecureSocket( this.address, int requestedPort, this.isServer, this.context, this._socket, StreamSubscription? subscription, this._bufferedData, this.requestClientCertificate, this.requireClientCertificate, this.onBadCertificate, this.keyLog, List? supportedProtocols, ) { _controller ..onListen = _onSubscriptionStateChange ..onPause = _onPauseStateChange ..onResume = _onPauseStateChange ..onCancel = _onSubscriptionStateChange; // Throw an ArgumentError if any field is invalid. After this, all // errors will be reported through the future or the stream. final secureFilter = _secureFilter!; secureFilter.init(); secureFilter.registerHandshakeCompleteCallback( _secureHandshakeCompleteHandler, ); if (keyLog != null) { final port = ReceivePort(); port.listen((line) { try { keyLog!((line as String) + '\n'); } catch (e, s) { // There is no obvious place to surface exceptions from the keyLog // callback so write the details to stderr. stderr.writeln("Failure in keyLog callback:"); stderr.writeln(s); } }); secureFilter.registerKeyLogPort(port.sendPort); keyLogPort = port; } if (onBadCertificate != null) { secureFilter.registerBadCertificateCallback(_onBadCertificateWrapper); } _socket.readEventsEnabled = true; _socket.writeEventsEnabled = false; if (subscription == null) { // If a current subscription is provided use this otherwise // create a new one. _socketSubscription = _socket.listen( _eventDispatcher, onError: _reportError, onDone: _doneHandler, ); } else { _socketSubscription = subscription; if (_socketSubscription.isPaused) { _socket.close(); throw ArgumentError("Subscription passed to TLS upgrade is paused"); } // If we are upgrading a socket that is already closed for read, // report an error as if we received readClosed during the handshake. if (_closedReadEventSent) { _eventDispatcher(RawSocketEvent.readClosed); } _socketSubscription ..onData(_eventDispatcher) ..onError(_reportError) ..onDone(_doneHandler); } try { var encodedProtocols = SecurityContext._protocolsToLengthEncoding( supportedProtocols, ); secureFilter.connect( address.host, context, isServer, requestClientCertificate || requireClientCertificate, requireClientCertificate, encodedProtocols, ); _secureHandshake(); } catch (e, s) { _reportError(e, s); } } StreamSubscription listen( void onData(RawSocketEvent data)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { _sendWriteEvent(); return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } static void _verifyFields( host, int requestedPort, bool requestClientCertificate, bool requireClientCertificate, ) { if (host is! String && host is! InternetAddress) { throw ArgumentError("host is not a String or an InternetAddress"); } if (requestedPort < 0 || requestedPort > 65535) { throw ArgumentError("requestedPort is not in the range 0..65535"); } } int get port => _socket.port; InternetAddress get remoteAddress => _socket.remoteAddress; int get remotePort => _socket.remotePort; bool get _closedReadEventSent => (_socket as _RawSocketBase)._closedReadEventSent; void set _owner(owner) { (_socket as _RawSocketBase)._owner = owner; } int available() { return _status != connectedStatus ? 0 : _secureFilter!.buffers![readPlaintextId].length; } Future close() { shutdown(SocketDirection.both); return _closeCompleter.future; } void _completeCloseCompleter([RawSocket? dummy]) { if (!_closeCompleter.isCompleted) _closeCompleter.complete(this); } void _close() { _closedWrite = true; _closedRead = true; _socket.close().then(_completeCloseCompleter); _socketClosedWrite = true; _socketClosedRead = true; if (!_filterActive && _secureFilter != null) { _secureFilter!.destroy(); _secureFilter = null; } keyLogPort?.close(); if (_socketSubscription != null) { _socketSubscription.cancel(); } _controller.close(); _status = closedStatus; } void shutdown(SocketDirection direction) { if (direction == SocketDirection.send || direction == SocketDirection.both) { _closedWrite = true; if (_filterStatus.writeEmpty) { _socket.shutdown(SocketDirection.send); _socketClosedWrite = true; if (_closedRead) { _close(); } } } if (direction == SocketDirection.receive || direction == SocketDirection.both) { _closedRead = true; _socketClosedRead = true; _socket.shutdown(SocketDirection.receive); if (_socketClosedWrite) { _close(); } } } bool get writeEventsEnabled => _writeEventsEnabled; void set writeEventsEnabled(bool value) { _writeEventsEnabled = value; if (value) { Timer.run(() => _sendWriteEvent()); } } bool get readEventsEnabled => _readEventsEnabled; void set readEventsEnabled(bool value) { _readEventsEnabled = value; _socket.readEventsEnabled = value; _scheduleReadEvent(); } Uint8List? read([int? length]) { if (length != null && length < 0) { throw ArgumentError( "Invalid length parameter in SecureSocket.read (length: $length)", ); } if (_closedRead) { throw SocketException("Reading from a closed socket"); } if (_status != connectedStatus) { return null; } var result = _secureFilter!.buffers![readPlaintextId].read(length); _scheduleFilter(); return result; } SocketMessage? readMessage([int? count]) { throw UnsupportedError("Message-passing not supported by secure sockets"); } // Write the data to the socket, and schedule the filter to encrypt it. int write(List data, [int offset = 0, int? bytes]) { if (bytes != null && bytes < 0) { throw ArgumentError( "Invalid bytes parameter in SecureSocket.read (bytes: $bytes)", ); } if (offset < 0) { throw ArgumentError( "Invalid offset parameter in SecureSocket.read (offset: $offset)", ); } if (_closedWrite) { _controller.addError(SocketException("Writing to a closed socket")); return 0; } if (_status != connectedStatus) return 0; bytes ??= data.length - offset; int written = _secureFilter!.buffers![writePlaintextId].write( data, offset, bytes, ); if (written > 0) { _filterStatus.writeEmpty = false; } _scheduleFilter(); return written; } int sendMessage( List controlMessages, List data, [ int offset = 0, int? count, ]) { throw UnsupportedError("Message-passing not supported by secure sockets"); } X509Certificate? get peerCertificate => _secureFilter!.peerCertificate; String? get selectedProtocol => _selectedProtocol; bool _onBadCertificateWrapper(X509Certificate certificate) { if (onBadCertificate == null) return false; return onBadCertificate!(certificate); } bool setOption(SocketOption option, bool enabled) { return _socket.setOption(option, enabled); } Uint8List getRawOption(RawSocketOption option) { return _socket.getRawOption(option); } void setRawOption(RawSocketOption option) { _socket.setRawOption(option); } void _eventDispatcher(RawSocketEvent event) { try { if (event == RawSocketEvent.read) { _readHandler(); } else if (event == RawSocketEvent.write) { _writeHandler(); } else if (event == RawSocketEvent.readClosed) { _closeHandler(); } } catch (e, stackTrace) { _reportError(e, stackTrace); } } void _readHandler() { _readSocket(); _scheduleFilter(); } void _writeHandler() { _writeSocket(); _scheduleFilter(); } void _doneHandler() { if (_filterStatus.readEmpty) { _close(); } } void _reportError(e, [StackTrace? stackTrace]) { if (_status == closedStatus) { return; } else if (_connectPending) { // _connectPending is true until the handshake has completed, and the // _handshakeComplete future returned from SecureSocket.connect has // completed. Before this point, we must complete it with an error. _handshakeComplete.completeError(e, stackTrace); } else { _controller.addError(e, stackTrace); } _close(); } void _closeHandler() async { if (_status == connectedStatus) { if (_closedRead) return; _socketClosedRead = true; if (_filterStatus.readEmpty) { _closedRead = true; _controller.add(RawSocketEvent.readClosed); if (_socketClosedWrite) { _close(); } } else { await _scheduleFilter(); } } else if (_status == handshakeStatus) { _socketClosedRead = true; // The other party might have disconnected, but if there still // bytes available we can continue handshake. if (_filterStatus.readEmpty) { _reportError( HandshakeException('Connection terminated during handshake'), null, ); } } } Future _secureHandshake() async { try { bool needRetryHandshake = await _secureFilter!.handshake(); if (needRetryHandshake) { // Some certificates have been evaluated, need to retry handshake. await _secureHandshake(); } else { _filterStatus.writeEmpty = false; _readSocket(); _writeSocket(); await _scheduleFilter(); } } catch (e, stackTrace) { _reportError(e, stackTrace); } } @Deprecated("Not implemented") void renegotiate({ bool useSessionCache = true, bool requestClientCertificate = false, bool requireClientCertificate = false, }) { if (_status != connectedStatus) { throw HandshakeException("Called renegotiate on a non-connected socket"); } _status = handshakeStatus; _filterStatus.writeEmpty = false; _scheduleFilter(); } void _secureHandshakeCompleteHandler() { _status = connectedStatus; if (_connectPending) { _connectPending = false; try { _selectedProtocol = _secureFilter!.selectedProtocol(); // We don't want user code to run synchronously in this callback. Timer.run(() => _handshakeComplete.complete(this)); } catch (error, stack) { _handshakeComplete.completeError(error, stack); } } } void _onPauseStateChange() { if (_controller.isPaused) { _pauseCount++; } else { _pauseCount--; if (_pauseCount == 0) { _scheduleReadEvent(); _sendWriteEvent(); // Can send event synchronously. } } if (!_socketClosedRead || !_socketClosedWrite) { if (_controller.isPaused) { _socketSubscription.pause(); } else { _socketSubscription.resume(); } } } void _onSubscriptionStateChange() { if (_controller.hasListener) { // TODO(ajohnsen): Do something here? } } Future _scheduleFilter() { _filterPending = true; return _tryFilter(); } Future _tryFilter() async { try { while (true) { if (_status == closedStatus) { return; } if (!_filterPending || _filterActive) { return; } _filterActive = true; _filterPending = false; _filterStatus = await _pushAllFilterStages(); _filterActive = false; if (_status == closedStatus) { _secureFilter!.destroy(); _secureFilter = null; return; } if (_readEventsEnabled) { _socket.readEventsEnabled = true; } if (_filterStatus.writeEmpty && _closedWrite && !_socketClosedWrite) { // Checks for and handles all cases of partially closed sockets. shutdown(SocketDirection.send); if (_status == closedStatus) { return; } } if (_filterStatus.readEmpty && _socketClosedRead && !_closedRead) { if (_status == handshakeStatus) { _secureFilter!.handshake(); if (_status == handshakeStatus) { throw HandshakeException( 'Connection terminated during handshake', ); } } _closeHandler(); } if (_status == closedStatus) { return; } if (_filterStatus.progress) { _filterPending = true; if (_filterStatus.writeEncryptedNoLongerEmpty) { _writeSocket(); } if (_filterStatus.writePlaintextNoLongerFull) { _sendWriteEvent(); } if (_filterStatus.readEncryptedNoLongerFull) { _readSocket(); } if (_filterStatus.readPlaintextNoLongerEmpty) { _scheduleReadEvent(); } if (_status == handshakeStatus) { await _secureHandshake(); } } } } catch (e, st) { _reportError(e, st); } } List? _readSocketOrBufferedData(int bytes) { final bufferedData = _bufferedData; if (bufferedData != null) { if (bytes > bufferedData.length - _bufferedDataIndex) { bytes = bufferedData.length - _bufferedDataIndex; } var result = bufferedData.sublist( _bufferedDataIndex, _bufferedDataIndex + bytes, ); _bufferedDataIndex += bytes; if (bufferedData.length == _bufferedDataIndex) { _bufferedData = null; } return result; } else if (!_socketClosedRead) { return _socket.read(bytes); } else { return null; } } void _readSocket() { if (_status == closedStatus) return; var buffer = _secureFilter!.buffers![readEncryptedId]; if (buffer.writeFromSource(_readSocketOrBufferedData) > 0) { _filterStatus.readEmpty = false; } else { _socket.readEventsEnabled = false; } } void _writeSocket() { if (_socketClosedWrite) return; var buffer = _secureFilter!.buffers![writeEncryptedId]; if (buffer.readToSocket(_socket)) { // Returns true if blocked _socket.writeEventsEnabled = true; } } // If a read event should be sent, add it to the controller. _scheduleReadEvent() { if (!_pendingReadEvent && !_closedRead && _readEventsEnabled && _pauseCount == 0 && _secureFilter != null && !_secureFilter!.buffers![readPlaintextId].isEmpty) { _pendingReadEvent = true; Timer.run(_sendReadEvent); } } _sendReadEvent() { _pendingReadEvent = false; if (_status != closedStatus && !_closedRead && _readEventsEnabled && _pauseCount == 0 && _secureFilter != null && !_secureFilter!.buffers![readPlaintextId].isEmpty) { _controller.add(RawSocketEvent.read); _scheduleReadEvent(); } } // If a write event should be sent, add it to the controller. _sendWriteEvent() { if (!_closedWrite && _writeEventsEnabled && _pauseCount == 0 && _secureFilter != null && _secureFilter!.buffers![writePlaintextId].free > 0) { _writeEventsEnabled = false; _controller.add(RawSocketEvent.write); } } Future<_FilterStatus> _pushAllFilterStages() async { bool wasInHandshake = _status != connectedStatus; List args = List.filled(2 + bufferCount * 2, null); args[0] = _secureFilter!._pointer(); args[1] = wasInHandshake; var bufs = _secureFilter!.buffers!; for (var i = 0; i < bufferCount; ++i) { args[2 * i + 2] = bufs[i].start; args[2 * i + 3] = bufs[i].end; } var response = (await _IOService._dispatch(_IOService.sslProcessFilter, args)) as List; if (response.length == 2) { if (wasInHandshake) { // If we're in handshake, throw a handshake error. _reportError( HandshakeException('${response[1]} error ${response[0]}'), null, ); } else { // If we're connected, throw a TLS error. _reportError(TlsException('${response[1]} error ${response[0]}'), null); } } int start(int index) => response[2 * index] as int; int end(int index) => response[2 * index + 1] as int; _FilterStatus status = _FilterStatus(); // Compute writeEmpty as "write plaintext buffer and write encrypted // buffer were empty when we started and are empty now". status.writeEmpty = bufs[writePlaintextId].isEmpty && start(writeEncryptedId) == end(writeEncryptedId); // If we were in handshake when this started, _writeEmpty may be false // because the handshake wrote data after we checked. if (wasInHandshake) status.writeEmpty = false; // Compute readEmpty as "both read buffers were empty when we started // and are empty now". status.readEmpty = bufs[readEncryptedId].isEmpty && start(readPlaintextId) == end(readPlaintextId); _ExternalBuffer buffer = bufs[writePlaintextId]; int new_start = start(writePlaintextId); if (new_start != buffer.start) { status.progress = true; if (buffer.free == 0) { status.writePlaintextNoLongerFull = true; } buffer.start = new_start; } buffer = bufs[readEncryptedId]; new_start = start(readEncryptedId); if (new_start != buffer.start) { status.progress = true; if (buffer.free == 0) { status.readEncryptedNoLongerFull = true; } buffer.start = new_start; } buffer = bufs[writeEncryptedId]; int new_end = end(writeEncryptedId); if (new_end != buffer.end) { status.progress = true; if (buffer.length == 0) { status.writeEncryptedNoLongerEmpty = true; } buffer.end = new_end; } buffer = bufs[readPlaintextId]; new_end = end(readPlaintextId); if (new_end != buffer.end) { status.progress = true; if (buffer.length == 0) { status.readPlaintextNoLongerEmpty = true; } buffer.end = new_end; } return status; } } /// A circular buffer backed by an external byte array. Accessed from /// both C++ and Dart code in an unsynchronized way, with one reading /// and one writing. All updates to start and end are done by Dart code. class _ExternalBuffer { // This will be an ExternalByteArray, backed by C allocated data. @pragma("vm:entry-point") List? data; @pragma("vm:entry-point") int start; @pragma("vm:entry-point") int end; final int size; _ExternalBuffer(int size) : size = size, start = size ~/ 2, end = size ~/ 2; void advanceStart(int bytes) { assert(start > end || start + bytes <= end); start += bytes; if (start >= size) { start -= size; assert(start <= end); assert(start < size); } } void advanceEnd(int bytes) { assert(start <= end || start > end + bytes); end += bytes; if (end >= size) { end -= size; assert(end < start); assert(end < size); } } bool get isEmpty => end == start; int get length => start > end ? size + end - start : end - start; int get linearLength => start > end ? size - start : end - start; int get free => start > end ? start - end - 1 : size + start - end - 1; int get linearFree { if (start > end) return start - end - 1; if (start == 0) return size - end - 1; return size - end; } Uint8List? read(int? bytes) { if (bytes == null) { bytes = length; } else { bytes = min(bytes, length); } if (bytes == 0) return null; Uint8List result = Uint8List(bytes); int bytesRead = 0; // Loop over zero, one, or two linear data ranges. while (bytesRead < bytes) { int toRead = min(bytes - bytesRead, linearLength); result.setRange(bytesRead, bytesRead + toRead, data!, start); advanceStart(toRead); bytesRead += toRead; } return result; } int write(List inputData, int offset, int bytes) { if (bytes > free) { bytes = free; } int written = 0; int toWrite = min(bytes, linearFree); // Loop over zero, one, or two linear data ranges. while (toWrite > 0) { data!.setRange(end, end + toWrite, inputData, offset); advanceEnd(toWrite); offset += toWrite; written += toWrite; toWrite = min(bytes - written, linearFree); } return written; } int writeFromSource(List? getData(int requested)) { int written = 0; int toWrite = linearFree; // Loop over zero, one, or two linear data ranges. while (toWrite > 0) { // Source returns at most toWrite bytes, and it returns null when empty. var inputData = getData(toWrite); if (inputData == null || inputData.length == 0) break; var len = inputData.length; data!.setRange(end, end + len, inputData); advanceEnd(len); written += len; toWrite = linearFree; } return written; } bool readToSocket(RawSocket socket) { // Loop over zero, one, or two linear data ranges. while (true) { var toWrite = linearLength; if (toWrite == 0) return false; int bytes = socket.write(data!, start, toWrite); advanceStart(bytes); if (bytes < toWrite) { // The socket has blocked while we have data to write. return true; } } } } abstract class _SecureFilter { external factory _SecureFilter._(); void connect( String hostName, SecurityContext context, bool isServer, bool requestClientCertificate, bool requireClientCertificate, Uint8List protocols, ); void destroy(); Future handshake(); String? selectedProtocol(); void rehandshake(); void init(); X509Certificate? get peerCertificate; int processBuffer(int bufferIndex); void registerBadCertificateCallback(bool Function(X509Certificate) callback); void registerHandshakeCompleteCallback(Function handshakeCompleteHandler); void registerKeyLogPort(SendPort port); // This call may cause a reference counted pointer in the native // implementation to be retained. It should only be called when the resulting // value is passed to the IO service through a call to dispatch(). int _pointer(); List<_ExternalBuffer>? get buffers; } /// A secure networking exception caused by a failure in the /// TLS/SSL protocol. @pragma("vm:entry-point") class TlsException implements IOException { final String type; final String message; final OSError? osError; @pragma("vm:entry-point") const TlsException([String message = "", OSError? osError]) : this._("TlsException", message, osError); const TlsException._(this.type, this.message, this.osError); String toString() { StringBuffer sb = StringBuffer(); sb.write(type); if (message.isNotEmpty) { sb.write(": $message"); if (osError != null) { sb.write(" ($osError)"); } } else if (osError != null) { sb.write(": $osError"); } return sb.toString(); } } /// An exception that happens in the handshake phase of establishing /// a secure network connection. @pragma("vm:entry-point") class HandshakeException extends TlsException { @pragma("vm:entry-point") const HandshakeException([String message = "", OSError? osError]) : super._("HandshakeException", message, osError); } /// An exception that happens in the handshake phase of establishing /// a secure network connection, when looking up or verifying a /// certificate. class CertificateException extends TlsException { @pragma("vm:entry-point") const CertificateException([String message = "", OSError? osError]) : super._("CertificateException", message, osError); } …ÙNN<$':>I/C8HL@OI8=1MOiMVJJ$&HQ KPM;QQKK3' 9&$ *.6CI< 9&) *.:2 I/ED@H0D?=7@HHHOI8=1MOiMVJJ$&HQ KPM;GAEC#;& 9&/))Q04 ;6ED@H6D?6>EDC@, ++&//)Q(@@4 ;9NLJ>(HQH* L!!++I;*@H2IJRIHGOI8=4OI8=1MOiMVJJ$&HQ KPM>* 9&>K,0 CL? 9&5*J.2 L/EDCH0DAAHE HHHOI8=1MOiMVJJ$&HQ KPM;G@FC#;)6 9&&'%1 "*.6EDCH6DGAHE EDCC/ 6++&&'% ""::.L!!++IGKA>(COH* E'+(01(JNPA++*,Q*!61**'C()()$54%9%DE!!''F,!#""OPNO!M14+-6++9&  3.B1 !6##&.&'/HA)4'",M69 6$M&( B, **MGL"5#  I >" -)&$ ##7I6I >,#80& ;$$#5F33 '$---&0# 0- 1 6,!*4+ '#(K =&H-OJ<&H K K.$C  (0OJ5?0+6/3)."0 *27##3#"LJK7 +& %4" !- E4%H * B K"*!#!!++&P&+ ?J<8  <5"%&&#,#$ ' 0  6'$  ", OK*) L,(.(:  ' &!; : 9 :" ,& 4( >:)$ #7%" );A' )$<(!)?!= !#!=,@!>#-76A)(,'%H ;E 2Q8:,I=*:K:3J)85-%2 $(%1 %)! 3$$! 2GFJD  O!11$DDJ-+  "!)7 9D:*7=2;7O(="1(7"&7?&##(&PM*CPE&=,>0?&!"E!0D7E@2F9dart:io/secure_socket.dartÀdÙÀh³Àe^org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/secure_socket_patch.dartª¹// 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 "common_patch.dart"; @patch class SecureSocket { @patch factory SecureSocket._(RawSecureSocket rawSocket) => _SecureSocket(rawSocket); } @patch class _SecureFilter { @patch factory _SecureFilter._() => _SecureFilterImpl._(); } @patch @pragma("vm:entry-point") class X509Certificate { @patch @pragma("vm:entry-point") factory X509Certificate._() => _X509CertificateImpl._(); } class _SecureSocket extends _Socket implements SecureSocket { _RawSecureSocket? get _raw => super._raw as _RawSecureSocket?; _SecureSocket(RawSecureSocket raw) : super(raw); void renegotiate({ bool useSessionCache = true, bool requestClientCertificate = false, bool requireClientCertificate = false, }) {} X509Certificate? get peerCertificate { if (_raw == null) { throw StateError("peerCertificate called on destroyed SecureSocket"); } return _raw!.peerCertificate; } String? get selectedProtocol { if (_raw == null) { throw StateError("selectedProtocol called on destroyed SecureSocket"); } return _raw!.selectedProtocol; } } /** * _SecureFilterImpl wraps a filter that encrypts and decrypts data travelling * over an encrypted socket. The filter also handles the handshaking * and certificate verification. * * The filter exposes its input and output buffers as Dart objects that * are backed by an external C array of bytes, so that both Dart code and * native code can access the same data. */ @pragma("vm:entry-point") base class _SecureFilterImpl extends NativeFieldWrapperClass1 implements _SecureFilter { // Performance is improved if a full buffer of plaintext fits // in the encrypted buffer, when encrypted. // SIZE and ENCRYPTED_SIZE are referenced from C++. @pragma("vm:entry-point") static final int SIZE = 8 * 1024; @pragma("vm:entry-point") static final int ENCRYPTED_SIZE = 10 * 1024; _SecureFilterImpl._() { buffers = <_ExternalBuffer>[ for (int i = 0; i < _RawSecureSocket.bufferCount; ++i) _ExternalBuffer( _RawSecureSocket._isBufferEncrypted(i) ? ENCRYPTED_SIZE : SIZE, ), ]; } @pragma("vm:external-name", "SecureSocket_Connect") external void connect( String hostName, SecurityContext context, bool isServer, bool requestClientCertificate, bool requireClientCertificate, Uint8List protocols, ); void destroy() { buffers = null; _destroy(); } @pragma("vm:external-name", "SecureSocket_Destroy") external void _destroy(); @pragma("vm:external-name", "SecureSocket_Handshake") external int _handshake(SendPort replyPort); @pragma("vm:external-name", "SecureSocket_MarkAsTrusted") external void _markAsTrusted(int certificatePtr, bool isTrusted); @pragma("vm:external-name", "SecureSocket_NewX509CertificateWrapper") external static X509Certificate _newX509CertificateWrapper( int certificatePtr, ); Future handshake() { Completer evaluatorCompleter = Completer(); ReceivePort rpEvaluateResponse = ReceivePort(); rpEvaluateResponse.listen((data) { List list = data as List; // incoming messages (bool isTrusted, int certificatePtr) is // sent by TrustEvaluator native port after system evaluates // the certificate chain if (list.length != 2) { throw Exception("Invalid number of arguments in evaluate response"); } bool isTrusted = list[0] as bool; int certificatePtr = list[1] as int; // Make sure certificatePtr gets released. X509Certificate certificate = _newX509CertificateWrapper(certificatePtr); if (!isTrusted) { if (badCertificateCallback != null) { try { isTrusted = badCertificateCallback!(certificate); } catch (e, st) { evaluatorCompleter.completeError(e, st); rpEvaluateResponse.close(); return; } } } _markAsTrusted(certificatePtr, isTrusted); evaluatorCompleter.complete(true); rpEvaluateResponse.close(); }); const int kSslErrorWantCertificateVerify = 16; // ssl.h:558 int handshakeResult; try { handshakeResult = _handshake(rpEvaluateResponse.sendPort); } catch (e, st) { rpEvaluateResponse.close(); rethrow; } if (handshakeResult == kSslErrorWantCertificateVerify) { return evaluatorCompleter.future; } else { // Response is ready, no need for evaluate response receive port rpEvaluateResponse.close(); return Future.value(false); } } void rehandshake() => throw UnimplementedError(); int processBuffer(int bufferIndex) => throw UnimplementedError(); @pragma("vm:external-name", "SecureSocket_GetSelectedProtocol") external String? selectedProtocol(); @pragma("vm:external-name", "SecureSocket_Init") external void init(); @pragma("vm:external-name", "SecureSocket_PeerCertificate") external X509Certificate? get peerCertificate; @pragma("vm:external-name", "SecureSocket_RegisterBadCertificateCallback") external void _registerBadCertificateCallback( bool Function(X509Certificate) callback, ); bool Function(X509Certificate)? badCertificateCallback; void registerBadCertificateCallback(bool Function(X509Certificate) callback) { badCertificateCallback = callback; _registerBadCertificateCallback(callback); } @pragma("vm:external-name", "SecureSocket_RegisterHandshakeCompleteCallback") external void registerHandshakeCompleteCallback( Function handshakeCompleteHandler, ); @pragma("vm:external-name", "SecureSocket_RegisterKeyLogPort") external void registerKeyLogPort(SendPort port); // This is a security issue, as it exposes a raw pointer to Dart code. @pragma("vm:external-name", "SecureSocket_FilterPointer") external int _pointer(); @pragma("vm:entry-point") List<_ExternalBuffer>? buffers; } @patch class SecurityContext { @patch factory SecurityContext({bool withTrustedRoots = false}) { return _SecurityContext(withTrustedRoots); } @patch static SecurityContext get defaultContext { return _SecurityContext.defaultContext; } @patch static bool get alpnSupported => true; } base class _SecurityContext extends NativeFieldWrapperClass1 implements SecurityContext { bool _allowLegacyUnsafeRenegotiation = false; _SecurityContext(bool withTrustedRoots) { _createNativeContext(); if (withTrustedRoots) { _trustBuiltinRoots(); } } set allowLegacyUnsafeRenegotiation(bool allow) { _allowLegacyUnsafeRenegotiation = allow; _setAllowTlsRenegotiation(allow); } bool get allowLegacyUnsafeRenegotiation => _allowLegacyUnsafeRenegotiation; set minimumTlsProtocolVersion(TlsProtocolVersion version) { _setMinimumProtocolVersion(version._version); } TlsProtocolVersion get minimumTlsProtocolVersion => TlsProtocolVersion._fromProtocolVersionConstant( _getMinimumProtocolVersion(), ); @pragma("vm:external-name", "SecurityContext_Allocate") external void _createNativeContext(); static final SecurityContext defaultContext = _SecurityContext(true); void usePrivateKey(String file, {String? password}) { List bytes = (File(file)).readAsBytesSync(); usePrivateKeyBytes(bytes, password: password); } @pragma("vm:external-name", "SecurityContext_UsePrivateKeyBytes") external void usePrivateKeyBytes(List keyBytes, {String? password}); void setTrustedCertificates(String file, {String? password}) { List bytes = (File(file)).readAsBytesSync(); setTrustedCertificatesBytes(bytes, password: password); } @pragma("vm:external-name", "SecurityContext_SetTrustedCertificatesBytes") external void setTrustedCertificatesBytes( List certBytes, { String? password, }); void useCertificateChain(String file, {String? password}) { List bytes = (File(file)).readAsBytesSync(); useCertificateChainBytes(bytes, password: password); } @pragma("vm:external-name", "SecurityContext_UseCertificateChainBytes") external void useCertificateChainBytes( List chainBytes, { String? password, }); void setClientAuthorities(String file, {String? password}) { List bytes = (File(file)).readAsBytesSync(); setClientAuthoritiesBytes(bytes, password: password); } @pragma("vm:external-name", "SecurityContext_SetClientAuthoritiesBytes") external void setClientAuthoritiesBytes( List authCertBytes, { String? password, }); void setAlpnProtocols(List protocols, bool isServer) { Uint8List encodedProtocols = SecurityContext._protocolsToLengthEncoding( protocols, ); _setAlpnProtocols(encodedProtocols, isServer); } @pragma("vm:external-name", "SecurityContext_SetAlpnProtocols") external void _setAlpnProtocols(Uint8List protocols, bool isServer); @pragma("vm:external-name", "SecurityContext_TrustBuiltinRoots") external void _trustBuiltinRoots(); @pragma("vm:external-name", "SecurityContext_SetAllowTlsRenegotiation") external void _setAllowTlsRenegotiation(bool allow); @pragma("vm:external-name", "SecurityContext_SetMinimumProtocolVersion") external void _setMinimumProtocolVersion(int version); @pragma("vm:external-name", "SecurityContext_GetMinimumProtocolVersion") external int _getMinimumProtocolVersion(); } /** * _X509CertificateImpl wraps an X509 certificate object held by the BoringSSL * library. It exposes the fields of the certificate object. */ base class _X509CertificateImpl extends NativeFieldWrapperClass1 implements X509Certificate { // The native field must be set manually on a new object, in native code. // This is done by WrappedX509Certificate in security_context.cc. _X509CertificateImpl._(); @pragma("vm:external-name", "X509_Der") external Uint8List get _der; late final Uint8List der = _der; @pragma("vm:external-name", "X509_Pem") external String get _pem; late final String pem = _pem; @pragma("vm:external-name", "X509_Sha1") external Uint8List get _sha1; late final Uint8List sha1 = _sha1; @pragma("vm:external-name", "X509_Subject") external String get subject; @pragma("vm:external-name", "X509_Issuer") external String get issuer; DateTime get startValidity { return DateTime.fromMillisecondsSinceEpoch(_startValidity(), isUtc: true); } DateTime get endValidity { return DateTime.fromMillisecondsSinceEpoch(_endValidity(), isUtc: true); } @pragma("vm:external-name", "X509_StartValidity") external int _startValidity(); @pragma("vm:external-name", "X509_EndValidity") external int _endValidity(); } �]NN< Q 6 ;>A3!++)L"!M#OF!HJ)>@.6$/!=J 6##68/<4' CCM(+1P.>5( 1)"@ A"=( G"(4DB'3>1M1-:Q'/P3'A3I<" =/ ., )=!0,3-&N>267& :(H863DLA6<M->69J*?6:K+AM3BGC&J7K9K-O=A!LD*#* + %.-OM4!2&dart:io-patch/secure_socket_patch.dartÀeÀh³Morg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/security_context.dart³// Copyright (c) 2015, 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:io"; /// A Transport Layer Security (TLS) version. /// /// Only TLS versions supported by `dart:io` are included. class TlsProtocolVersion { /// Transport Layer Security (TLS) Protocol Version 1.2. /// /// See RFC-5246. static const tls1_2 = TlsProtocolVersion._(0x0303); /// Transport Layer Security (TLS) Protocol Version 1.3. /// /// See RFC-8446. static const tls1_3 = TlsProtocolVersion._(0x0304); final int _version; const TlsProtocolVersion._(this._version); static TlsProtocolVersion _fromProtocolVersionConstant(int version) => switch (version) { 0x0303 => tls1_2, 0x0304 => tls1_3, _ => throw ArgumentError.value(version, 'version'), }; } /// The object containing the certificates to trust when making /// a secure client connection, and the certificate chain and /// private key to serve from a secure server. /// /// The [SecureSocket] and [SecureServerSocket] classes take a SecurityContext /// as an argument to their connect and bind methods. /// /// Certificates and keys can be added to a SecurityContext from either PEM /// or PKCS12 containers. /// /// iOS note: Some methods to add, remove, and inspect certificates are not yet /// implemented. However, the platform's built-in trusted certificates can /// be used, by way of [SecurityContext.defaultContext]. abstract final class SecurityContext { /// Creates a new [SecurityContext]. /// /// By default, the created [SecurityContext] contains no keys or certificates. /// These can be added by calling the methods of this class. /// /// If [withTrustedRoots] is passed as `true`, the [SecurityContext] will be /// seeded by the trusted root certificates provided as explained below. To /// obtain a [SecurityContext] containing trusted root certificates, /// [SecurityContext.defaultContext] is usually sufficient, and should /// be used instead. However, if the [SecurityContext] containing the trusted /// root certificates must be modified per-connection, then [withTrustedRoots] /// should be used. external factory SecurityContext({bool withTrustedRoots = false}); /// The default security context used by most operation requiring one. /// /// Secure networking classes with an optional `context` parameter /// use the [defaultContext] object if the parameter is omitted. /// This object can also be accessed, and modified, directly. /// Each isolate has a different [defaultContext] object. /// The [defaultContext] object uses a list of well-known trusted /// certificate authorities as its trusted roots. On Linux and Windows, this /// list is taken from Mozilla, who maintains it as part of Firefox. On, /// MacOS, iOS, and Android, this list comes from the trusted certificates /// stores built into the platforms. external static SecurityContext get defaultContext; /// Sets the private key for a server certificate or client certificate. /// /// A secure connection using this SecurityContext will use this key with /// the server or client certificate to sign and decrypt messages. /// [file] is the path to a PEM or PKCS12 file containing an encrypted /// private key, encrypted with [password]. Assuming it is well-formatted, all /// other contents of [file] are ignored. An unencrypted file can be used, /// but this is not usual. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [usePrivateKeyBytes]. /// /// iOS note: Only PKCS12 data is supported. It should contain both the private /// key and the certificate chain. On iOS one call to [usePrivateKey] with this /// data is used instead of two calls to [useCertificateChain] and /// [usePrivateKey]. void usePrivateKey(String file, {String? password}); /// Sets the private key for a server certificate or client certificate. /// /// Like [usePrivateKey], but takes the contents of the file as a list /// of bytes. void usePrivateKeyBytes(List keyBytes, {String? password}); /// Add a certificate to the set of trusted X509 certificates /// used by [SecureSocket] client connections. /// /// [file] is the path to a PEM or PKCS12 file containing X509 certificates, /// usually root certificates from certificate authorities. For PKCS12 files, /// [password] is the password for the file. For PEM files, [password] is /// ignored. Assuming it is well-formatted, all other contents of [file] are /// ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [setTrustedCertificatesBytes]. /// /// iOS note: On iOS, this call takes only the bytes for a single DER /// encoded X509 certificate. It may be called multiple times to add /// multiple trusted certificates to the context. A DER encoded certificate /// can be obtained from a PEM encoded certificate by using the openssl tool: /// ```bash /// $ openssl x509 -outform der -in cert.pem -out cert.der /// ``` void setTrustedCertificates(String file, {String? password}); /// Add a certificate to the set of trusted X509 certificates /// used by [SecureSocket] client connections. /// /// Like [setTrustedCertificates] but takes the contents of the file. void setTrustedCertificatesBytes(List certBytes, {String? password}); /// Sets the chain of X509 certificates served by [SecureServerSocket] /// when making secure connections, including the server certificate. /// /// [file] is a PEM or PKCS12 file containing X509 certificates, starting with /// the root authority and intermediate authorities forming the signed /// chain to the server certificate, and ending with the server certificate. /// The private key for the server certificate is set by [usePrivateKey]. For /// PKCS12 files, [password] is the password for the file. For PEM files, /// [password] is ignored. Assuming it is well-formatted, all /// other contents of [file] are ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [useCertificateChainBytes]. /// /// iOS note: As noted above, [usePrivateKey] does the job of both /// that call and this one. On iOS, this call is a no-op. void useCertificateChain(String file, {String? password}); /// Sets the chain of X509 certificates served by [SecureServerSocket] /// when making secure connections, including the server certificate. /// /// Like [useCertificateChain] but takes the contents of the file. void useCertificateChainBytes(List chainBytes, {String? password}); /// Sets the list of authority names that a [SecureServerSocket] will advertise /// as accepted when requesting a client certificate from a connecting /// client. /// /// The [file] is a PEM or PKCS12 file containing the accepted signing /// authority certificates - the authority names are extracted from the /// certificates. For PKCS12 files, [password] is the password for the file. /// For PEM files, [password] is ignored. Assuming it is well-formatted, all /// other contents of [file] are ignored. /// /// NB: This function calls [File.readAsBytesSync], and will block on file IO. /// Prefer using [setClientAuthoritiesBytes]. /// /// iOS note: This call is not supported. void setClientAuthorities(String file, {String? password}); /// Sets the list of authority names that a [SecureServerSocket] will advertise /// as accepted, when requesting a client certificate from a connecting /// client. /// /// Like [setClientAuthorities] but takes the contents of the file. void setClientAuthoritiesBytes(List authCertBytes, {String? password}); /// Whether the platform supports ALPN. This always returns true and will be /// removed in a future release. @deprecated external static bool get alpnSupported; /// Sets the list of application-level protocols supported by a client /// connection or server connection. The ALPN (application level protocol /// negotiation) extension to TLS allows a client to send a list of /// protocols in the TLS client hello message, and the server to pick /// one and send the selected one back in its server hello message. /// /// Separate lists of protocols can be sent for client connections and /// for server connections, using the same SecurityContext. The [isServer] /// boolean argument specifies whether to set the list for server connections /// or client connections. void setAlpnProtocols(List protocols, bool isServer); /// If `true`, the [SecurityContext] will allow TLS renegotiation. /// Renegotiation is only supported as a client and the HelloRequest must be /// received at a quiet point in the application protocol. This is sufficient /// to support the legacy use case of requesting a new client certificate /// between an HTTP request and response in (unpipelined) HTTP/1.1. /// NOTE: Renegotiation is an extremely problematic protocol feature and /// should only be used to communicate with legacy servers in environments /// where it is known to be safe. abstract bool allowLegacyUnsafeRenegotiation; /// The minimum TLS version to use when establishing a secure connection. /// /// If the peer does not support `minimumTlsProtocolVersion` or later /// then [SecureSocket.connect] will throw a [TlsException]. /// /// If the value is changed, it will only affect new connections. Existing /// connections will continue to use the protocol that was negotiated with the /// peer. /// /// The default value is [TlsProtocolVersion.tls1_2]. abstract TlsProtocolVersion minimumTlsProtocolVersion; /// Encodes a set of supported protocols for ALPN/NPN usage. /// /// The [protocols] list is expected to contain protocols in descending order /// of preference. /// /// See RFC 7301 (https://tools.ietf.org/html/rfc7301) for the encoding of /// `List protocols`: /// ```plaintext /// opaque ProtocolName<1..2^8-1>; /// /// struct { /// ProtocolName protocol_name_list<2..2^16-1> /// } ProtocolNameList; /// ``` /// The encoding of the opaque `ProtocolName` vector is /// described in RFC 2246: 4.3 Vectors. /// /// Note: Even though this encoding scheme would allow a total /// `ProtocolNameList` length of 65535, this limit cannot be reached. Testing /// showed that more than ~ 2^14 bytes will fail to negotiate a protocol. /// We will be conservative and support only messages up to (1<<13)-1 bytes. static Uint8List _protocolsToLengthEncoding(List? protocols) { if (protocols == null || protocols.length == 0) { return Uint8List(0); } int protocolsLength = protocols.length; // Calculate the number of bytes we will need if it is ASCII. int expectedLength = protocolsLength; for (int i = 0; i < protocolsLength; i++) { int length = protocols[i].length; if (length > 0 && length <= 255) { expectedLength += length; } else { throw ArgumentError( 'Length of protocol must be between 1 and 255 (was: $length).', ); } } if (expectedLength >= (1 << 13)) { throw ArgumentError('The maximum message length supported is 2^13-1.'); } // Try encoding the `List protocols` array using fast ASCII path. var bytes = Uint8List(expectedLength); int bytesOffset = 0; for (int i = 0; i < protocolsLength; i++) { String proto = protocols[i]; // Add length byte. bytes[bytesOffset++] = proto.length; int bits = 0; // Add protocol bytes. for (int j = 0; j < proto.length; j++) { var char = proto.codeUnitAt(j); bits |= char; bytes[bytesOffset++] = char & 0xff; } // Go slow case if we have encountered anything non-ascii. if (bits > 0x7f) { return _protocolsToLengthEncodingNonAsciiBailout(protocols); } } return bytes; } static Uint8List _protocolsToLengthEncodingNonAsciiBailout( List protocols, ) { void addProtocol(List outBytes, String protocol) { var protocolBytes = utf8.encode(protocol); var len = protocolBytes.length; if (len > 255) { throw ArgumentError( 'Length of protocol must be between 1 and 255 (was: $len)', ); } // Add length byte. outBytes.add(len); // Add protocol bytes. outBytes.addAll(protocolBytes); } List bytes = []; for (var i = 0; i < protocols.length; i++) { addProtocol(bytes, protocols[i]); } if (bytes.length >= (1 << 13)) { throw ArgumentError('The maximum message length supported is 2^13-1.'); } return Uint8List.fromList(bytes); } } �8NN<.;;6;6-I< @>/P6LPK9''R?ONGIPQEIEC@RJFOO#*ILFHFINP@EOPLFKM$0LH?MQ 89?PM %5 H*APMOI6,B*0()"J 'NN+0#+/(,AE><1&F &1(%N&dart:io/security_context.dartÀH[Àh³Korg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/service_object.dart‚É// Copyright (c) 2014, 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:io"; int _nextServiceId = 1; // TODO(ajohnsen): Use other way of getting a unique id. mixin _ServiceObject { int __serviceId = 0; int get _serviceId { if (__serviceId == 0) __serviceId = _nextServiceId++; return __serviceId; } String get _servicePath => "$_serviceTypePath/$_serviceId"; String get _serviceTypePath; String get _serviceTypeName; String _serviceType(bool ref) { if (ref) return "@$_serviceTypeName"; return _serviceTypeName; } } NN<9:>"*dart:io/service_object.dartCorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/socket.dartÀÌi// Copyright (c) 2013, 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:io"; /// The type, or address family, of an [InternetAddress]. /// /// Currently, IP version 4 (IPv4), IP version 6 (IPv6) /// and Unix domain address are supported. /// Unix domain sockets are available only on Linux, MacOS and Android. final class InternetAddressType { static const InternetAddressType IPv4 = InternetAddressType._(0); static const InternetAddressType IPv6 = InternetAddressType._(1); static const InternetAddressType unix = InternetAddressType._(2); static const InternetAddressType any = InternetAddressType._(-1); final int _value; const InternetAddressType._(this._value); factory InternetAddressType._from(int value) { if (value == IPv4._value) return IPv4; if (value == IPv6._value) return IPv6; if (value == unix._value) return unix; throw ArgumentError("Invalid type: $value"); } /// Get the name of the type, e.g. "IPv4" or "IPv6". String get name => const ["ANY", "IPv4", "IPv6", "Unix"][_value + 1]; String toString() => "InternetAddressType: $name"; } /// An internet address or a Unix domain address. /// /// This object holds an internet address. If this internet address /// is the result of a DNS lookup, the address also holds the hostname /// used to make the lookup. /// An Internet address combined with a port number represents an /// endpoint to which a socket can connect or a listening socket can /// bind. abstract interface class InternetAddress { /// IP version 4 loopback address. /// /// Use this address when listening on or connecting /// to the loopback adapter using IP version 4 (IPv4). external static InternetAddress get loopbackIPv4; /// IP version 6 loopback address. /// /// Use this address when listening on or connecting to /// the loopback adapter using IP version 6 (IPv6). external static InternetAddress get loopbackIPv6; /// IP version 4 any address. /// /// Use this address when listening on the addresses /// of all adapters using IP version 4 (IPv4). external static InternetAddress get anyIPv4; /// IP version 6 any address. /// /// Use this address when listening on the addresses /// of all adapters using IP version 6 (IPv6). external static InternetAddress get anyIPv6; /// The address family of the [InternetAddress]. InternetAddressType get type; /// The numeric address of the host. /// /// For IPv4 addresses this is using the dotted-decimal notation. /// For IPv6 it is using the hexadecimal representation. /// For Unix domain addresses, this is a file path. String get address; /// The host used to lookup the address. /// /// If there is no host associated with the address this returns the [address]. String get host; /// The raw address of this [InternetAddress]. /// /// For an IP address, the result is either a 4 or 16 byte long list. /// For a Unix domain address, UTF-8 encoded byte sequences that represents /// [address] is returned. /// /// The returned list is a fresh copy, making it possible to change the list without /// modifying the [InternetAddress]. Uint8List get rawAddress; /// Whether the [InternetAddress] is a loopback address. bool get isLoopback; /// Whether the scope of the [InternetAddress] is a link-local. bool get isLinkLocal; /// Whether the scope of the [InternetAddress] is multicast. bool get isMulticast; /// Creates a new [InternetAddress] from a numeric address or a file path. /// /// If [type] is [InternetAddressType.IPv4], [address] must be a numeric IPv4 /// address (dotted-decimal notation). /// If [type] is [InternetAddressType.IPv6], [address] must be a numeric IPv6 /// address (hexadecimal notation). /// If [type] is [InternetAddressType.unix], [address] must be a valid file /// path. /// If [type] is omitted, [address] must be either a numeric IPv4 or IPv6 /// address and the type is inferred from the format. external factory InternetAddress(String address, {InternetAddressType? type}); /// Creates a new [InternetAddress] from the provided raw address bytes. /// /// If the [type] is [InternetAddressType.IPv4], the [rawAddress] must have /// length 4. /// If the [type] is [InternetAddressType.IPv6], the [rawAddress] must have /// length 16. /// If the [type] is [InternetAddressType.unix], the [rawAddress] must be a /// valid UTF-8 encoded file path. /// /// If [type] is omitted, the [rawAddress] must have a length of either 4 or /// 16, in which case the type defaults to [InternetAddressType.IPv4] or /// [InternetAddressType.IPv6] respectively. external factory InternetAddress.fromRawAddress( Uint8List rawAddress, { InternetAddressType? type, }); /// Performs a reverse DNS lookup on this [address] /// /// Returns a new [InternetAddress] with the same address, but where the [host] /// field set to the result of the lookup. /// /// If this address is Unix domain addresses, no lookup is performed and this /// address is returned directly. Future reverse(); /// Looks up the addresses of a host. /// /// If [type] is [InternetAddressType.any], it will lookup both /// IP version 4 (IPv4) and IP version 6 (IPv6) addresses. /// If [type] is either [InternetAddressType.IPv4] or /// [InternetAddressType.IPv6] it will only lookup addresses of the /// specified type. The order of the list can, and most likely will, /// change over time. external static Future> lookup( String host, { InternetAddressType type = InternetAddressType.any, }); /// Clones the given [address] with the new [host]. /// /// The [address] must be an [InternetAddress] that was created with one /// of the static methods of this class. external static InternetAddress _cloneWithNewHost( InternetAddress address, String host, ); /// Attempts to parse [address] as a numeric address. /// /// Returns `null` If [address] is not a numeric IPv4 (dotted-decimal /// notation) or IPv6 (hexadecimal representation) address. external static InternetAddress? tryParse(String address); } /// A [NetworkInterface] represents an active network interface on the current /// system. It contains a list of [InternetAddress]es that are bound to the /// interface. abstract interface class NetworkInterface { /// The name of the [NetworkInterface]. String get name; /// The index of the [NetworkInterface]. int get index; /// The list of [InternetAddress]es currently bound to this /// [NetworkInterface]. List get addresses; /// Whether the [list] method is supported. /// /// The [list] method is supported on all platforms supported by Dart so this /// property is always true. @Deprecated("listSupported is always true.") external static bool get listSupported; /// Query the system for [NetworkInterface]s. /// /// If [includeLoopback] is `true`, the returned list will include the /// loopback device. Default is `false`. /// /// If [includeLinkLocal] is `true`, the list of addresses of the returned /// [NetworkInterface]s, may include link local addresses. Default is `false`. /// /// If [type] is either [InternetAddressType.IPv4] or /// [InternetAddressType.IPv6] it will only lookup addresses of the /// specified type. Default is [InternetAddressType.any]. external static Future> list({ bool includeLoopback = false, bool includeLinkLocal = false, InternetAddressType type = InternetAddressType.any, }); } /// A listening socket. /// /// A `RawServerSocket` and provides a stream of low-level [RawSocket] objects, /// one for each connection made to the listening socket. /// /// See [RawSocket] for more information. abstract interface class RawServerSocket implements Stream { /// Listens on a given address and port. /// /// When the returned future completes the server socket is bound /// to the given [address] and [port] and has started listening on it. /// /// The [address] can either be a [String] or an /// [InternetAddress]. If [address] is a [String], [bind] will /// perform a [InternetAddress.lookup] and use the first value in the /// list. To listen on the loopback adapter, which will allow only /// incoming connections from the local host, use the value /// [InternetAddress.loopbackIPv4] or /// [InternetAddress.loopbackIPv6]. To allow for incoming /// connection from the network use either one of the values /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to /// bind to all interfaces or the IP address of a specific interface. /// /// If an IP version 6 (IPv6) address is used, both IP version 6 /// (IPv6) and version 4 (IPv4) connections will be accepted. To /// restrict this to version 6 (IPv6) only, use [v6Only] to set /// version 6 only. /// /// If [port] has the value `0` an ephemeral port will /// be chosen by the system. The actual port used can be retrieved /// using the `port` getter. /// /// The optional argument [backlog] can be used to specify the listen /// backlog for the underlying OS listen setup. If [backlog] has the /// value of `0` (the default) a reasonable value will be chosen by /// the system. /// /// The optional argument [shared] specifies whether additional RawServerSocket /// objects can bind to the same combination of [address], [port] and [v6Only]. /// If [shared] is `true` and more [RawServerSocket]s from this isolate or /// other isolates are bound to the port, then the incoming connections will be /// distributed among all the bound [RawServerSocket]s. Connections can be /// distributed over multiple isolates this way. external static Future bind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }); /// The port used by this socket. int get port; /// The address used by this socket. InternetAddress get address; /// Closes the socket. /// /// The returned future completes when the socket /// is fully closed and is no longer bound. Future close(); } /// A listening socket. /// /// A [ServerSocket] provides a stream of [Socket] objects, /// one for each connection made to the listening socket. /// /// See [Socket] for more info. abstract interface class ServerSocket implements ServerSocketBase { /// Listens on a given address and port. /// /// When the returned future completes the server socket is bound /// to the given [address] and [port] and has started listening on it. /// /// The [address] can either be a [String] or an /// [InternetAddress]. If [address] is a [String], [bind] will /// perform a [InternetAddress.lookup] and use the first value in the /// list. To listen on the loopback adapter, which will allow only /// incoming connections from the local host, use the value /// [InternetAddress.loopbackIPv4] or /// [InternetAddress.loopbackIPv6]. To allow for incoming /// connection from the network use either one of the values /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to /// bind to all interfaces or the IP address of a specific interface. /// /// If an IP version 6 (IPv6) address is used, both IP version 6 /// (IPv6) and version 4 (IPv4) connections will be accepted. To /// restrict this to version 6 (IPv6) only, use [v6Only] to set /// version 6 only. /// /// If [port] has the value `0` an ephemeral port will be chosen by /// the system. The actual port used can be retrieved using the /// [port] getter. /// /// The optional argument [backlog] can be used to specify the listen /// backlog for the underlying OS listen setup. If [backlog] has the /// value of `0` (the default) a reasonable value will be chosen by /// the system. /// /// The optional argument [shared] specifies whether additional ServerSocket /// objects can bind to the same combination of [address], [port] and /// [v6Only]. If [shared] is `true` and more server sockets from this /// isolate or other isolates are bound to the port, then the incoming /// connections will be distributed among all the bound server sockets. /// Connections can be distributed over multiple isolates this way. static Future bind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return ServerSocket._bind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); } return overrides.serverSocketBind( address, port, backlog: backlog, v6Only: v6Only, shared: shared, ); } external static Future _bind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }); /// The port used by this socket. int get port; /// The address used by this socket. InternetAddress get address; /// Closes the socket. /// /// The returned future completes when the socket /// is fully closed and is no longer bound. Future close(); } /// The [SocketDirection] is used as a parameter to [Socket.close] and /// [RawSocket.close] to close a socket in the specified direction(s). final class SocketDirection { static const SocketDirection receive = SocketDirection._(0); static const SocketDirection send = SocketDirection._(1); static const SocketDirection both = SocketDirection._(2); final _value; const SocketDirection._(this._value); } /// An option for a socket which is configured using [Socket.setOption]. /// /// The [SocketOption] is used as a parameter to [Socket.setOption] and /// [RawSocket.setOption] to customize the behaviour of the underlying /// socket. final class SocketOption { /// Enable or disable no-delay on the socket. If tcpNoDelay is enabled, the /// socket will not buffer data internally, but instead write each data chunk /// as an individual TCP packet. /// /// tcpNoDelay is disabled by default. static const SocketOption tcpNoDelay = SocketOption._(0); static const SocketOption _ipMulticastLoop = SocketOption._(1); static const SocketOption _ipMulticastHops = SocketOption._(2); static const SocketOption _ipMulticastIf = SocketOption._(3); static const SocketOption _ipBroadcast = SocketOption._(4); final _value; const SocketOption._(this._value); } // Must be kept in sync with enum in socket.cc enum _RawSocketOptions { SOL_SOCKET, // 0 IPPROTO_IP, // 1 IP_MULTICAST_IF, // 2 IPPROTO_IPV6, // 3 IPV6_MULTICAST_IF, // 4 IPPROTO_TCP, // 5 IPPROTO_UDP, // 6 } /// The [RawSocketOption] is used as a parameter to [Socket.setRawOption] and /// [RawSocket.setRawOption] to customize the behaviour of the underlying /// socket. /// /// It allows for fine grained control of the socket options, and its values /// will be passed to the underlying platform's implementation of setsockopt and /// getsockopt. final class RawSocketOption { /// Creates a [RawSocketOption] for [RawSocket.getRawOption] /// and [RawSocket.setRawOption]. /// /// The [level] and [option] arguments correspond to `level` and `optname` arguments /// on the `getsockopt()` and `setsockopt()` native calls. /// /// The value argument and its length correspond to the optval and length /// arguments on the native call. /// /// For a [RawSocket.getRawOption] call, the value parameter will be updated /// after a successful call (although its length will not be changed). /// /// For a [RawSocket.setRawOption] call, the value parameter will be used set /// the option. const RawSocketOption(this.level, this.option, this.value); /// Convenience constructor for creating an integer based [RawSocketOption]. factory RawSocketOption.fromInt(int level, int option, int value) { final Uint8List list = Uint8List(4); final buffer = ByteData.view(list.buffer, list.offsetInBytes); buffer.setInt32(0, value, Endian.host); return RawSocketOption(level, option, list); } /// Convenience constructor for creating a boolean based [RawSocketOption]. factory RawSocketOption.fromBool(int level, int option, bool value) => RawSocketOption.fromInt(level, option, value ? 1 : 0); /// The level for the option to set or get. /// /// See also: /// * [RawSocketOption.levelSocket] /// * [RawSocketOption.levelIPv4] /// * [RawSocketOption.levelIPv6] /// * [RawSocketOption.levelTcp] /// * [RawSocketOption.levelUdp] final int level; /// The numeric ID of the option to set or get. final int option; /// The raw data to set, or the array to write the current option value into. /// /// This list must be the correct length for the expected option. For most /// options that take [int] or [bool] values, the length should be 4. For options /// that expect a struct (such as an in_addr_t), the length should be the /// correct length for that struct. final Uint8List value; /// Socket level option for `SOL_SOCKET`. static int get levelSocket => _getOptionValue(_RawSocketOptions.SOL_SOCKET.index); /// Socket level option for `IPPROTO_IP`. static int get levelIPv4 => _getOptionValue(_RawSocketOptions.IPPROTO_IP.index); /// Socket option for `IP_MULTICAST_IF`. static int get IPv4MulticastInterface => _getOptionValue(_RawSocketOptions.IP_MULTICAST_IF.index); /// Socket level option for `IPPROTO_IPV6`. static int get levelIPv6 => _getOptionValue(_RawSocketOptions.IPPROTO_IPV6.index); /// Socket option for `IPV6_MULTICAST_IF`. static int get IPv6MulticastInterface => _getOptionValue(_RawSocketOptions.IPV6_MULTICAST_IF.index); /// Socket level option for `IPPROTO_TCP`. static int get levelTcp => _getOptionValue(_RawSocketOptions.IPPROTO_TCP.index); /// Socket level option for `IPPROTO_UDP`. static int get levelUdp => _getOptionValue(_RawSocketOptions.IPPROTO_UDP.index); external static int _getOptionValue(int key); } /// Events for the [RawDatagramSocket], [RawSecureSocket], and [RawSocket]. /// /// These event objects are used by the [Stream] behavior of the sockets /// (for example [RawSocket.listen], [RawSocket.forEach]) /// when the socket's state change. /// /// ```dart /// import 'dart:convert'; /// import 'dart:io'; /// /// void main() async { /// final socket = await RawSocket.connect("example.com", 80); /// /// socket.listen((event) { /// switch (event) { /// case RawSocketEvent.read: /// final data = socket.read(); /// if (data != null) { /// print(ascii.decode(data)); /// } /// break; /// case RawSocketEvent.write: /// socket.write(ascii.encode('GET /\r\nHost: example.com\r\n\r\n')); /// socket.writeEventsEnabled = false; /// break; /// case RawSocketEvent.readClosed: /// socket.close(); /// break; /// case RawSocketEvent.closed: /// break; /// default: /// throw "Unexpected event $event"; /// } /// }); /// } /// ``` class RawSocketEvent { /// An event indicates the socket is ready to be read. static const RawSocketEvent read = RawSocketEvent._(0); /// An event indicates the socket is ready to write. static const RawSocketEvent write = RawSocketEvent._(1); /// An event indicates the reading from the socket is closed static const RawSocketEvent readClosed = RawSocketEvent._(2); /// An event indicates the socket is closed. static const RawSocketEvent closed = RawSocketEvent._(3); final int _value; const RawSocketEvent._(this._value); String toString() { return const [ 'RawSocketEvent.read', 'RawSocketEvent.write', 'RawSocketEvent.readClosed', 'RawSocketEvent.closed', ][_value]; } } /// A cancelable connection attempt. /// /// Returned by the `startConnect` methods on client-side socket types `S`, /// `ConnectionTask` allows canceling an attempt to connect to a host. final class ConnectionTask { /// A `Future` that completes with value that `S.connect()` would return /// unless [cancel] is called on this [ConnectionTask]. /// /// If [cancel] is called, the future completes with a [SocketException] /// error whose message indicates that the connection attempt was cancelled. final Future socket; final void Function() _onCancel; ConnectionTask._(Future this.socket, void Function() onCancel) : _onCancel = onCancel; /// Create a `ConnectionTask` from an existing `Future`. /// /// You can use this method to return existing socket connections in /// [HttpClient.connectionFactory]. /// /// For example: /// /// ```dart /// final clientSocketFuture = Socket.connect( /// serverUri.host, serverUri.port); /// final client = HttpClient() /// ..connectionFactory = (uri, proxyHost, proxyPort) { /// return Future.value( /// ConnectionTask.fromSocket(clientSocketFuture, () {})); /// final response = await client.getUrl(serverUri); /// ``` static ConnectionTask fromSocket( Future socket, void Function() onCancel, ) => ConnectionTask._(socket, onCancel); /// Cancels the connection attempt. /// /// This also causes the [socket] `Future` to complete with a /// [SocketException] error. void cancel() { _onCancel(); } } /// A TCP connection. /// /// A *socket connection* connects a *local* socket to a *remote* socket. /// Data, as [Uint8List]s, is received by the local socket and made /// available by the [read] method, and can be sent to the remote socket /// through the [write] method. /// /// The [Stream] interface of this class provides event notification about when /// a certain change has happened, for example when data has become available /// ([RawSocketEvent.read]) or when the remote end has stopped listening /// ([RawSocketEvent.closed]). abstract interface class RawSocket implements Stream { /// Set or get, if the [RawSocket] should listen for [RawSocketEvent.read] /// and [RawSocketEvent.readClosed] events. Default is `true`. /// /// Warning: setting [readEventsEnabled] to `false` might prevent socket /// from fully closing when [SocketDirection.receive] and /// [SocketDirection.send] directions are shutdown independently. See /// [shutdown] for more details. abstract bool readEventsEnabled; /// Set or get, if the [RawSocket] should listen for [RawSocketEvent.write] /// events. Default is `true`. /// This is a one-shot listener, and writeEventsEnabled must be set /// to true again to receive another write event. abstract bool writeEventsEnabled; /// Creates a new socket connection to the host and port. /// /// Returns a [Future] that will complete with either a [RawSocket] /// once connected, or an error if the host-lookup or connection failed. /// /// The [host] can either be a [String] or an [InternetAddress]. If [host] is a /// [String], [connect] will perform a [InternetAddress.lookup] and try /// all returned [InternetAddress]es, until connected. If IPv4 and IPv6 /// addresses are both available then connections over IPv4 are preferred. If /// no connection can be established then the error from the first failing /// connection is returned. /// /// The argument [sourceAddress] can be used to specify the local /// address to bind when making the connection. The [sourceAddress] can either /// be a [String] or an [InternetAddress]. If a [String] is passed it must /// hold a numeric IP address. /// /// The [sourcePort] defines the local port to bind to. If [sourcePort] is /// not specified or zero, a port will be chosen. /// /// The argument [timeout] is used to specify the maximum allowed time to wait /// for a connection to be established. If [timeout] is longer than the system /// level timeout duration, a timeout may occur sooner than specified in /// [timeout]. On timeout, a [SocketException] is thrown and all ongoing /// connection attempts to [host] are cancelled. external static Future connect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }); /// Like [connect], but returns a [Future] that completes with a /// [ConnectionTask] that can be cancelled if the [RawSocket] is no /// longer needed. external static Future> startConnect( host, int port, { sourceAddress, int sourcePort = 0, }); /// The number of received and non-read bytes in the socket that can be read. int available(); /// Read up to [len] bytes from the socket. /// /// This function is non-blocking and will only return data /// if data is available. /// The number of bytes read can be less than [len] if fewer bytes are /// available for immediate reading. If no data is available `null` /// is returned. Uint8List? read([int? len]); /// Reads a message containing up to [count] bytes from the socket. /// /// This function differs from [read] in that it will also return any /// [SocketControlMessage] that have been sent. /// /// This function is non-blocking and will only return data /// if data is available. /// The number of bytes read can be less than [count] if fewer bytes are /// available for immediate reading. /// Length of data buffer in [SocketMessage] indicates number of bytes read. /// /// Returns `null` if no data is available. /// /// Unsupported by [RawSecureSocket]. /// /// Unsupported on Android, Fuchsia, Windows. SocketMessage? readMessage([int? count]); /// Writes up to [count] bytes of the buffer from [offset] buffer offset to /// the socket. /// /// The number of successfully written bytes is returned. This function is /// non-blocking and will only write data if buffer space is available in /// the socket. This means that the number of successfully written bytes may /// be less than `count` or even 0. /// /// Transmission of the buffer may be delayed unless /// [SocketOption.tcpNoDelay] is set with [RawSocket.setOption]. /// /// The default value for [offset] is 0, and the default value for [count] is /// `buffer.length - offset`. int write(List buffer, [int offset = 0, int? count]); /// Writes socket control messages and data bytes to the socket. /// /// Writes [controlMessages] and up to [count] bytes of [data], /// starting at [offset], to the socket. If [count] is not provided, /// as many bytes as possible are written. Use [write] instead if no control /// messages are required to be sent. /// /// When sent control messages are received, they are retained until the /// next call to [readMessage], where all currently available control messages /// are provided as part of the returned [SocketMessage]. /// Calling [read] will read only data bytes, and will not affect control /// messages. /// /// The [count] must be positive (greater than zero). /// /// Returns the number of bytes written, which cannot be greater than /// [count], nor greater than `data.length - offset`. /// Return value of zero indicates that control messages were not sent. /// /// This function is non-blocking and will only write data /// if buffer space is available in the socket. /// /// Throws an [OSError] if message could not be sent out. /// /// Unsupported by [RawSecureSocket]. /// /// Unsupported on Android, Fuchsia, Windows. int sendMessage( List controlMessages, List data, [ int offset = 0, int? count, ]); /// The port used by this socket. /// /// Throws a [SocketException] if the socket is closed. int get port; /// The remote port connected to by this socket. /// /// Throws a [SocketException] if the socket is closed. int get remotePort; /// The [InternetAddress] used to connect this socket. /// /// Throws a [SocketException] if the socket is closed. InternetAddress get address; /// The remote [InternetAddress] connected to by this socket. /// /// Throws a [SocketException] if the socket is closed. InternetAddress get remoteAddress; /// Closes the socket. /// /// Returns a future that completes with this socket when the /// underlying connection is completely destroyed. /// /// Calling [close] will never throw an exception /// and calling it several times is supported. Calling [close] can result in /// a [RawSocketEvent.readClosed] event. Future close(); /// Shuts down the socket in the [direction]. /// /// Calling [shutdown] will never throw an exception /// and calling it several times is supported. Calling /// shutdown with either [SocketDirection.both] or [SocketDirection.receive] /// can result in a [RawSocketEvent.readClosed] event. /// /// Warning: [SocketDirection.receive] direction is only considered to be /// to be fully shutdown once all available data is drained and /// [RawSocketEvent.readClosed] is dispatched. Shutting down /// [SocketDirection.receive] and [SocketDirection.send] directions separately /// without draining the data will lead to socket staying around until the /// data is drained. This can happen if [readEventsEnabled] is set /// to `false` or if received data is not [read] in response to these /// events. This does not apply to shutting down both directions /// simultaneously using [SocketDirection.both] which will discard all /// received data instead. void shutdown(SocketDirection direction); /// Customize the [RawSocket]. /// /// See [SocketOption] for available options. /// /// Returns `true` if the option was set successfully, `false` otherwise. bool setOption(SocketOption option, bool enabled); /// Reads low level information about the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Returns the [RawSocketOption.value] on success. /// /// Throws an [OSError] on failure. Uint8List getRawOption(RawSocketOption option); /// Customizes the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Throws an [OSError] on failure. void setRawOption(RawSocketOption option); } /// A TCP connection between two sockets. /// /// A *socket connection* connects a *local* socket to a *remote* socket. /// Data, as [Uint8List]s, is received by the local socket, made available /// by the [Stream] interface of this class, and can be sent to the remote /// socket through the [IOSink] interface of this class. /// /// Transmission of the data sent through the [IOSink] interface may be /// delayed unless [SocketOption.tcpNoDelay] is set with /// [Socket.setOption]. abstract interface class Socket implements Stream, IOSink { /// Creates a new socket connection to the host and port and returns a [Future] /// that will complete with either a [Socket] once connected or an error /// if the host-lookup or connection failed. /// /// [host] can either be a [String] or an [InternetAddress]. If [host] is a /// [String], [connect] will perform a [InternetAddress.lookup] and try /// all returned [InternetAddress]es, until connected. Unless a /// connection was established, the error from the first failing connection is /// returned. /// /// The argument [sourceAddress] can be used to specify the local /// address to bind when making the connection. The [sourceAddress] can either /// be a [String] or an [InternetAddress]. If a [String] is passed it must /// hold a numeric IP address. /// /// The [sourcePort] defines the local port to bind to. If [sourcePort] is /// not specified or zero, a port will be chosen. /// /// The argument [timeout] is used to specify the maximum allowed time to wait /// for a connection to be established. If [timeout] is longer than the system /// level timeout duration, a timeout may occur sooner than specified in /// [timeout]. On timeout, a [SocketException] is thrown and all ongoing /// connection attempts to [host] are cancelled. static Future connect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return Socket._connect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); } return overrides.socketConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ); } /// Like [connect], but returns a [Future] that completes with a /// [ConnectionTask] that can be cancelled if the [Socket] is no /// longer needed. static Future> startConnect( host, int port, { sourceAddress, int sourcePort = 0, }) { final IOOverrides? overrides = IOOverrides.current; if (overrides == null) { return Socket._startConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); } return overrides.socketStartConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ); } external static Future _connect( host, int port, { sourceAddress, int sourcePort = 0, Duration? timeout, }); external static Future> _startConnect( host, int port, { sourceAddress, int sourcePort = 0, }); Future> _detachRaw(); /// Destroys the socket in both directions. /// /// Calling [destroy] will make the send a close event on the stream /// and will no longer react on data being piped to it. /// /// Call [close] (inherited from [IOSink]) to only close the [Socket] /// for sending data. void destroy(); /// Customizes the [RawSocket]. /// /// See [SocketOption] for available options. /// /// Returns `true` if the option was set successfully, false otherwise. /// /// Throws a [SocketException] if the socket has been destroyed or upgraded to /// a secure socket. bool setOption(SocketOption option, bool enabled); /// Reads low level information about the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Returns the [RawSocketOption.value] on success. /// /// Throws an [OSError] on failure and a [SocketException] if the socket has /// been destroyed or upgraded to a secure socket. Uint8List getRawOption(RawSocketOption option); /// Customizes the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Throws an [OSError] on failure and a [SocketException] if the socket has /// been destroyed or upgraded to a secure socket. void setRawOption(RawSocketOption option); /// Unsupported operation on sockets. /// /// This method, which is inherited from [IOSink], is not supported on /// sockets, and **must not** be called. /// Sockets have no way to report errors, so any error passed in to /// a socket using [addError] would be lost. void addError(Object error, [StackTrace? stackTrace]); /// The port used by this socket. /// /// Throws a [SocketException] if the socket is closed. /// The port is 0 if the socket is a Unix domain socket. int get port; /// The remote port connected to by this socket. /// /// Throws a [SocketException] if the socket is closed. /// The port is 0 if the socket is a Unix domain socket. int get remotePort; /// The [InternetAddress] used to connect this socket. /// /// Throws a [SocketException] if the socket is closed. InternetAddress get address; /// The remote [InternetAddress] connected to by this socket. /// /// Throws a [SocketException] if the socket is closed. InternetAddress get remoteAddress; Future close(); Future get done; } /// A data packet received by a [RawDatagramSocket]. final class Datagram { /// The actual bytes of the message. Uint8List data; /// The address of the socket which sends the data. InternetAddress address; /// The port of the socket which sends the data. int port; Datagram(this.data, this.address, this.port); } /// A wrapper around OS resource handle so it can be passed via Socket /// as part of [SocketMessage]. abstract interface class ResourceHandle { /// Creates wrapper around opened file. external factory ResourceHandle.fromFile(RandomAccessFile file); /// Creates wrapper around opened socket. external factory ResourceHandle.fromSocket(Socket socket); /// Creates wrapper around opened raw socket. external factory ResourceHandle.fromRawSocket(RawSocket socket); /// Creates wrapper around opened raw datagram socket. external factory ResourceHandle.fromRawDatagramSocket( RawDatagramSocket socket, ); /// Creates wrapper around current stdin. external factory ResourceHandle.fromStdin(Stdin stdin); /// Creates wrapper around current stdout. external factory ResourceHandle.fromStdout(Stdout stdout); // Creates wrapper around a readable pipe. external factory ResourceHandle.fromReadPipe(ReadPipe pipe); // Creates wrapper around a writeable pipe. external factory ResourceHandle.fromWritePipe(WritePipe pipe); /// Extracts opened file from resource handle. /// /// This can also be used when receiving stdin and stdout handles and read /// and write pipes. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. /// /// If this resource handle is not a file or stdio handle, the behavior of the /// returned [RandomAccessFile] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. RandomAccessFile toFile(); /// Extracts opened socket from resource handle. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. // /// If this resource handle is not a socket handle, the behavior of the /// returned [Socket] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. Socket toSocket(); /// Extracts opened raw socket from resource handle. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. /// /// If this resource handle is not a socket handle, the behavior of the /// returned [RawSocket] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. RawSocket toRawSocket(); /// Extracts opened raw datagram socket from resource handle. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. /// /// If this resource handle is not a datagram socket handle, the behavior of /// the returned [RawDatagramSocket] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. RawDatagramSocket toRawDatagramSocket(); /// Extracts a read pipe from resource handle. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. /// /// If this resource handle is not a readable pipe, the behavior of the /// returned [ReadPipe] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. ReadPipe toReadPipe(); /// Extracts a write pipe from resource handle. /// /// Since the [ResourceHandle] represents a single OS resource, /// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket], /// [toReadPipe], [toWritePipe], can be called after a call to this method. /// /// If this resource handle is not a writeable pipe, the behavior of the /// returned [ReadPipe] is completely unspecified. /// Be very careful to avoid using a handle incorrectly. WritePipe toWritePipe(); } /// Control message part of the [SocketMessage] received by a call to /// [RawSocket.readMessage]. /// /// Control messages could carry different information including /// [ResourceHandle]. If [ResourceHandle]s are available as part of this message, /// they can be extracted via [extractHandles]. abstract interface class SocketControlMessage { /// Creates a control message containing the provided [handles]. /// /// This is used by the sender when it sends handles across the socket. /// Receiver can extract the handles from the message using [extractHandles]. external factory SocketControlMessage.fromHandles( List handles, ); /// Extracts the list of handles embedded in this message. /// /// This method must only be used to extract handles from messages /// received on a socket. It must not be used on a socket control /// message that is created locally, and has not been sent using /// [RawSocket.sendMessage]. /// /// This method must only be called once. /// Calling it multiple times may cause duplicated handles with unspecified /// behavior. List extractHandles(); /// A platform specific value used to determine the kind of control message. /// /// Together with [type], these two integers identify the kind of control /// message in a platform specific way. /// For example, on Linux certain combinations of these values indicate /// that this is a control message that carries [ResourceHandle]s. int get level; /// A platform specific value used to determine the kind of control message. /// /// Together with [level], these two integers identify the kind of control /// message in a platform specific way. /// For example, on Linux certain combinations of these values indicate /// that this is a control message that carries [ResourceHandle]s. int get type; /// Actual bytes that were passed as part of the control message by the /// underlying platform. /// /// The bytes are interpreted differently depending on the [level] and /// [type]. These actual bytes can be used to inspect and interpret /// non-handle-carrying messages. Uint8List get data; } /// A socket message received by a [RawDatagramSocket]. /// /// A socket message consists of [data] bytes and [controlMessages]. final class SocketMessage { /// The actual bytes of the message. final Uint8List data; /// The control messages sent as part of this socket message. /// /// This list can be empty. final List controlMessages; SocketMessage(this.data, this.controlMessages); } /// An unbuffered interface to a UDP socket. /// /// The raw datagram socket delivers a [Stream] of [RawSocketEvent]s in the /// same chunks as the underlying operating system receives them. /// /// Note that the event [RawSocketEvent.readClosed] will never be /// received as an UDP socket cannot be closed by a remote peer. /// /// It is not the same as a /// [POSIX raw socket](http://man7.org/linux/man-pages/man7/raw.7.html). /// /// ```dart /// import 'dart:io'; /// import 'dart:typed_data'; /// /// void main() async { /// // Read the current time from an NTP server. /// final serverAddress = (await InternetAddress.lookup('pool.ntp.org')).first; /// final clientSocket = await RawDatagramSocket.bind( /// serverAddress.type == InternetAddressType.IPv6 /// ? InternetAddress.anyIPv6 /// : InternetAddress.anyIPv4, /// 0); /// final ntpQuery = Uint8List(48); /// ntpQuery[0] = 0x23; // See RFC 5905 7.3 /// /// clientSocket.listen((event) { /// switch (event) { /// case RawSocketEvent.read: /// final datagram = clientSocket.receive(); /// // Parse `datagram.data` /// clientSocket.close(); /// break; /// case RawSocketEvent.write: /// if (clientSocket.send(ntpQuery, serverAddress, 123) > 0) { /// clientSocket.writeEventsEnabled = false; /// } /// break; /// case RawSocketEvent.closed: /// break; /// default: /// throw "Unexpected event $event"; /// } /// }); /// } /// ``` abstract interface class RawDatagramSocket extends Stream { /// Whether the [RawDatagramSocket] should listen for /// [RawSocketEvent.read] events. /// /// Default is `true`. abstract bool readEventsEnabled; /// Whether the [RawDatagramSocket] should listen for /// [RawSocketEvent.write] events. /// /// Default is `true`. /// This is a one-shot listener, and [writeEventsEnabled] must be set to true /// again to receive another write event. abstract bool writeEventsEnabled; /// Whether multicast traffic is looped back to the host. /// /// By default multicast loopback is enabled. abstract bool multicastLoopback; /// The maximum network hops for multicast packages /// originating from this socket. /// /// For IPv4 this is referred to as TTL (time to live). /// /// By default this value is 1 causing multicast traffic to stay on /// the local network. abstract int multicastHops; /// The network interface used for outgoing multicast packages. /// /// A value of `null` indicate that the system chooses the network /// interface to use. /// /// By default this value is `null` @Deprecated( "This property is not implemented. Use getRawOption and " "setRawOption instead.", ) NetworkInterface? multicastInterface; /// Whether IPv4 broadcast is enabled. /// /// IPv4 broadcast needs to be enabled by the sender for sending IPv4 /// broadcast packages. By default IPv4 broadcast is disabled. /// /// For IPv6 there is no general broadcast mechanism. Use multicast /// instead. abstract bool broadcastEnabled; /// Binds a socket to the given [host] and [port]. /// /// When the socket is bound and has started listening on [port], the returned /// future completes with the [RawDatagramSocket] of the bound socket. /// /// The [host] can either be a [String] or an [InternetAddress]. If [host] is a /// [String], [bind] will perform a [InternetAddress.lookup] and use the first /// value in the list. To listen on the loopback interface, which will allow /// only incoming connections from the local host, use the value /// [InternetAddress.loopbackIPv4] or [InternetAddress.loopbackIPv6]. /// To allow for incoming connection from any network use either one of /// the values [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to /// bind to all interfaces, or use the IP address of a specific interface. /// /// The [reuseAddress] should be set for all listeners that bind to the same /// address. Otherwise, it will fail with a [SocketException]. /// /// The [reusePort] specifies whether the port can be reused. /// /// The [ttl] sets `time to live` of a datagram sent on the socket. external static Future bind( host, int port, { bool reuseAddress = true, bool reusePort = false, int ttl = 1, }); /// The port used by this socket. int get port; /// The address used by this socket. InternetAddress get address; /// Closes the datagram socket. void close(); /// Asynchronously sends a datagram. /// /// Returns the number of bytes written. This will always be either /// the size of [buffer] or `0`. /// /// A return value of `0` indicates that sending the datagram would block and /// that the [send] call can be tried again. /// /// A return value of the size of [buffer] indicates that a request to /// transmit the datagram was made to the operating system. It does not /// indicate that the operating system successfully sent the datagram. If a /// local failure to send the datagram occurs then an error event will be /// added to the [Stream]. If a networking or remote failure occurs then it /// will not be reported. /// /// The maximum size of a IPv4 UDP datagram is 65535 bytes (including both /// data and headers) but the practical maximum size is likely to be much /// lower due to operating system limits and the network's maximum /// transmission unit (MTU). /// /// Some IPv6 implementations may support payloads up to 4GB (see RFC-2675) /// but that support is limited (see RFC-6434) and has been removed in later /// standards (see RFC-8504). /// /// [Emperical testing by the Chromium team](https://groups.google.com/a/chromium.org/g/proto-quic/c/uKWLRh9JPCo) /// suggests that payloads later than 1350 cannot be reliably received. int send(List buffer, InternetAddress address, int port); /// Receives a datagram. /// /// Returns `null` if there are no datagrams available. Datagram? receive(); /// Joins a multicast group. /// /// If an error occur when trying to join the multicast group, an /// exception is thrown. void joinMulticast(InternetAddress group, [NetworkInterface? interface]); /// Leaves a multicast group. /// /// If an error occur when trying to join the multicast group, an /// exception is thrown. void leaveMulticast(InternetAddress group, [NetworkInterface? interface]); /// Reads low level information about the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Returns [RawSocketOption.value] on success. /// /// Throws an [OSError] on failure. Uint8List getRawOption(RawSocketOption option); /// Customizes the [RawSocket]. /// /// See [RawSocketOption] for available options. /// /// Throws an [OSError] on failure. void setRawOption(RawSocketOption option); } /// Exception thrown when a socket operation fails. class SocketException implements IOException { /// Description of the error. final String message; /// The underlying OS error. /// /// If this exception is not thrown due to an OS error, the value is `null`. final OSError? osError; /// The address of the socket giving rise to the exception. /// /// This is either the source or destination address of a socket, /// or it can be `null` if no socket end-point was involved in the cause of /// the exception. final InternetAddress? address; /// The port of the socket giving rise to the exception. /// /// This is either the source or destination address of a socket, /// or it can be `null` if no socket end-point was involved in the cause of /// the exception. final int? port; /// Creates a [SocketException] with the provided values. const SocketException(this.message, {this.osError, this.address, this.port}); /// Creates an exception reporting that a socket was used after it was closed. const SocketException.closed() : message = 'Socket has been closed', osError = null, address = null, port = null; String toString() { StringBuffer sb = StringBuffer(); sb.write("SocketException"); if (message.isNotEmpty) { sb.write(": $message"); if (osError != null) { sb.write(" ($osError)"); } } else if (osError != null) { sb.write(": $osError"); } if (address != null) { sb.write(", address = ${address!.host}"); } if (port != null) { sb.write(", port = $port"); } return sb.toString(); } } …—NN<:8+H"DDDD,1+++17H52DGBE +%794%:64 71/ 71/3 'D;6+R1HNW';B?MP)P&N L8QKNNN%OK/36R-P$%(B=8FG886K+58H>=OL,*+>'.P/*0I+MQ8F<8"#8P:*H+DI3AHE>((%/NJ MQ?$W=L$OIP>OF)C,1NI=.(&&%%2PMTL&, ;,;++@.=-+B-<-<0LI:$ A$( )%N/*&- 9:7;?@/<'#%LJ K:KO#DCG&1+";D7 9.&@JDI PNIGMAKIFFH2>K'O.(0,NMLO&7CP =CBGO(KQ()HAF"5QIRQOCHJKMOA@F2 $'"'F#P/IJNLNMLENO tJA:DL DM932&2"3&-4/ O>DN";DN bind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) { return _RawServerSocket.bind(address, port, backlog, v6Only, shared); } } @patch class RawSocket { @patch static Future connect( dynamic host, int port, { dynamic sourceAddress, int sourcePort = 0, Duration? timeout, }) { return _RawSocket.connect(host, port, sourceAddress, sourcePort, timeout); } @patch static Future> startConnect( dynamic host, int port, { dynamic sourceAddress, int sourcePort = 0, }) { return _RawSocket.startConnect(host, port, sourceAddress, sourcePort); } } @patch class RawSocketOption { static final List _optionsCache = List.filled( _RawSocketOptions.values.length, null, ); @patch static int _getOptionValue(int key) { if (key > _RawSocketOptions.values.length) { throw ArgumentError.value(key, 'key'); } return _optionsCache[key] ??= _getNativeOptionValue(key); } @pragma("vm:external-name", "RawSocketOption_GetOptionValue") external static int _getNativeOptionValue(int key); } @patch class InternetAddress { @patch static InternetAddress get loopbackIPv4 { return _InternetAddress.loopbackIPv4; } @patch static InternetAddress get loopbackIPv6 { return _InternetAddress.loopbackIPv6; } @patch static InternetAddress get anyIPv4 { return _InternetAddress.anyIPv4; } @patch static InternetAddress get anyIPv6 { return _InternetAddress.anyIPv6; } @patch factory InternetAddress(String address, {InternetAddressType? type}) { return _InternetAddress.fromString(address, type: type); } @patch factory InternetAddress.fromRawAddress( Uint8List rawAddress, { InternetAddressType? type, }) { return _InternetAddress.fromRawAddress(rawAddress, type: type); } @patch static Future> lookup( String host, { InternetAddressType type = InternetAddressType.any, }) { return _NativeSocket.lookup(host, type: type); } @patch static InternetAddress _cloneWithNewHost( InternetAddress address, String host, ) { return (address as _InternetAddress)._cloneWithNewHost(host); } @patch static InternetAddress? tryParse(String address) { return _InternetAddress.tryParse(address); } } @patch class NetworkInterface { @patch static bool get listSupported => true; @patch static Future> list({ bool includeLoopback = false, bool includeLinkLocal = false, InternetAddressType type = InternetAddressType.any, }) { return _NativeSocket.listInterfaces( includeLoopback: includeLoopback, includeLinkLocal: includeLinkLocal, type: type, ); } } void _throwOnBadPort(int port) { if ((port < 0) || (port > 0xFFFF)) { throw ArgumentError("Invalid port $port"); } } void _throwOnBadTtl(int ttl) { if (ttl < 1 || ttl > 255) { throw ArgumentError('Invalid ttl $ttl'); } } class _InternetAddress implements InternetAddress { static const int _addressLoopbackIPv4 = 0; static const int _addressLoopbackIPv6 = 1; static const int _addressAnyIPv4 = 2; static const int _addressAnyIPv6 = 3; static const int _IPv4AddrLength = 4; static const int _IPv6AddrLength = 16; static _InternetAddress loopbackIPv4 = _InternetAddress.fixed( _addressLoopbackIPv4, ); static _InternetAddress loopbackIPv6 = _InternetAddress.fixed( _addressLoopbackIPv6, ); static _InternetAddress anyIPv4 = _InternetAddress.fixed(_addressAnyIPv4); static _InternetAddress anyIPv6 = _InternetAddress.fixed(_addressAnyIPv6); final String address; final String? _host; final Uint8List _in_addr; final int _scope_id; final InternetAddressType type; String get host => _host ?? address; Uint8List get rawAddress => Uint8List.fromList(_in_addr); bool get isLoopback { switch (type) { case InternetAddressType.IPv4: return _in_addr[0] == 127; case InternetAddressType.IPv6: for (int i = 0; i < _IPv6AddrLength - 1; i++) { if (_in_addr[i] != 0) return false; } return _in_addr[_IPv6AddrLength - 1] == 1; case InternetAddressType.unix: return false; } throw UnsupportedError("Unexpected address type $type"); } bool get isLinkLocal { switch (type) { case InternetAddressType.IPv4: // Checking for 169.254.0.0/16. return _in_addr[0] == 169 && _in_addr[1] == 254; case InternetAddressType.IPv6: // Checking for fe80::/10. return _in_addr[0] == 0xFE && (_in_addr[1] & 0xB0) == 0x80; case InternetAddressType.unix: return false; } throw UnsupportedError("Unexpected address type $type"); } bool get isMulticast { switch (type) { case InternetAddressType.IPv4: // Checking for 224.0.0.0 through 239.255.255.255. return _in_addr[0] >= 224 && _in_addr[0] < 240; case InternetAddressType.IPv6: // Checking for ff00::/8. return _in_addr[0] == 0xFF; case InternetAddressType.unix: return false; } throw UnsupportedError("Unexpected address type $type"); } Future reverse() { if (type == InternetAddressType.unix) { return Future.value(this); } return _NativeSocket.reverseLookup(this); } _InternetAddress( this.type, this.address, this._host, this._in_addr, [ this._scope_id = 0, ]); static Object _parseAddressString( String address, { InternetAddressType? type, }) { if (type == InternetAddressType.unix) { var rawAddress = FileSystemEntity._toUtf8Array(address); return _InternetAddress( InternetAddressType.unix, address, null, rawAddress, ); } else { int index = address.indexOf('%'); String originalAddress = address; String? scopeID; if (index > 0) { scopeID = address.substring(index, address.length); address = address.substring(0, index); } var inAddr = _parse(address); if (inAddr == null) { return ArgumentError('Invalid internet address $address'); } InternetAddressType type = inAddr.length == _IPv4AddrLength ? InternetAddressType.IPv4 : InternetAddressType.IPv6; if (scopeID != null && scopeID.length > 0) { if (type != InternetAddressType.IPv6) { return ArgumentError.value( address, 'address', 'IPv4 addresses cannot have a scope ID', ); } final scopeID = _parseScopedLinkLocalAddress(originalAddress); if (scopeID is int) { return _InternetAddress( InternetAddressType.IPv6, originalAddress, null, inAddr, scopeID, ); } else { return ArgumentError.value( address, 'address', 'Invalid IPv6 address with scope ID', ); } } return _InternetAddress(type, originalAddress, null, inAddr, 0); } } factory _InternetAddress.fromString( String address, { InternetAddressType? type, }) { final parsedAddress = _parseAddressString(address, type: type); if (parsedAddress is _InternetAddress) { return parsedAddress; } else { assert(parsedAddress is ArgumentError); throw parsedAddress; } } static _InternetAddress? tryParse(String address) { final parsedAddress = _parseAddressString(address); if (parsedAddress is _InternetAddress) { return parsedAddress; } else { assert(parsedAddress is ArgumentError); return null; } } factory _InternetAddress.fromRawAddress( Uint8List rawAddress, { InternetAddressType? type, }) { if (type == InternetAddressType.unix) { ArgumentError.checkNotNull(rawAddress, 'rawAddress'); var rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawAddress); var address = FileSystemEntity._toStringFromUtf8Array(rawAddress); return _InternetAddress(InternetAddressType.unix, address, null, rawPath); } else { int type = -1; if (rawAddress.length == _IPv4AddrLength) { type = 0; } else { if (rawAddress.length != _IPv6AddrLength) { throw ArgumentError("Invalid internet address ${rawAddress}"); } type = 1; } var address = _rawAddrToString(rawAddress); return _InternetAddress( InternetAddressType._from(type), address, null, rawAddress, ); } } factory _InternetAddress.fixed(int id) { switch (id) { case _addressLoopbackIPv4: var in_addr = Uint8List(_IPv4AddrLength); in_addr[0] = 127; in_addr[_IPv4AddrLength - 1] = 1; return _InternetAddress( InternetAddressType.IPv4, "127.0.0.1", null, in_addr, ); case _addressLoopbackIPv6: var in_addr = Uint8List(_IPv6AddrLength); in_addr[_IPv6AddrLength - 1] = 1; return _InternetAddress(InternetAddressType.IPv6, "::1", null, in_addr); case _addressAnyIPv4: var in_addr = Uint8List(_IPv4AddrLength); return _InternetAddress( InternetAddressType.IPv4, "0.0.0.0", "0.0.0.0", in_addr, ); case _addressAnyIPv6: var in_addr = Uint8List(_IPv6AddrLength); return _InternetAddress(InternetAddressType.IPv6, "::", "::", in_addr); default: assert(false); throw ArgumentError(); } } // Create a clone of this _InternetAddress replacing the host. _InternetAddress _cloneWithNewHost(String host) { return _InternetAddress( type, address, host, Uint8List.fromList(_in_addr), _scope_id, ); } bool operator ==(other) { if (!(other is _InternetAddress)) return false; if (other.type != type) return false; if (type == InternetAddressType.unix) { return address == other.address; } bool equals = true; for (int i = 0; i < _in_addr.length && equals; i++) { equals = other._in_addr[i] == _in_addr[i]; } return equals; } int get hashCode { if (type == InternetAddressType.unix) { return address.hashCode; } int result = 1; for (int i = 0; i < _in_addr.length; i++) { result = (result * 31 + _in_addr[i]) & 0x3FFFFFFF; } return result; } String toString() { return "InternetAddress('$address', ${type.name})"; } @pragma("vm:external-name", "InternetAddress_RawAddrToString") external static String _rawAddrToString(Uint8List address); @pragma("vm:external-name", "InternetAddress_ParseScopedLinkLocalAddress") external static dynamic /* int | OSError */ _parseScopedLinkLocalAddress( String address, ); @pragma("vm:external-name", "InternetAddress_Parse") external static Uint8List? _parse(String address); } class _NetworkInterface implements NetworkInterface { final String name; final int index; final List addresses = []; _NetworkInterface(this.name, this.index); String toString() { return "NetworkInterface('$name', $addresses)"; } } // The NativeFieldWrapperClass1 cannot be used with a mixin, due to missing // implicit constructor. base class _NativeSocketNativeWrapper extends NativeFieldWrapperClass1 {} /// Returns error code that corresponds to EINPROGRESS OS error. @pragma("vm:external-name", "OSError_inProgressErrorCode") external int get _inProgressErrorCode; // The _NativeSocket class encapsulates an OS socket. base class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { // Bit flags used when communicating between the eventhandler and // dart code. The EVENT flags are used to indicate events of // interest when sending a message from dart code to the // eventhandler. When receiving a message from the eventhandler the // EVENT flags indicate the events that actually happened. The // COMMAND flags are used to send commands from dart to the // eventhandler. COMMAND flags are never received from the // eventhandler. Additional flags are used to communicate other // information. static const int readEvent = 0; static const int writeEvent = 1; static const int errorEvent = 2; static const int closedEvent = 3; static const int destroyedEvent = 4; static const int firstEvent = readEvent; static const int lastEvent = destroyedEvent; static const int eventCount = lastEvent - firstEvent + 1; static const int closeCommand = 8; static const int shutdownReadCommand = 9; static const int shutdownWriteCommand = 10; // The lower bits of returnTokenCommand messages contains the number // of tokens returned. static const int returnTokenCommand = 11; static const int setEventMaskCommand = 12; static const int firstCommand = closeCommand; static const int lastCommand = setEventMaskCommand; // Type flag send to the eventhandler providing additional // information on the type of the file descriptor. static const int listeningSocket = 16; static const int pipeSocket = 17; static const int typeNormalSocket = 0; static const int typeListeningSocket = 1 << listeningSocket; static const int typePipe = 1 << pipeSocket; static const int typeTypeMask = typeListeningSocket | pipeSocket; // Protocol flags. // Keep in sync with SocketType enum in socket.h. static const int tcpSocket = 18; static const int udpSocket = 19; static const int internalSocket = 20; static const int internalSignalSocket = 21; static const int typeTcpSocket = 1 << tcpSocket; static const int typeUdpSocket = 1 << udpSocket; static const int typeInternalSocket = 1 << internalSocket; static const int typeInternalSignalSocket = 1 << internalSignalSocket; static const int typeProtocolMask = typeTcpSocket | typeUdpSocket | typeInternalSocket | typeInternalSignalSocket; // Native port messages. static const hostNameLookupMessage = 0; static const listInterfacesMessage = 1; static const reverseLookupMessage = 2; // Protocol flags. static const int protocolIPv4 = 1 << 0; static const int protocolIPv6 = 1 << 1; static const int normalTokenBatchSize = 8; static const int listeningTokenBatchSize = 2; static const Duration _retryDuration = Duration(milliseconds: 250); static const Duration _retryDurationLoopback = Duration(milliseconds: 25); // Socket close state bool isClosed = false; bool isClosing = false; bool isClosedRead = false; bool closedReadEventSent = false; bool isClosedWrite = false; Completer closeCompleter = Completer.sync(); // Handlers and receive port for socket events from the event handler. void Function()? readEventHandler; void Function()? writeEventHandler; void Function(Object e, StackTrace? st)? errorEventHandler; void Function()? closedEventHandler; void Function()? destroyedEventHandler; RawReceivePort? eventPort; bool flagsSent = false; // The type flags for this socket. final int typeFlags; // Holds the port of the socket, 0 if not known. int localPort = 0; // Holds the address used to connect or bind the socket. late InternetAddress localAddress; // The size of data that is ready to be read, for TCP sockets. // This might be out-of-date when Read is called. // The number of pending connections, for Listening sockets. int available = 0; // Only used for UDP sockets. bool _availableDatagram = false; // The number of incoming connections for Listening socket. int connections = 0; // The count of received event from eventhandler. int tokens = 0; bool sendReadEvents = false; bool readEventIssued = false; bool sendWriteEvents = false; bool writeEventIssued = false; bool writeAvailable = false; // The owner object is the object that the Socket is being used by, e.g. // a HttpServer, a WebSocket connection, a process pipe, etc. Object? owner; static int _nextSocketId = 0; final int id = _nextSocketId++; static Future> lookup( String host, { InternetAddressType type = InternetAddressType.any, }) { return _IOService._dispatch(_IOService.socketLookup, [ host, type._value, ]).then((response) { if (isErrorResponse(response)) { throw createError(response, "Failed host lookup: '$host'"); } return [ for (List result in (response as List).skip(1)) _InternetAddress( InternetAddressType._from(result[0] as int), result[1] as String, host, result[2] as Uint8List, result[3] as int, ), ]; }); } static Future reverseLookup(InternetAddress addr) { return _IOService._dispatch(_IOService.socketReverseLookup, [ (addr as _InternetAddress)._in_addr, ]).then((response) { if (isErrorResponse(response)) { throw createError(response, "Failed reverse host lookup", addr); } else { return addr._cloneWithNewHost(response as String); } }); } static Future> listInterfaces({ bool includeLoopback = false, bool includeLinkLocal = false, InternetAddressType type = InternetAddressType.any, }) { return _IOService._dispatch(_IOService.socketListInterfaces, [ type._value, ]).then((response) { if (isErrorResponse(response)) { throw createError(response, "Failed listing interfaces"); } else { var map = (response as List).skip(1).fold( Map(), (map, result) { List resultList = result as List; var type = InternetAddressType._from(resultList[0] as int); var name = resultList[3] as String; var index = resultList[4] as int; var address = _InternetAddress( type, resultList[1] as String, "", resultList[2] as Uint8List, ); if (!includeLinkLocal && address.isLinkLocal) return map; if (!includeLoopback && address.isLoopback) return map; map.putIfAbsent(name, () => _NetworkInterface(name, index)); (map[name] as _NetworkInterface).addresses.add(address); return map; }, ); return map.values.toList(); } }); } static String escapeLinkLocalAddress(String host) { // if the host contains escape, host is an IPv6 address with scope ID. // Remove '25' before feeding into native calls. int index = host.indexOf('%'); if (index >= 0) { if (!checkLinkLocalAddress(host)) { // The only well defined usage is link-local address. Checks Section 4 of https://tools.ietf.org/html/rfc6874. // If it is not a valid link-local address and contains escape character, throw an exception. throw FormatException( '${host} is not a valid link-local address but contains %. Scope id should be used as part of link-local address.', host, index, ); } if (host.startsWith("25", index + 1)) { // Remove '25' after '%' if present host = host.replaceRange(index + 1, index + 3, ''); } } return host; } static bool checkLinkLocalAddress(String host) { // The shortest possible link-local address is [fe80::1] if (host.length < 7) return false; var char = host[2]; return host.startsWith('fe') && (char == '8' || char == '9' || char == 'a' || char == 'b'); } /// Explicitly makes two separate OS lookup requests: first for IPv4 then, /// after a short delay, for IPv6. /// /// This avoids making a single OS lookup request that internally does both /// IPv4 and IPv6 lookups together, which will be as slow as the slowest /// lookup. Some broken DNS servers do not support IPv6 AAAA records and /// will cause the IPv6 lookup to timeout. /// /// The IPv4 lookup is done first because, in practice, IPv4 traffic is /// routed more reliably. /// /// See https://dartbug.com/50868. static Stream> staggeredLookup( String host, _InternetAddress? source, ) { final controller = StreamController>(sync: true); controller.onListen = () { // Completed when there are no further addresses, or when the returned // stream is canceled, // The latter signals that no further addresses are needed. // When both completers are completed, one way or another, the stream is // closed. final ipv4Completer = Completer(); final ipv6Completer = Completer(); // Only report an error if no address lookups were sucessful. var anySuccess = false; void lookupAddresses( InternetAddressType type, Completer done, ) async { try { final addresses = await lookup(host, type: type); anySuccess = true; if (done.isCompleted) { // By the time lookup is done, [connectNext] might have // been able to connect to one of the resolved addresses. return; } controller.add(addresses); done.complete(); } catch (e, st) { if (done.isCompleted) { // By the time lookup is done, [connectNext] might have // been able to connect to one of the resolved addresses. return; } done.completeError(e, st); } } const concurrentLookupDelay = Duration(milliseconds: 10); Timer? ipv6LookupDelay; lookupAddresses(InternetAddressType.IPv4, ipv4Completer); if (source != null && source.type == InternetAddressType.IPv4) { // Binding to an IPv4 address and connecting to an IPv6 address will // never work. ipv6Completer.complete(); } else { // Introduce a delay before IPv6 lookup in order to favor IPv4. ipv6LookupDelay = Timer( concurrentLookupDelay, () => lookupAddresses(InternetAddressType.IPv6, ipv6Completer), ); } Future.wait([ipv4Completer.future, ipv6Completer.future]).then( (_) => controller.close(), onError: (e, st) { if (!anySuccess) { controller.addError(e, st); } controller.close(); }, ); controller.onCancel = () { // This is invoked when [connectNext] managed to connect to one of the // looked-up addresses at which point we want to stop looking up // the addresses. if (!ipv4Completer.isCompleted) ipv4Completer.complete(); if (!ipv6Completer.isCompleted) ipv6Completer.complete(); ipv6LookupDelay?.cancel(); }; }; return controller.stream; } static Future> startConnect( dynamic host, int port, dynamic sourceAddress, int sourcePort, ) { // Looks up [sourceAddress] to one or more IP addresses, // then tries connecting to each one until a connection succeeds. // Attempts are staggered by a minimum delay, so a new // attempt isn't made until either a previous attempt has *failed*, // or the delay has passed. // This ensures that at most *n* uncompleted connections can be // active after *n* × *delay* time has passed. if (host is String) { host = escapeLinkLocalAddress(host); } _throwOnBadPort(port); _throwOnBadPort(sourcePort); _InternetAddress? source; if (sourceAddress != null) { if (sourceAddress is _InternetAddress) { // A host of type [String] is interpreted to be a an internet host // (or numeric IP e.g. '127.0.0.1'), which is never reachable using // a Unix Domain Socket. if (host is String && sourceAddress.type == InternetAddressType.unix) { // ArgumentError would be better but changing it would not be // backwards-compatible. throw SocketException( "Cannot connect to an internet host using a unix domain socket", ); } source = sourceAddress; } else if (sourceAddress is String) { source = _InternetAddress.fromString(sourceAddress); } else { throw ArgumentError.value( sourceAddress, "sourceAddress", "Must be a string or native InternetAddress", ); } } final stackTrace = StackTrace.current; return Future.value(host).then>((host) { if (host is String) { // Attempt to interpret the host as a numeric address // (e.g. "127.0.0.1"). This will prevent [InternetAddress.lookup] from // generating an unnecessary address in a different address family e.g. // `InternetAddress.lookup('127.0.0.1', InternetAddressType.IPv6)` // may return `InternetAddress('::ffff:127.0.0.1'). host = _InternetAddress.tryParse(host) ?? host; } if (host is _InternetAddress) { return tryConnectToResolvedAddresses( host, port, source, sourcePort, Stream.value(<_InternetAddress>[host]), stackTrace, ); } final hostname = host as String; final Stream> addresses = staggeredLookup( hostname, source, ); return tryConnectToResolvedAddresses( host, port, source, sourcePort, addresses, stackTrace, ); }); } static ConnectionTask<_NativeSocket> tryConnectToResolvedAddresses( dynamic host, int port, _InternetAddress? source, int sourcePort, Stream> addresses, StackTrace callerStackTrace, ) { // Completer for result. final result = Completer<_NativeSocket>(); // Error, set if an error occurs. // Keeps first error if multiple errors occur. var error = null; // Contains all sockets which haven't received an initial // write or error event. final connecting = <_NativeSocket>{}; // Timer counting down from the last connection attempt. // Reset when a new connection is attempted, // which happens either when a previous timer runs out, // or when a previous connection attempt fails. Timer? timer; // Addresses arrived from lookup stream, but haven't been tried to connect // to yet due to Timer-based throttling. final pendingLookedUp = Queue(); // When deciding how to handle errors we need to know whether more // addresses potentially are coming from the lookup stream. bool isLookedUpStreamClosed = false; late StreamSubscription> addressesSubscription; Object? createConnection( InternetAddress address, _InternetAddress? source, _NativeSocket socket, ) { Object? connectionResult; if (address.type == InternetAddressType.unix) { if (source == null) { connectionResult = socket._nativeCreateUnixDomainConnect( address.address, _Namespace._namespace, ); } else { if (source.type != InternetAddressType.unix) { return SocketException( // Use the same error message as used on Linux for better // searchability... "Address family not supported by protocol family, " // ...and then add some details. "sourceAddress.type must be ${InternetAddressType.unix} but was " "${source.type}", address: address, ); } connectionResult = socket._nativeCreateUnixDomainBindConnect( address.address, source.address, _Namespace._namespace, ); } assert( connectionResult == true || connectionResult is Error || connectionResult is OSError, ); } else { final address_ = address as _InternetAddress; if (source == null && sourcePort == 0) { connectionResult = socket._nativeCreateConnect( address_._in_addr, port, address_._scope_id, ); } else { // allow specified port without address if (source == null) { source = address_.type == InternetAddressType.IPv4 ? _InternetAddress.anyIPv4 : _InternetAddress.anyIPv6; } if (source.type != InternetAddressType.IPv4 && source.type != InternetAddressType.IPv6) { return SocketException( // Use the same error message as used on Linux for better // searchability... "Address family not supported by protocol family, " // ...and then add some details. "sourceAddress.type must be ${InternetAddressType.IPv4} or " "${InternetAddressType.IPv6} but was ${source.type}", address: address, ); } connectionResult = socket._nativeCreateBindConnect( address_._in_addr, port, source._in_addr, sourcePort, address_._scope_id, ); } assert(connectionResult == true || connectionResult is OSError); } return connectionResult; } createConnectionError( Object? connectionResult, InternetAddress address, int port, _NativeSocket socket, ) { if (connectionResult is OSError) { final errorCode = connectionResult.errorCode; if (source != null && errorCode != null && socket.isBindError(errorCode)) { return createError(connectionResult, "Bind failed", source); } else { return createError( connectionResult, "Connection failed", address, port, ); } } else if (connectionResult is SocketException) { return connectionResult; } else if (connectionResult is Error) { return connectionResult; } return createError(null, "Connection failed", address); } // Invoked either directly or via throttling Timer callback when we // are ready to verify that we can connect to resolved address. connectNext() { timer?.cancel(); if (isLookedUpStreamClosed && connecting.isEmpty && pendingLookedUp.isEmpty) { assert(error != null); if (!result.isCompleted) { // Might be already completed via onCancel result.completeError(error, callerStackTrace); } return; } if (pendingLookedUp.isEmpty) { assert(!isLookedUpStreamClosed || connecting.isNotEmpty); return; } final address = pendingLookedUp.removeFirst(); final socket = _NativeSocket.normal(address); // Will contain values of various types representing the result // of trying to create a connection. // A value of `true` means success, everything else means failure. final Object? connectionResult = createConnection( address, source, socket, ); if (connectionResult != true) { // connectionResult was not a success. error = createConnectionError(connectionResult, address, port, socket); connectNext(); // Try again after failure to connect. return; } // Query the local port for error messages. try { socket.port; } catch (e) { if (e is OSError && e.errorCode == _inProgressErrorCode) { // Ignore the error, proceed with waiting for a socket to become open. // In non-blocking mode connect might not be established away, socket // have to be waited for. // EINPROGRESS error is ignored during |connect| call in native code, // it has be ignored here during |port| query here. } else { error ??= createError(e, "Connection failed", address, port); connectNext(); // Try again after failure to connect. return; } } // Try again if no response (failure or success) within a duration. // If this occurs, the socket is still trying to connect, and might // succeed or fail later. final duration = address.isLoopback ? _retryDurationLoopback : _retryDuration; timer = Timer(duration, connectNext); connecting.add(socket); // Setup handlers for receiving the first write event which // indicate that the socket is fully connected. socket.setHandlers( write: () { // First remote response on connection. // If error, drop the socket and go to the next address. // If success, complete with the socket // and stop all other open connection attempts. connecting.remove(socket); // From 'man 2 connect': // After select(2) indicates writability, use getsockopt(2) to read // the SO_ERROR option at level SOL_SOCKET to determine whether // connect() completed successfully (SO_ERROR is zero) or // unsuccessfully. final osError = socket._nativeGetError(); if (osError != null) { socket.close(); error ??= osError; connectNext(); return; } // Connection success! // Stop all other connecting sockets and the timer. timer!.cancel(); socket.setListening(read: false, write: false); for (var s in connecting) { s.close(); s.setHandlers(); s.setListening(read: false, write: false); } connecting.clear(); addressesSubscription.cancel(); if (!result.isCompleted) { // Might be already completed via onCancel result.complete(socket); } }, error: (e, st) { connecting.remove(socket); socket.close(); socket.setHandlers(); socket.setListening(read: false, write: false); // Keep first error, if present. error ??= e; connectNext(); // Try again after failure to connect. }, ); socket.setListening(read: false, write: true); } void onCancel() { timer?.cancel(); for (var s in connecting) { s.close(); s.setHandlers(); s.setListening(read: false, write: false); } addressesSubscription.cancel(); connecting.clear(); if (!result.isCompleted) { error ??= createError( null, "Connection attempt cancelled, host: ${host}, port: ${port}", ); result.completeError(error, callerStackTrace); } } addressesSubscription = addresses.listen( (address) { pendingLookedUp.addAll(address); if (timer == null || !timer!.isActive) { connectNext(); } }, onDone: () { isLookedUpStreamClosed = true; connectNext(); }, onError: (e, st) { error = e; }, ); connectNext(); return ConnectionTask<_NativeSocket>._(result.future, onCancel); } static Future<_NativeSocket> connect( dynamic host, int port, dynamic sourceAddress, int sourcePort, Duration? timeout, ) { return startConnect(host, port, sourceAddress, sourcePort).then(( ConnectionTask<_NativeSocket> task, ) { Future<_NativeSocket> socketFuture = task.socket; if (timeout != null) { socketFuture = socketFuture.timeout( timeout, onTimeout: () { task.cancel(); throw createError( OSError( "Connection timed out", Platform.isWindows ? 10060 // WSAETIMEDOUT : 110, // ETIMEDOUT ), "Connection timed out, host: ${host}, port: ${port}", ); }, ); } return socketFuture; }); } static Future<_InternetAddress> _resolveHost(dynamic host) async { if (host is _InternetAddress) { return host; } else { final list = await lookup(host); if (list.isEmpty) { throw createError(null, "Failed host lookup: '$host'"); } return list.first as _InternetAddress; } } static Future<_NativeSocket> bind( host, int port, int backlog, bool v6Only, bool shared, ) async { _throwOnBadPort(port); if (host is String) { host = escapeLinkLocalAddress(host); } final address = await _resolveHost(host); var socket = _NativeSocket.listen(address); var result; if (address.type == InternetAddressType.unix) { var path = address.address; if (FileSystemEntity.isLinkSync(path)) { path = Link(path).targetSync(); } result = socket._nativeCreateUnixDomainBindListen( path, backlog, shared, _Namespace._namespace, ); } else { result = socket._nativeCreateBindListen( address._in_addr, port, backlog, v6Only, shared, address._scope_id, ); } if (result is OSError) { throw SocketException( "Failed to create server socket", osError: result, address: address, port: port, ); } if (port != 0) socket.localPort = port; socket.connectToEventHandler(); return socket; } static Future<_NativeSocket> bindDatagram( host, int port, bool reuseAddress, bool reusePort, int ttl, ) async { _throwOnBadPort(port); _throwOnBadTtl(ttl); final address = await _resolveHost(host); if (address.type != InternetAddressType.IPv6 && address.type != InternetAddressType.IPv4) { throw SocketException( 'Cannot bind datagram socket on non-IPv4/IPv6 address (was: $address)', ); } var socket = _NativeSocket.datagram(address); var result = socket._nativeCreateBindDatagram( address._in_addr, port, reuseAddress, reusePort, ttl, ); if (result is OSError) { throw SocketException( "Failed to create datagram socket", osError: result, address: address, port: port, ); } if (port != 0) socket.localPort = port; return socket; } _NativeSocket.datagram(this.localAddress) : typeFlags = typeNormalSocket | typeUdpSocket; _NativeSocket.normal(this.localAddress) : typeFlags = typeNormalSocket | typeTcpSocket; _NativeSocket.listen(this.localAddress) : typeFlags = typeListeningSocket | typeTcpSocket { isClosedWrite = true; } _NativeSocket.pipe() : typeFlags = typePipe; _NativeSocket._watchCommon(int id, int type) : typeFlags = typeNormalSocket | type { isClosedWrite = true; _nativeSetSocketId(id, typeFlags); } _NativeSocket._watchSignal(int id) : this._watchCommon(id, typeInternalSignalSocket); _NativeSocket._watch(int id) : this._watchCommon(id, typeInternalSocket); bool get isListening => (typeFlags & typeListeningSocket) != 0; bool get isPipe => (typeFlags & typePipe) != 0; bool get isInternal => (typeFlags & typeInternalSocket) != 0; bool get isInternalSignal => (typeFlags & typeInternalSignalSocket) != 0; bool get isTcp => (typeFlags & typeTcpSocket) != 0; bool get isUdp => (typeFlags & typeUdpSocket) != 0; String get _serviceTypePath => throw UnimplementedError(); String get _serviceTypeName => throw UnimplementedError(); Uint8List? read(int? count) { if (count != null && count <= 0) { throw ArgumentError("Illegal length $count"); } if (isClosing || isClosed) return null; try { Uint8List? list; if (count != null) { list = _nativeRead(count); available = _nativeAvailable(); } else { // If count is null, read as many bytes as possible. // Loop here to ensure bytes that arrived while this read was // issued are also read. BytesBuilder builder = BytesBuilder(copy: false); do { assert(available > 0); list = _nativeRead(available); if (list == null) { break; } builder.add(list); available = _nativeAvailable(); const MAX_BUFFER_SIZE = 4 * 1024 * 1024; if (builder.length > MAX_BUFFER_SIZE) { // Don't consume too many bytes, otherwise we risk running // out of memory when handling the whole aggregated lot. break; } } while (available > 0); if (builder.isEmpty) { list = null; } else { list = builder.toBytes(); } } if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.readBytes, list?.length, ); } return list; } catch (e) { reportError(e, StackTrace.current, "Read failed"); return null; } } Datagram? receive() { if (isClosing || isClosed) return null; try { Datagram? result = _nativeRecvFrom(); if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.readBytes, result?.data.length, ); } _availableDatagram = _nativeAvailableDatagram(); return result; } catch (e) { reportError(e, StackTrace.current, "Receive failed"); return null; } } SocketMessage? readMessage([int? count]) { if (count != null && count <= 0) { throw ArgumentError("Illegal length $count"); } if (isClosing || isClosed) return null; try { final bytesCount = count ?? _nativeAvailable(); // Returned messagesData is a list of triples (level, type, uint8list) // followed by uint8list with raw data. // This is kept at this level to minimize dart api use in native method. final List messagesData = _nativeReceiveMessage(bytesCount); final messages = []; if (messagesData.isNotEmpty) { final triplesCount = (messagesData.length - 1) / 3; assert((triplesCount * 3) == (messagesData.length - 1)); for (int i = 0; i < triplesCount; i++) { final message = _SocketControlMessageImpl( messagesData[i * 3] as int, messagesData[i * 3 + 1] as int, messagesData[i * 3 + 2] as Uint8List, ); messages.add(message); } } final socketMessage = SocketMessage( messagesData[messagesData.length - 1] as Uint8List, messages, ); available = _nativeAvailable(); if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.readBytes, bytesCount, ); } return socketMessage; } catch (e, st) { reportError(e, st, "Read failed"); return null; } } // This code issues a native write operation. // // On POSIX systems the data will be written using `write` syscall. // When `write` returns a positive value this means that this number // of bytes have been transferred from [buffer] into the OS buffer. // At this point if the underlying descriptor is closed the OS will // still attempt to deliver already written bytes to the destination. // // On Windows we use overlapped IO instead: `write` returning a positive // value simply means that we have initiated an asynchronous IO operation // for this many bytes. Closing the underlying handle will simply cancel the // operation midway. Consequently you can only assume that bytes left userland // when asynchronous write operation completes and this socket receives // a [writeEvent]. int write(List buffer, int offset, int? bytes) { if (bytes == null) { if (offset > buffer.length) { throw RangeError.value(offset); } bytes = buffer.length - offset; } if (offset < 0) throw RangeError.value(offset); if (bytes < 0) throw RangeError.value(bytes); if ((offset + bytes) > buffer.length) { throw RangeError.value(offset + bytes); } if (isClosing || isClosed) return 0; if (bytes == 0) return 0; try { _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, offset, offset + bytes, ); if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.writeBytes, bufferAndStart.buffer.length - bufferAndStart.start, ); } int result = _nativeWrite( bufferAndStart.buffer, bufferAndStart.start, bytes, ); if (result >= 0) { // If write succeeded only partially or is pending then we should // pause writing and wait for the write event to arrive from the // event handler. If the write has fully completed then we should // continue writing. writeAvailable = (result == bytes) && !hasPendingWrite(); } else { // Negative result indicates that we forced a short write for testing // purpose. We are not guaranteed to get a writeEvent in this case // unless there is a pending write - which will trigger an event // when it completes. So the caller should continue writing into // this socket. result = -result; writeAvailable = !hasPendingWrite(); } return result; } catch (e) { StackTrace st = StackTrace.current; scheduleMicrotask(() => reportError(e, st, "Write failed")); return 0; } } int send( List buffer, int offset, int bytes, InternetAddress address, int port, ) { _throwOnBadPort(port); if (isClosing || isClosed) return 0; try { _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, offset, bytes, ); if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.writeBytes, bufferAndStart.buffer.length - bufferAndStart.start, ); } int result = _nativeSendTo( bufferAndStart.buffer, bufferAndStart.start, bytes, (address as _InternetAddress)._in_addr, port, ); return result; } catch (e) { StackTrace st = StackTrace.current; scheduleMicrotask(() => reportError(e, st, "Send failed")); return 0; } } int sendMessage( List buffer, int offset, int? bytes, List controlMessages, ) { if (offset < 0) throw RangeError.value(offset); if (bytes != null) { if (bytes < 0) throw RangeError.value(bytes); } else { bytes = buffer.length - offset; } if ((offset + bytes) > buffer.length) { throw RangeError.value(offset + bytes); } if (isClosing || isClosed) return 0; try { _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, offset, bytes, ); if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic( id, _SocketProfileType.writeBytes, bufferAndStart.buffer.length - bufferAndStart.start, ); } // list of triples arranged to minimize dart api // use in native method. List messages = []; for (SocketControlMessage controlMessage in controlMessages) { messages.add(controlMessage.level); messages.add(controlMessage.type); messages.add(controlMessage.data); } return _nativeSendMessage( bufferAndStart.buffer, bufferAndStart.start, bytes, messages, ); } catch (e, st) { scheduleMicrotask(() => reportError(e, st, "SendMessage failed")); return 0; } } _NativeSocket? accept() { // Don't issue accept if we're closing. if (isClosing || isClosed) return null; assert(connections > 0); connections--; tokens++; returnTokens(listeningTokenBatchSize); var socket = _NativeSocket.normal(address); if (_nativeAccept(socket) != true) return null; socket.localPort = localPort; return socket; } int get port { if (localAddress.type == InternetAddressType.unix) return 0; if (localPort != 0) return localPort; _ensureNotClosingOrClosed(); var result = _nativeGetPort(); if (result is OSError) { throw result; } return localPort = result; } int get remotePort { if (localAddress.type == InternetAddressType.unix) return 0; _ensureNotClosingOrClosed(); return _nativeGetRemotePeer()[1]; } InternetAddress get address => localAddress; InternetAddress get remoteAddress { _ensureNotClosingOrClosed(); var result = _nativeGetRemotePeer(); var addr = result[0] as List; var type = InternetAddressType._from(addr[0] as int); if (type == InternetAddressType.unix) { return _InternetAddress.fromString( addr[1] as String, type: InternetAddressType.unix, ); } return _InternetAddress( type, addr[1] as String, null, addr[2] as Uint8List, ); } void issueReadEvent() { if (closedReadEventSent) return; if (readEventIssued) return; readEventIssued = true; void issue() { readEventIssued = false; if (isClosing) return; // Note: it is by design that we don't deliver closedRead event // unless read events are enabled. This also means we will not // fully close (and dispose) of the socket unless it is drained // of accumulated incomming data. if (!sendReadEvents) return; if (stopRead()) { if (isClosedRead && !closedReadEventSent) { if (isClosedWrite) close(); var handler = closedEventHandler; if (handler == null) return; closedReadEventSent = true; handler(); } return; } var handler = readEventHandler; if (handler == null) return; handler(); readEventIssued = true; scheduleMicrotask(issue); } scheduleMicrotask(issue); } bool stopRead() { if (isUdp) { return !_availableDatagram; } else { return available == 0; } } void issueWriteEvent({bool delayed = true}) { if (writeEventIssued) return; if (!writeAvailable) return; void issue() { writeEventIssued = false; if (!writeAvailable) return; if (isClosing) return; if (!sendWriteEvents) return; sendWriteEvents = false; var handler = writeEventHandler; if (handler == null) return; handler(); } if (delayed) { writeEventIssued = true; scheduleMicrotask(issue); } else { issue(); } } // Multiplexes socket events to the socket handlers. void multiplex(Object eventsObj) { // TODO(paulberry): when issue #31305 is fixed, we should be able to simply // declare `events` as a `covariant int` parameter. int events = eventsObj as int; for (int i = firstEvent; i <= lastEvent; i++) { if (((events & (1 << i)) != 0)) { if (isClosing && i != destroyedEvent) continue; switch (i) { case readEvent: if (isClosedRead) continue; if (isListening) { connections++; if (!isClosed) { // If the connection is closed right after it's accepted, there's a // chance the close-handler is not set. var handler = readEventHandler; if (handler != null) handler(); } } else { if (isUdp) { _availableDatagram = _nativeAvailableDatagram(); } else { available = _nativeAvailable(); } issueReadEvent(); continue; } break; case writeEvent: // On Windows there are two sources of write events: when pending // write completes and when we subscribe to write events via // setEventMaskCommand. Furthermore we don't always wait for a // write event to issue a write. This means when event triggered by // setEventMaskCommand arrives we might have already initiated a // write. This means we should check [hasPendingWrite] here to // be absolutely certain that the pending write operation has // completed. writeAvailable = !hasPendingWrite(); issueWriteEvent(delayed: false); continue; case errorEvent: if (!isClosing) { final osError = _nativeGetError(); if (osError != null) { reportError(osError, null, osError.message); } else { reportError( Error(), StackTrace.current, "Error event raised in event handler : " "error condition has been reset", ); } } break; case closedEvent: if (isClosedRead) continue; if (!isListening && !isClosing && !isClosed) { isClosedRead = true; issueReadEvent(); continue; } else if (!isClosed) { // If the connection is closed right after it's accepted, there's a // chance the close-handler is not set. var handler = closedEventHandler; if (handler != null) handler(); } break; case destroyedEvent: assert(isClosing); assert(!isClosed); isClosed = true; closeCompleter.complete(); disconnectFromEventHandler(); var handler = destroyedEventHandler; if (handler != null) handler(); continue; } } } if (!isListening) { tokens++; returnTokens(normalTokenBatchSize); } } void returnTokens(int tokenBatchSize) { if (!isClosing && !isClosed) { assert(eventPort != null); // Return in batches. if (tokens == tokenBatchSize) { assert(tokens < (1 << firstCommand)); _sendToEventHandler((1 << returnTokenCommand) | tokens); tokens = 0; } } } void setHandlers({ void Function()? read, void Function()? write, void Function(Object e, StackTrace? st)? error, void Function()? closed, void Function()? destroyed, }) { readEventHandler = read; writeEventHandler = write; errorEventHandler = error; closedEventHandler = closed; destroyedEventHandler = destroyed; } void setListening({bool read = true, bool write = true}) { sendReadEvents = read; sendWriteEvents = write; if (read) issueReadEvent(); if (write) issueWriteEvent(); if (!flagsSent && !isClosing) { flagsSent = true; int flags = 1 << setEventMaskCommand; if (!isClosedRead) flags |= 1 << readEvent; if (!isClosedWrite) flags |= 1 << writeEvent; _sendToEventHandler(flags); } } Future close() { if (!isClosing && !isClosed) { _sendToEventHandler(1 << closeCommand); isClosing = true; } return closeCompleter.future; } void shutdown(SocketDirection direction) { if (!isClosing && !isClosed) { switch (direction) { case SocketDirection.receive: shutdownRead(); break; case SocketDirection.send: shutdownWrite(); break; case SocketDirection.both: close(); break; default: throw ArgumentError(direction); } } } void shutdownWrite() { if (!isClosing && !isClosed) { if (closedReadEventSent) { close(); } else { _sendToEventHandler(1 << shutdownWriteCommand); } isClosedWrite = true; } } void shutdownRead() { if (!isClosing && !isClosed) { if (isClosedWrite) { close(); } else { _sendToEventHandler(1 << shutdownReadCommand); } isClosedRead = true; // Make sure to dispatch a closedRead event. Shutdown is only complete // once the socket is drained of data and readClosed is dispatched. issueReadEvent(); } } void _sendToEventHandler(int data) { int fullData = (typeFlags & typeTypeMask) | data; assert(!isClosing); connectToEventHandler(); _EventHandler._sendData(this, eventPort!.sendPort, fullData); } void connectToEventHandler() { assert(!isClosed); if (eventPort == null) { eventPort = RawReceivePort(multiplex, 'Socket Event Handler'); } } void disconnectFromEventHandler() { eventPort!.close(); eventPort = null; // Now that we don't track this Socket anymore, we can clear the owner // field. owner = null; } // Check whether this is an error response from a native port call. static bool isErrorResponse(response) { return response is List && response[0] != _successResponse; } // Create the appropriate error/exception from different returned // error objects. static createError( error, String message, [ InternetAddress? address, int? port, ]) { if (error is OSError) { return SocketException( message, osError: error, address: address, port: port, ); } else if (error is List) { assert(isErrorResponse(error)); switch (error[0]) { case _illegalArgumentResponse: return ArgumentError(); case _osErrorResponse: return SocketException( message, osError: OSError(error[2], error[1]), address: address, port: port, ); default: return AssertionError("Unknown error"); } } else { return SocketException(message, address: address, port: port); } } void reportError(error, StackTrace? st, String message) { var e = createError( error, message, isUdp || isTcp ? address : null, localPort, ); // Invoke the error handler if any. var handler = errorEventHandler; if (handler != null) { handler(e, st); } // For all errors we close the socket close(); } dynamic getOption(SocketOption option) { _ensureNotClosingOrClosed(); var result = _nativeGetOption(option._value, address.type._value); if (result is OSError) throw result; return result; } bool setOption(SocketOption option, value) { _ensureNotClosingOrClosed(); _nativeSetOption(option._value, address.type._value, value); return true; } Uint8List getRawOption(RawSocketOption option) { _ensureNotClosingOrClosed(); _nativeGetRawOption(option.level, option.option, option.value); return option.value; } void setRawOption(RawSocketOption option) { _ensureNotClosingOrClosed(); _nativeSetRawOption(option.level, option.option, option.value); } InternetAddress? multicastAddress( InternetAddress addr, NetworkInterface? interface, ) { // On Mac OS using the interface index for joining IPv4 multicast groups // is not supported. Here the IP address of the interface is needed. if ((Platform.isMacOS || Platform.isIOS) && addr.type == InternetAddressType.IPv4) { if (interface != null) { for (int i = 0; i < interface.addresses.length; i++) { if (interface.addresses[i].type == InternetAddressType.IPv4) { return interface.addresses[i]; } } // No IPv4 address found on the interface. throw SocketException( "The network interface does not have an address " "of the same family as the multicast address", ); } else { // Default to the ANY address if no interface is specified. return InternetAddress.anyIPv4; } } else { return null; } } void joinMulticast(InternetAddress addr, NetworkInterface? interface) { _ensureNotClosingOrClosed(); final interfaceAddr = multicastAddress(addr, interface) as _InternetAddress?; var interfaceIndex = interface == null ? 0 : interface.index; _nativeJoinMulticast( (addr as _InternetAddress)._in_addr, interfaceAddr?._in_addr, interfaceIndex, ); } void leaveMulticast(InternetAddress addr, NetworkInterface? interface) { _ensureNotClosingOrClosed(); final interfaceAddr = multicastAddress(addr, interface) as _InternetAddress?; var interfaceIndex = interface == null ? 0 : interface.index; _nativeLeaveMulticast( (addr as _InternetAddress)._in_addr, interfaceAddr?._in_addr, interfaceIndex, ); } bool hasPendingWrite() { return Platform.isWindows && _nativeHasPendingWrite(); } // Native methods are not guarding against closed sockets, which can // lead to crashes. void _ensureNotClosingOrClosed() { if (isClosing || isClosed) throw const SocketException.closed(); } @pragma("vm:external-name", "Socket_SetSocketId") external void _nativeSetSocketId(int id, int typeFlags); @pragma("vm:external-name", "Socket_Available") external int _nativeAvailable(); @pragma("vm:external-name", "Socket_AvailableDatagram") external bool _nativeAvailableDatagram(); @pragma("vm:external-name", "Socket_Read") external Uint8List? _nativeRead(int len); @pragma("vm:external-name", "Socket_RecvFrom") external Datagram? _nativeRecvFrom(); @pragma("vm:external-name", "Socket_ReceiveMessage") external List _nativeReceiveMessage(int len); @pragma("vm:external-name", "Socket_WriteList") external int _nativeWrite(List buffer, int offset, int bytes); @pragma("vm:external-name", "Socket_HasPendingWrite") external bool _nativeHasPendingWrite(); @pragma("vm:external-name", "Socket_SendTo") external int _nativeSendTo( List buffer, int offset, int bytes, Uint8List address, int port, ); @pragma("vm:external-name", "Socket_SendMessage") external _nativeSendMessage( List buffer, int offset, int bytes, List controlMessages, ); @pragma("vm:external-name", "Socket_CreateConnect") external _nativeCreateConnect(Uint8List addr, int port, int scope_id); @pragma("vm:external-name", "Socket_CreateUnixDomainConnect") external _nativeCreateUnixDomainConnect(String addr, _Namespace namespace); @pragma("vm:external-name", "Socket_CreateBindConnect") external _nativeCreateBindConnect( Uint8List addr, int port, Uint8List sourceAddr, int sourcePort, int scope_id, ); @pragma("vm:external-name", "Socket_CreateUnixDomainBindConnect") external _nativeCreateUnixDomainBindConnect( String addr, String sourceAddr, _Namespace namespace, ); @pragma("vm:external-name", "SocketBase_IsBindError") external bool isBindError(int errorNumber); @pragma("vm:external-name", "ServerSocket_CreateBindListen") external _nativeCreateBindListen( Uint8List addr, int port, int backlog, bool v6Only, bool shared, int scope_id, ); @pragma("vm:external-name", "ServerSocket_CreateUnixDomainBindListen") external _nativeCreateUnixDomainBindListen( String addr, int backlog, bool shared, _Namespace namespace, ); @pragma("vm:external-name", "Socket_CreateBindDatagram") external _nativeCreateBindDatagram( Uint8List addr, int port, bool reuseAddress, bool reusePort, int ttl, ); @pragma("vm:external-name", "ServerSocket_Accept") external bool _nativeAccept(_NativeSocket socket); @pragma("vm:external-name", "Socket_GetPort") external dynamic _nativeGetPort(); @pragma("vm:external-name", "Socket_GetRemotePeer") external List _nativeGetRemotePeer(); @pragma("vm:external-name", "Socket_GetSocketId") external int _nativeGetSocketId(); @pragma("vm:external-name", "Socket_GetFD") external int get fd; @pragma("vm:external-name", "Socket_GetError") external OSError? _nativeGetError(); @pragma("vm:external-name", "Socket_GetOption") external _nativeGetOption(int option, int protocol); @pragma("vm:external-name", "Socket_GetRawOption") external void _nativeGetRawOption(int level, int option, Uint8List data); @pragma("vm:external-name", "Socket_SetOption") external void _nativeSetOption(int option, int protocol, value); @pragma("vm:external-name", "Socket_SetRawOption") external void _nativeSetRawOption(int level, int option, Uint8List data); @pragma("vm:external-name", "Socket_JoinMulticast") external void _nativeJoinMulticast( Uint8List addr, Uint8List? interfaceAddr, int interfaceIndex, ); @pragma("vm:external-name", "Socket_LeaveMulticast") external void _nativeLeaveMulticast( Uint8List addr, Uint8List? interfaceAddr, int interfaceIndex, ); @pragma("vm:external-name", "Socket_Fatal") external static void _nativeFatal(msg); } class _RawServerSocket extends Stream implements RawServerSocket, _RawSocketBase { final _NativeSocket _socket; StreamController? _controller; bool _v6Only; static Future<_RawServerSocket> bind( address, int port, int backlog, bool v6Only, bool shared, ) { _throwOnBadPort(port); if (backlog < 0) throw ArgumentError("Invalid backlog $backlog"); return _NativeSocket.bind( address, port, backlog, v6Only, shared, ).then((socket) => _RawServerSocket(socket, v6Only)); } _RawServerSocket(this._socket, this._v6Only); StreamSubscription listen( void onData(RawSocket event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { if (_controller != null) { throw StateError("Stream was already listened to"); } var zone = Zone.current; final controller = _controller = StreamController( sync: true, onListen: _resume, onCancel: close, onPause: _pause, onResume: _resume, ); _socket.setHandlers( read: zone.bindCallbackGuarded(() { while (_socket.connections > 0) { var socket = _socket.accept(); if (socket == null) return; if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectNewSocket( socket.id, _tcpSocket, socket.address, socket.port, ); } controller.add(_RawSocket(socket)); if (controller.isPaused) return; } }), error: zone.bindBinaryCallbackGuarded((Object e, StackTrace? st) { controller.addError(e, st); controller.close(); }), destroyed: () => controller.close(), ); return controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } int get port => _socket.port; InternetAddress get address => _socket.address; Future close() { return _socket.close().then((_) => this); } void _pause() { _socket.setListening(read: false, write: false); } void _resume() { _socket.setListening(read: true, write: false); } bool get _closedReadEventSent => _socket.closedReadEventSent; void set _owner(owner) { _socket.owner = owner; } } class _RawSocket extends Stream implements RawSocket, _RawSocketBase { final _NativeSocket _socket; final _controller = StreamController(sync: true); bool _readEventsEnabled = true; bool _writeEventsEnabled = true; // Flag to handle Ctrl-D closing of stdio on Mac OS. bool _isMacOSTerminalInput = false; static Future connect( dynamic host, int port, dynamic sourceAddress, int sourcePort, Duration? timeout, ) { return _NativeSocket.connect( host, port, sourceAddress, sourcePort, timeout, ).then((socket) { if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectNewSocket( socket.id, _tcpSocket, socket.address, port, ); } return _RawSocket(socket); }); } static Future> startConnect( dynamic host, int port, dynamic sourceAddress, int sourcePort, ) { return _NativeSocket.startConnect( host, port, sourceAddress, sourcePort, ).then((ConnectionTask<_NativeSocket> nativeTask) { final Future<_RawSocket> raw = nativeTask.socket.then(( _NativeSocket nativeSocket, ) { if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectNewSocket( nativeSocket.id, _tcpSocket, nativeSocket.address, port, ); } return _RawSocket(nativeSocket); }); return ConnectionTask<_RawSocket>._(raw, nativeTask._onCancel); }); } _RawSocket(this._socket) { var zone = Zone.current; _controller ..onListen = _resume ..onCancel = close ..onPause = _pause ..onResume = _resume; _socket.setHandlers( read: () => _controller.add(RawSocketEvent.read), write: () { // The write event handler is automatically disabled by the // event handler when it fires. writeEventsEnabled = false; _controller.add(RawSocketEvent.write); }, closed: () => _controller.add(RawSocketEvent.readClosed), destroyed: () { _controller.add(RawSocketEvent.closed); _controller.close(); }, error: zone.bindBinaryCallbackGuarded((Object e, StackTrace? st) { _controller.addError(e, st); _socket.close(); }), ); } factory _RawSocket._writePipe() { var native = _NativeSocket.pipe(); native.isClosedRead = true; native.closedReadEventSent = true; return _RawSocket(native); } factory _RawSocket._readPipe(int? fd) { var native = _NativeSocket.pipe(); native.isClosedWrite = true; if (fd != null) _getStdioHandle(native, fd); var result = _RawSocket(native); if (fd != null) { var socketType = _StdIOUtils._nativeSocketType(result._socket); result._isMacOSTerminalInput = Platform.isMacOS && socketType == _stdioHandleTypeTerminal; } return result; } StreamSubscription listen( void onData(RawSocketEvent event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } int available() => _socket.available; Uint8List? read([int? len]) { if (_isMacOSTerminalInput) { var available = this.available(); if (available == 0) return null; var data = _socket.read(len); if (data == null || data.length < available) { // Reading less than available from a Mac OS terminal indicate Ctrl-D. // This is interpreted as read closed. scheduleMicrotask(() => _controller.add(RawSocketEvent.readClosed)); } return data; } else { return _socket.read(len); } } SocketMessage? readMessage([int? count]) { return _socket.readMessage(count); } /// See [_NativeSocket.write] for some implementation notes. int write(List buffer, [int offset = 0, int? count]) => _socket.write(buffer, offset, count); int sendMessage( List controlMessages, List data, [ int offset = 0, int? count, ]) => _socket.sendMessage(data, offset, count, controlMessages); Future close() => _socket.close().then((_) { if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic(_socket.id, _SocketProfileType.endTime); } return this; }); void shutdown(SocketDirection direction) => _socket.shutdown(direction); int get port => _socket.port; int get remotePort => _socket.remotePort; InternetAddress get address => _socket.address; InternetAddress get remoteAddress => _socket.remoteAddress; bool get readEventsEnabled => _readEventsEnabled; void set readEventsEnabled(bool value) { if (value != _readEventsEnabled) { _readEventsEnabled = value; if (!_controller.isPaused) _resume(); } } bool get writeEventsEnabled => _writeEventsEnabled; void set writeEventsEnabled(bool value) { if (value != _writeEventsEnabled) { _writeEventsEnabled = value; if (!_controller.isPaused) _resume(); } } bool setOption(SocketOption option, bool enabled) => _socket.setOption(option, enabled); Uint8List getRawOption(RawSocketOption option) => _socket.getRawOption(option); void setRawOption(RawSocketOption option) => _socket.setRawOption(option); void _pause() { _socket.setListening(read: false, write: false); } void _resume() { _socket.setListening(read: _readEventsEnabled, write: _writeEventsEnabled); } bool get _closedReadEventSent => _socket.closedReadEventSent; void set _owner(owner) { _socket.owner = owner; } } @patch class ServerSocket { @patch static Future _bind( address, int port, { int backlog = 0, bool v6Only = false, bool shared = false, }) { return _ServerSocket.bind(address, port, backlog, v6Only, shared); } } class _ServerSocket extends Stream implements ServerSocket { final _socket; static Future<_ServerSocket> bind( address, int port, int backlog, bool v6Only, bool shared, ) { return _RawServerSocket.bind( address, port, backlog, v6Only, shared, ).then((socket) => _ServerSocket(socket)); } _ServerSocket(this._socket); StreamSubscription listen( void onData(Socket event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _socket .map((rawSocket) => _Socket(rawSocket)) .listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } int get port => _socket.port; InternetAddress get address => _socket.address; Future close() => _socket.close().then((_) => this); void set _owner(owner) { _socket._owner = owner; } } @patch class Socket { @patch static Future _connect( dynamic host, int port, { dynamic sourceAddress, int sourcePort = 0, Duration? timeout, }) { return RawSocket.connect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout, ).then((socket) => _Socket(socket)); } @patch static Future> _startConnect( dynamic host, int port, { dynamic sourceAddress, int sourcePort = 0, }) { return RawSocket.startConnect( host, port, sourceAddress: sourceAddress, sourcePort: sourcePort, ).then((rawTask) { Future socket = rawTask.socket.then( (rawSocket) => _Socket(rawSocket), ); return ConnectionTask._(socket, rawTask._onCancel); }); } } class _SocketStreamConsumer implements StreamConsumer> { StreamSubscription? subscription; final _Socket socket; int? offset; List? buffer; bool paused = false; Completer? streamCompleter; _SocketStreamConsumer(this.socket); Future addStream(Stream> stream) { socket._ensureRawSocketSubscription(); final completer = streamCompleter = Completer(); if (socket._raw != null) { subscription = stream.listen( (data) { assert(!paused); assert(buffer == null); buffer = data; offset = 0; try { write(); } catch (e) { buffer = null; offset = 0; socket.destroy(); stop(); done(e); } }, onError: (error, [stackTrace]) { socket.destroy(); done(error, stackTrace); }, onDone: () { // Note: stream only delivers done event if subscription is not paused. // so it is crucial to keep subscription paused while writes are // in flight. assert(buffer == null); done(); }, cancelOnError: true, ); } else { done(); } return completer.future; } Future close() { socket._consumerDone(); return Future.value(socket); } bool get _previousWriteHasCompleted { final rawSocket = socket._raw; if (rawSocket is _RawSocket) { return rawSocket._socket.writeAvailable; } assert(rawSocket is _RawSecureSocket); // _RawSecureSocket has an internal buffering mechanism and it is going // to flush its buffer before it shutsdown. return true; } void write() { final sub = subscription; if (sub == null) return; // We have something to write out. if (offset! < buffer!.length) { offset = offset! + socket._write(buffer!, offset!, buffer!.length - offset!); } if (offset! < buffer!.length || !_previousWriteHasCompleted) { // On Windows we might have written the whole buffer out but we are // still waiting for the write to complete. We should not resume the // subscription until the pending write finishes and we receive a // writeEvent signaling that we can write the next chunk or that we // can consider all data flushed from our side into kernel buffers. if (!paused) { paused = true; sub.pause(); } socket._enableWriteEvent(); } else { // Write fully completed. buffer = null; if (paused) { paused = false; sub.resume(); } } } void done([error, stackTrace]) { final completer = streamCompleter; if (completer != null) { if (error != null) { completer.completeError(error, stackTrace); } else { completer.complete(socket); } streamCompleter = null; } } void stop() { final sub = subscription; if (sub == null) return; sub.cancel(); subscription = null; paused = false; socket._disableWriteEvent(); } } class _Socket extends Stream implements Socket { RawSocket? _raw; // Set to null when the raw socket is closed. bool _closed = false; // Set to true when the raw socket is closed. final _controller = StreamController(sync: true); bool _controllerClosed = false; late _SocketStreamConsumer _consumer; late IOSink _sink; StreamSubscription? _subscription; Completer? _detachReady; _Socket(RawSocket raw) : _raw = raw { _controller ..onListen = _onSubscriptionStateChange ..onCancel = _onSubscriptionStateChange ..onPause = _onPauseStateChange ..onResume = _onPauseStateChange; _consumer = _SocketStreamConsumer(this); _sink = IOSink(_consumer); // Disable read events until there is a subscription. raw.readEventsEnabled = false; // Disable write events until the consumer needs it for pending writes. raw.writeEventsEnabled = false; } factory _Socket._writePipe() { return _Socket(_RawSocket._writePipe()); } factory _Socket._readPipe([int? fd]) { return _Socket(_RawSocket._readPipe(fd)); } // Note: this code seems a bit suspicious because _raw can be _RawSocket and // it can be _RawSecureSocket because _SecureSocket extends _Socket // and these two types are incompatible because _RawSecureSocket._socket // is Socket and not _NativeSocket. _NativeSocket get _nativeSocket => (_raw as _RawSocket)._socket; StreamSubscription listen( void onData(Uint8List event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } Encoding get encoding => _sink.encoding; void set encoding(Encoding value) { _sink.encoding = value; } void write(Object? obj) => _sink.write(obj); void writeln([Object? obj = ""]) => _sink.writeln(obj); void writeCharCode(int charCode) => _sink.writeCharCode(charCode); void writeAll(Iterable objects, [String sep = ""]) => _sink.writeAll(objects, sep); void add(List bytes) => _sink.add(bytes); /// Unsupported operation on sockets. /// /// Throws an [UnsupportedError] because errors cannot be transmitted over a /// [Socket]. void addError(Object error, [StackTrace? stackTrace]) { throw UnsupportedError("Cannot send errors on sockets"); } Future addStream(Stream> stream) { return _sink.addStream(stream); } Future flush() => _sink.flush(); Future close() => _sink.close(); Future get done => _sink.done; void destroy() { // Destroy can always be called to get rid of a socket. if (_raw == null) return; _consumer.stop(); _closeRawSocket(); _controllerClosed = true; _controller.close(); } bool setOption(SocketOption option, bool enabled) { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.setOption(option, enabled); } Uint8List getRawOption(RawSocketOption option) { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.getRawOption(option); } void setRawOption(RawSocketOption option) { final raw = _raw; if (raw == null) throw const SocketException.closed(); raw.setRawOption(option); } int get port { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.port; } InternetAddress get address { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.address; } int get remotePort { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.remotePort; } InternetAddress get remoteAddress { final raw = _raw; if (raw == null) throw const SocketException.closed(); return raw.remoteAddress; } Future> _detachRaw() { var completer = Completer(); _detachReady = completer; _sink.close(); return completer.future.then((_) { assert(_consumer.buffer == null); var raw = _raw; _raw = null; return [raw, _subscription]; }); } // Ensure a subscription on the raw socket. Both the stream and the // consumer needs a subscription as they share the error and done // events from the raw socket. void _ensureRawSocketSubscription() { final raw = _raw; if (_subscription == null && raw != null) { _subscription = raw.listen( _onData, onError: _onError, onDone: _onDone, cancelOnError: true, ); } } _closeRawSocket() { var raw = _raw!; _raw = null; _closed = true; raw.close(); } void _onSubscriptionStateChange() { final raw = _raw; if (_controller.hasListener) { _ensureRawSocketSubscription(); // Enable read events for providing data to subscription. if (raw != null) { raw.readEventsEnabled = true; } } else { _controllerClosed = true; if (raw != null) { raw.shutdown(SocketDirection.receive); } } } void _onPauseStateChange() { _raw?.readEventsEnabled = !_controller.isPaused; } void _onData(event) { switch (event) { case RawSocketEvent.read: if (_raw == null) break; var buffer = _raw!.read(); if (buffer != null) _controller.add(buffer); break; case RawSocketEvent.write: _consumer.write(); break; case RawSocketEvent.readClosed: _controllerClosed = true; _controller.close(); break; } } void _onDone() { if (!_controllerClosed) { _controllerClosed = true; _controller.close(); } _consumer.done(); } void _onError(error, stackTrace) { if (!_controllerClosed) { _controllerClosed = true; _controller.addError(error, stackTrace); _controller.close(); } _consumer.done(error, stackTrace); } int _write(List data, int offset, int length) { final raw = _raw; if (raw != null) { return raw.write(data, offset, length); } return 0; } void _enableWriteEvent() { _raw?.writeEventsEnabled = true; } void _disableWriteEvent() { _raw?.writeEventsEnabled = false; } void _consumerDone() { if (_detachReady != null) { _detachReady!.complete(null); } else { final raw = _raw; if (raw != null) { raw.shutdown(SocketDirection.send); _disableWriteEvent(); } } } void set _owner(owner) { // Note: _raw can be _RawSocket and _RawSecureSocket. (_raw as _RawSocketBase)._owner = owner; } } @patch class RawDatagramSocket { @patch static Future bind( host, int port, { bool reuseAddress = true, bool reusePort = false, int ttl = 1, }) { return _RawDatagramSocket.bind(host, port, reuseAddress, reusePort, ttl); } } class _RawDatagramSocket extends Stream implements RawDatagramSocket { _NativeSocket _socket; late StreamController _controller; bool _readEventsEnabled = true; bool _writeEventsEnabled = true; _RawDatagramSocket(this._socket) { var zone = Zone.current; _controller = StreamController( sync: true, onListen: _resume, onCancel: close, onPause: _pause, onResume: _resume, ); _socket.setHandlers( read: () => _controller.add(RawSocketEvent.read), write: () { // The write event handler is automatically disabled by the // event handler when it fires. writeEventsEnabled = false; _controller.add(RawSocketEvent.write); }, closed: () => _controller.add(RawSocketEvent.readClosed), destroyed: () { _controller.add(RawSocketEvent.closed); _controller.close(); }, error: zone.bindBinaryCallbackGuarded((Object e, StackTrace? st) { _controller.addError(e, st); _socket.close(); }), ); } static Future bind( host, int port, bool reuseAddress, bool reusePort, int ttl, ) { _throwOnBadPort(port); _throwOnBadTtl(ttl); return _NativeSocket.bindDatagram( host, port, reuseAddress, reusePort, ttl, ).then((socket) { if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectNewSocket( socket.id, _udpSocket, socket.address, port, ); } return _RawDatagramSocket(socket); }); } StreamSubscription listen( void onData(RawSocketEvent event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } Future close() => _socket.close().then((_) { if (!const bool.fromEnvironment("dart.vm.product")) { _SocketProfile.collectStatistic(_socket.id, _SocketProfileType.endTime); } return this; }); int send(List buffer, InternetAddress address, int port) => _socket.send(buffer, 0, buffer.length, address, port); Datagram? receive() { return _socket.receive(); } void joinMulticast(InternetAddress group, [NetworkInterface? interface]) { _socket.joinMulticast(group, interface); } void leaveMulticast(InternetAddress group, [NetworkInterface? interface]) { _socket.leaveMulticast(group, interface); } bool get readEventsEnabled => _readEventsEnabled; void set readEventsEnabled(bool value) { if (value != _readEventsEnabled) { _readEventsEnabled = value; if (!_controller.isPaused) _resume(); } } bool get writeEventsEnabled => _writeEventsEnabled; void set writeEventsEnabled(bool value) { if (value != _writeEventsEnabled) { _writeEventsEnabled = value; if (!_controller.isPaused) _resume(); } } bool get multicastLoopback => _socket.getOption(SocketOption._ipMulticastLoop); void set multicastLoopback(bool value) => _socket.setOption(SocketOption._ipMulticastLoop, value); int get multicastHops => _socket.getOption(SocketOption._ipMulticastHops); void set multicastHops(int value) => _socket.setOption(SocketOption._ipMulticastHops, value); NetworkInterface get multicastInterface => throw UnimplementedError(); void set multicastInterface(NetworkInterface? value) => throw UnimplementedError(); bool get broadcastEnabled => _socket.getOption(SocketOption._ipBroadcast); void set broadcastEnabled(bool value) => _socket.setOption(SocketOption._ipBroadcast, value); int get port => _socket.port; InternetAddress get address => _socket.address; void _pause() { _socket.setListening(read: false, write: false); } void _resume() { _socket.setListening(read: _readEventsEnabled, write: _writeEventsEnabled); } Uint8List getRawOption(RawSocketOption option) => _socket.getRawOption(option); void setRawOption(RawSocketOption option) => _socket.setRawOption(option); } @pragma("vm:entry-point", "call") Datagram _makeDatagram( Uint8List data, String address, Uint8List in_addr, int port, int type, ) { return Datagram( data, _InternetAddress(InternetAddressType._from(type), address, null, in_addr), port, ); } @patch @pragma("vm:entry-point") class ResourceHandle { @patch factory ResourceHandle.fromFile(RandomAccessFile file) { int fd = (file as _RandomAccessFile).fd; return _ResourceHandleImpl(fd); } @patch factory ResourceHandle.fromSocket(Socket socket) { final _socket = socket as _Socket; if (_socket._raw == null) { throw ArgumentError("Socket is closed"); } final _RawSocket raw = _socket._raw! as _RawSocket; final _NativeSocket nativeSocket = raw._socket; int fd = nativeSocket.fd; return _ResourceHandleImpl(fd); } @patch factory ResourceHandle.fromRawSocket(RawSocket socket) { final _RawSocket raw = socket as _RawSocket; final _NativeSocket nativeSocket = raw._socket; int fd = nativeSocket.fd; return _ResourceHandleImpl(fd); } @patch factory ResourceHandle.fromRawDatagramSocket(RawDatagramSocket socket) { final _RawDatagramSocket raw = socket as _RawDatagramSocket; final _NativeSocket nativeSocket = socket._socket; int fd = nativeSocket.fd; return _ResourceHandleImpl(fd); } @patch factory ResourceHandle.fromStdin(Stdin stdin) { return _ResourceHandleImpl(stdin._fd); } @patch factory ResourceHandle.fromStdout(Stdout stdout) { return _ResourceHandleImpl(stdout._fd); } @patch factory ResourceHandle.fromReadPipe(ReadPipe pipe) { _ReadPipe rp = pipe as _ReadPipe; return ResourceHandle.fromFile(rp._openedFile!); } @patch factory ResourceHandle.fromWritePipe(WritePipe pipe) { _WritePipe wp = pipe as _WritePipe; return ResourceHandle.fromFile(wp._file); } } @pragma("vm:entry-point") class _ResourceHandleImpl implements ResourceHandle { bool _toMethodCalled = false; @pragma("vm:entry-point") int _handle; // file descriptor on linux @pragma("vm:entry-point") _ResourceHandleImpl(this._handle); RandomAccessFile toFile() { if (_toMethodCalled) { throw StateError('Resource handle has already been used.'); } _toMethodCalled = true; return _toFile(); } RawDatagramSocket toRawDatagramSocket() { if (_toMethodCalled) { throw StateError('Resource handle has already been used.'); } _toMethodCalled = true; return _toRawDatagramSocket(); } RawSocket toRawSocket() { if (_toMethodCalled) { throw StateError('Resource handle has already been used.'); } _toMethodCalled = true; List list = _toRawSocket(); InternetAddressType type = InternetAddressType._from(list[0] as int); String hostname = list[1] as String; Uint8List rawAddr = list[2] as Uint8List; int fd = list[3] as int; InternetAddress internetAddress = type == InternetAddressType.unix ? _InternetAddress.fromString(hostname, type: InternetAddressType.unix) : _InternetAddress(type, hostname, null, rawAddr); final nativeSocket = _NativeSocket.normal(internetAddress); nativeSocket._nativeSetSocketId(fd, _NativeSocket.typeInternalSocket); return _RawSocket(nativeSocket); } Socket toSocket() { if (_toMethodCalled) { throw StateError('Resource handle has already been used.'); } _toMethodCalled = true; return _toSocket(); } _ReadPipe toReadPipe() { return _ReadPipe(toFile()); } _WritePipe toWritePipe() { return _WritePipe(toFile()); } @pragma("vm:external-name", "ResourceHandleImpl_toFile") external RandomAccessFile _toFile(); @pragma("vm:external-name", "ResourceHandleImpl_toSocket") external Socket _toSocket(); @pragma("vm:external-name", "ResourceHandleImpl_toRawSocket") external List _toRawSocket(); @pragma("vm:external-name", "ResourceHandleImpl_toRawDatagramSocket") external RawDatagramSocket _toRawDatagramSocket(); @pragma("vm:entry-point") static final _ResourceHandleImpl _sentinel = _ResourceHandleImpl(-1); } @patch class SocketControlMessage { @pragma("vm:external-name", "SocketControlMessage_fromHandles") @patch external factory SocketControlMessage.fromHandles( List handles, ); } @pragma("vm:entry-point") class _SocketControlMessageImpl implements SocketControlMessage { @pragma("vm:entry-point") final int level; @pragma("vm:entry-point") final int type; @pragma("vm:entry-point") final Uint8List data; @pragma("vm:entry-point") _SocketControlMessageImpl(this.level, this.type, this.data); @pragma("vm:external-name", "SocketControlMessageImpl_extractHandles") external List extractHandles(); static final _sentinel = _SocketControlMessageImpl(0, 0, Uint8List(0)); } ŒÌNN< ' J $O 9K=%  (1->@6 ,* ,* '% '% I= *D /83 ,B 5/ ) /"#8)(*!'/-4--((()AAMM"'<%#%8. 3%=%(9%#D%=%;8%"$%=&,!.%,?" ((ML756.,4LJA;'6<D?;FA>=B"##$'+/<%,.G,-06=5)$)?/D4##(.33=I& **)**-0FM$/I%&>'*%3;%A4? #>4  !K@ "/8; 'DA9!$ GB+'I;9"#8C'B3+@H0.,'*FDIE $6K5#*wf~ .,<3='$DM%NKK-J%8LMBO//D"<"DF %"DF % @@GM"H!!J F#(  !OIBB# ==F;H D3+!!/KL!PH#!M ,=#8 +K>OPK<8&.2 'G , F-!/&3>*=1<4O-6G@)J  6D# 9$H"B/P  H# &++ 61: 2 ?+, 99$H"B/KD  > I )6!-G! 8!.!>HD$ %#59 %B54F+I9 &/P>2 CQP$P>H@ JJ *#,B62C2:%#NJD4! !>:&7 *%7% % :+@ 5"3&!H 7.)1 '  E(F*8-(#,(D E$ '@-%  +.04"/(9 / * ,$-  .44P 23  , ,,4*4*8//,'%7LB2@L66== '4, #(=F!: !) *32GE !$ <)( 9, ,<)( 7<-'4, 6M.OL1%$ >+" ) F8D($/ @0 I% $' '*'!1%F%F-*&$( !('$5O/M -'??,0CE:OK ,2>4+'",6,(#,7*4$M5P@ % GE% " /%"8(  2"7 " $) 7# $3+ BC$&&7+=$" )# RK"  !(##/+L0'$OCJKHJJ" #'4$!> { final Stream> _stream; _StdStream(this._stream); StreamSubscription> listen( void onData(List event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { return _stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } } /// The standard input stream of the process. /// /// Allows both synchronous and asynchronous reads from the standard /// input stream. /// /// Mixing synchronous and asynchronous reads is undefined. class Stdin extends _StdStream implements Stream> { int _fd; Stdin._(Stream> stream, this._fd) : super(stream); /// Reads a line from stdin. /// /// Blocks until a full line is available. /// /// Lines may be terminated by either `` or ``. On Windows, /// in cases where the [stdioType] of stdin is [StdioType.terminal], /// the terminator may also be a single ``. /// /// Input bytes are converted to a string by [encoding]. /// If [encoding] is omitted, it defaults to [systemEncoding]. /// /// If [retainNewlines] is `false`, the returned string will not include the /// final line terminator. If `true`, the returned string will include the line /// terminator. Default is `false`. /// /// If end-of-file is reached after any bytes have been read from stdin, /// that data is returned without a line terminator. /// Returns `null` if no bytes preceded the end of input. String? readLineSync({ Encoding encoding = systemEncoding, bool retainNewlines = false, }) { const CR = 13; const LF = 10; final List line = []; // On Windows, if lineMode is disabled, only CR is received. bool crIsNewline = Platform.isWindows && (stdioType(stdin) == StdioType.terminal) && !lineMode; if (retainNewlines) { int byte; do { byte = readByteSync(); if (byte < 0) { break; } line.add(byte); } while (byte != LF && !(byte == CR && crIsNewline)); if (line.isEmpty) { return null; } } else if (crIsNewline) { // CR and LF are both line terminators, neither is retained. while (true) { int byte = readByteSync(); if (byte < 0) { if (line.isEmpty) return null; break; } if (byte == LF || byte == CR) break; line.add(byte); } } else { // Case having to handle CR LF as a single unretained line terminator. outer: while (true) { int byte = readByteSync(); if (byte == LF) break; if (byte == CR) { do { byte = readByteSync(); if (byte == LF) break outer; line.add(CR); } while (byte == CR); // Fall through and handle non-CR character. } if (byte < 0) { if (line.isEmpty) return null; break; } line.add(byte); } } return encoding.decode(line); } /// Whether echo mode is enabled on [stdin]. /// /// If disabled, input from the console will not be echoed. /// /// Default depends on the parent process, but is usually enabled. /// /// On POSIX systems this mode is the `echo` local terminal mode. Before /// Dart 2.18, it also controlled the `echonl` mode, which is now controlled /// by [echoNewlineMode]. /// /// On Windows this mode can only be enabled if [lineMode] is enabled as well. external bool get echoMode; external set echoMode(bool echoMode); /// Whether echo newline mode is enabled on [stdin]. /// /// If enabled, newlines from the terminal will be echoed even if the regular /// [echoMode] is disabled. This mode may require `lineMode` to be turned on /// to have an effect. /// /// Default depends on the parent process, but is usually disabled. /// /// On POSIX systems this mode is the `echonl` local terminal mode. /// /// On Windows this mode cannot be set. @Since("2.18") external bool get echoNewlineMode; @Since("2.18") external set echoNewlineMode(bool echoNewlineMode); /// Whether line mode is enabled on [stdin]. /// /// If enabled, characters are delayed until a newline character is entered. /// If disabled, characters will be available as typed. /// /// Default depends on the parent process, but is usually enabled. /// /// On POSIX systems this mode is the `icanon` local terminal mode. /// /// On Windows this mode can only be disabled if [echoMode] is disabled as /// well. external bool get lineMode; external set lineMode(bool lineMode); /// Whether connected to a terminal that supports ANSI escape sequences. /// /// Not all terminals are recognized, and not all recognized terminals can /// report whether they support ANSI escape sequences, so this value is a /// best-effort attempt at detecting the support. /// /// The actual escape sequence support may differ between terminals, /// with some terminals supporting more escape sequences than others, /// and some terminals even differing in behavior for the same escape /// sequence. /// /// The ANSI color selection is generally supported. /// /// Currently, a `TERM` environment variable containing the string `xterm`, /// `screen`, `rxvt`, or `tmux` will be taken as evidence that ANSI escape /// sequences are supported. On Windows, only versions of Windows 10 after /// v.1511 ("TH2", OS build 10586) will be detected as supporting the output /// of ANSI escape sequences, and only versions after v.1607 ("Anniversary /// Update", OS build 14393) will be detected as supporting the input of /// ANSI escape sequences. external bool get supportsAnsiEscapes; /// Synchronously reads a byte from stdin. /// /// This call will block until a byte is available. /// /// If at end of file, -1 is returned. external int readByteSync(); /// Whether there is a terminal attached to stdin. bool get hasTerminal { try { return stdioType(this) == StdioType.terminal; } on FileSystemException catch (_) { // If stdioType throws a FileSystemException, then it is not hooked up to // a terminal, probably because it is closed, but let other exception // types bubble up. return false; } } } /// An [IOSink] connected to either the standard out or error of the process. /// /// Provides a *blocking* `IOSink`, so using it to write will block until /// the output is written. /// /// In some situations this blocking behavior is undesirable as it does not /// provide the same non-blocking behavior that `dart:io` in general exposes. /// Use the property [nonBlocking] to get an [IOSink] which has the non-blocking /// behavior. /// /// This class can also be used to check whether `stdout` or `stderr` is /// connected to a terminal and query some terminal properties. /// /// The [addError] API is inherited from [StreamSink] and calling it will result /// in an unhandled asynchronous error unless there is an error handler on /// [done]. /// /// The [lineTerminator] field is used by the [write], [writeln], [writeAll] /// and [writeCharCode] methods to translate `"\n"`. By default, `"\n"` is /// output literally. class Stdout extends _StdSink implements IOSink { final int _fd; IOSink? _nonBlocking; Stdout._(IOSink sink, this._fd) : super(sink); /// Whether there is a terminal attached to stdout. bool get hasTerminal => _hasTerminal(_fd); /// The number of columns of the terminal. /// /// If no terminal is attached to stdout, a [StdoutException] is thrown. See /// [hasTerminal] for more info. int get terminalColumns => _terminalColumns(_fd); /// The number of lines of the terminal. /// /// If no terminal is attached to stdout, a [StdoutException] is thrown. See /// [hasTerminal] for more info. int get terminalLines => _terminalLines(_fd); /// Whether connected to a terminal that supports ANSI escape sequences. /// /// Not all terminals are recognized, and not all recognized terminals can /// report whether they support ANSI escape sequences, so this value is a /// best-effort attempt at detecting the support. /// /// The actual escape sequence support may differ between terminals, /// with some terminals supporting more escape sequences than others, /// and some terminals even differing in behavior for the same escape /// sequence. /// /// The ANSI color selection is generally supported. /// /// Currently, a `TERM` environment variable containing the string `xterm` /// will be taken as evidence that ANSI escape sequences are supported. /// On Windows, only versions of Windows 10 after v.1511 /// ("TH2", OS build 10586) will be detected as supporting the output of /// ANSI escape sequences, and only versions after v.1607 ("Anniversary /// Update", OS build 14393) will be detected as supporting the input of /// ANSI escape sequences. bool get supportsAnsiEscapes => _supportsAnsiEscapes(_fd); external bool _hasTerminal(int fd); external int _terminalColumns(int fd); external int _terminalLines(int fd); external static bool _supportsAnsiEscapes(int fd); /// A non-blocking `IOSink` for the same output. /// /// The returned `IOSink` will be initialized with an [encoding] of UTF-8 and /// will not do line ending conversion. IOSink get nonBlocking { return _nonBlocking ??= new IOSink(new _FileStreamConsumer._fromStdio(_fd)); } } /// Exception thrown by some operations of [Stdout] class StdoutException implements IOException { /// Message describing cause of the exception. final String message; /// The underlying OS error, if available. final OSError? osError; const StdoutException(this.message, [this.osError]); String toString() { return "StdoutException: $message${osError == null ? "" : ", $osError"}"; } } /// Exception thrown by some operations of [Stdin] class StdinException implements IOException { /// Message describing cause of the exception. final String message; /// The underlying OS error, if available. final OSError? osError; const StdinException(this.message, [this.osError]); String toString() { return "StdinException: $message${osError == null ? "" : ", $osError"}"; } } class _StdConsumer implements StreamConsumer> { final RandomAccessFile _file; _StdConsumer._(int fd) : _file = _File._openStdioSync(fd); Future addStream(Stream> stream) { var completer = new Completer(); late StreamSubscription> sub; sub = stream.listen( (data) { try { _file.writeFromSync(data); } catch (e, s) { sub.cancel(); completer.completeError(e, s); } }, onError: completer.completeError, onDone: completer.complete, cancelOnError: true, ); return completer.future; } Future close() { _file.closeSync(); return new Future.value(); } } /// Pattern matching a `"\n"` character not following a `"\r"`. /// /// Used to replace such with `"\r\n"` in the [_StdSink] write methods. final _newLineDetector = RegExp(r'(? _windowsLineTerminator ? "\r\n" : "\n"; @Since("3.4") set lineTerminator(String lineTerminator) { if (lineTerminator == "\r\n") { assert(!_lastWrittenCharIsCR || _windowsLineTerminator); _windowsLineTerminator = true; } else if (lineTerminator == "\n") { _windowsLineTerminator = false; _lastWrittenCharIsCR = false; } else { throw ArgumentError.value( lineTerminator, "lineTerminator", r'invalid line terminator, must be one of "\n" or "\r\n"', ); } } Encoding get encoding => _sink.encoding; void set encoding(Encoding encoding) { _sink.encoding = encoding; } String _convertToWindowsLineTerminators(String string) { assert(!string.isEmpty); var pattern = _lastWrittenCharIsCR ? _newLineDetectorAfterCR : _newLineDetector; var result = string.replaceAll(pattern, "\r\n"); _lastWrittenCharIsCR = result.endsWith('\r'); return result; } void _write(Object? object) { if (!_windowsLineTerminator) { _sink.write(object); return; } var string = object.toString(); if (string.isEmpty) return; _sink.write(_convertToWindowsLineTerminators(string)); } void write(Object? object) => _write(object); void writeln([Object? object = ""]) { if (!_windowsLineTerminator) { _sink.write('$object\n'); } else { _sink.write(_convertToWindowsLineTerminators('$object\r\n')); } } void writeAll(Iterable objects, [String sep = ""]) { Iterator iterator = objects.iterator; if (!iterator.moveNext()) return; if (sep.isEmpty) { do { _write(iterator.current); } while (iterator.moveNext()); } else { _write(iterator.current); while (iterator.moveNext()) { _write(sep); _write(iterator.current); } } } void add(List data) { _lastWrittenCharIsCR = false; _sink.add(data); } void addError(error, [StackTrace? stackTrace]) { _sink.addError(error, stackTrace); } void writeCharCode(int charCode) { if (!_windowsLineTerminator) { _sink.writeCharCode(charCode); return; } _write(String.fromCharCode(charCode)); } Future addStream(Stream> stream) { _lastWrittenCharIsCR = false; return _sink.addStream(stream); } Future flush() => _sink.flush(); Future close() => _sink.close(); Future get done => _sink.done; } /// The type of object a standard IO stream can be attached to. final class StdioType { static const StdioType terminal = const StdioType._("terminal"); static const StdioType pipe = const StdioType._("pipe"); static const StdioType file = const StdioType._("file"); static const StdioType other = const StdioType._("other"); final String name; const StdioType._(this.name); String toString() => "StdioType: $name"; } final Stdin _stdin = _StdIOUtils._getStdioInputStream(_stdinFD); final Stdout _stdout = _StdIOUtils._getStdioOutputStream(_stdoutFD); final Stdout _stderr = _StdIOUtils._getStdioOutputStream(_stderrFD); // These may be set to different values by the embedder by calling // _setStdioFDs when initializing dart:io. int _stdinFD = 0; int _stdoutFD = 1; int _stderrFD = 2; @pragma('vm:entry-point', 'call') void _setStdioFDs(int stdin, int stdout, int stderr) { _stdinFD = stdin; _stdoutFD = stdout; _stderrFD = stderr; } /// The standard input stream of data read by this program. Stdin get stdin { return IOOverrides.current?.stdin ?? _stdin; } /// The standard output stream of data written by this program. /// /// The `addError` API is inherited from `StreamSink` and calling it will /// result in an unhandled asynchronous error unless there is an error handler /// on `done`. Stdout get stdout { return IOOverrides.current?.stdout ?? _stdout; } /// The standard output stream of errors written by this program. /// /// The `addError` API is inherited from `StreamSink` and calling it will /// result in an unhandled asynchronous error unless there is an error handler /// on `done`. Stdout get stderr { return IOOverrides.current?.stderr ?? _stderr; } /// Whether a stream is attached to a file, pipe, terminal, or /// something else. StdioType stdioType(object) { if (object is _StdStream) { object = object._stream; } else if (object == stdout || object == stderr) { int stdiofd = object == stdout ? _stdoutFD : _stderrFD; final type = _StdIOUtils._getStdioHandleType(stdiofd); if (type is OSError) { throw FileSystemException( "Failed to get type of stdio handle (fd $stdiofd)", "", type, ); } switch (type) { case _stdioHandleTypeTerminal: return StdioType.terminal; case _stdioHandleTypePipe: return StdioType.pipe; case _stdioHandleTypeFile: return StdioType.file; } } if (object is _FileStream) { return StdioType.file; } if (object is Socket) { int? socketType = _StdIOUtils._socketType(object); if (socketType == null) return StdioType.other; switch (socketType) { case _stdioHandleTypeTerminal: return StdioType.terminal; case _stdioHandleTypePipe: return StdioType.pipe; case _stdioHandleTypeFile: return StdioType.file; } } if (object is _IOSinkImpl) { try { if (object._target is _FileStreamConsumer) { return StdioType.file; } } catch (e) { // Only the interface implemented, _sink not available. } } return StdioType.other; } class _StdIOUtils { external static _getStdioOutputStream(int fd); external static Stdin _getStdioInputStream(int fd); /// Returns the socket type or `null` if [socket] is not a builtin socket. external static int? _socketType(Socket socket); external static _getStdioHandleType(int fd); } ‚^NN<.($$&%%-#(%$.E<> ?-JG2;AOR&K7<(!$A4  <C#) - M ##) 7 ) "/>EKOQ(7POFF*%6/O:EFM (KML4GHH7NMMOMK)-6)5 4)PLNJLNQI@QK MK216--O#4+O#0KML4GHH7MJ;KJK=&)'53P*Q4/1-7N3.1-6M: =/%,%) ("@H/IHID9#'%OBKGIMGOKB77 KN@G.$?%)&$ !C +);'"52 #$ ;0(# D7*& "% $""3'%#%+/"$##!@C;;= +AEEC+"7</@KO1BKO1?5<;!<  %#!!74%#!! 3>16M3/dart:io/stdio.dartœœÀh³ÀJ}ÀeVorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/stdio_patch.dart•%// Copyright (c) 2013, 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 "common_patch.dart"; @patch class _StdIOUtils { @patch static Stdin _getStdioInputStream(int fd) { final type = _getStdioHandleType(fd); if (type is OSError) { throw FileSystemException( "Failed to get type of stdio handle (fd $fd)", "", type, ); } switch (type) { case _stdioHandleTypeTerminal: case _stdioHandleTypePipe: case _stdioHandleTypeSocket: case _stdioHandleTypeOther: return Stdin._(_Socket._readPipe(fd), fd); case _stdioHandleTypeFile: return Stdin._(_FileStream.forStdin(), fd); } throw UnsupportedError("Unexpected handle type $type"); } @patch static _getStdioOutputStream(int fd) { final type = _getStdioHandleType(fd); if (type is OSError) { throw FileSystemException( "Failed to get type of stdio handle (fd $fd)", "", type, ); } return Stdout._(IOSink(_StdConsumer._(fd)), fd); } @patch static int? _socketType(Socket socket) { if (socket is _Socket) return _nativeSocketType(socket._nativeSocket); return null; } static int _nativeSocketType(_NativeSocket nativeSocket) { if (nativeSocket.isClosed) { throw FileSystemException("Socket is closed"); } if (nativeSocket.isClosing) { throw FileSystemException("Socket is being closed"); } var result = _getSocketType(nativeSocket); if (result is OSError) { throw FileSystemException("Error retrieving socket type", "", result); } return result; } @patch @pragma("vm:external-name", "File_GetStdioHandleType") external static _getStdioHandleType(int fd); } @patch class Stdin { @patch int readByteSync() { var result = _readByte(_fd); if (result is OSError) { throw StdinException("Error reading byte from stdin", result); } return result; } @patch bool get echoMode { var result = _echoMode(_fd); if (result is OSError) { throw StdinException("Error getting terminal echo mode", result); } return result; } @patch void set echoMode(bool enabled) { var result = _setEchoMode(_fd, enabled); if (result is OSError) { throw StdinException("Error setting terminal echo mode", result); } } @patch bool get echoNewlineMode { var result = _echoNewlineMode(_fd); if (result is OSError) { throw StdinException("Error getting terminal echo newline mode", result); } return result; } @patch void set echoNewlineMode(bool enabled) { var result = _setEchoNewlineMode(_fd, enabled); if (result is OSError) { throw StdinException("Error setting terminal echo newline mode", result); } } @patch bool get lineMode { var result = _lineMode(_fd); if (result is OSError) { throw StdinException("Error getting terminal line mode", result); } return result; } @patch void set lineMode(bool enabled) { var result = _setLineMode(_fd, enabled); if (result is OSError) { throw StdinException("Error setting terminal line mode", result); } } @patch bool get supportsAnsiEscapes { var result = _supportsAnsiEscapes(_fd); if (result is OSError) { throw StdinException("Error determining ANSI support", result); } return result; } @pragma("vm:external-name", "Stdin_GetEchoMode") external static _echoMode(int fd); @pragma("vm:external-name", "Stdin_SetEchoMode") external static _setEchoMode(int fd, bool enabled); @pragma("vm:external-name", "Stdin_GetEchoNewlineMode") external static _echoNewlineMode(int fd); @pragma("vm:external-name", "Stdin_SetEchoNewlineMode") external static _setEchoNewlineMode(int fd, bool enabled); @pragma("vm:external-name", "Stdin_GetLineMode") external static _lineMode(int fd); @pragma("vm:external-name", "Stdin_SetLineMode") external static _setLineMode(int fd, bool enabled); @pragma("vm:external-name", "Stdin_ReadByte") external static _readByte(int fd); @pragma("vm:external-name", "Stdin_AnsiSupported") external static _supportsAnsiEscapes(int fd); } @patch class Stdout { @patch bool _hasTerminal(int fd) => _getTerminalSize(fd) is List; @patch int _terminalColumns(int fd) => _terminalSize(fd)[0]; @patch int _terminalLines(int fd) => _terminalSize(fd)[1]; static List _terminalSize(int fd) { var size = _getTerminalSize(fd); if (size is! List) { throw StdoutException("Could not get terminal size", size); } return size; } @pragma("vm:external-name", "Stdout_GetTerminalSize") external static _getTerminalSize(int fd); @patch static bool _supportsAnsiEscapes(int fd) { var result = _getAnsiSupported(fd); if (result is! bool) { throw StdoutException("Error determining ANSI support", result); } return result; } @pragma("vm:external-name", "Stdout_AnsiSupported") external static _getAnsiSupported(int fd); } @pragma("vm:external-name", "Socket_GetStdioHandle") external bool _getStdioHandle(_NativeSocket socket, int num); @pragma("vm:external-name", "Socket_GetType") external _getSocketType(_NativeSocket nativeSocket); €ÄNN< .*!7  %!#"3!4< )*!7  5 +K=!5";/M 9/ !E !H $-H (P +4P !H $-H !,F3%36:,:=3%360%50 = 8 6&%B8, -(G6-5>.5dart:io-patch/stdio_patch.dartÀeÀh³Oorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/string_transformer.dart�[// Copyright (c) 2013, 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:io"; /// The current system encoding. /// /// This is used for converting from bytes to and from Strings when /// communicating on stdin, stdout and stderr. /// /// On Windows this will use the currently active code page for the conversion. /// On all other systems it will always use UTF-8. const SystemEncoding systemEncoding = const SystemEncoding(); /// The system encoding is the current code page on Windows and UTF-8 on Linux /// and Mac. final class SystemEncoding extends Encoding { /// Creates a const SystemEncoding. /// /// Users should use the top-level constant, [systemEncoding]. const SystemEncoding(); String get name => 'system'; List encode(String input) => encoder.convert(input); String decode(List encoded) => decoder.convert(encoded); Converter> get encoder { if (Platform.operatingSystem == "windows") { return const _WindowsCodePageEncoder(); } else { return const Utf8Encoder(); } } Converter, String> get decoder { if (Platform.operatingSystem == "windows") { return const _WindowsCodePageDecoder(); } else { return const Utf8Decoder(); } } } class _WindowsCodePageEncoder extends Converter> { const _WindowsCodePageEncoder(); List convert(String input) { List encoded = _encodeString(input); if (encoded == null) { throw new FormatException("Invalid character for encoding"); } return encoded; } /// Starts a chunked conversion. StringConversionSink startChunkedConversion(Sink> sink) { return new _WindowsCodePageEncoderSink(sink); } external static List _encodeString(String string); } class _WindowsCodePageEncoderSink extends StringConversionSink { // TODO(floitsch): provide more efficient conversions when the input is // not a String. final Sink> _sink; _WindowsCodePageEncoderSink(this._sink); void close() { _sink.close(); } void add(String string) { List encoded = _WindowsCodePageEncoder._encodeString(string); if (encoded == null) { throw new FormatException("Invalid character for encoding"); } _sink.add(encoded); } void addSlice(String source, int start, int end, bool isLast) { if (start != 0 || end != source.length) { source = source.substring(start, end); } add(source); if (isLast) close(); } } class _WindowsCodePageDecoder extends Converter, String> { const _WindowsCodePageDecoder(); String convert(List input) { return _decodeBytes(input); } /// Starts a chunked conversion. ByteConversionSink startChunkedConversion(Sink sink) { return new _WindowsCodePageDecoderSink(sink); } external static String _decodeBytes(List bytes); } class _WindowsCodePageDecoderSink extends ByteConversionSink { // TODO(floitsch): provide more efficient conversions when the input is // a slice. final Sink _sink; _WindowsCodePageDecoderSink(this._sink); void close() { _sink.close(); } void add(List bytes) { _sink.add(_WindowsCodePageDecoder._decodeBytes(bytes)); } } ~NN<!D/P3>O .&A<@-1. "-1. "E#$.C#F2:AJ+GCB.-E#$ #A28?J+<dart:io/string_transformer.dart ÀJ‘�«�kÀh³ÀJ™�Ž’ÀJ®’KHorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/io/sync_socket.dartŽÉ// Copyright (c) 2017, 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:io"; /// A low-level class for communicating synchronously over a TCP socket. /// /// Warning: [RawSynchronousSocket] should probably only be used to connect to /// 'localhost'. The operations below will block the calling thread to wait for /// a response from the network. The thread can process no other events while /// waiting for these operations to complete. [RawSynchronousSocket] is not /// suitable for applications that require high performance or asynchronous I/O /// such as a server. Instead such applications should use the non-blocking /// sockets and asynchronous operations in the [Socket] or [RawSocket] classes. abstract interface class RawSynchronousSocket { /// Creates a new socket connection and returns a [RawSynchronousSocket]. /// /// The [host] can either be a [String] or an [InternetAddress]. /// If [host] is a [String], [connectSync] will perform a /// [InternetAddress.lookup] and try all returned [InternetAddress]es, /// until connected. /// Unless a connection was established, /// the error from the first failing connection is returned. external static RawSynchronousSocket connectSync(host, int port); /// The number of received and unread bytes in the socket that can be read. int available(); /// Closes the [RawSynchronousSocket]. /// /// Once [closeSync] has been called, attempting to call [readSync], /// [readIntoSync], [writeFromSync], [remoteAddress], and [remotePort] will /// cause a [SocketException] to be thrown. void closeSync(); /// Reads bytes into an existing [buffer]. /// /// Reads bytes and writes then into the range of [buffer] /// from [start] to [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. /// /// Returns the number of bytes read. This maybe be less than `end - start` /// if the file doesn't have that many bytes to read. int readIntoSync(List buffer, [int start = 0, int? end]); /// Reads up to [bytes] bytes from the socket. /// /// Blocks and waits for a response of up to a specified number of bytes /// sent by the socket. [bytes] specifies the maximum number of bytes to /// be read. Returns the list of bytes read, which could be less than the /// value specified by [bytes]. List? readSync(int bytes); /// Shuts down a socket in the provided direction. /// /// Calling shutdown will never throw an exception and calling it several times /// is supported. If both [SocketDirection.receive] and [SocketDirection.send] /// directions are closed, the socket is closed completely, the same as if /// [closeSync] has been called. void shutdown(SocketDirection direction); /// Writes from a [buffer] to the socket. /// /// Will read the buffer from index [start] to index [end]. /// The [start] must be non-negative and no greater than [buffer].length. /// If [end] is omitted, it defaults to [buffer].length. /// Otherwise [end] must be no less than [start] /// and no greater than [buffer].length. void writeFromSync(List buffer, [int start = 0, int? end]); /// The port used by this socket. int get port; /// The remote port connected to by this socket. int get remotePort; /// The [InternetAddress] used to connect this socket. InternetAddress get address; /// The remote [InternetAddress] connected to by this socket. InternetAddress get remoteAddress; } XNN<IOPNLPLP0LCL;3+C$39@%dart:io/sync_socket.dart\org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/sync_socket_patch.dart¤X// Copyright (c) 2017, 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 "common_patch.dart"; @patch class RawSynchronousSocket { @patch static RawSynchronousSocket connectSync(host, int port) { return _RawSynchronousSocket.connectSync(host, port); } } class _RawSynchronousSocket implements RawSynchronousSocket { final _NativeSynchronousSocket _socket; _RawSynchronousSocket(this._socket); static RawSynchronousSocket connectSync(host, int port) { _throwOnBadPort(port); return _RawSynchronousSocket( _NativeSynchronousSocket.connectSync(host, port), ); } InternetAddress get address => _socket.address; int get port => _socket.port; InternetAddress get remoteAddress => _socket.remoteAddress; int get remotePort => _socket.remotePort; int available() => _socket.available; void closeSync() => _socket.closeSync(); int readIntoSync(List buffer, [int start = 0, int? end]) => _socket.readIntoSync(buffer, start, end); List? readSync(int bytes) => _socket.readSync(bytes); void shutdown(SocketDirection direction) => _socket.shutdown(direction); void writeFromSync(List buffer, [int start = 0, int? end]) => _socket.writeFromSync(buffer, start, end); } // The NativeFieldWrapperClass1 can not be used with a mixin, due to missing // implicit constructor. base class _NativeSynchronousSocketNativeWrapper extends NativeFieldWrapperClass1 {} // The _NativeSynchronousSocket class encapsulates a synchronous OS socket. base class _NativeSynchronousSocket extends _NativeSynchronousSocketNativeWrapper { // Socket close state. bool isClosed = false; bool isClosedRead = false; bool isClosedWrite = false; // Holds the address used to connect the socket. late InternetAddress localAddress; // Holds the port of the socket, 0 if not known. int localPort = 0; static _NativeSynchronousSocket connectSync(host, int port) { if (host == null) { throw ArgumentError("Parameter host cannot be null"); } late List<_InternetAddress> addresses; var error = null; if (host is _InternetAddress) { addresses = [host]; } else { try { addresses = lookup(host); } catch (e) { error = e; } if (error != null || addresses == null || addresses.isEmpty) { throw createError(error, "Failed host lookup: '$host'"); } } var it = addresses.iterator; _NativeSynchronousSocket connectNext() { if (!it.moveNext()) { // Could not connect. Throw the first connection error we encountered. assert(error != null); throw error; } var address = it.current; var socket = _NativeSynchronousSocket(); socket.localAddress = address; var result = socket._nativeCreateConnectSync(address._in_addr, port); if (result is OSError) { // Keep first error, if present. if (error == null) { error = createError(result, "Connection failed", address, port); } return connectNext(); } else { // Query the local port, for error messages. try { socket.port; } catch (e) { if (error == null) { error = createError(e, "Connection failed", address, port); } return connectNext(); } } return socket; } return connectNext(); } InternetAddress get address => localAddress; int get available => _nativeAvailable(); int get port { if (localPort != 0) { return localPort; } if (isClosed) { throw const SocketException.closed(); } var result = _nativeGetPort(); if (result is OSError) { throw result; } return localPort = result; } InternetAddress get remoteAddress { if (isClosed) { throw const SocketException.closed(); } var result = _nativeGetRemotePeer(); if (result is OSError) { throw result; } var addr = result[0]; var type = InternetAddressType._from(addr[0]); if (type == InternetAddressType.unix) { return _InternetAddress.fromString( addr[1], type: InternetAddressType.unix, ); } return _InternetAddress(type, addr[1], null, addr[2]); } int get remotePort { if (isClosed) { throw const SocketException.closed(); } var result = _nativeGetRemotePeer(); if (result is OSError) { throw result; } return result[1]; } void closeSync() { if (!isClosed) { _nativeCloseSync(); isClosed = true; } } // Create the appropriate error/exception from different returned // error objects. static createError( error, String message, [ InternetAddress? address, int? port, ]) { if (error is OSError) { return SocketException( message, osError: error, address: address, port: port, ); } else { return SocketException(message, address: address, port: port); } } static List<_InternetAddress> lookup( String host, { InternetAddressType type = InternetAddressType.any, }) { var response = _nativeLookupRequest(host, type._value); if (response is OSError) { throw response; } return <_InternetAddress>[ for (int i = 0; i < response.length; ++i) _InternetAddress( InternetAddressType._from(response[i][0]), response[i][1], host, response[i][2], ), ]; } int readIntoSync(List buffer, int start, int? end) { _checkAvailable(); if (isClosedRead) { throw SocketException("Socket is closed for reading"); } end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return 0; } var result = _nativeReadInto(buffer, start, end - start); if (result is OSError) { throw SocketException("readIntoSync failed", osError: result); } return result; } List? readSync(int len) { _checkAvailable(); if (isClosedRead) { throw SocketException("Socket is closed for reading"); } if ((len != null) && (len < 0)) { throw ArgumentError("Illegal length $len"); } if (len == 0) { return null; } var result = _nativeRead(len); if (result is OSError) { throw result; } return result; } void shutdown(SocketDirection direction) { if (isClosed) { return; } switch (direction) { case SocketDirection.receive: shutdownRead(); break; case SocketDirection.send: shutdownWrite(); break; case SocketDirection.both: closeSync(); break; default: throw ArgumentError(direction); } } void shutdownRead() { if (isClosed || isClosedRead) { return; } if (isClosedWrite) { closeSync(); } else { _nativeShutdownRead(); } isClosedRead = true; } void shutdownWrite() { if (isClosed || isClosedWrite) { return; } if (isClosedRead) { closeSync(); } else { _nativeShutdownWrite(); } isClosedWrite = true; } void writeFromSync(List buffer, int start, int? end) { _checkAvailable(); if (isClosedWrite) { throw SocketException("Socket is closed for writing"); } end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return; } _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, start, end, ); var result = _nativeWrite( bufferAndStart.buffer, bufferAndStart.start, end - (start - bufferAndStart.start), ); if (result is OSError) { throw SocketException("writeFromSync failed", osError: result); } } void _checkAvailable() { if (isClosed) { throw const SocketException.closed(); } } // Native method declarations. @pragma("vm:external-name", "SynchronousSocket_LookupRequest") external static _nativeLookupRequest(host, int type); @pragma("vm:external-name", "SynchronousSocket_CreateConnectSync") external _nativeCreateConnectSync(host, int port); @pragma("vm:external-name", "SynchronousSocket_Available") external _nativeAvailable(); @pragma("vm:external-name", "SynchronousSocket_CloseSync") external _nativeCloseSync(); @pragma("vm:external-name", "SynchronousSocket_GetPort") external _nativeGetPort(); @pragma("vm:external-name", "SynchronousSocket_GetRemotePeer") external _nativeGetRemotePeer(); @pragma("vm:external-name", "SynchronousSocket_Read") external _nativeRead(int len); @pragma("vm:external-name", "SynchronousSocket_ReadList") external _nativeReadInto(List buffer, int offset, int bytes); @pragma("vm:external-name", "SynchronousSocket_ShutdownRead") external _nativeShutdownRead(); @pragma("vm:external-name", "SynchronousSocket_ShutdownWrite") external _nativeShutdownWrite(); @pragma("vm:external-name", "SynchronousSocket_WriteList") external _nativeWrite(List buffer, int offset, int bytes); } �XNN< <:>*'<"82 >,(+C0=KE1M1(L$43%3@<+$ "EA!-O /%L)K 5H /+,#&,)3,*( ;,)D  E(8<05 <=A>E!=&2#-$!!($ % >=AI ,F,!A8E5==;A#8! Function(ByteData? data, PlatformMessageResponseCallback callback); /// Signature for [ChannelBuffers.setListener]'s `callback` argument. /// /// The first argument is the data sent by the plugin. /// /// The second argument is a closure that, when called, will send messages /// back to the plugin. /// /// See also: /// /// * [PlatformMessageResponseCallback], the type used for replies. typedef ChannelCallback = void Function(ByteData? data, PlatformMessageResponseCallback callback); /// The data and logic required to store and invoke a callback. /// /// This tracks (and applies) the [Zone]. class _ChannelCallbackRecord { _ChannelCallbackRecord(this._callback) : _zone = Zone.current; final ChannelCallback _callback; final Zone _zone; /// Call [callback] in [zone], using the given arguments. void invoke(ByteData? dataArg, PlatformMessageResponseCallback callbackArg) { _invoke2(_callback, _zone, dataArg, callbackArg); } } /// A saved platform message for a channel with its callback. class _StoredMessage { /// Wraps the data and callback for a platform message into /// a [_StoredMessage] instance. /// /// The first argument is a [ByteData] that represents the /// payload of the message and a [PlatformMessageResponseCallback] /// that represents the callback that will be called when the message /// is handled. _StoredMessage(this.data, this._callback) : _zone = Zone.current; /// Representation of the message's payload. final ByteData? data; /// Callback to be used when replying to the message. final PlatformMessageResponseCallback _callback; final Zone _zone; void invoke(ByteData? dataArg) { _invoke1(_callback, _zone, dataArg); } } /// The internal storage for a platform channel. /// /// This consists of a fixed-size circular queue of [_StoredMessage]s, /// and the channel's callback, if any has been registered. class _Channel { _Channel([this._capacity = ChannelBuffers.kDefaultBufferSize]) : _queue = collection.ListQueue<_StoredMessage>(_capacity); /// The underlying data for the buffered messages. final collection.ListQueue<_StoredMessage> _queue; /// The number of messages currently in the [_Channel]. /// /// This is equal to or less than the [capacity]. int get length => _queue.length; /// Whether to dump messages to the console when a message is /// discarded due to the channel overflowing. /// /// Has no effect in release builds. bool debugEnableDiscardWarnings = true; /// The number of messages that _can_ be stored in the [_Channel]. /// /// When additional messages are stored, earlier ones are discarded, /// in a first-in-first-out fashion. int get capacity => _capacity; int _capacity; /// Set the [capacity] of the channel to the given size. /// /// If the new size is smaller than the [length], the oldest /// messages are discarded until the capacity is reached. No /// message is shown in case of overflow, regardless of the /// value of [debugEnableDiscardWarnings]. set capacity(int newSize) { _capacity = newSize; _dropOverflowMessages(newSize); } /// Whether a microtask is queued to call [_drainStep]. /// /// This is used to queue messages received while draining, rather /// than sending them out of order. This generally cannot happen in /// production but is possible in test scenarios. /// /// This is also necessary to avoid situations where multiple drains are /// invoked simultaneously. For example, if a listener is set /// (queuing a drain), then unset, then set again (which would queue /// a drain again), all in one stack frame (not allowing the drain /// itself an opportunity to check if a listener is set). bool _draining = false; /// Adds a message to the channel. /// /// If the channel overflows, earlier messages are discarded, in a /// first-in-first-out fashion. See [capacity]. If /// [debugEnableDiscardWarnings] is true, this method returns true /// on overflow. It is the responsibility of the caller to show the /// warning message. bool push(_StoredMessage message) { if (!_draining && _channelCallbackRecord != null) { assert(_queue.isEmpty); _channelCallbackRecord!.invoke(message.data, message.invoke); return false; } if (_capacity <= 0) { return debugEnableDiscardWarnings; } final bool result = _dropOverflowMessages(_capacity - 1); _queue.addLast(message); return result; } /// Returns the first message in the channel and removes it. /// /// Throws when empty. _StoredMessage pop() => _queue.removeFirst(); /// Removes messages until [length] reaches `lengthLimit`. /// /// The callback of each removed message is invoked with null /// as its argument. /// /// If any messages are removed, and [debugEnableDiscardWarnings] is /// true, then returns true. The caller is responsible for showing /// the warning message in that case. bool _dropOverflowMessages(int lengthLimit) { var result = false; while (_queue.length > lengthLimit) { final _StoredMessage message = _queue.removeFirst(); message.invoke(null); // send empty reply to the plugin side result = true; } return result; } _ChannelCallbackRecord? _channelCallbackRecord; /// Sets the listener for this channel. /// /// When there is a listener, messages are sent immediately. /// /// If any messages were queued before the listener is added, /// they are drained asynchronously after this method returns. /// (See [_drain].) /// /// Only one listener may be set at a time. Setting a /// new listener clears the previous one. /// /// Callbacks are invoked in their own stack frame and /// use the zone that was current when the callback was /// registered. void setListener(ChannelCallback callback) { final needDrain = _channelCallbackRecord == null; _channelCallbackRecord = _ChannelCallbackRecord(callback); if (needDrain && !_draining) { _drain(); } } /// Clears the listener for this channel. /// /// When there is no listener, messages are queued, up to [capacity], /// and then discarded in a first-in-first-out fashion. void clearListener() { _channelCallbackRecord = null; } /// Drains all the messages in the channel (invoking the currently /// registered listener for each one). /// /// Each message is handled in its own microtask. No messages can /// be queued by plugins while the queue is being drained, but any /// microtasks queued by the handler itself will be processed before /// the next message is handled. /// /// The draining stops if the listener is removed. /// /// See also: /// /// * [setListener], which is used to register the callback. /// * [clearListener], which removes it. void _drain() { assert(!_draining); _draining = true; scheduleMicrotask(_drainStep); } /// Drains a single message and then reinvokes itself asynchronously. /// /// See [_drain] for more details. void _drainStep() { assert(_draining); if (_queue.isNotEmpty && _channelCallbackRecord != null) { final _StoredMessage message = pop(); _channelCallbackRecord!.invoke(message.data, message.invoke); scheduleMicrotask(_drainStep); } else { _draining = false; } } } /// The buffering and dispatch mechanism for messages sent by plugins /// on the engine side to their corresponding plugin code on the /// framework side. /// /// Messages for a channel are stored until a listener is provided for that channel, /// using [setListener]. Only one listener may be configured per channel. /// /// Typically these buffers are drained once a callback is set up on /// the [BinaryMessenger] in the Flutter framework. (See [setListener].) /// /// ## Channel names /// /// By convention, channels are normally named with a reverse-DNS prefix, a /// slash, and then a domain-specific name. For example, `com.example/demo`. /// /// Channel names cannot contain the U+0000 NULL character, because they /// are passed through APIs that use null-terminated strings. /// /// ## Buffer capacity and overflow /// /// Each channel has a finite buffer capacity and messages will /// be deleted in a first-in-first-out (FIFO) manner if the capacity is exceeded. /// /// By default buffers store one message per channel, and when a /// message overflows, in debug mode, a message is printed to the /// console. The message looks like the following: /// /// > A message on the com.example channel was discarded before it could be /// > handled. /// > This happens when a plugin sends messages to the framework side before the /// > framework has had an opportunity to register a listener. See the /// > ChannelBuffers API documentation for details on how to configure the channel /// > to expect more messages, or to expect messages to get discarded: /// > https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html /// /// There are tradeoffs associated with any size. The correct size /// should be chosen for the semantics of the channel. To change the /// size a plugin can send a message using the control channel, /// as described below. /// /// Size 0 is appropriate for channels where messages sent before /// the engine and framework are ready should be ignored. For /// example, a plugin that notifies the framework any time a /// radiation sensor detects an ionization event might set its size /// to zero since past ionization events are typically not /// interesting, only instantaneous readings are worth tracking. /// /// Size 1 is appropriate for level-triggered plugins. For example, /// a plugin that notifies the framework of the current value of a /// pressure sensor might leave its size at one (the default), while /// sending messages continually; once the framework side of the plugin /// registers with the channel, it will immediately receive the most /// up to date value and earlier messages will have been discarded. /// /// Sizes greater than one are appropriate for plugins where every /// message is important. For example, a plugin that itself /// registers with another system that has been buffering events, /// and immediately forwards all the previously-buffered events, /// would likely wish to avoid having any messages dropped on the /// floor. In such situations, it is important to select a size that /// will avoid overflows. It is also important to consider the /// potential for the framework side to never fully initialize (e.g. if /// the user starts the application, but terminates it soon /// afterwards, leaving time for the platform side of a plugin to /// run but not the framework side). /// /// ## The control channel /// /// A plugin can configure its channel's buffers by sending messages to the /// control channel, `dev.flutter/channel-buffers` (see [kControlChannelName]). /// /// There are two messages that can be sent to this control channel, to adjust /// the buffer size and to disable the overflow warnings. See [handleMessage] /// for details on these messages. class ChannelBuffers { /// Create a buffer pool for platform messages. /// /// It is generally not necessary to create an instance of this class; /// the global [channelBuffers] instance is the one used by the engine. ChannelBuffers(); /// The number of messages that channel buffers will store by default. static const int kDefaultBufferSize = 1; /// The name of the channel that plugins can use to communicate with the /// channel buffers system. /// /// These messages are handled by [handleMessage]. static const String kControlChannelName = 'dev.flutter/channel-buffers'; /// A mapping between a channel name and its associated [_Channel]. final Map _channels = {}; /// Adds a message (`data`) to the named channel buffer (`name`). /// /// The `callback` argument is a closure that, when called, will send messages /// back to the plugin. /// /// If a message overflows the channel, and the channel has not been /// configured to expect overflow, then, in debug mode, a message /// will be printed to the console warning about the overflow. /// /// Channel names cannot contain the U+0000 NULL character, because they /// are passed through APIs that use null-terminated strings. void push(String name, ByteData? data, PlatformMessageResponseCallback callback) { assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.'); final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); if (channel.push(_StoredMessage(data, callback))) { _printDebug( 'A message on the $name channel was discarded before it could be handled.\n' 'This happens when a plugin sends messages to the framework side before the ' 'framework has had an opportunity to register a listener. See the ChannelBuffers ' 'API documentation for details on how to configure the channel to expect more ' 'messages, or to expect messages to get discarded:\n' ' https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html\n' 'The capacity of the $name channel is ${channel._capacity} message${channel._capacity != 1 ? 's' : ''}.', ); } } /// Sets the listener for the specified channel. /// /// When there is a listener, messages are sent immediately. /// /// Each channel may have up to one listener set at a time. Setting /// a new listener on a channel with an existing listener clears the /// previous one. /// /// Callbacks are invoked in their own stack frame and /// use the zone that was current when the callback was /// registered. /// /// ## Draining /// /// If any messages were queued before the listener is added, /// they are drained asynchronously after this method returns. /// /// Each message is handled in its own microtask. No messages can /// be queued by plugins while the queue is being drained, but any /// microtasks queued by the handler itself will be processed before /// the next message is handled. /// /// The draining stops if the listener is removed. void setListener(String name, ChannelCallback callback) { assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.'); final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); channel.setListener(callback); sendChannelUpdate(name, listening: true); } /// Clears the listener for the specified channel. /// /// When there is no listener, messages on that channel are queued, /// up to [kDefaultBufferSize] (or the size configured via the /// control channel), and then discarded in a first-in-first-out /// fashion. void clearListener(String name) { final _Channel? channel = _channels[name]; if (channel != null) { channel.clearListener(); sendChannelUpdate(name, listening: false); } } @Native(symbol: 'PlatformConfigurationNativeApi::SendChannelUpdate') external static void _sendChannelUpdate(String name, bool listening); // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs void sendChannelUpdate(String name, {required bool listening}) => _sendChannelUpdate(name, listening); /// Deprecated. Migrate to [setListener] instead. /// /// Remove and process all stored messages for a given channel. /// /// This should be called once a channel is prepared to handle messages /// (i.e. when a message handler is set up in the framework). /// /// The messages are processed by calling the given `callback`. Each message /// is processed in its own microtask. @Deprecated( 'Migrate to setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) Future drain(String name, DrainChannelCallback callback) async { final _Channel? channel = _channels[name]; while (channel != null && !channel._queue.isEmpty) { final _StoredMessage message = channel.pop(); await callback(message.data, message.invoke); } } /// Handle a control message. /// /// This is intended to be called by the platform messages dispatcher, forwarding /// messages from plugins to the [kControlChannelName] channel. /// /// Messages use the [StandardMethodCodec] format. There are two methods /// supported: `resize` and `overflow`. The `resize` method changes the size /// of the buffer, and the `overflow` method controls whether overflow is /// expected or not. /// /// ## `resize` /// /// The `resize` method takes as its argument a list with two values, first /// the channel name (a UTF-8 string less than 254 bytes long and not /// containing any null bytes), and second the allowed size of the channel /// buffer (an integer between 0 and 2147483647). /// /// Upon receiving the message, the channel's buffer is resized. If necessary, /// messages are silently discarded to ensure the buffer is no bigger than /// specified. /// /// For historical reasons, this message can also be sent using a bespoke /// format consisting of a UTF-8-encoded string with three parts separated /// from each other by U+000D CARRIAGE RETURN (CR) characters, the three parts /// being the string `resize`, the string giving the channel name, and then /// the string giving the decimal serialization of the new channel buffer /// size. For example: `resize\rchannel\r1` /// /// ## `overflow` /// /// The `overflow` method takes as its argument a list with two values, first /// the channel name (a UTF-8 string less than 254 bytes long and not /// containing any null bytes), and second a boolean which is true if overflow /// is expected and false if it is not. /// /// This sets a flag on the channel in debug mode. In release mode the message /// is silently ignored. The flag indicates whether overflow is expected on this /// channel. When the flag is set, messages are discarded silently. When the /// flag is cleared (the default), any overflow on the channel causes a message /// to be printed to the console, warning that a message was lost. void handleMessage(ByteData data) { // We hard-code the deserialization here because the StandardMethodCodec class // is part of the framework, not dart:ui. final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); if (bytes[0] == 0x07) { // 7 = value code for string final int methodNameLength = bytes[1]; if (methodNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); } var index = 2; // where we are in reading the bytes final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength)); index += methodNameLength; switch (methodName) { case 'resize': if (bytes[index] != 0x0C) { // 12 = value code for list throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", ); } index += 1; if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", ); } index += 1; if (bytes[index] != 0x07) { // 7 = value code for string throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)", ); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)", ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); if (channelName.contains('\u0000')) { throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)", ); } index += channelNameLength; if (bytes[index] != 0x03) { // 3 = value code for uint32 throw Exception( "Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)", ); } index += 1; resize(channelName, data.getUint32(index, Endian.host)); case 'overflow': if (bytes[index] != 0x0C) { // 12 = value code for list throw Exception( "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", ); } index += 1; if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. throw Exception( "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", ); } index += 1; if (bytes[index] != 0x07) { // 7 = value code for string throw Exception( "Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)", ); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception( "Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)", ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); index += channelNameLength; if (bytes[index] != 0x01 && bytes[index] != 0x02) { // 1 = value code for true, 2 = value code for false throw Exception( "Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)", ); } allowOverflow(channelName, bytes[index] == 0x01); default: throw Exception("Unrecognized method '$methodName' sent to $kControlChannelName"); } } else { final List parts = utf8.decode(bytes).split('\r'); if (parts.length == 1 + /*arity=*/ 2 && parts[0] == 'resize') { resize(parts[1], int.parse(parts[2])); } else { // If the message couldn't be decoded as UTF-8, a FormatException will // have been thrown by utf8.decode() above. throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); } } } /// Changes the capacity of the queue associated with the given channel. /// /// This could result in the dropping of messages if newSize is less /// than the current length of the queue. /// /// This is expected to be called by platform-specific plugin code (indirectly /// via the control channel), not by code on the framework side. See /// [handleMessage]. /// /// Calling this from framework code is redundant since by the time framework /// code can be running, it can just subscribe to the relevant channel and /// there is therefore no need for any buffering. void resize(String name, int newSize) { _Channel? channel = _channels[name]; if (channel == null) { assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.'); channel = _Channel(newSize); _channels[name] = channel; } else { channel.capacity = newSize; } } /// Toggles whether the channel should show warning messages when discarding /// messages due to overflow. /// /// This is expected to be called by platform-specific plugin code (indirectly /// via the control channel), not by code on the framework side. See /// [handleMessage]. /// /// Calling this from framework code is redundant since by the time framework /// code can be running, it can just subscribe to the relevant channel and /// there is therefore no need for any messages to overflow. /// /// This method has no effect in release builds. void allowOverflow(String name, bool allowed) { assert(() { _Channel? channel = _channels[name]; if (channel == null && allowed) { assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.'); channel = _Channel(); _channels[name] = channel; } channel?.debugEnableDiscardWarnings = !allowed; return true; }()); } } /// [ChannelBuffers] that allow the storage of messages between the /// Engine and the Framework. Typically messages that can't be delivered /// are stored here until the Framework is able to process them. /// /// See also: /// /// * [BinaryMessenger], where [ChannelBuffers] are typically read. final ChannelBuffers channelBuffers = ChannelBuffers(); ‚‚>#=EHD/83#)1G<A@55:4#@0'*EG'!;??>-$:EF4K@GE<%E5EF&8D)>?0=@GE(0*;C2*?@A8,9:/6?#,H:#E)DEG#5@,#H%?,D% FAUJEILMI>$@RAB3LQGSGJCE@B>=D;ADCEHEDCPr 3?FG9:@ADEG#5<`L#.5FAC$/1dHm$D+4BJ@O)';I/944 TBKOLNHM4QMLMQNL.PHQ*QSORE&S.]#-%Ae:]!&(€š %s€š &)v 6*E€‰ c0€ƒ &&)€– C&(€š %s€š &)x 6*E€‹ c&>Az <] AF/O4VKG,QGPM4*)b#! "O QGPM?32+(d#6 DJAD8dart:ui/channel_buffers.dartÀdÙÀh³—ì-org-dartlang-sdk:///flutter/lib/ui/hooks.dart½^// Copyright 2013 The Flutter Authors. 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.ui; @pragma('vm:entry-point') void _addView( int viewId, double devicePixelRatio, double width, double height, double viewPaddingTop, double viewPaddingRight, double viewPaddingBottom, double viewPaddingLeft, double viewInsetTop, double viewInsetRight, double viewInsetBottom, double viewInsetLeft, double systemGestureInsetTop, double systemGestureInsetRight, double systemGestureInsetBottom, double systemGestureInsetLeft, double physicalTouchSlop, List displayFeaturesBounds, List displayFeaturesType, List displayFeaturesState, int displayId, double minWidth, double maxWidth, double minHeight, double maxHeight, double displayCornerRadiusTopLeft, double displayCornerRadiusTopRight, double displayCornerRadiusBottomRight, double displayCornerRadiusBottomLeft, ) { final _ViewConfiguration viewConfiguration = _buildViewConfiguration( devicePixelRatio, width, height, viewPaddingTop, viewPaddingRight, viewPaddingBottom, viewPaddingLeft, viewInsetTop, viewInsetRight, viewInsetBottom, viewInsetLeft, systemGestureInsetTop, systemGestureInsetRight, systemGestureInsetBottom, systemGestureInsetLeft, physicalTouchSlop, displayFeaturesBounds, displayFeaturesType, displayFeaturesState, displayId, minWidth, maxWidth, minHeight, maxHeight, displayCornerRadiusTopLeft, displayCornerRadiusTopRight, displayCornerRadiusBottomRight, displayCornerRadiusBottomLeft, ); PlatformDispatcher.instance._addView(viewId, viewConfiguration); } @pragma('vm:entry-point') void _removeView(int viewId) { PlatformDispatcher.instance._removeView(viewId); } @pragma('vm:entry-point') void _sendViewFocusEvent(int viewId, int viewFocusState, int viewFocusDirection) { final viewFocusEvent = ViewFocusEvent( viewId: viewId, state: ViewFocusState.values[viewFocusState], direction: ViewFocusDirection.values[viewFocusDirection], ); PlatformDispatcher.instance._sendViewFocusEvent(viewFocusEvent); } @pragma('vm:entry-point') void _setEngineId(int engineId) { PlatformDispatcher.instance._engineId = engineId; } @pragma('vm:entry-point') void _updateDisplays( List ids, List widths, List heights, List devicePixelRatios, List refreshRates, ) { assert(ids.length == widths.length); assert(ids.length == heights.length); assert(ids.length == devicePixelRatios.length); assert(ids.length == refreshRates.length); final displays = []; for (var index = 0; index < ids.length; index += 1) { final int displayId = ids[index]; displays.add( Display._( id: displayId, size: Size(widths[index], heights[index]), devicePixelRatio: devicePixelRatios[index], refreshRate: refreshRates[index], ), ); } PlatformDispatcher.instance._updateDisplays(displays); } List _decodeDisplayFeatures({ required List bounds, required List type, required List state, required double devicePixelRatio, }) { assert(bounds.length / 4 == type.length, 'Bounds are rectangles, requiring 4 measurements each'); assert(type.length == state.length); final result = []; for (var i = 0; i < type.length; i++) { final int rectOffset = i * 4; result.add( DisplayFeature( bounds: Rect.fromLTRB( bounds[rectOffset] / devicePixelRatio, bounds[rectOffset + 1] / devicePixelRatio, bounds[rectOffset + 2] / devicePixelRatio, bounds[rectOffset + 3] / devicePixelRatio, ), type: DisplayFeatureType.values[type[i]], state: state[i] < DisplayFeatureState.values.length ? DisplayFeatureState.values[state[i]] : DisplayFeatureState.unknown, ), ); } return result; } DisplayCornerRadii? _decodeDisplayCornerRadii({ required double displayCornerRadiusTopLeft, required double displayCornerRadiusTopRight, required double displayCornerRadiusBottomRight, required double displayCornerRadiusBottomLeft, }) { assert(() { final isTopLeftSet = displayCornerRadiusTopLeft != _kUnsetDisplayCornerRadius; final bool isConsistent = (displayCornerRadiusTopRight != _kUnsetDisplayCornerRadius) == isTopLeftSet && (displayCornerRadiusBottomRight != _kUnsetDisplayCornerRadius) == isTopLeftSet && (displayCornerRadiusBottomLeft != _kUnsetDisplayCornerRadius) == isTopLeftSet; if (!isConsistent) { throw ArgumentError( 'The display corner radii must be either all set or all unset.\n' 'Provided values were inconsistent:\n' ' TopLeft: $displayCornerRadiusTopLeft\n' ' TopRight: $displayCornerRadiusTopRight\n' ' BottomRight: $displayCornerRadiusBottomRight\n' ' BottomLeft: $displayCornerRadiusBottomLeft', ); } return true; }()); if (displayCornerRadiusTopLeft == _kUnsetDisplayCornerRadius || displayCornerRadiusTopRight == _kUnsetDisplayCornerRadius || displayCornerRadiusBottomRight == _kUnsetDisplayCornerRadius || displayCornerRadiusBottomLeft == _kUnsetDisplayCornerRadius) { return null; } return DisplayCornerRadii( topLeft: displayCornerRadiusTopLeft, topRight: displayCornerRadiusTopRight, bottomRight: displayCornerRadiusBottomRight, bottomLeft: displayCornerRadiusBottomLeft, ); } _ViewConfiguration _buildViewConfiguration( double devicePixelRatio, double width, double height, double viewPaddingTop, double viewPaddingRight, double viewPaddingBottom, double viewPaddingLeft, double viewInsetTop, double viewInsetRight, double viewInsetBottom, double viewInsetLeft, double systemGestureInsetTop, double systemGestureInsetRight, double systemGestureInsetBottom, double systemGestureInsetLeft, double physicalTouchSlop, List displayFeaturesBounds, List displayFeaturesType, List displayFeaturesState, int displayId, double minWidth, double maxWidth, double minHeight, double maxHeight, double displayCornerRadiusTopLeft, double displayCornerRadiusTopRight, double displayCornerRadiusBottomRight, double displayCornerRadiusBottomLeft, ) { return _ViewConfiguration( devicePixelRatio: devicePixelRatio, size: Size(width, height), viewPadding: ViewPadding._( top: viewPaddingTop, right: viewPaddingRight, bottom: viewPaddingBottom, left: viewPaddingLeft, ), viewInsets: ViewPadding._( top: viewInsetTop, right: viewInsetRight, bottom: viewInsetBottom, left: viewInsetLeft, ), padding: ViewPadding._( top: math.max(0.0, viewPaddingTop - viewInsetTop), right: math.max(0.0, viewPaddingRight - viewInsetRight), bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), left: math.max(0.0, viewPaddingLeft - viewInsetLeft), ), systemGestureInsets: ViewPadding._( top: math.max(0.0, systemGestureInsetTop), right: math.max(0.0, systemGestureInsetRight), bottom: math.max(0.0, systemGestureInsetBottom), left: math.max(0.0, systemGestureInsetLeft), ), gestureSettings: GestureSettings( physicalTouchSlop: physicalTouchSlop == _kUnsetGestureSetting ? null : physicalTouchSlop, ), displayFeatures: _decodeDisplayFeatures( bounds: displayFeaturesBounds, type: displayFeaturesType, state: displayFeaturesState, devicePixelRatio: devicePixelRatio, ), displayId: displayId, viewConstraints: ViewConstraints( minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, ), displayCornerRadii: _decodeDisplayCornerRadii( displayCornerRadiusTopLeft: displayCornerRadiusTopLeft, displayCornerRadiusTopRight: displayCornerRadiusTopRight, displayCornerRadiusBottomRight: displayCornerRadiusBottomRight, displayCornerRadiusBottomLeft: displayCornerRadiusBottomLeft, ), ); } @pragma('vm:entry-point') void _updateWindowMetrics( int viewId, double devicePixelRatio, double width, double height, double viewPaddingTop, double viewPaddingRight, double viewPaddingBottom, double viewPaddingLeft, double viewInsetTop, double viewInsetRight, double viewInsetBottom, double viewInsetLeft, double systemGestureInsetTop, double systemGestureInsetRight, double systemGestureInsetBottom, double systemGestureInsetLeft, double physicalTouchSlop, List displayFeaturesBounds, List displayFeaturesType, List displayFeaturesState, int displayId, double minWidth, double maxWidth, double minHeight, double maxHeight, double displayCornerRadiusTopLeft, double displayCornerRadiusTopRight, double displayCornerRadiusBottomRight, double displayCornerRadiusBottomLeft, ) { final _ViewConfiguration viewConfiguration = _buildViewConfiguration( devicePixelRatio, width, height, viewPaddingTop, viewPaddingRight, viewPaddingBottom, viewPaddingLeft, viewInsetTop, viewInsetRight, viewInsetBottom, viewInsetLeft, systemGestureInsetTop, systemGestureInsetRight, systemGestureInsetBottom, systemGestureInsetLeft, physicalTouchSlop, displayFeaturesBounds, displayFeaturesType, displayFeaturesState, displayId, minWidth, maxWidth, minHeight, maxHeight, displayCornerRadiusTopLeft, displayCornerRadiusTopRight, displayCornerRadiusBottomRight, displayCornerRadiusBottomLeft, ); PlatformDispatcher.instance._updateWindowMetrics(viewId, viewConfiguration); } typedef _LocaleClosure = String Function(); @pragma('vm:entry-point') _LocaleClosure? _getLocaleClosure() => PlatformDispatcher.instance._localeClosure; @pragma('vm:entry-point') void _updateLocales(List locales) { PlatformDispatcher.instance._updateLocales(locales); } @pragma('vm:entry-point') void _updateUserSettingsData(String jsonData) { PlatformDispatcher.instance._updateUserSettingsData(jsonData); } @pragma('vm:entry-point') void _updateInitialLifecycleState(String state) { PlatformDispatcher.instance._updateInitialLifecycleState(state); } @pragma('vm:entry-point') void _updateSemanticsEnabled(bool enabled) { PlatformDispatcher.instance._updateSemanticsEnabled(enabled); } @pragma('vm:entry-point') void _updateAccessibilityFeatures(int values) { PlatformDispatcher.instance._updateAccessibilityFeatures(values); } @pragma('vm:entry-point') void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { PlatformDispatcher.instance._dispatchPlatformMessage(name, data, responseId); } @pragma('vm:entry-point') void _dispatchPointerDataPacket(ByteData packet) { PlatformDispatcher.instance._dispatchPointerDataPacket(packet); } @pragma('vm:entry-point') void _dispatchSemanticsAction(int viewId, int nodeId, int action, ByteData? args) { PlatformDispatcher.instance._dispatchSemanticsAction(viewId, nodeId, action, args); } @pragma('vm:entry-point') void _beginFrame(int microseconds, int frameNumber) { PlatformDispatcher.instance._beginFrame(microseconds); PlatformDispatcher.instance._updateFrameData(frameNumber); } @pragma('vm:entry-point') void _reportTimings(List timings) { PlatformDispatcher.instance._reportTimings(timings); } @pragma('vm:entry-point') void _drawFrame() { PlatformDispatcher.instance._drawFrame(); } @pragma('vm:entry-point') bool _onError(Object error, StackTrace? stackTrace) { return PlatformDispatcher.instance._dispatchError(error, stackTrace ?? StackTrace.empty); } typedef _ListStringArgFunction = Object? Function(List args); @pragma('vm:entry-point') void _runMain(Function startMainIsolateFunction, Function userMainFunction, List args) { // ignore: avoid_dynamic_calls startMainIsolateFunction(() { if (userMainFunction is _ListStringArgFunction) { userMainFunction(args); } else { userMainFunction(); // ignore: avoid_dynamic_calls } }, null); } /// Invokes [callback] inside the given [zone]. void _invoke(void Function()? callback, Zone zone) { if (callback == null) { return; } if (identical(zone, Zone.current)) { callback(); } else { zone.runGuarded(callback); } } /// Invokes [callback] inside the given [zone] passing it [arg]. /// /// The 1 in the name refers to the number of arguments expected by /// the callback (and thus passed to this function, in addition to the /// callback itself and the zone in which the callback is executed). void _invoke1(void Function(A a)? callback, Zone zone, A arg) { if (callback == null) { return; } if (identical(zone, Zone.current)) { callback(arg); } else { zone.runUnaryGuarded(callback, arg); } } /// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. /// /// The 2 in the name refers to the number of arguments expected by /// the callback (and thus passed to this function, in addition to the /// callback itself and the zone in which the callback is executed). void _invoke2(void Function(A1 a1, A2 a2)? callback, Zone zone, A1 arg1, A2 arg2) { if (callback == null) { return; } if (identical(zone, Zone.current)) { callback(arg1, arg2); } else { zone.runGuarded(() { callback(arg1, arg2); }); } } /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. /// /// The 3 in the name refers to the number of arguments expected by /// the callback (and thus passed to this function, in addition to the /// callback itself and the zone in which the callback is executed). void _invoke3( void Function(A1 a1, A2 a2, A3 a3)? callback, Zone zone, A1 arg1, A2 arg2, A3 arg3, ) { if (callback == null) { return; } if (identical(zone, Zone.current)) { callback(arg1, arg2, arg3); } else { zone.runGuarded(() { callback(arg1, arg2, arg3); }); } } bool _isLoopback(String host) { if (host.isEmpty) { return false; } if ('localhost' == host) { return true; } try { return InternetAddress(host).isLoopback; } on ArgumentError { return false; } } /// Loopback connections are always allowed. /// Zone override with 'flutter.io.allow_http' takes first priority. /// If zone override is not provided, engine setting is checked. @pragma('vm:entry-point') void Function(Uri) _getHttpConnectionHookClosure(bool mayInsecurelyConnectToAllDomains) { return (Uri uri) { final Object? zoneOverride = Zone.current[#flutter.io.allow_http]; if (zoneOverride == true) { return; } if (zoneOverride == false && uri.isScheme('http')) { // Going to _isLoopback check before throwing } else if (mayInsecurelyConnectToAllDomains || uri.isScheme('https')) { // In absence of zone override, if engine setting allows the connection // or if connection is to `https`, allow the connection. return; } // Loopback connections are always allowed // Check at last resort to avoid debug annoyance of try/on ArgumentError if (_isLoopback(uri.host)) { return; } throw UnsupportedError( 'Non-https connection "$uri" is not supported by the platform. ' 'Refer to https://docs.flutter.dev/release/breaking-changes/network-policy-ios-android.', ); }; } ‚C"4"'(2- 8&34* 9. $d'%*"1555 2<3+ 0./21SWZWJ/35;8 BCFE)+1/, "#!&!"%&)(( !9?B<(1573&`-%!#*&3>@FD "#!&!"%&)(H  !$#O,S,70A2C-@0DMP3BTV69=)7,6\Fa! 6 9 05 ' ADGEC ' ,MDGE\ ' VDGE0  ' " --EAZG 94LN?/M!G`dart:ui/hooks.dartÀeÀh³/org-dartlang-sdk:///flutter/lib/ui/natives.dart“G// Copyright 2013 The Flutter Authors. 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.ui; /// Helper functions for Dart Plugin Registrants. abstract final class DartPluginRegistrant { static bool _wasInitialized = false; /// Makes sure the that the Dart Plugin Registrant has been called for this /// isolate. This can safely be executed multiple times on the same isolate, /// but should not be called on the Root isolate. static void ensureInitialized() { if (!_wasInitialized) { _wasInitialized = true; _ensureInitialized(); } } @Native(symbol: 'DartPluginRegistrant_EnsureInitialized') external static void _ensureInitialized(); } // Corelib 'print' implementation. void _print(String arg) { _Logger._printString(arg); } void _printDebug(String arg) { _Logger._printDebugString(arg); } class _Logger { @Native(symbol: 'DartRuntimeHooks::Logger_PrintString') external static void _printString(String? s); @Native(symbol: 'DartRuntimeHooks::Logger_PrintDebugString') external static void _printDebugString(String? s); } // If we actually run on big endian machines, we'll need to do something smarter // here. We don't use [Endian.Host] because it's not a compile-time // constant and can't propagate into the set/get calls. const Endian _kFakeHostEndian = Endian.little; // A service protocol extension to schedule a frame to be rendered into the // window. Future _scheduleFrame( String method, Map parameters, ) async { // Schedule the frame. PlatformDispatcher.instance.scheduleFrame(); // Always succeed. return developer.ServiceExtensionResponse.result( json.encode({'type': 'Success'}), ); } Future _reinitializeShader( String method, Map parameters, ) async { final String? assetKey = parameters['assetKey']; if (assetKey != null) { FragmentProgram._reinitializeShader(assetKey); } // Always succeed. return developer.ServiceExtensionResponse.result( json.encode({'type': 'Success'}), ); } Future _getImpellerEnabled( String method, Map parameters, ) async { return developer.ServiceExtensionResponse.result( json.encode({'type': 'Success', 'enabled': _impellerEnabled}), ); } const bool _kReleaseMode = bool.fromEnvironment('dart.vm.product'); @pragma('vm:entry-point') void _setupHooks() { assert(() { // In debug mode, register the schedule frame extension. developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); // In debug mode, allow shaders to be reinitialized. developer.registerExtension('ext.ui.window.reinitializeShader', _reinitializeShader); return true; }()); // In debug and profile mode, allow tools to display the current rendering backend. if (!_kReleaseMode) { developer.registerExtension('ext.ui.window.impellerEnabled', _getImpellerEnabled); } } @Native(symbol: 'DartRuntimeHooks::ScheduleMicrotask') external void _scheduleMicrotask(void Function() callback); @Native(symbol: 'DartRuntimeHooks::GetCallbackHandle') external int? _getCallbackHandle(Function closure); @Native(symbol: 'DartRuntimeHooks::GetCallbackFromHandle') external Function? _getCallbackFromHandle(int handle); typedef _PrintClosure = void Function(String line); // Used by the embedder to initialize how printing is performed. // See also https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/print_patch.dart @pragma('vm:entry-point') _PrintClosure _getPrintClosure() => _print; typedef _ScheduleImmediateClosure = void Function(void Function()); // Used by the embedder to initialize how microtasks are scheduled. // See also https://github.com/dart-lang/sdk/blob/main/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart @pragma('vm:entry-point') _ScheduleImmediateClosure _getScheduleMicrotaskClosure() => _scheduleMicrotask; // Used internally to indicate whether the Engine is using Impeller for // rendering. @pragma('vm:entry-point') bool _impellerEnabled = false; // Used internally to indicate whether the embedder enables the implicit view, // and the implicit view's ID if so. // // The exact value of this variable is an implementation detail that may change // at any time. Apps should always use PlatformDispatcher.implicitView to // determine the current implicit view, if any. @pragma('vm:entry-point') int? _implicitViewId; // Used internally to indicate whether isolates running on the platform thread // are enabled. @pragma('vm:entry-point') bool _platformIsolatesEnabled = false; €’ value.isFinite), 'Matrix4 entries must be finite.'); return true; } bool _radiusIsValid(Radius radius) { assert(!radius.x.isNaN && !radius.y.isNaN, 'Radius argument contained a NaN value.'); return true; } Color _scaleAlpha(Color x, double factor) { return x.withValues(alpha: clampDouble(x.a * factor, 0, 1)); } ColorSpace _widerColorSpace(ColorSpace a, ColorSpace b) { return a == ColorSpace.displayP3 || b == ColorSpace.displayP3 ? ColorSpace.displayP3 : a; } /// An immutable color value in ARGB format. /// /// Consider the light teal of the [Flutter logo](https://flutter.dev/brand). It /// is fully opaque, with a red [r] channel value of `0.2588` (or `0x42` or `66` /// as an 8-bit value), a green [g] channel value of `0.6471` (or `0xA5` or /// `165` as an 8-bit value), and a blue [b] channel value of `0.9608` (or /// `0xF5` or `245` as an 8-bit value). In a common [CSS hex color syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color) /// for RGB color values, it would be described as `#42A5F5`. /// /// Here are some ways it could be constructed: /// /// ```dart /// const Color c1 = Color.from(alpha: 1.0, red: 0.2588, green: 0.6471, blue: 0.9608); /// const Color c2 = Color(0xFF42A5F5); /// const Color c3 = Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5); /// const Color c4 = Color.fromARGB(255, 66, 165, 245); /// const Color c5 = Color.fromRGBO(66, 165, 245, 1.0); /// ``` /// /// If you are having a problem with [Color.new] wherein it seems your color is /// just not painting, check to make sure you are specifying the full 8 /// hexadecimal digits. If you only specify six, then the leading two digits are /// assumed to be zero, which means fully-transparent: /// /// ```dart /// const Color c1 = Color(0xFFFFFF); // fully transparent white (invisible) /// const Color c2 = Color(0xFFFFFFFF); // fully opaque white (visible) /// /// // Or use double-based channel values: /// const Color c3 = Color.from(alpha: 1.0, red: 1.0, green: 1.0, blue: 1.0); /// ``` /// /// [Color]'s color components are stored as floating-point values. Care should /// be taken if one does not want the literal equality provided by `operator==`. /// To test equality inside of Flutter tests consider using [`isSameColorAs`][]. /// /// See also: /// /// * [Colors](https://api.flutter.dev/flutter/material/Colors-class.html), /// which defines the colors found in the Material Design specification. /// * [`isSameColorAs`][], /// a Matcher to handle floating-point deltas when checking [Color] equality. /// /// [`isSameColorAs`]: https://api.flutter.dev/flutter/flutter_test/isSameColorAs.html class Color { /// Construct an [ColorSpace.sRGB] color from the lower 32 bits of an [int]. /// /// The bits are interpreted as follows: /// /// * Bits 24-31 are the alpha value. /// * Bits 16-23 are the red value. /// * Bits 8-15 are the green value. /// * Bits 0-7 are the blue value. /// /// In other words, if AA is the alpha value in hex, RR the red value in hex, /// GG the green value in hex, and BB the blue value in hex, a color can be /// expressed as `Color(0xAARRGGBB)`. /// /// For example, to get a fully opaque orange, you would use `const /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the /// green, and `00` for the blue). /// /// {@template dart.ui.Color.componentsStoredAsFloatingPoint} /// > [!NOTE] /// > Each color is stored as floating-point color components, where the final /// > value of each component is approximated by storing `c / 255`, where `c` /// is one of the four components (alpha, red, green, blue). /// {@endtemplate} const Color(int value) : this._fromARGBC(value >> 24, value >> 16, value >> 8, value, ColorSpace.sRGB); /// Construct a color with floating-point color components. /// /// Color components allows arbitrary bit depths for color components to be be /// supported. The values are interpreted relative to the [ColorSpace] /// argument. /// /// ## Example /// /// ```dart /// // Fully opaque maximum red color /// const Color c1 = Color.from(alpha: 1.0, red: 1.0, green: 0.0, blue: 0.0); /// /// // Partially transparent moderately blue and green color /// const Color c2 = Color.from(alpha: 0.5, red: 0.0, green: 0.5, blue: 0.5); /// /// // Fully transparent color /// const Color c3 = Color.from(alpha: 0.0, red: 0.0, green: 0.0, blue: 0.0); /// ``` const Color.from({ required double alpha, required double red, required double green, required double blue, this.colorSpace = ColorSpace.sRGB, }) : a = alpha, r = red, g = green, b = blue; /// Construct an sRGB color from the lower 8 bits of four integers. /// /// * `a` is the alpha value, with 0 being transparent and 255 being fully /// opaque. /// * `r` is [red], from 0 to 255. /// * `g` is [green], from 0 to 255. /// * `b` is [blue], from 0 to 255. /// /// Out of range values are brought into range using modulo 255. /// /// See also [fromRGBO], which takes the alpha value as a floating point /// value. /// /// {@macro dart.ui.Color.componentsStoredAsFloatingPoint} const Color.fromARGB(int a, int r, int g, int b) : this._fromARGBC(a, r, g, b, ColorSpace.sRGB); const Color._fromARGBC(int alpha, int red, int green, int blue, ColorSpace colorSpace) : this._fromRGBOC(red, green, blue, (alpha & 0xff) / 255, colorSpace); /// Create an sRGB color from red, green, blue, and opacity, similar to /// `rgba()` in CSS. /// /// * `r` is [red], from 0 to 255. /// * `g` is [green], from 0 to 255. /// * `b` is [blue], from 0 to 255. /// * `opacity` is alpha channel of this color as a double, with 0.0 being /// transparent and 1.0 being fully opaque. /// /// Out of range values are brought into range using modulo 255. /// /// See also [fromARGB], which takes the opacity as an integer value. /// /// {@macro dart.ui.Color.componentsStoredAsFloatingPoint} const Color.fromRGBO(int r, int g, int b, double opacity) : this._fromRGBOC(r, g, b, opacity, ColorSpace.sRGB); const Color._fromRGBOC(int r, int g, int b, double opacity, this.colorSpace) : a = opacity, r = (r & 0xff) / 255, g = (g & 0xff) / 255, b = (b & 0xff) / 255; /// The alpha channel of this color. final double a; /// The red channel of this color. final double r; /// The green channel of this color. final double g; /// The blue channel of this color. final double b; /// The color space of this color. final ColorSpace colorSpace; static int _floatToInt8(double x) { return (x * 255.0).round().clamp(0, 255); } /// A 32 bit value representing this color. /// /// This getter is a _stub_. It is recommended instead to use the explicit /// [toARGB32] method. @Deprecated('Use component accessors like .r or .g, or toARGB32 for an explicit conversion') int get value => toARGB32(); /// Returns a 32-bit value representing this color. /// /// The returned value is compatible with the default constructor /// ([Color.new]) but does _not_ guarantee to result in the same color due to /// [imprecisions in numeric conversions](https://en.wikipedia.org/wiki/Floating-point_error_mitigation). /// /// Unlike accessing the floating point equivalent channels individually /// ([a], [r], [g], [b]), this method is intentionally _lossy_, and scales /// each channel using `(channel * 255.0).round().clamp(0, 255)`. /// /// While useful for storing a 32-bit integer value, prefer accessing the /// individual channels (and storing the double equivalent) where higher /// precision is required. /// /// The bits are assigned as follows: /// /// * Bits 24-31 represents the [a] channel as an 8-bit unsigned integer. /// * Bits 16-23 represents the [r] channel as an 8-bit unsigned integer. /// * Bits 8-15 represents the [g] channel as an 8-bit unsigned integer. /// * Bits 0-7 represents the [b] channel as an 8-bit unsigned integer. /// /// > [!WARNING] /// > The value returned by this getter implicitly converts floating-point /// > component values (such as `0.5`) into their 8-bit equivalent by using /// > the [toARGB32] method; the returned value is not guaranteed to be stable /// > across different platforms or executions due to the complexity of /// > floating-point math. int toARGB32() { return _floatToInt8(a) << 24 | _floatToInt8(r) << 16 | _floatToInt8(g) << 8 | _floatToInt8(b) << 0; } /// The alpha channel of this color in an 8 bit value. /// /// A value of 0 means this color is fully transparent. A value of 255 means /// this color is fully opaque. @Deprecated('Use (*.a * 255.0).round().clamp(0, 255)') int get alpha => (0xff000000 & value) >> 24; /// The alpha channel of this color as a double. /// /// A value of 0.0 means this color is fully transparent. A value of 1.0 means /// this color is fully opaque. @Deprecated('Use .a.') double get opacity => alpha / 0xFF; /// The red channel of this color in an 8 bit value. @Deprecated('Use (*.r * 255.0).round().clamp(0, 255)') int get red => (0x00ff0000 & value) >> 16; /// The green channel of this color in an 8 bit value. @Deprecated('Use (*.g * 255.0).round().clamp(0, 255)') int get green => (0x0000ff00 & value) >> 8; /// The blue channel of this color in an 8 bit value. @Deprecated('Use (*.b * 255.0).round().clamp(0, 255)') int get blue => (0x000000ff & value) >> 0; /// Returns a new color with the provided components updated. /// /// Each component ([alpha], [red], [green], [blue]) represents a /// floating-point value; see [Color.from] for details and examples. /// /// If [colorSpace] is provided, and is different than the current color /// space, the component values are updated before transforming them to the /// provided [ColorSpace]. /// /// Example: /// ```dart /// import 'dart:ui'; /// /// Create a color with 50% opacity. /// Color makeTransparent(Color color) => color.withValues(alpha: 0.5); /// ``` Color withValues({ double? alpha, double? red, double? green, double? blue, ColorSpace? colorSpace, }) { Color? updatedComponents; if (alpha != null || red != null || green != null || blue != null) { updatedComponents = Color.from( alpha: alpha ?? a, red: red ?? r, green: green ?? g, blue: blue ?? b, colorSpace: this.colorSpace, ); } if (colorSpace != null && colorSpace != this.colorSpace) { final _ColorTransform transform = _getColorTransform(this.colorSpace, colorSpace); return transform.transform(updatedComponents ?? this, colorSpace); } else { return updatedComponents ?? this; } } /// Returns a new color that matches this color with the alpha channel /// replaced with `a` (which ranges from 0 to 255). /// /// Out of range values will have unexpected effects. Color withAlpha(int a) { return Color.fromARGB(a, red, green, blue); } /// Returns a new color that matches this color with the alpha channel /// replaced with the given `opacity` (which ranges from 0.0 to 1.0). /// /// Out of range values will have unexpected effects. @Deprecated('Use .withValues() to avoid precision loss.') Color withOpacity(double opacity) { assert(opacity >= 0.0 && opacity <= 1.0); return withAlpha((255.0 * opacity).round()); } /// Returns a new color that matches this color with the red channel replaced /// with `r` (which ranges from 0 to 255). /// /// Out of range values will have unexpected effects. Color withRed(int r) { return Color.fromARGB(alpha, r, green, blue); } /// Returns a new color that matches this color with the green channel /// replaced with `g` (which ranges from 0 to 255). /// /// Out of range values will have unexpected effects. Color withGreen(int g) { return Color.fromARGB(alpha, red, g, blue); } /// Returns a new color that matches this color with the blue channel replaced /// with `b` (which ranges from 0 to 255). /// /// Out of range values will have unexpected effects. Color withBlue(int b) { return Color.fromARGB(alpha, red, green, b); } // See static double _linearizeColorComponent(double component) { if (component <= 0.03928) { return component / 12.92; } return math.pow((component + 0.055) / 1.055, 2.4) as double; } /// Returns a brightness value between 0 for darkest and 1 for lightest. /// /// Represents the relative luminance of the color. This value is computationally /// expensive to calculate. /// /// See . double computeLuminance() { assert(colorSpace != ColorSpace.extendedSRGB); // See final double R = _linearizeColorComponent(r); final double G = _linearizeColorComponent(g); final double B = _linearizeColorComponent(b); return 0.2126 * R + 0.7152 * G + 0.0722 * B; } /// Linearly interpolate between two colors. /// /// This is intended to be fast but as a result may be ugly. Consider /// [HSVColor] or writing custom logic for interpolating colors. /// /// If either color is null, this function linearly interpolates from a /// transparent instance of the other color. This is usually preferable to /// interpolating from [material.Colors.transparent] (`const /// Color(0x00000000)`), which is specifically transparent _black_. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). Each channel /// will be clamped to the range 0 to 255. /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. /// /// If the two colors are in different color spaces, both are converted to /// the wider gamut color space before interpolating. The result will be in /// the wider gamut color space. For example, interpolating between an sRGB /// color and a Display P3 color will produce a Display P3 result. static Color? lerp(Color? x, Color? y, double t) { assert(x?.colorSpace != ColorSpace.extendedSRGB); assert(y?.colorSpace != ColorSpace.extendedSRGB); if (y == null) { if (x == null) { return null; } else { return _scaleAlpha(x, 1.0 - t); } } else { if (x == null) { return _scaleAlpha(y, t); } else { final Color a; final Color b; final ColorSpace resultColorSpace; if (x.colorSpace == y.colorSpace) { a = x; b = y; resultColorSpace = x.colorSpace; } else { resultColorSpace = _widerColorSpace(x.colorSpace, y.colorSpace); a = x.withValues(colorSpace: resultColorSpace); b = y.withValues(colorSpace: resultColorSpace); } return Color.from( alpha: clampDouble(_lerpDouble(a.a, b.a, t), 0, 1), red: clampDouble(_lerpDouble(a.r, b.r, t), 0, 1), green: clampDouble(_lerpDouble(a.g, b.g, t), 0, 1), blue: clampDouble(_lerpDouble(a.b, b.b, t), 0, 1), colorSpace: resultColorSpace, ); } } } /// Combine the foreground color as a transparent color over top /// of a background color, and return the resulting combined color. /// /// This uses standard alpha blending ("SRC over DST") rules to produce a /// blended color from two colors. This can be used as a performance /// enhancement when trying to avoid needless alpha blending compositing /// operations for two things that are solid colors with the same shape, but /// overlay each other: instead, just paint one with the combined color. static Color alphaBlend(Color foreground, Color background) { assert(foreground.colorSpace == background.colorSpace); assert(foreground.colorSpace != ColorSpace.extendedSRGB); final double alpha = foreground.a; if (alpha == 0) { // Foreground completely transparent. return background; } final double invAlpha = 1 - alpha; double backAlpha = background.a; if (backAlpha == 1) { // Opaque background case return Color.from( alpha: 1, red: alpha * foreground.r + invAlpha * background.r, green: alpha * foreground.g + invAlpha * background.g, blue: alpha * foreground.b + invAlpha * background.b, colorSpace: foreground.colorSpace, ); } else { // General case backAlpha = backAlpha * invAlpha; final double outAlpha = alpha + backAlpha; assert(outAlpha != 0); return Color.from( alpha: outAlpha, red: (foreground.r * alpha + background.r * backAlpha) / outAlpha, green: (foreground.g * alpha + background.g * backAlpha) / outAlpha, blue: (foreground.b * alpha + background.b * backAlpha) / outAlpha, colorSpace: foreground.colorSpace, ); } } /// Returns an alpha value representative of the provided [opacity] value. /// /// The [opacity] value may not be null. static int getAlphaFromOpacity(double opacity) { return (clampDouble(opacity, 0.0, 1.0) * 255).round(); } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is Color && other.a == a && other.r == r && other.g == g && other.b == b && other.colorSpace == colorSpace; } @override int get hashCode => Object.hash(a, r, g, b, colorSpace); @override String toString() => 'Color(alpha: ${a.toStringAsFixed(4)}, red: ${r.toStringAsFixed(4)}, green: ${g.toStringAsFixed(4)}, blue: ${b.toStringAsFixed(4)}, colorSpace: $colorSpace)'; } /// Algorithms to use when painting on the canvas. /// /// When drawing a shape or image onto a canvas, different algorithms can be /// used to blend the pixels. The different values of [BlendMode] specify /// different such algorithms. /// /// Each algorithm has two inputs, the _source_, which is the image being drawn, /// and the _destination_, which is the image into which the source image is /// being composited. The destination is often thought of as the _background_. /// The source and destination both have four color channels, the red, green, /// blue, and alpha channels. These are typically represented as numbers in the /// range 0.0 to 1.0. The output of the algorithm also has these same four /// channels, with values computed from the source and destination. /// /// The documentation of each value below describes how the algorithm works. In /// each case, an image shows the output of blending a source image with a /// destination image. In the images below, the destination is represented by an /// image with horizontal lines and an opaque landscape photograph, and the /// source is represented by an image with vertical lines (the same lines but /// rotated) and a bird clip-art image. The [src] mode shows only the source /// image, and the [dst] mode shows only the destination image. In the /// documentation below, the transparency is illustrated by a checkerboard /// pattern. The [clear] mode drops both the source and destination, resulting /// in an output that is entirely transparent (illustrated by a solid /// checkerboard pattern). /// /// The horizontal and vertical bars in these images show the red, green, and /// blue channels with varying opacity levels, then all three color channels /// together with those same varying opacity levels, then all three color /// channels set to zero with those varying opacity levels, then two bars showing /// a red/green/blue repeating gradient, the first with full opacity and the /// second with partial opacity, and finally a bar with the three color channels /// set to zero but the opacity varying in a repeating gradient. /// /// ## Application to the [Canvas] API /// /// When using [Canvas.saveLayer] and [Canvas.restore], the blend mode of the /// [Paint] given to the [Canvas.saveLayer] will be applied when /// [Canvas.restore] is called. Each call to [Canvas.saveLayer] introduces a new /// layer onto which shapes and images are painted; when [Canvas.restore] is /// called, that layer is then composited onto the parent layer, with the source /// being the most-recently-drawn shapes and images, and the destination being /// the parent layer. (For the first [Canvas.saveLayer] call, the parent layer /// is the canvas itself.) /// /// See also: /// /// * [Paint.blendMode], which uses [BlendMode] to define the compositing /// strategy. enum BlendMode { // This list comes from Skia's SkXfermode.h and the values (order) should be // kept in sync. // See: https://skia.org/docs/user/api/skpaint_overview/#SkXfermode /// Drop both the source and destination images, leaving nothing. /// /// This corresponds to the "clear" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_clear.png) clear, /// Drop the destination image, only paint the source image. /// /// Conceptually, the destination is first cleared, then the source image is /// painted. /// /// This corresponds to the "Copy" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_src.png) src, /// Drop the source image, only paint the destination image. /// /// Conceptually, the source image is discarded, leaving the destination /// untouched. /// /// This corresponds to the "Destination" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dst.png) dst, /// Composite the source image over the destination image. /// /// This is the default value. It represents the most intuitive case, where /// shapes are painted on top of what is below, with transparent areas showing /// the destination layer. /// /// This corresponds to the "Source over Destination" Porter-Duff operator, /// also known as the Painter's Algorithm. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOver.png) srcOver, /// Composite the source image under the destination image. /// /// This is the opposite of [srcOver]. /// /// This corresponds to the "Destination over Source" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOver.png) /// /// This is useful when the source image should have been painted before the /// destination image, but could not be. dstOver, /// Show the source image, but only where the two images overlap. The /// destination image is not rendered, it is treated merely as a mask. The /// color channels of the destination are ignored, only the opacity has an /// effect. /// /// To show the destination image instead, consider [dstIn]. /// /// To reverse the semantic of the mask (only showing the source where the /// destination is absent, rather than where it is present), consider /// [srcOut]. /// /// This corresponds to the "Source in Destination" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcIn.png) srcIn, /// Show the destination image, but only where the two images overlap. The /// source image is not rendered, it is treated merely as a mask. The color /// channels of the source are ignored, only the opacity has an effect. /// /// To show the source image instead, consider [srcIn]. /// /// To reverse the semantic of the mask (only showing the source where the /// destination is present, rather than where it is absent), consider [dstOut]. /// /// This corresponds to the "Destination in Source" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstIn.png) dstIn, /// Show the source image, but only where the two images do not overlap. The /// destination image is not rendered, it is treated merely as a mask. The color /// channels of the destination are ignored, only the opacity has an effect. /// /// To show the destination image instead, consider [dstOut]. /// /// To reverse the semantic of the mask (only showing the source where the /// destination is present, rather than where it is absent), consider [srcIn]. /// /// This corresponds to the "Source out Destination" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOut.png) srcOut, /// Show the destination image, but only where the two images do not overlap. The /// source image is not rendered, it is treated merely as a mask. The color /// channels of the source are ignored, only the opacity has an effect. /// /// To show the source image instead, consider [srcOut]. /// /// To reverse the semantic of the mask (only showing the destination where the /// source is present, rather than where it is absent), consider [dstIn]. /// /// This corresponds to the "Destination out Source" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOut.png) dstOut, /// Composite the source image over the destination image, but only where it /// overlaps the destination. /// /// This corresponds to the "Source atop Destination" Porter-Duff operator. /// /// This is essentially the [srcOver] operator, but with the output's opacity /// channel being set to that of the destination image instead of being a /// combination of both image's opacity channels. /// /// For a variant with the destination on top instead of the source, see /// [dstATop]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcATop.png) srcATop, /// Composite the destination image over the source image, but only where it /// overlaps the source. /// /// This corresponds to the "Destination atop Source" Porter-Duff operator. /// /// This is essentially the [dstOver] operator, but with the output's opacity /// channel being set to that of the source image instead of being a /// combination of both image's opacity channels. /// /// For a variant with the source on top instead of the destination, see /// [srcATop]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstATop.png) dstATop, /// Apply a bitwise `xor` operator to the source and destination images. This /// leaves transparency where they would overlap. /// /// This corresponds to the "Source xor Destination" Porter-Duff operator. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_xor.png) xor, /// Sum the components of the source and destination images. /// /// Transparency in a pixel of one of the images reduces the contribution of /// that image to the corresponding output pixel, as if the color of that /// pixel in that image was darker. /// /// This corresponds to the "Source plus Destination" Porter-Duff operator. /// /// This is the right blend mode for cross-fading between two images. Consider /// two images A and B, and an interpolation time variable _t_ (from 0.0 to /// 1.0). To cross fade between them, A should be drawn with opacity 1.0 - _t_ /// into a new layer using [BlendMode.srcOver], and B should be drawn on top /// of it, at opacity _t_, into the same layer, using [BlendMode.plus]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_plus.png) plus, /// Multiply the color components of the source and destination images. /// /// This can only result in the same or darker colors (multiplying by white, /// 1.0, results in no change; multiplying by black, 0.0, results in black). /// /// When compositing two opaque images, this has similar effect to overlapping /// two transparencies on a projector. /// /// For a variant that also multiplies the alpha channel, consider [multiply]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_modulate.png) /// /// See also: /// /// * [screen], which does a similar computation but inverted. /// * [overlay], which combines [modulate] and [screen] to favor the /// destination image. /// * [hardLight], which combines [modulate] and [screen] to favor the /// source image. modulate, // Following blend modes are defined in the CSS Compositing standard. /// Multiply the inverse of the components of the source and destination /// images, and inverse the result. /// /// Inverting the components means that a fully saturated channel (opaque /// white) is treated as the value 0.0, and values normally treated as 0.0 /// (black, transparent) are treated as 1.0. /// /// This is essentially the same as [modulate] blend mode, but with the values /// of the colors inverted before the multiplication and the result being /// inverted back before rendering. /// /// This can only result in the same or lighter colors (multiplying by black, /// 1.0, results in no change; multiplying by white, 0.0, results in white). /// Similarly, in the alpha channel, it can only result in more opaque colors. /// /// This has similar effect to two projectors displaying their images on the /// same screen simultaneously. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_screen.png) /// /// See also: /// /// * [modulate], which does a similar computation but without inverting the /// values. /// * [overlay], which combines [modulate] and [screen] to favor the /// destination image. /// * [hardLight], which combines [modulate] and [screen] to favor the /// source image. screen, // The last coeff mode. /// Multiply the components of the source and destination images after /// adjusting them to favor the destination. /// /// Specifically, if the destination value is smaller, this multiplies it with /// the source value, whereas is the source value is smaller, it multiplies /// the inverse of the source value with the inverse of the destination value, /// then inverts the result. /// /// Inverting the components means that a fully saturated channel (opaque /// white) is treated as the value 0.0, and values normally treated as 0.0 /// (black, transparent) are treated as 1.0. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_overlay.png) /// /// See also: /// /// * [modulate], which always multiplies the values. /// * [screen], which always multiplies the inverses of the values. /// * [hardLight], which is similar to [overlay] but favors the source image /// instead of the destination image. overlay, /// Composite the source and destination image by choosing the lowest value /// from each color channel. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_darken.png) darken, /// Composite the source and destination image by choosing the highest value /// from each color channel. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_lighten.png) lighten, /// Divide the destination by the inverse of the source. /// /// Inverting the components means that a fully saturated channel (opaque /// white) is treated as the value 0.0, and values normally treated as 0.0 /// (black, transparent) are treated as 1.0. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorDodge.png) colorDodge, /// Divide the inverse of the destination by the source, and inverse the result. /// /// Inverting the components means that a fully saturated channel (opaque /// white) is treated as the value 0.0, and values normally treated as 0.0 /// (black, transparent) are treated as 1.0. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorBurn.png) colorBurn, /// Multiply the components of the source and destination images after /// adjusting them to favor the source. /// /// Specifically, if the source value is smaller, this multiplies it with the /// destination value, whereas is the destination value is smaller, it /// multiplies the inverse of the destination value with the inverse of the /// source value, then inverts the result. /// /// Inverting the components means that a fully saturated channel (opaque /// white) is treated as the value 0.0, and values normally treated as 0.0 /// (black, transparent) are treated as 1.0. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hardLight.png) /// /// See also: /// /// * [modulate], which always multiplies the values. /// * [screen], which always multiplies the inverses of the values. /// * [overlay], which is similar to [hardLight] but favors the destination /// image instead of the source image. hardLight, /// Use [colorDodge] for source values below 0.5 and [colorBurn] for source /// values above 0.5. /// /// This results in a similar but softer effect than [overlay]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_softLight.png) /// /// See also: /// /// * [color], which is a more subtle tinting effect. softLight, /// Subtract the smaller value from the bigger value for each channel. /// /// Compositing black has no effect; compositing white inverts the colors of /// the other image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. /// /// The effect is similar to [exclusion] but harsher. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_difference.png) difference, /// Subtract double the product of the two images from the sum of the two /// images. /// /// Compositing black has no effect; compositing white inverts the colors of /// the other image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. /// /// The effect is similar to [difference] but softer. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_exclusion.png) exclusion, /// Multiply the components of the source and destination images, including /// the alpha channel. /// /// This can only result in the same or darker colors (multiplying by white, /// 1.0, results in no change; multiplying by black, 0.0, results in black). /// /// Since the alpha channel is also multiplied, a fully-transparent pixel /// (opacity 0.0) in one image results in a fully transparent pixel in the /// output. This is similar to [dstIn], but with the colors combined. /// /// For a variant that multiplies the colors but does not multiply the alpha /// channel, consider [modulate]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_multiply.png) multiply, // The last separable mode. /// Take the hue of the source image, and the saturation and luminosity of the /// destination image. /// /// The effect is to tint the destination image with the source image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. Regions that are entirely transparent in the source image take /// their hue from the destination. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hue.png) /// /// See also: /// /// * [color], which is a similar but stronger effect as it also applies the /// saturation of the source image. /// * [HSVColor], which allows colors to be expressed using Hue rather than /// the red/green/blue channels of [Color]. hue, /// Take the saturation of the source image, and the hue and luminosity of the /// destination image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. Regions that are entirely transparent in the source image take /// their saturation from the destination. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_saturation.png) /// /// See also: /// /// * [color], which also applies the hue of the source image. /// * [luminosity], which applies the luminosity of the source image to the /// destination. saturation, /// Take the hue and saturation of the source image, and the luminosity of the /// destination image. /// /// The effect is to tint the destination image with the source image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. Regions that are entirely transparent in the source image take /// their hue and saturation from the destination. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_color.png) /// /// See also: /// /// * [hue], which is a similar but weaker effect. /// * [softLight], which is a similar tinting effect but also tints white. /// * [saturation], which only applies the saturation of the source image. color, /// Take the luminosity of the source image, and the hue and saturation of the /// destination image. /// /// The opacity of the output image is computed in the same way as for /// [srcOver]. Regions that are entirely transparent in the source image take /// their luminosity from the destination. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_luminosity.png) /// /// See also: /// /// * [saturation], which applies the saturation of the source image to the /// destination. /// * [ImageFilter.blur], which can be used with [BackdropFilter] for a /// related effect. luminosity, } /// Quality levels for image sampling in [ImageFilter] and [Shader] objects that sample /// images and for [Canvas] operations that render images. /// /// When scaling up typically the quality is lowest at [none], higher at [low] and [medium], /// and for very large scale factors (over 10x) the highest at [high]. /// /// When scaling down, [medium] provides the best quality especially when scaling an /// image to less than half its size or for animating the scale factor between such /// reductions. Otherwise, [low] and [high] provide similar effects for reductions of /// between 50% and 100% but the image may lose detail and have dropouts below 50%. /// /// To get high quality when scaling images up and down, or when the scale is /// unknown, [medium] is typically a good balanced choice. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/filter_quality.png) /// /// When building for the web using the `--web-renderer=html` option, filter /// quality has no effect. All images are rendered using the respective /// browser's default setting. /// /// See also: /// /// * [Paint.filterQuality], which is used to pass [FilterQuality] to the /// engine while using drawImage calls on a [Canvas]. /// * [ImageShader]. /// * [FragmentShader.setImageSampler]. /// * [ImageFilter.matrix]. /// * [Canvas.drawImage]. /// * [Canvas.drawImageRect]. /// * [Canvas.drawImageNine]. /// * [Canvas.drawAtlas]. enum FilterQuality { // This list and the values (order) should be kept in sync with the equivalent list // in lib/ui/painting/image_filter.cc /// The fastest filtering method, albeit also the lowest quality. /// /// This value results in a "Nearest Neighbor" algorithm which just /// repeats or eliminates pixels as an image is scaled up or down. none, /// Better quality than [none], faster than [medium]. /// /// This value results in a "Bilinear" algorithm which smoothly /// interpolates between pixels in an image. low, /// The best all around filtering method that is only worse than [high] /// at extremely large scale factors. /// /// This value improves upon the "Bilinear" algorithm specified by [low] /// by utilizing a Mipmap that pre-computes high quality lower resolutions /// of the image at half (and quarter and eighth, etc.) sizes and then /// blends between those to prevent loss of detail at small scale sizes. /// /// {@template dart.ui.filterQuality.seeAlso} /// See also: /// /// * [FilterQuality] class-level documentation that goes into detail about /// relative qualities of the constant values. /// {@endtemplate} medium, /// Best possible quality when scaling up images by scale factors larger than /// 5-10x. /// /// When images are scaled down, this can be worse than [medium] for scales /// smaller than 0.5x, or when animating the scale factor. /// /// This option is also the slowest. /// /// This value results in a standard "Bicubic" algorithm which uses a 3rd order /// equation to smooth the abrupt transitions between pixels while preserving /// some of the sense of an edge and avoiding sharp peaks in the result. /// /// {@macro dart.ui.filterQuality.seeAlso} high, } /// Styles to use for line endings. /// /// See also: /// /// * [Paint.strokeCap] for how this value is used. /// * [StrokeJoin] for the different kinds of line segment joins. // These enum values must be kept in sync with DlStrokeCap. enum StrokeCap { /// Begin and end contours with a flat edge and no extension. /// /// ![A butt cap ends line segments with a square end that stops at the end of /// the line segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/butt_cap.png) /// /// Compare to the [square] cap, which has the same shape, but extends past /// the end of the line by half a stroke width. butt, /// Begin and end contours with a semi-circle extension. /// /// ![A round cap adds a rounded end to the line segment that protrudes /// by one half of the thickness of the line (which is the radius of the cap) /// past the end of the segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_cap.png) /// /// The cap is colored in the diagram above to highlight it: in normal use it /// is the same color as the line. round, /// Begin and end contours with a half square extension. This is /// similar to extending each contour by half the stroke width (as /// given by [Paint.strokeWidth]). /// /// ![A square cap has a square end that effectively extends the line length /// by half of the stroke width.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/square_cap.png) /// /// The cap is colored in the diagram above to highlight it: in normal use it /// is the same color as the line. /// /// Compare to the [butt] cap, which has the same shape, but doesn't extend /// past the end of the line. square, } /// Styles to use for line segment joins. /// /// This only affects line joins for polygons drawn by [Canvas.drawPath] and /// rectangles, not points drawn as lines with [Canvas.drawPoints]. /// /// See also: /// /// * [Paint.strokeJoin] and [Paint.strokeMiterLimit] for how this value is /// used. /// * [StrokeCap] for the different kinds of line endings. // These enum values must be kept in sync with DlStrokeJoin. enum StrokeJoin { /// Joins between line segments form sharp corners. /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the /// line. /// /// See also: /// /// * [Paint.strokeJoin], used to set the line segment join style to this /// value. /// * [Paint.strokeMiterLimit], used to define when a miter is drawn instead /// of a bevel when the join is set to this value. miter, /// Joins between line segments are semi-circular. /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the /// line. /// /// See also: /// /// * [Paint.strokeJoin], used to set the line segment join style to this /// value. round, /// Joins between line segments connect the corners of the butt ends of the /// line segments to give a beveled appearance. /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the /// line. /// /// See also: /// /// * [Paint.strokeJoin], used to set the line segment join style to this /// value. bevel, } /// Strategies for painting shapes and paths on a canvas. /// /// See [Paint.style]. // These enum values must be kept in sync with DlDrawStyle. enum PaintingStyle { // This list comes from dl_paint.h and the values (order) should be kept // in sync. /// Apply the [Paint] to the inside of the shape. For example, when /// applied to the [Canvas.drawCircle] call, this results in a disc /// of the given size being painted. fill, /// Apply the [Paint] to the edge of the shape. For example, when /// applied to the [Canvas.drawCircle] call, this results is a hoop /// of the given size being painted. The line drawn on the edge will /// be the width given by the [Paint.strokeWidth] property. stroke, } /// Different ways to clip content. /// /// See also: /// /// * [Paint.isAntiAlias], the anti-aliasing switch for general draw operations. enum Clip { /// No clip at all. /// /// This is the default option for most widgets: if the content does not /// overflow the widget boundary, don't pay any performance cost for clipping. /// /// If the content does overflow, consider the following [Clip] options: /// /// * [hardEdge], which is the fastest clipping, but with lower fidelity. /// * [antiAlias], which is a little slower than [hardEdge], but with smoothed edges. /// * [antiAliasWithSaveLayer], which is much slower than [antiAlias], and should /// rarely be used. none, /// Clip, but do not apply anti-aliasing. /// /// This mode enables clipping, but curves and non-axis-aligned straight lines will be /// jagged as no effort is made to anti-alias. /// /// Faster than other clipping modes, but slower than [none]. /// /// This is a reasonable choice when clipping is needed, if the container is an axis- /// aligned rectangle or an axis-aligned rounded rectangle with very small corner radii. /// /// See also: /// /// * [antiAlias], recommended when clipping is needed and the shape is not /// an axis-aligned rectangle. hardEdge, /// Clip with anti-aliasing. /// /// This mode has anti-aliased clipping edges, which reduces jagged edges when /// the clip shape itself has edges that are diagonal, curved, or otherwise /// not axis-aligned. /// /// This is much faster than [antiAliasWithSaveLayer], but slower than [hardEdge]. /// /// Unlike [hardEdge] and [antiAliasWithSaveLayer], this clipping can have /// bleeding edge artifacts /// ([Skia Fiddle example](https://fiddle.skia.org/c/21cb4c2b2515996b537f36e7819288ae)). /// /// See also: /// /// * [hardEdge], which is faster, but with lower fidelity. /// * [antiAliasWithSaveLayer], which is much slower, but avoids bleeding /// edge artifacts. /// * [Paint.isAntiAlias], which is the anti-aliasing switch for general draw operations. antiAlias, /// Clip with anti-aliasing and `saveLayer` immediately following the clip. /// /// This mode not only clips with anti-aliasing, but also allocates an offscreen /// buffer. All subsequent paints are carried out on that buffer before finally /// being clipped and composited back. /// /// This is very slow. It has no bleeding edge artifacts, unlike [antiAlias], /// but it changes the semantics as it introduces an offscreen buffer. /// For example, see this /// [Skia Fiddle without `saveLayer`](https://fiddle.skia.org/c/83ed46ceadaf90f36a4df3b98cbe1c35) /// and this /// [Skia Fiddle with `saveLayer`](https://fiddle.skia.org/c/704acfa049a7e99fbe685232c45d1582). /// /// Use this mode only if necessary. For example, if you have an /// image overlaid on a very different background color. In these /// cases, consider if you can avoid overlaying multiple colors in one /// location (e.g. by having the background color only present where the image is /// absent). If possible, prefer [antiAlias] as it is much faster. /// /// See also: /// /// * [antiAlias], which is much faster, and has similar clipping results. /// * [Canvas.saveLayer]. antiAliasWithSaveLayer, } /// A description of the style to use when drawing on a [Canvas]. /// /// Most APIs on [Canvas] take a [Paint] object to describe the style /// to use for that operation. final class Paint { /// Constructs an empty [Paint] object with all fields initialized to /// their defaults. Paint(); /// Constructs a new [Paint] object with the same fields as [other]. /// /// Any changes made to the object returned will not affect [other], and /// changes to [other] will not affect the object returned. /// /// Backends (for example web versus native) may have different performance /// characteristics. If the code is performance-sensitive, consider profiling /// and falling back to reusing a single [Paint] object if necessary. Paint.from(Paint other) { // Every field on Paint is deeply immutable, so to create a copy of a Paint // object, we copy the underlying data buffer and the list of objects (which // are also deeply immutable). _data.buffer.asUint32List().setAll(0, other._data.buffer.asUint32List()); _objects = other._objects?.toList(); } // Paint objects are encoded in two buffers: // // * _data is binary data in four-byte fields, each of which is either a // uint32_t or a float. The default value for each field is encoded as // zero to make initialization trivial. Most values already have a default // value of zero, but some, such as color, have a non-zero default value. // To encode or decode these values, XOR the value with the default value. // // * _objects is a list of unencodable objects, typically wrappers for native // objects. The objects are simply stored in the list without any additional // encoding. // // The binary format must match the deserialization code in paint.cc. // C++ unit tests access this. @pragma('vm:entry-point') final ByteData _data = ByteData(_kDataByteCount); // Must match //lib/ui/painting/paint.cc. static const int _kIsAntiAliasIndex = 0; static const int _kColorRedIndex = 1; static const int _kColorGreenIndex = 2; static const int _kColorBlueIndex = 3; static const int _kColorAlphaIndex = 4; static const int _kColorSpaceIndex = 5; static const int _kBlendModeIndex = 6; static const int _kStyleIndex = 7; static const int _kStrokeWidthIndex = 8; static const int _kStrokeCapIndex = 9; static const int _kStrokeJoinIndex = 10; static const int _kStrokeMiterLimitIndex = 11; static const int _kFilterQualityIndex = 12; static const int _kMaskFilterIndex = 13; static const int _kMaskFilterBlurStyleIndex = 14; static const int _kMaskFilterSigmaIndex = 15; static const int _kInvertColorIndex = 16; static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2; static const int _kColorRedOffset = _kColorRedIndex << 2; static const int _kColorGreenOffset = _kColorGreenIndex << 2; static const int _kColorBlueOffset = _kColorBlueIndex << 2; static const int _kColorAlphaOffset = _kColorAlphaIndex << 2; static const int _kColorSpaceOffset = _kColorSpaceIndex << 2; static const int _kBlendModeOffset = _kBlendModeIndex << 2; static const int _kStyleOffset = _kStyleIndex << 2; static const int _kStrokeWidthOffset = _kStrokeWidthIndex << 2; static const int _kStrokeCapOffset = _kStrokeCapIndex << 2; static const int _kStrokeJoinOffset = _kStrokeJoinIndex << 2; static const int _kStrokeMiterLimitOffset = _kStrokeMiterLimitIndex << 2; static const int _kFilterQualityOffset = _kFilterQualityIndex << 2; static const int _kMaskFilterOffset = _kMaskFilterIndex << 2; static const int _kMaskFilterBlurStyleOffset = _kMaskFilterBlurStyleIndex << 2; static const int _kMaskFilterSigmaOffset = _kMaskFilterSigmaIndex << 2; static const int _kInvertColorOffset = _kInvertColorIndex << 2; // If you add more fields, remember to update _kDataByteCount. static const int _kDataByteCount = 68; // 4 * (last index + 1). // Binary format must match the deserialization code in paint.cc. // C++ unit tests access this. @pragma('vm:entry-point') List? _objects; List _ensureObjectsInitialized() { return _objects ??= List.filled(_kObjectCount, null); } static const int _kShaderIndex = 0; static const int _kColorFilterIndex = 1; static const int _kImageFilterIndex = 2; static const int _kObjectCount = 3; // Must be one larger than the largest index. /// Whether to apply anti-aliasing to lines and images drawn on the /// canvas. /// /// Defaults to true. bool get isAntiAlias { return _data.getInt32(_kIsAntiAliasOffset, _kFakeHostEndian) == 0; } set isAntiAlias(bool value) { // We encode true as zero and false as one because the default value, which // we always encode as zero, is true. final encoded = value ? 0 : 1; _data.setInt32(_kIsAntiAliasOffset, encoded, _kFakeHostEndian); } // Must be kept in sync with the default in paint.cc. static const int _kColorDefault = 0xFF000000; /// The color to use when stroking or filling a shape. /// /// Defaults to opaque black. /// /// See also: /// /// * [style], which controls whether to stroke or fill (or both). /// * [colorFilter], which overrides [color]. /// * [shader], which overrides [color] with more elaborate effects. /// /// This color is not used when compositing. To colorize a layer, use /// [colorFilter]. Color get color { final double red = _data.getFloat32(_kColorRedOffset, _kFakeHostEndian); final double green = _data.getFloat32(_kColorGreenOffset, _kFakeHostEndian); final double blue = _data.getFloat32(_kColorBlueOffset, _kFakeHostEndian); final double alpha = 1.0 - _data.getFloat32(_kColorAlphaOffset, _kFakeHostEndian); final ColorSpace colorSpace = _indexToColorSpace( _data.getInt32(_kColorSpaceOffset, _kFakeHostEndian), ); return Color.from(alpha: alpha, red: red, green: green, blue: blue, colorSpace: colorSpace); } set color(Color value) { _data.setFloat32(_kColorRedOffset, value.r, _kFakeHostEndian); _data.setFloat32(_kColorGreenOffset, value.g, _kFakeHostEndian); _data.setFloat32(_kColorBlueOffset, value.b, _kFakeHostEndian); _data.setFloat32(_kColorAlphaOffset, 1.0 - value.a, _kFakeHostEndian); _data.setInt32(_kColorSpaceOffset, _colorSpaceToIndex(value.colorSpace), _kFakeHostEndian); } // Must be kept in sync with the default in paint.cc. static final int _kBlendModeDefault = BlendMode.srcOver.index; /// A blend mode to apply when a shape is drawn or a layer is composited. /// /// The source colors are from the shape being drawn (e.g. from /// [Canvas.drawPath]) or layer being composited (the graphics that were drawn /// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying /// the [colorFilter], if any. /// /// The destination colors are from the background onto which the shape or /// layer is being composited. /// /// Defaults to [BlendMode.srcOver]. /// /// See also: /// /// * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite /// the layer when [Canvas.restore] is called. /// * [BlendMode], which discusses the user of [Canvas.saveLayer] with /// [blendMode]. BlendMode get blendMode { final int encoded = _data.getInt32(_kBlendModeOffset, _kFakeHostEndian); return BlendMode.values[encoded ^ _kBlendModeDefault]; } set blendMode(BlendMode value) { final int encoded = value.index ^ _kBlendModeDefault; _data.setInt32(_kBlendModeOffset, encoded, _kFakeHostEndian); } /// Whether to paint inside shapes, the edges of shapes, or both. /// /// Defaults to [PaintingStyle.fill]. PaintingStyle get style { return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)]; } set style(PaintingStyle value) { final int encoded = value.index; _data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian); } /// How wide to make edges drawn when [style] is set to /// [PaintingStyle.stroke]. The width is given in logical pixels measured in /// the direction orthogonal to the direction of the path. /// /// Defaults to 0.0, which correspond to a hairline width. double get strokeWidth { return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian); } set strokeWidth(double value) { final encoded = value; _data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian); } /// The kind of finish to place on the end of lines drawn when /// [style] is set to [PaintingStyle.stroke]. /// /// Defaults to [StrokeCap.butt], i.e. no caps. StrokeCap get strokeCap { return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)]; } set strokeCap(StrokeCap value) { final int encoded = value.index; _data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian); } /// The kind of finish to place on the joins between segments. /// /// This applies to paths drawn when [style] is set to [PaintingStyle.stroke], /// It does not apply to points drawn as lines with [Canvas.drawPoints]. /// /// Defaults to [StrokeJoin.miter], i.e. sharp corners. /// /// Some examples of joins: /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} /// /// The centers of the line segments are colored in the diagrams above to /// highlight the joins, but in normal usage the join is the same color as the /// line. /// /// See also: /// /// * [strokeMiterLimit] to control when miters are replaced by bevels when /// this is set to [StrokeJoin.miter]. /// * [strokeCap] to control what is drawn at the ends of the stroke. /// * [StrokeJoin] for the definitive list of stroke joins. StrokeJoin get strokeJoin { return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)]; } set strokeJoin(StrokeJoin value) { final int encoded = value.index; _data.setInt32(_kStrokeJoinOffset, encoded, _kFakeHostEndian); } // Must be kept in sync with the default in paint.cc. static const double _kStrokeMiterLimitDefault = 4.0; /// The limit for miters to be drawn on segments when the join is set to /// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If /// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn /// instead. This may cause some 'popping' of the corners of a path if the /// angle between line segments is animated, as seen in the diagrams below. /// /// This limit is expressed as a limit on the length of the miter. /// /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel] /// join to be used all the time. /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4} /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4} /// /// The centers of the line segments are colored in the diagrams above to /// highlight the joins, but in normal usage the join is the same color as the /// line. /// /// See also: /// /// * [strokeJoin] to control the kind of finish to place on the joins /// between segments. /// * [strokeCap] to control what is drawn at the ends of the stroke. double get strokeMiterLimit { return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian); } set strokeMiterLimit(double value) { final double encoded = value - _kStrokeMiterLimitDefault; _data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian); } /// A mask filter (for example, a blur) to apply to a shape after it has been /// drawn but before it has been composited into the image. /// /// See [MaskFilter] for details. MaskFilter? get maskFilter { switch (_data.getInt32(_kMaskFilterOffset, _kFakeHostEndian)) { case MaskFilter._TypeNone: return null; case MaskFilter._TypeBlur: return MaskFilter.blur( BlurStyle.values[_data.getInt32(_kMaskFilterBlurStyleOffset, _kFakeHostEndian)], _data.getFloat32(_kMaskFilterSigmaOffset, _kFakeHostEndian), ); } return null; } set maskFilter(MaskFilter? value) { if (value == null) { _data.setInt32(_kMaskFilterOffset, MaskFilter._TypeNone, _kFakeHostEndian); _data.setInt32(_kMaskFilterBlurStyleOffset, 0, _kFakeHostEndian); _data.setFloat32(_kMaskFilterSigmaOffset, 0.0, _kFakeHostEndian); } else { // For now we only support one kind of MaskFilter, so we don't need to // check what the type is if it's not null. _data.setInt32(_kMaskFilterOffset, MaskFilter._TypeBlur, _kFakeHostEndian); _data.setInt32(_kMaskFilterBlurStyleOffset, value._style.index, _kFakeHostEndian); _data.setFloat32(_kMaskFilterSigmaOffset, value._sigma, _kFakeHostEndian); } } /// Controls the performance vs quality trade-off to use when sampling bitmaps, /// as with an [ImageShader], or when drawing images, as with [Canvas.drawImage], /// [Canvas.drawImageRect], [Canvas.drawImageNine] or [Canvas.drawAtlas]. /// /// Defaults to [FilterQuality.none]. // TODO(ianh): verify that the image drawing methods actually respect this FilterQuality get filterQuality { return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)]; } set filterQuality(FilterQuality value) { final int encoded = value.index; _data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian); } /// The shader to use when stroking or filling a shape. /// /// When this is null, the [color] is used instead. /// /// See also: /// /// * [Gradient], a shader that paints a color gradient. /// * [ImageShader], a shader that tiles an [Image]. /// * [colorFilter], which overrides [shader]. /// * [color], which is used if [shader] and [colorFilter] are null. Shader? get shader { return _objects?[_kShaderIndex] as Shader?; } set shader(Shader? value) { assert(() { assert(value == null || !value.debugDisposed, 'Attempted to set a disposed shader to $this'); return true; }()); assert(() { if (value is FragmentShader) { if (!value._validateSamplers()) { throw Exception('Invalid FragmentShader ${value._debugName ?? ''}: missing sampler'); } } return true; }()); _ensureObjectsInitialized()[_kShaderIndex] = value; } /// A color filter to apply when a shape is drawn or when a layer is /// composited. /// /// See [ColorFilter] for details. /// /// When a shape is being drawn, [colorFilter] overrides [color] and [shader]. ColorFilter? get colorFilter { final nativeFilter = _objects?[_kColorFilterIndex] as _ColorFilter?; return nativeFilter?.creator; } set colorFilter(ColorFilter? value) { final _ColorFilter? nativeFilter = value?._toNativeColorFilter(); if (nativeFilter == null) { if (_objects != null) { _objects![_kColorFilterIndex] = null; } } else { _ensureObjectsInitialized()[_kColorFilterIndex] = nativeFilter; } } /// The [ImageFilter] to use when drawing raster images. /// /// For example, to blur an image using [Canvas.drawImage], apply an /// [ImageFilter.blur]: /// /// ```dart /// void paint(Canvas canvas, Size size) { /// canvas.drawImage( /// _image, /// ui.Offset.zero, /// Paint()..imageFilter = ui.ImageFilter.blur(sigmaX: 0.5, sigmaY: 0.5), /// ); /// } /// ``` /// /// See also: /// /// * [MaskFilter], which is used for drawing geometry. ImageFilter? get imageFilter { final nativeFilter = _objects?[_kImageFilterIndex] as _ImageFilter?; return nativeFilter?.creator; } set imageFilter(ImageFilter? value) { if (value == null) { if (_objects != null) { _objects![_kImageFilterIndex] = null; } } else { final List objects = _ensureObjectsInitialized(); final imageFilter = objects[_kImageFilterIndex] as _ImageFilter?; if (imageFilter?.creator != value) { objects[_kImageFilterIndex] = value._toNativeImageFilter(); } } } /// Whether the colors of the image are inverted when drawn. /// /// Inverting the colors of an image applies a new color filter that will /// be composed with any user provided color filters. This is primarily /// used for implementing smart invert on iOS. bool get invertColors { return _data.getInt32(_kInvertColorOffset, _kFakeHostEndian) == 1; } set invertColors(bool value) { _data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian); } @override String toString() { if (const bool.fromEnvironment('dart.vm.product')) { return super.toString(); } final result = StringBuffer(); var semicolon = ''; result.write('Paint('); if (style == PaintingStyle.stroke) { result.write('$style'); if (strokeWidth != 0.0) { result.write(' ${strokeWidth.toStringAsFixed(1)}'); } else { result.write(' hairline'); } if (strokeCap != StrokeCap.butt) { result.write(' $strokeCap'); } if (strokeJoin == StrokeJoin.miter) { if (strokeMiterLimit != _kStrokeMiterLimitDefault) { result.write(' $strokeJoin up to ${strokeMiterLimit.toStringAsFixed(1)}'); } } else { result.write(' $strokeJoin'); } semicolon = '; '; } if (!isAntiAlias) { result.write('${semicolon}antialias off'); semicolon = '; '; } if (color != const Color(_kColorDefault)) { result.write('$semicolon$color'); semicolon = '; '; } if (blendMode.index != _kBlendModeDefault) { result.write('$semicolon$blendMode'); semicolon = '; '; } if (colorFilter != null) { result.write('${semicolon}colorFilter: $colorFilter'); semicolon = '; '; } if (maskFilter != null) { result.write('${semicolon}maskFilter: $maskFilter'); semicolon = '; '; } if (filterQuality != FilterQuality.none) { result.write('${semicolon}filterQuality: $filterQuality'); semicolon = '; '; } if (shader != null) { result.write('${semicolon}shader: $shader'); semicolon = '; '; } if (imageFilter != null) { result.write('${semicolon}imageFilter: $imageFilter'); semicolon = '; '; } if (invertColors) { result.write('${semicolon}invert: $invertColors'); } result.write(')'); return result.toString(); } } /// The color space describes the colors that are available to an [Image]. /// /// This value can help decide which [ImageByteFormat] to use with /// [Image.toByteData]. Images that are in the [extendedSRGB] color space /// should use something like [ImageByteFormat.rawExtendedRgba128] so that /// colors outside of the sRGB gamut aren't lost. /// /// This is also the result of [Image.colorSpace]. /// /// See also: https://en.wikipedia.org/wiki/Color_space enum ColorSpace { /// The sRGB color space. /// /// You may know this as the standard color space for the web or the color /// space of non-wide-gamut Flutter apps. /// /// See also: https://en.wikipedia.org/wiki/SRGB sRGB, /// A color space that is backwards compatible with sRGB but can represent /// colors outside of that gamut with values outside of [0..1]. In order to /// see the extended values an [ImageByteFormat] like /// [ImageByteFormat.rawExtendedRgba128] must be used. extendedSRGB, /// The Display P3 color space. /// /// This is a wide gamut color space that has broad hardware support. It's /// supported in cases like using Impeller on iOS. When used on a platform /// that doesn't support Display P3, the colors will be clamped to sRGB. /// /// See also: https://en.wikipedia.org/wiki/DCI-P3 displayP3, } int _colorSpaceToIndex(ColorSpace colorSpace) { switch (colorSpace) { case ColorSpace.sRGB: return 0; case ColorSpace.extendedSRGB: return 1; case ColorSpace.displayP3: return 2; } } ColorSpace _indexToColorSpace(int index) { switch (index) { case 0: return ColorSpace.sRGB; case 1: return ColorSpace.extendedSRGB; case 2: return ColorSpace.displayP3; default: throw ArgumentError('Unknown color space: $index'); } } /// The format in which image bytes should be returned when using /// [Image.toByteData]. // We do not expect to add more encoding formats to the ImageByteFormat enum, // considering the binary size of the engine after LTO optimization. You can // use the third-party pure dart image library to encode other formats. // See: https://github.com/flutter/flutter/issues/16635 for more details. enum ImageByteFormat { /// Raw RGBA format. /// /// Unencoded bytes, in RGBA row-primary form with premultiplied alpha, 8 bits per channel. rawRgba, /// Raw straight RGBA format. /// /// Unencoded bytes, in RGBA row-primary form with straight alpha, 8 bits per channel. rawStraightRgba, /// Raw unmodified format. /// /// Unencoded bytes, in the image's existing format. For example, a grayscale /// image may use a single 8-bit channel for each pixel. rawUnmodified, /// Raw extended range RGBA format. /// /// Unencoded bytes, in RGBA row-primary form with straight alpha, 32 bit /// float (IEEE 754 binary32) per channel. /// /// Example usage: /// /// ```dart /// import 'dart:ui' as ui; /// import 'dart:typed_data'; /// /// Future> getFirstPixel(ui.Image image) async { /// final ByteData data = /// (await image.toByteData(format: ui.ImageByteFormat.rawExtendedRgba128))!; /// final Float32List floats = Float32List.view(data.buffer); /// return { /// 'r': floats[0], /// 'g': floats[1], /// 'b': floats[2], /// 'a': floats[3], /// }; /// } /// ``` rawExtendedRgba128, /// PNG format. /// /// A loss-less compression format for images. This format is well suited for /// images with hard edges, such as screenshots or sprites, and images with /// text. Transparency is supported. The PNG format supports images up to /// 2,147,483,647 pixels in either dimension, though in practice available /// memory provides a more immediate limitation on maximum image size. /// /// PNG images normally use the `.png` file extension and the `image/png` MIME /// type. /// /// See also: /// /// * , the Wikipedia page on PNG. /// * , the PNG standard. png, } /// The format of pixel data given to [decodeImageFromPixels]. enum PixelFormat { /// Each pixel is 32 bits, with the highest 8 bits encoding red, the next 8 /// bits encoding green, the next 8 bits encoding blue, and the lowest 8 bits /// encoding alpha. Premultiplied alpha is used. rgba8888, /// Each pixel is 32 bits, with the highest 8 bits encoding blue, the next 8 /// bits encoding green, the next 8 bits encoding red, and the lowest 8 bits /// encoding alpha. Premultiplied alpha is used. bgra8888, /// Each pixel is 128 bits, where each color component is a 32 bit float that /// is normalized across the sRGB gamut. The first float is the red /// component, followed by: green, blue and alpha. Premultiplied alpha isn't /// used, matching [ImageByteFormat.rawExtendedRgba128]. rgbaFloat32, /// Each pixel is 32 bits, the red channel is just one 32 bit float. rFloat32, } /// The format of pixel data of the texture generated by /// [decodeImageFromPixels]. enum TargetPixelFormat { /// Unspecified pixel format, let the engine decide the best pixel format. dontCare, /// Each pixel is 128 bits, where each color component is a 32 bit float. rgbaFloat32, /// Each pixel is 32 bits, the red channel is just one 32 bit float. rFloat32, } /// Signature for [Image] lifecycle events. typedef ImageEventCallback = void Function(Image image); /// Opaque handle to raw decoded image data (pixels). /// /// To obtain an [Image] object, use the [ImageDescriptor] API. /// /// To draw an [Image], use one of the methods on the [Canvas] class, such as /// [Canvas.drawImage]. /// /// A class or method that receives an image object must call [dispose] on the /// handle when it is no longer needed. To create a shareable reference to the /// underlying image, call [clone]. The method or object that receives /// the new instance will then be responsible for disposing it, and the /// underlying image itself will be disposed when all outstanding handles are /// disposed. /// /// If `dart:ui` passes an `Image` object and the recipient wishes to share /// that handle with other callers, [clone] must be called _before_ [dispose]. /// A handle that has been disposed cannot create new handles anymore. /// /// See also: /// /// * [Image](https://api.flutter.dev/flutter/widgets/Image-class.html), the class in the [widgets] library. /// * [ImageDescriptor], which allows reading information about the image and /// creating a codec to decode it. /// * [instantiateImageCodec], a utility method that wraps [ImageDescriptor]. class Image { Image._(this._image, this.width, this.height) { assert(() { _debugStack = StackTrace.current; return true; }()); _image._handles.add(this); onCreate?.call(this); } // C++ unit tests access this. @pragma('vm:entry-point') final _Image _image; /// A callback that is invoked to report an image creation. /// /// It's preferred to use [MemoryAllocations] in flutter/foundation.dart /// than to use [onCreate] directly because [MemoryAllocations] /// allows multiple callbacks. static ImageEventCallback? onCreate; /// A callback that is invoked to report the image disposal. /// /// It's preferred to use [MemoryAllocations] in flutter/foundation.dart /// than to use [onDispose] directly because [MemoryAllocations] /// allows multiple callbacks. static ImageEventCallback? onDispose; StackTrace? _debugStack; /// The number of image pixels along the image's horizontal axis. final int width; /// The number of image pixels along the image's vertical axis. final int height; bool _disposed = false; /// Release this handle's claim on the underlying Image. This handle is no /// longer usable after this method is called. /// /// Once all outstanding handles have been disposed, the underlying image will /// be disposed as well. /// /// In debug mode, [debugGetOpenHandleStackTraces] will return a list of /// [StackTrace] objects from all open handles' creation points. This is /// useful when trying to determine what parts of the program are keeping an /// image resident in memory. void dispose() { onDispose?.call(this); assert(!_disposed && !_image._disposed); assert(_image._handles.contains(this)); _disposed = true; final bool removed = _image._handles.remove(this); assert(removed); if (_image._handles.isEmpty) { _image.dispose(); } } /// Whether this reference to the underlying image is [dispose]d. /// /// This only returns a valid value if asserts are enabled, and must not be /// used otherwise. bool get debugDisposed { bool? disposed; assert(() { disposed = _disposed; return true; }()); return disposed ?? (throw StateError('Image.debugDisposed is only available when asserts are enabled.')); } /// Converts the [Image] object into a byte array. /// /// The [format] argument specifies the format in which the bytes will be /// returned. /// /// Using [ImageByteFormat.rawRgba] on an image in the color space /// [ColorSpace.extendedSRGB] will result in the gamut being squished to fit /// into the sRGB gamut, resulting in the loss of wide-gamut colors. /// /// Returns a future that completes with the binary image data or an error /// if encoding fails. // We do not expect to add more encoding formats to the ImageByteFormat enum, // considering the binary size of the engine after LTO optimization. You can // use the third-party pure dart image library to encode other formats. // See: https://github.com/flutter/flutter/issues/16635 for more details. Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) { assert(!_disposed && !_image._disposed); return _image.toByteData(format: format); } /// The color space that is used by the [Image]'s colors. /// /// This value is a consequence of how the [Image] has been created. For /// example, loading a PNG that is in the Display P3 color space will result /// in a [ColorSpace.extendedSRGB] image. /// /// On rendering backends that don't support wide gamut colors (anything but /// iOS impeller), wide gamut images will still report [ColorSpace.sRGB] if /// rendering wide gamut colors isn't supported. // Note: The docstring will become outdated as new platforms support wide // gamut color, please keep it up to date. ColorSpace get colorSpace { final int colorSpaceValue = _image.colorSpace; switch (colorSpaceValue) { case 0: return ColorSpace.sRGB; case 1: return ColorSpace.extendedSRGB; default: throw UnsupportedError('Unrecognized color space: $colorSpaceValue'); } } /// If asserts are enabled, returns the [StackTrace]s of each open handle from /// [clone], in creation order. /// /// If asserts are disabled, this method always returns null. List? debugGetOpenHandleStackTraces() { List? stacks; assert(() { stacks = _image._handles.map((Image handle) => handle._debugStack!).toList(); return true; }()); return stacks; } /// Creates a disposable handle to this image. /// /// Holders of an [Image] must dispose of the image when they no longer need /// to access it or draw it. However, once the underlying image is disposed, /// it is no longer possible to use it. If a holder of an image needs to share /// access to that image with another object or method, [clone] creates a /// duplicate handle. The underlying image will only be disposed once all /// outstanding handles are disposed. This allows for safe sharing of image /// references while still disposing of the underlying resources when all /// consumers are finished. /// /// It is safe to pass an [Image] handle to another object or method if the /// current holder no longer needs it. /// /// To check whether two [Image] references are referring to the same /// underlying image memory, use [isCloneOf] rather than the equality operator /// or [identical]. /// /// The following example demonstrates valid usage. /// /// ```dart /// import 'dart:async'; /// import 'dart:typed_data'; /// import 'dart:ui'; /// /// Future _loadImage(int width, int height) { /// final Completer completer = Completer(); /// decodeImageFromPixels( /// Uint8List.fromList(List.filled(width * height * 4, 0xFF)), /// width, /// height, /// PixelFormat.rgba8888, /// // Don't worry about disposing or cloning this image - responsibility /// // is transferred to the caller, and that is safe since this method /// // will not touch it again. /// (Image image) => completer.complete(image), /// ); /// return completer.future; /// } /// /// Future main() async { /// final Image image = await _loadImage(5, 5); /// // Make sure to clone the image, because MyHolder might dispose it /// // and we need to access it again. /// final MyImageHolder holder = MyImageHolder(image.clone()); /// final MyImageHolder holder2 = MyImageHolder(image.clone()); /// // Now we dispose it because we won't need it again. /// image.dispose(); /// /// final PictureRecorder recorder = PictureRecorder(); /// final Canvas canvas = Canvas(recorder); /// /// holder.draw(canvas); /// holder.dispose(); /// /// canvas.translate(50, 50); /// holder2.draw(canvas); /// holder2.dispose(); /// } /// /// class MyImageHolder { /// MyImageHolder(this.image); /// /// final Image image; /// /// void draw(Canvas canvas) { /// canvas.drawImage(image, Offset.zero, Paint()); /// } /// /// void dispose() => image.dispose(); /// } /// ``` /// /// The returned object behaves identically to this image. Calling /// [dispose] on it will only dispose the underlying native resources if it /// is the last remaining handle. Image clone() { if (_disposed) { throw StateError( 'Cannot clone a disposed image.\n' 'The clone() method of a previously-disposed Image was called. Once an ' 'Image object has been disposed, it can no longer be used to create ' 'handles, as the underlying data may have been released.', ); } assert(!_image._disposed); return Image._(_image, width, height); } /// Returns true if `other` is a [clone] of this and thus shares the same /// underlying image memory, even if this or `other` is [dispose]d. /// /// This method may return false for two images that were decoded from the /// same underlying asset, if they are not sharing the same memory. For /// example, if the same file is decoded using [instantiateImageCodec] twice, /// or the same bytes are decoded using [decodeImageFromPixels] twice, there /// will be two distinct [Image]s that render the same but do not share /// underlying memory, and so will not be treated as clones of each other. bool isCloneOf(Image other) => other._image == _image; @override String toString() => _image.toString(); } @pragma('vm:entry-point') base class _Image extends NativeFieldWrapperClass1 { // This class is created by the engine, and should not be instantiated // or extended directly. // // _Images are always handed out wrapped in [Image]s. To create an [Image], // use the ImageDescriptor API. @pragma('vm:entry-point') _Image._(); @Native)>(symbol: 'Image::width', isLeaf: true) external int get width; @Native)>(symbol: 'Image::height', isLeaf: true) external int get height; Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) { return _futurizeWithError((_CallbackWithError callback) { return _toByteData(format.index, (Uint8List? encoded, String? error) { if (error == null && encoded != null) { callback(encoded.buffer.asByteData(), null); } else { callback(null, error); } }); }); } /// Returns an error message on failure, null on success. @Native, Int32, Handle)>(symbol: 'Image::toByteData') external String? _toByteData(int format, void Function(Uint8List?, String?) callback); bool _disposed = false; void dispose() { assert(!_disposed); assert( _handles.isEmpty, 'Attempted to dispose of an Image object that has ${_handles.length} ' 'open handles.\n' 'If you see this, it is a bug in dart:ui. Please file an issue at ' 'https://github.com/flutter/flutter/issues/new.', ); _disposed = true; _dispose(); } /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'Image::dispose') external void _dispose(); final Set _handles = {}; @Native)>(symbol: 'Image::colorSpace') external int get colorSpace; @override String toString() => '[$width\u00D7$height]'; } @pragma('vm:entry-point') Image _wrapImage(_Image image) { return Image._(image, image.width, image.height); } /// Callback signature for [decodeImageFromList]. typedef ImageDecoderCallback = void Function(Image result); /// Information for a single frame of an animation. /// /// To obtain an instance of the [FrameInfo] interface, see /// [Codec.getNextFrame]. /// /// The recipient of an instance of this class is responsible for calling /// [Image.dispose] on [image]. To share the image with other interested /// parties, use [Image.clone]. If the [FrameInfo] object itself is passed to /// another method or object, that method or object must assume it is /// responsible for disposing the image when done, and the passer must not /// access the [image] after that point. /// /// For example, the following code sample is incorrect: /// /// ```dart /// /// BAD /// Future nextFrameRoutine(ui.Codec codec) async { /// final ui.FrameInfo frameInfo = await codec.getNextFrame(); /// _cacheImage(frameInfo); /// // ERROR - _cacheImage is now responsible for disposing the image, and /// // the image may not be available any more for this drawing routine. /// _drawImage(frameInfo); /// // ERROR again - the previous methods might or might not have created /// // handles to the image. /// frameInfo.image.dispose(); /// } /// ``` /// /// Correct usage is: /// /// ```dart /// /// GOOD /// Future nextFrameRoutine(ui.Codec codec) async { /// final ui.FrameInfo frameInfo = await codec.getNextFrame(); /// _cacheImage(frameInfo.image.clone(), frameInfo.duration); /// _drawImage(frameInfo.image.clone(), frameInfo.duration); /// // This method is done with its handle, and has passed handles to its /// // clients already. /// // The image will live until those clients dispose of their handles, and /// // this one must not be disposed since it will not be used again. /// frameInfo.image.dispose(); /// } /// ``` class FrameInfo { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To obtain an instance of the [FrameInfo] interface, see /// [Codec.getNextFrame]. FrameInfo._({required this.duration, required this.image}); /// The duration this frame should be shown. /// /// A zero duration indicates that the frame should be shown indefinitely. final Duration duration; /// The [Image] object for this frame. /// /// This object must be disposed by the recipient of this frame info. /// /// To share this image with other interested parties, use [Image.clone]. final Image image; } /// A handle to an image codec. /// /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To obtain an instance of the [Codec] interface, see /// [instantiateImageCodec]. abstract class Codec { /// Number of frames in this image. int get frameCount; /// Number of times to repeat the animation. /// /// * 0 when the animation should be played once. /// * -1 for infinity repetitions. int get repetitionCount; /// Fetches the next animation frame. /// /// Wraps back to the first frame after returning the last frame. /// /// The returned future can complete with an error if the decoding has failed. /// /// The caller of this method is responsible for disposing the /// [FrameInfo.image] on the returned object. Future getNextFrame(); /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). void dispose(); } base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec { // // This class is created by the engine, and should not be instantiated // or extended directly. // // To obtain an instance of the [Codec] interface, see // [instantiateImageCodec]. _NativeCodec._(); int? _cachedFrameCount; @override int get frameCount => _cachedFrameCount ??= _frameCount; @Native)>(symbol: 'Codec::frameCount', isLeaf: true) external int get _frameCount; int? _cachedRepetitionCount; @override int get repetitionCount => _cachedRepetitionCount ??= _repetitionCount; @Native)>(symbol: 'Codec::repetitionCount', isLeaf: true) external int get _repetitionCount; @override Future getNextFrame() async { final completer = Completer.sync(); final String? error = _getNextFrame(( _Image? image, int durationMilliseconds, String decodeError, ) { if (image == null) { if (decodeError.isEmpty) { decodeError = 'Codec failed to produce an image, possibly due to invalid image data.'; } completer.completeError(Exception(decodeError)); } else { completer.complete( FrameInfo._( image: Image._(image, image.width, image.height), duration: Duration(milliseconds: durationMilliseconds), ), ); } }); if (error != null) { throw Exception(error); } return completer.future; } /// Returns an error message on failure, null on success. @Native, Handle)>(symbol: 'Codec::getNextFrame') external String? _getNextFrame(void Function(_Image?, int, String) callback); @override @Native)>(symbol: 'Codec::dispose') external void dispose(); @override String toString() => 'Codec(${_cachedFrameCount == null ? "" : "$_cachedFrameCount frames"})'; } /// Instantiates an image [Codec]. /// /// This method is a convenience wrapper around the [ImageDescriptor] API, and /// using [ImageDescriptor] directly is preferred since it allows the caller to /// make better determinations about how and whether to use the `targetWidth` /// and `targetHeight` parameters. /// /// The `list` parameter is the binary image data (e.g a PNG or GIF binary data). /// The data can be for either static or animated images. The following image /// formats are supported: // Update this list when changing the list of supported codecs. /// {@template dart.ui.imageFormats} /// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional /// formats may be supported by the underlying platform. Flutter will /// attempt to call platform API to decode unrecognized formats, and if the /// platform API supports decoding the image Flutter will be able to render it. /// {@endtemplate} /// /// The `targetWidth` and `targetHeight` arguments specify the size of the /// output image, in image pixels. If they are not equal to the intrinsic /// dimensions of the image, then the image will be scaled after being decoded. /// If the `allowUpscaling` parameter is not set to true, both dimensions will /// be capped at the intrinsic dimensions of the image, even if only one of /// them would have exceeded those intrinsic dimensions. If exactly one of these /// two arguments is specified, then the aspect ratio will be maintained while /// forcing the image to match the other given dimension. If neither is /// specified, then the image maintains its intrinsic size. /// /// Scaling the image to larger than its intrinsic size should usually be /// avoided, since it causes the image to use more memory than necessary. /// Instead, prefer scaling the [Canvas] transform. If the image must be scaled /// up, the `allowUpscaling` parameter must be set to true. /// /// The returned future can complete with an error if the image decoding has /// failed. Future instantiateImageCodec( Uint8List list, { int? targetWidth, int? targetHeight, bool allowUpscaling = true, }) async { final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(list); return instantiateImageCodecFromBuffer( buffer, targetWidth: targetWidth, targetHeight: targetHeight, allowUpscaling: allowUpscaling, ); } /// Instantiates an image [Codec]. /// /// This method is a convenience wrapper around the [ImageDescriptor] API, and /// using [ImageDescriptor] directly is preferred since it allows the caller to /// make better determinations about how and whether to use the `targetWidth` /// and `targetHeight` parameters. /// /// The [buffer] parameter is the binary image data (e.g a PNG or GIF binary data). /// The data can be for either static or animated images. The following image /// formats are supported: {@macro dart.ui.imageFormats} /// /// The [buffer] will be disposed by this method once the codec has been created, /// so the caller must relinquish ownership of the [buffer] when they call this /// method. /// /// The [targetWidth] and [targetHeight] arguments specify the size of the /// output image, in image pixels. If they are not equal to the intrinsic /// dimensions of the image, then the image will be scaled after being decoded. /// If the `allowUpscaling` parameter is not set to true, both dimensions will /// be capped at the intrinsic dimensions of the image, even if only one of /// them would have exceeded those intrinsic dimensions. If exactly one of these /// two arguments is specified, then the aspect ratio will be maintained while /// forcing the image to match the other given dimension. If neither is /// specified, then the image maintains its intrinsic size. /// /// Scaling the image to larger than its intrinsic size should usually be /// avoided, since it causes the image to use more memory than necessary. /// Instead, prefer scaling the [Canvas] transform. If the image must be scaled /// up, the `allowUpscaling` parameter must be set to true. /// /// The returned future can complete with an error if the image decoding has /// failed. /// /// ## Compatibility note on the web /// /// When running Flutter on the web, only the CanvasKit renderer supports image /// resizing capabilities (not the HTML renderer). So if image resizing is /// critical to your use case, and you're deploying to the web, you should /// build using the CanvasKit renderer. Future instantiateImageCodecFromBuffer( ImmutableBuffer buffer, { int? targetWidth, int? targetHeight, bool allowUpscaling = true, }) { return instantiateImageCodecWithSize( buffer, getTargetSize: (int intrinsicWidth, int intrinsicHeight) { if (!allowUpscaling) { if (targetWidth != null && targetWidth! > intrinsicWidth) { targetWidth = intrinsicWidth; } if (targetHeight != null && targetHeight! > intrinsicHeight) { targetHeight = intrinsicHeight; } } return TargetImageSize(width: targetWidth, height: targetHeight); }, ); } /// Instantiates an image [Codec]. /// /// This method is a convenience wrapper around the [ImageDescriptor] API. /// /// The [buffer] parameter is the binary image data (e.g a PNG or GIF binary /// data). The data can be for either static or animated images. The following /// image formats are supported: {@macro dart.ui.imageFormats} /// /// The [buffer] will be disposed by this method once the codec has been /// created, so the caller must relinquish ownership of the [buffer] when they /// call this method. /// /// The [getTargetSize] parameter, when specified, will be invoked and passed /// the image's intrinsic size to determine the size to decode the image to. /// The width and the height of the size it returns must be positive values /// greater than or equal to 1, or null. It is valid to return a /// [TargetImageSize] that specifies only one of `width` and `height` with the /// other remaining null, in which case the omitted dimension will be scaled to /// maintain the aspect ratio of the original dimensions. When both are null or /// omitted, the image will be decoded at its native resolution (as will be the /// case if the [getTargetSize] parameter is omitted). /// /// Scaling the image to larger than its intrinsic size should usually be /// avoided, since it causes the image to use more memory than necessary. /// Instead, prefer scaling the [Canvas] transform. /// /// The returned future can complete with an error if the image decoding has /// failed. /// /// ## Compatibility note on the web /// /// When running Flutter on the web, only the CanvasKit renderer supports image /// resizing capabilities (not the HTML renderer). So if image resizing is /// critical to your use case, and you're deploying to the web, you should /// build using the CanvasKit renderer. Future instantiateImageCodecWithSize( ImmutableBuffer buffer, { TargetImageSizeCallback? getTargetSize, }) async { getTargetSize ??= _getDefaultImageSize; final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer); try { final TargetImageSize targetSize = getTargetSize(descriptor.width, descriptor.height); assert(targetSize.width == null || targetSize.width! > 0); assert(targetSize.height == null || targetSize.height! > 0); return descriptor.instantiateCodec( targetWidth: targetSize.width, targetHeight: targetSize.height, ); } finally { buffer.dispose(); } } TargetImageSize _getDefaultImageSize(int intrinsicWidth, int intrinsicHeight) { return const TargetImageSize(); } /// Signature for a callback that determines the size to which an image should /// be decoded given its intrinsic size. /// /// See also: /// /// * [instantiateImageCodecWithSize], which used this signature for its /// `getTargetSize` argument. typedef TargetImageSizeCallback = TargetImageSize Function(int intrinsicWidth, int intrinsicHeight); /// A specification of the size to which an image should be decoded. /// /// See also: /// /// * [TargetImageSizeCallback], a callback that returns instances of this /// class when consulted by image decoding methods such as /// [instantiateImageCodecWithSize]. class TargetImageSize { /// Creates a new instance of this class. /// /// The `width` and `height` may both be null, but if they're non-null, they /// must be positive. const TargetImageSize({this.width, this.height}) : assert(width == null || width > 0), assert(height == null || height > 0); /// The width into which to load the image. /// /// If this is non-null, the image will be decoded into the specified width. /// If this is null and [height] is also null, the image will be decoded into /// its intrinsic size. If this is null and [height] is non-null, the image /// will be decoded into a width that maintains its intrinsic aspect ratio /// while respecting the [height] value. /// /// If this value is non-null, it must be positive. final int? width; /// The height into which to load the image. /// /// If this is non-null, the image will be decoded into the specified height. /// If this is null and [width] is also null, the image will be decoded into /// its intrinsic size. If this is null and [width] is non-null, the image /// will be decoded into a height that maintains its intrinsic aspect ratio /// while respecting the [width] value. /// /// If this value is non-null, it must be positive. final int? height; @override String toString() => 'TargetImageSize($width x $height)'; } /// Loads a single image frame from a byte array into an [Image] object. /// /// This is a convenience wrapper around [instantiateImageCodec]. Prefer using /// [instantiateImageCodec] which also supports multi frame images and offers /// better error handling. This function swallows asynchronous errors. void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) { _decodeImageFromListAsync(list, callback); } Future _decodeImageFromListAsync(Uint8List list, ImageDecoderCallback callback) async { final Codec codec = await instantiateImageCodec(list); final FrameInfo frameInfo; try { frameInfo = await codec.getNextFrame(); } finally { codec.dispose(); } callback(frameInfo.image); } /// Convert an array of pixel values into an [Image] object. /// /// The `pixels` parameter is the pixel data. They are packed in bytes in the /// order described by `format`, then grouped in rows, from left to right, /// then top to bottom. /// /// The `rowBytes` parameter is the number of bytes consumed by each row of /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied /// by the number of bytes per pixel in the provided `format`. /// /// The `targetWidth` and `targetHeight` arguments specify the size of the /// output image, in image pixels. If they are not equal to the intrinsic /// dimensions of the image, then the image will be scaled after being decoded. /// If the `allowUpscaling` parameter is not set to true, both dimensions will /// be capped at the intrinsic dimensions of the image, even if only one of /// them would have exceeded those intrinsic dimensions. If exactly one of these /// two arguments is specified, then the aspect ratio will be maintained while /// forcing the image to match the other given dimension. If neither is /// specified, then the image maintains its intrinsic size. /// /// Scaling the image to larger than its intrinsic size should usually be /// avoided, since it causes the image to use more memory than necessary. /// Instead, prefer scaling the [Canvas] transform. If the image must be scaled /// up, the `allowUpscaling` parameter must be set to true. void decodeImageFromPixels( Uint8List pixels, int width, int height, PixelFormat format, ImageDecoderCallback callback, { int? rowBytes, int? targetWidth, int? targetHeight, bool allowUpscaling = true, TargetPixelFormat targetFormat = TargetPixelFormat.dontCare, }) { if (targetWidth != null) { assert(allowUpscaling || targetWidth <= width); } if (targetHeight != null) { assert(allowUpscaling || targetHeight <= height); } ImmutableBuffer.fromUint8List(pixels).then((ImmutableBuffer buffer) { final descriptor = ImageDescriptor.raw( buffer, width: width, height: height, rowBytes: rowBytes, pixelFormat: format, ); if (!allowUpscaling) { if (targetWidth != null && targetWidth! > descriptor.width) { targetWidth = descriptor.width; } if (targetHeight != null && targetHeight! > descriptor.height) { targetHeight = descriptor.height; } } descriptor .instantiateCodec( targetWidth: targetWidth, targetHeight: targetHeight, targetFormat: targetFormat, ) .then((Codec codec) { final Future frameInfo = codec.getNextFrame(); codec.dispose(); return frameInfo; }) .then((FrameInfo frameInfo) { buffer.dispose(); descriptor.dispose(); return callback(frameInfo.image); }); }); } /// Decodes the given [pixels] into an [Image] synchronously. /// /// The [pixels] are expected to be in the format specified by [format]. /// /// The [width] and [height] arguments specify the dimensions of the image. /// /// This function returns an [Image] immediately. The image might not be /// fully decoded yet, but it can be drawn to a [Canvas]. Image decodeImageFromPixelsSync(Uint8List pixels, int width, int height, PixelFormat format) { final image = Image._(_Image._(), width, height); _decodeImageFromPixelsSync(pixels, width, height, format.index, image._image); return image; } @Native( symbol: 'Image::decodeImageFromPixelsSync', ) external void _decodeImageFromPixelsSync( Uint8List pixels, int width, int height, int format, _Image outImage, ); /// Determines the winding rule that decides how the interior of a [Path] is /// calculated. /// /// This enum is used by the [Path.fillType] property. enum PathFillType { /// The interior is defined by a non-zero sum of signed edge crossings. /// /// For a given point, the point is considered to be on the inside of the path /// if a line drawn from the point to infinity crosses lines going clockwise /// around the point a different number of times than it crosses lines going /// counter-clockwise around that point. /// /// See: nonZero, /// The interior is defined by an odd number of edge crossings. /// /// For a given point, the point is considered to be on the inside of the path /// if a line drawn from the point to infinity crosses an odd number of lines. /// /// See: evenOdd, } /// Strategies for combining paths. /// /// See also: /// /// * [Path.combine], which uses this enum to decide how to combine two paths. // Must be kept in sync with SkPathOp enum PathOperation { /// Subtract the second path from the first path. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the result would be a crescent portion of the /// first circle that was not overlapped by the second circle. /// /// See also: /// /// * [reverseDifference], which is the same but subtracting the first path /// from the second. difference, /// Create a new path that is the intersection of the two paths, leaving the /// overlapping pieces of the path. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the result would be only the overlapping portion /// of the two circles. /// /// See also: /// * [xor], which is the inverse of this operation intersect, /// Create a new path that is the union (inclusive-or) of the two paths. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the result would be a figure-eight like shape /// matching the outer boundaries of both circles. union, /// Create a new path that is the exclusive-or of the two paths, leaving /// everything but the overlapping pieces of the path. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the figure-eight like shape less the overlapping parts /// /// See also: /// * [intersect], which is the inverse of this operation xor, /// Subtract the first path from the second path. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the result would be a crescent portion of the /// second circle that was not overlapped by the first circle. /// /// See also: /// /// * [difference], which is the same but subtracting the second path /// from the first. reverseDifference, } /// A handle for the framework to hold and retain an engine layer across frames. abstract class EngineLayer { /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// /// EngineLayers indirectly retain platform specific graphics resources. Some /// of these resources, such as images, may be memory intensive. It is /// important to dispose of EngineLayer objects that will no longer be used as /// soon as possible to avoid retaining these resources until the next /// garbage collection. /// /// Once this EngineLayer is disposed, it is no longer eligible for use as a /// retained layer, and must not be passed as an `oldLayer` to any of the /// [SceneBuilder] methods which accept that parameter. /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). void dispose(); } base class _NativeEngineLayer extends NativeFieldWrapperClass1 implements EngineLayer { /// This class is created by the engine, and should not be instantiated /// or extended directly. _NativeEngineLayer._(); @override @Native)>(symbol: 'EngineLayer::dispose') external void dispose(); } /// A complex, one-dimensional subset of a plane. /// /// A path consists of a number of sub-paths, and a _current point_. /// /// Sub-paths consist of segments of various types, such as lines, /// arcs, or beziers. Sub-paths can be open or closed, and can /// self-intersect. /// /// Closed sub-paths enclose a (possibly discontiguous) region of the /// plane based on the current [fillType]. /// /// The _current point_ is initially at the origin. After each /// operation adding a segment to a sub-path, the current point is /// updated to the end of that segment. /// /// Paths can be drawn on canvases using [Canvas.drawPath], and can /// used to create clip regions using [Canvas.clipPath]. abstract class Path { // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs factory Path() = _NativePath; /// Creates a copy of another [Path]. /// /// This copy is fast and does not require additional memory unless either /// the `source` path or the path returned by this constructor are modified. factory Path.from(Path source) { final clonedPath = _NativePath._(); (source as _NativePath)._clone(clonedPath); return clonedPath; } /// Determines how the interior of this path is calculated. /// /// Defaults to the non-zero winding rule, [PathFillType.nonZero]. PathFillType get fillType; set fillType(PathFillType value); /// Starts a new sub-path at the given coordinate. void moveTo(double x, double y); /// Starts a new sub-path at the given offset from the current point. void relativeMoveTo(double dx, double dy); /// Adds a straight line segment from the current point to the given /// point. void lineTo(double x, double y); /// Adds a straight line segment from the current point to the point /// at the given offset from the current point. void relativeLineTo(double dx, double dy); /// Adds a quadratic bezier segment that curves from the current /// point to the given point (x2,y2), using the control point /// (x1,y1). /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_quadratic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_quadratic_to_dark.png#gh-dark-mode-only) void quadraticBezierTo(double x1, double y1, double x2, double y2); /// Adds a quadratic bezier segment that curves from the current /// point to the point at the offset (x2,y2) from the current point, /// using the control point at the offset (x1,y1) from the current /// point. void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2); /// Adds a cubic bezier segment that curves from the current point /// to the given point (x3,y3), using the control points (x1,y1) and /// (x2,y2). /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_cubic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_cubic_to_dark.png#gh-dark-mode-only) void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); /// Adds a cubic bezier segment that curves from the current point /// to the point at the offset (x3,y3) from the current point, using /// the control points at the offsets (x1,y1) and (x2,y2) from the /// current point. void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); /// Adds a bezier segment that curves from the current point to the /// given point (x2,y2), using the control points (x1,y1) and the /// weight w. If the weight is greater than 1, then the curve is a /// hyperbola; if the weight equals 1, it's a parabola; and if it is /// less than 1, it is an ellipse. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_conic_to.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_conic_to_dark.png#gh-dark-mode-only) void conicTo(double x1, double y1, double x2, double y2, double w); /// Adds a bezier segment that curves from the current point to the /// point at the offset (x2,y2) from the current point, using the /// control point at the offset (x1,y1) from the current point and /// the weight w. If the weight is greater than 1, then the curve is /// a hyperbola; if the weight equals 1, it's a parabola; and if it /// is less than 1, it is an ellipse. void relativeConicTo(double x1, double y1, double x2, double y2, double w); /// If the `forceMoveTo` argument is false, adds a straight line /// segment and an arc segment. /// /// If the `forceMoveTo` argument is true, starts a new sub-path /// consisting of an arc segment. /// /// In either case, the arc segment consists of the arc that follows /// the edge of the oval bounded by the given rectangle, from /// startAngle radians around the oval up to startAngle + sweepAngle /// radians around the oval, with zero radians being the point on /// the right hand side of the oval that crosses the horizontal line /// that intersects the center of the rectangle and with positive /// angles going clockwise around the oval. /// /// The line segment added if `forceMoveTo` is false starts at the /// current point and ends at the start of the arc. Note that this /// method does not draw anything if the [sweepAngle] is a multiple /// of $2\pi$ (e.g., $2\pi$, $4\pi$). If you need to draw a full /// circle or an overlapping arc, use [addArc] as a workaround. void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo); /// Appends up to four conic curves weighted to describe an oval of `radius` /// and rotated by `rotation` (measured in degrees and clockwise). /// /// The first curve begins from the last point in the path and the last ends /// at `arcEnd`. The curves follow a path in a direction determined by /// `clockwise` and `largeArc` in such a way that the sweep angle /// is always less than 360 degrees. /// /// A simple line is appended if either radii are zero or the last /// point in the path is `arcEnd`. The radii are scaled to fit the last path /// point if both are greater than zero but too small to describe an arc. /// void arcToPoint( Offset arcEnd, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }); /// Appends up to four conic curves weighted to describe an oval of `radius` /// and rotated by `rotation` (measured in degrees and clockwise). /// /// The last path point is described by (px, py). /// /// The first curve begins from the last point in the path and the last ends /// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a /// path in a direction determined by `clockwise` and `largeArc` /// in such a way that the sweep angle is always less than 360 degrees. /// /// A simple line is appended if either radii are zero, or, both /// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to /// fit the last path point if both are greater than zero but too small to /// describe an arc. void relativeArcToPoint( Offset arcEndDelta, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }); /// Adds a new sub-path that consists of four lines that outline the /// given rectangle. void addRect(Rect rect); /// Adds a new sub-path that consists of a curve that forms the /// ellipse that fills the given rectangle. /// /// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle] /// can be used to easily describe the circle's center [Offset] and radius. void addOval(Rect oval); /// Adds a new sub-path with one arc segment that consists of the arc /// that follows the edge of the oval bounded by the given /// rectangle, from startAngle radians around the oval up to /// startAngle + sweepAngle radians around the oval, with zero /// radians being the point on the right hand side of the oval that /// crosses the horizontal line that intersects the center of the /// rectangle and with positive angles going clockwise around the /// oval. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc_dark.png#gh-dark-mode-only) /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc_ccw.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/path_add_arc_ccw_dark.png#gh-dark-mode-only) void addArc(Rect oval, double startAngle, double sweepAngle); /// Adds a new sub-path with a sequence of line segments that connect the given /// points. /// /// If `close` is true, a final line segment will be added that connects the /// last point to the first point. /// /// The `points` argument is interpreted as offsets from the origin. void addPolygon(List points, bool close); /// Adds a new sub-path that consists of the straight lines and /// curves needed to form the rounded rectangle described by the /// argument. void addRRect(RRect rrect); /// Adds a new sub-path that consists of curves needed to form the rounded /// superellipse described by the argument. void addRSuperellipse(RSuperellipse rsuperellipse); /// Adds the sub-paths of `path`, offset by `offset`, to this path. /// /// If `matrix4` is specified, the path will be transformed by this matrix /// after the matrix is translated by the given offset. The matrix is a 4x4 /// matrix stored in column major order. void addPath(Path path, Offset offset, {Float64List? matrix4}); /// Adds the sub-paths of `path`, offset by `offset`, to this path. /// The current sub-path is extended with the first sub-path /// of `path`, connecting them with a lineTo if necessary. /// /// If `matrix4` is specified, the path will be transformed by this matrix /// after the matrix is translated by the given `offset`. The matrix is a 4x4 /// matrix stored in column major order. void extendWithPath(Path path, Offset offset, {Float64List? matrix4}); /// Closes the last sub-path, as if a straight line had been drawn /// from the current point to the first point of the sub-path. void close(); /// Clears the [Path] object of all sub-paths, returning it to the /// same state it had when it was created. The _current point_ is /// reset to the origin. void reset(); /// Tests to see if the given point is within the path. (That is, whether the /// point would be in the visible portion of the path if the path was used /// with [Canvas.clipPath].) /// /// The `point` argument is interpreted as an offset from the origin. /// /// Returns true if the point is in the path, and false otherwise. bool contains(Offset point); /// Returns a copy of the path with all the segments of every /// sub-path translated by the given offset. Path shift(Offset offset); /// Returns a copy of the path with all the segments of every /// sub-path transformed by the given matrix. Path transform(Float64List matrix4); /// Computes the bounding rectangle for this path. /// /// A path containing only axis-aligned points on the same straight line will /// have no area, and therefore `Rect.isEmpty` will return true for such a /// path. Consider checking `rect.width + rect.height > 0.0` instead, or /// using the [computeMetrics] API to check the path length. /// /// For many more elaborate paths, the bounds may be inaccurate. For example, /// when a path contains a circle, the points used to compute the bounds are /// the circle's implied control points, which form a square around the circle; /// if the circle has a transformation applied using [transform] then that /// square is rotated, and the (axis-aligned, non-rotated) bounding box /// therefore ends up grossly overestimating the actual area covered by the /// circle. // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds Rect getBounds(); /// Combines the two paths according to the manner specified by the given /// `operation`. /// /// The resulting path will be constructed from non-overlapping contours. The /// curve order is reduced where possible so that cubics may be turned into /// quadratics, and quadratics maybe turned into lines. static Path combine(PathOperation operation, Path path1, Path path2) { final path = _NativePath(); if (path._op(path1 as _NativePath, path2 as _NativePath, operation.index)) { return path; } throw StateError( 'Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.', ); } /// Creates a [PathMetrics] object for this path, which can describe various /// properties about the contours of the path. /// /// A [Path] is made up of zero or more contours. A contour is made up of /// connected curves and segments, created via methods like [lineTo], /// [cubicTo], [arcTo], [quadraticBezierTo], their relative counterparts, as /// well as the add* methods such as [addRect]. Creating a new [Path] starts /// a new contour once it has any drawing instructions, and another new /// contour is started for each [moveTo] instruction. /// /// A [PathMetric] object describes properties of an individual contour, /// such as its length, whether it is closed, what the tangent vector of a /// particular offset along the path is. It also provides a method for /// creating sub-paths: [PathMetric.extractPath]. /// /// Calculating [PathMetric] objects is not trivial. The [PathMetrics] object /// returned by this method is a lazy [Iterable], meaning it only performs /// calculations when the iterator is moved to the next [PathMetric]. Callers /// that wish to memoize this iterable can easily do so by using /// [Iterable.toList] on the result of this method. In particular, callers /// looking for information about how many contours are in the path should /// either store the result of `path.computeMetrics().length`, or should use /// `path.computeMetrics().toList()` so they can repeatedly check the length, /// since calling `Iterable.length` causes traversal of the entire iterable. /// /// In particular, callers should be aware that [PathMetrics.length] is the /// number of contours, **not the length of the path**. To get the length of /// a contour in a path, use [PathMetric.length]. /// /// Zero-length contours (where the start and end points are the same, such as /// `Path()..lineTo(0, 0)`) are not included in the returned [PathMetrics]. /// Only contours with a positive length will have a corresponding [PathMetric]. /// /// If `forceClosed` is set to true, the contours of the path will be measured /// as if they had been closed, even if they were not explicitly closed. PathMetrics computeMetrics({bool forceClosed = false}); } base class _NativePath extends NativeFieldWrapperClass1 implements Path { /// Create a new empty [Path] object. _NativePath() { _constructor(); } /// Avoids creating a new native backing for the path for methods that will /// create it later, such as [Path.from], [shift] and [transform]. _NativePath._(); @Native(symbol: 'Path::Create') external void _constructor(); @Native, Handle)>(symbol: 'Path::clone') external void _clone(Path outPath); @override PathFillType get fillType => PathFillType.values[_getFillType()]; @override set fillType(PathFillType value) => _setFillType(value.index); @Native)>(symbol: 'Path::getFillType', isLeaf: true) external int _getFillType(); @Native, Int32)>(symbol: 'Path::setFillType', isLeaf: true) external void _setFillType(int fillType); @override @Native, Double, Double)>(symbol: 'Path::moveTo', isLeaf: true) external void moveTo(double x, double y); @override @Native, Double, Double)>( symbol: 'Path::relativeMoveTo', isLeaf: true, ) external void relativeMoveTo(double dx, double dy); @override @Native, Double, Double)>(symbol: 'Path::lineTo', isLeaf: true) external void lineTo(double x, double y); @override @Native, Double, Double)>( symbol: 'Path::relativeLineTo', isLeaf: true, ) external void relativeLineTo(double dx, double dy); @override @Native, Double, Double, Double, Double)>( symbol: 'Path::quadraticBezierTo', isLeaf: true, ) external void quadraticBezierTo(double x1, double y1, double x2, double y2); @override @Native, Double, Double, Double, Double)>( symbol: 'Path::relativeQuadraticBezierTo', isLeaf: true, ) external void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2); @override @Native, Double, Double, Double, Double, Double, Double)>( symbol: 'Path::cubicTo', isLeaf: true, ) external void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); @override @Native, Double, Double, Double, Double, Double, Double)>( symbol: 'Path::relativeCubicTo', isLeaf: true, ) external void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); @override @Native, Double, Double, Double, Double, Double)>( symbol: 'Path::conicTo', isLeaf: true, ) external void conicTo(double x1, double y1, double x2, double y2, double w); @override @Native, Double, Double, Double, Double, Double)>( symbol: 'Path::relativeConicTo', isLeaf: true, ) external void relativeConicTo(double x1, double y1, double x2, double y2, double w); @override void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { assert(_rectIsValid(rect)); _arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo); } @Native, Double, Double, Double, Double, Double, Double, Bool)>( symbol: 'Path::arcTo', isLeaf: true, ) external void _arcTo( double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool forceMoveTo, ); @override void arcToPoint( Offset arcEnd, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }) { assert(_offsetIsValid(arcEnd)); assert(_radiusIsValid(radius)); _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, largeArc, clockwise); } @Native, Double, Double, Double, Double, Double, Bool, Bool)>( symbol: 'Path::arcToPoint', isLeaf: true, ) external void _arcToPoint( double arcEndX, double arcEndY, double radiusX, double radiusY, double rotation, bool largeArc, bool clockwise, ); @override void relativeArcToPoint( Offset arcEndDelta, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true, }) { assert(_offsetIsValid(arcEndDelta)); assert(_radiusIsValid(radius)); _relativeArcToPoint( arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, rotation, largeArc, clockwise, ); } @Native, Double, Double, Double, Double, Double, Bool, Bool)>( symbol: 'Path::relativeArcToPoint', isLeaf: true, ) external void _relativeArcToPoint( double arcEndX, double arcEndY, double radiusX, double radiusY, double rotation, bool largeArc, bool clockwise, ); @override void addRect(Rect rect) { assert(_rectIsValid(rect)); _addRect(rect.left, rect.top, rect.right, rect.bottom); } @Native, Double, Double, Double, Double)>( symbol: 'Path::addRect', isLeaf: true, ) external void _addRect(double left, double top, double right, double bottom); @override void addOval(Rect oval) { assert(_rectIsValid(oval)); _addOval(oval.left, oval.top, oval.right, oval.bottom); } @Native, Double, Double, Double, Double)>( symbol: 'Path::addOval', isLeaf: true, ) external void _addOval(double left, double top, double right, double bottom); @override void addArc(Rect oval, double startAngle, double sweepAngle) { assert(_rectIsValid(oval)); _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); } @Native, Double, Double, Double, Double, Double, Double)>( symbol: 'Path::addArc', isLeaf: true, ) external void _addArc( double left, double top, double right, double bottom, double startAngle, double sweepAngle, ); @override void addPolygon(List points, bool close) { _addPolygon(_encodePointList(points), close); } @Native, Handle, Bool)>(symbol: 'Path::addPolygon') external void _addPolygon(Float32List points, bool close); @override void addRRect(RRect rrect) { assert(_rrectIsValid(rrect)); _addRRect(rrect._getValue32()); } @Native, Handle)>(symbol: 'Path::addRRect') external void _addRRect(Float32List rrect); @override void addRSuperellipse(RSuperellipse rsuperellipse) { assert(_rsuperellipseIsValid(rsuperellipse)); _addRSuperellipse(rsuperellipse._native()); } @Native, Pointer)>(symbol: 'Path::addRSuperellipse') external void _addRSuperellipse(_NativeRSuperellipse rsuperellipse); @override void addPath(Path path, Offset offset, {Float64List? matrix4}) { assert(_offsetIsValid(offset)); if (matrix4 != null) { assert(_matrix4IsValid(matrix4)); _addPathWithMatrix(path as _NativePath, offset.dx, offset.dy, matrix4); } else { _addPath(path as _NativePath, offset.dx, offset.dy); } } @Native, Pointer, Double, Double)>(symbol: 'Path::addPath') external void _addPath(_NativePath path, double dx, double dy); @Native, Pointer, Double, Double, Handle)>( symbol: 'Path::addPathWithMatrix', ) external void _addPathWithMatrix(_NativePath path, double dx, double dy, Float64List matrix); @override void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) { assert(_offsetIsValid(offset)); if (matrix4 != null) { assert(_matrix4IsValid(matrix4)); _extendWithPathAndMatrix(path as _NativePath, offset.dx, offset.dy, matrix4); } else { _extendWithPath(path as _NativePath, offset.dx, offset.dy); } } @Native, Pointer, Double, Double)>( symbol: 'Path::extendWithPath', ) external void _extendWithPath(_NativePath path, double dx, double dy); @Native, Pointer, Double, Double, Handle)>( symbol: 'Path::extendWithPathAndMatrix', ) external void _extendWithPathAndMatrix( _NativePath path, double dx, double dy, Float64List matrix, ); @override @Native)>(symbol: 'Path::close', isLeaf: true) external void close(); @override @Native)>(symbol: 'Path::reset', isLeaf: true) external void reset(); @override bool contains(Offset point) { assert(_offsetIsValid(point)); return _contains(point.dx, point.dy); } @Native, Double, Double)>(symbol: 'Path::contains', isLeaf: true) external bool _contains(double x, double y); @override Path shift(Offset offset) { assert(_offsetIsValid(offset)); final path = _NativePath._(); _shift(path, offset.dx, offset.dy); return path; } @Native, Handle, Double, Double)>(symbol: 'Path::shift') external void _shift(Path outPath, double dx, double dy); @override Path transform(Float64List matrix4) { assert(_matrix4IsValid(matrix4)); final path = _NativePath._(); _transform(path, matrix4); return path; } @Native, Handle, Handle)>(symbol: 'Path::transform') external void _transform(Path outPath, Float64List matrix4); @override Rect getBounds() { final Float32List rect = _getBounds(); return Rect.fromLTRB(rect[0], rect[1], rect[2], rect[3]); } @Native)>(symbol: 'Path::getBounds') external Float32List _getBounds(); @Native, Pointer, Pointer, Int32)>(symbol: 'Path::op') external bool _op(_NativePath path1, _NativePath path2, int operation); @override PathMetrics computeMetrics({bool forceClosed = false}) { return PathMetrics._(this, forceClosed); } @override String toString() => 'Path'; } /// The geometric description of a tangent: the angle at a point. /// /// See also: /// * [PathMetric.getTangentForOffset], which returns the tangent of an offset along a path. class Tangent { /// Creates a [Tangent] with the given values. /// /// The arguments must not be null. const Tangent(this.position, this.vector); /// Creates a [Tangent] based on the angle rather than the vector. /// /// The [vector] is computed to be the unit vector at the given angle, interpreted /// as clockwise radians from the x axis. factory Tangent.fromAngle(Offset position, double angle) { return Tangent(position, Offset(math.cos(angle), math.sin(angle))); } /// Position of the tangent. /// /// When used with [PathMetric.getTangentForOffset], this represents the precise /// position that the given offset along the path corresponds to. final Offset position; /// The vector of the curve at [position]. /// /// When used with [PathMetric.getTangentForOffset], this is the vector of the /// curve that is at the given offset along the path (i.e. the direction of the /// curve at [position]). final Offset vector; /// The direction of the curve at [position]. /// /// When used with [PathMetric.getTangentForOffset], this is the angle of the /// curve that is the given offset along the path (i.e. the direction of the /// curve at [position]). /// /// This value is in radians, with 0.0 meaning pointing along the x axis in /// the positive x-axis direction, positive numbers pointing downward toward /// the negative y-axis, i.e. in a clockwise direction, and negative numbers /// pointing upward toward the positive y-axis, i.e. in a counter-clockwise /// direction. // flip the sign to be consistent with [Path.arcTo]'s `sweepAngle` double get angle => -math.atan2(vector.dy, vector.dx); } /// An iterable collection of [PathMetric] objects describing a [Path]. /// /// A [PathMetrics] object is created by using the [Path.computeMetrics] method, /// and represents the path as it stood at the time of the call. Subsequent /// modifications of the path do not affect the [PathMetrics] object. /// /// Each path metric corresponds to a segment, or contour, of a path. /// /// For example, a path consisting of a [Path.lineTo], a [Path.moveTo], and /// another [Path.lineTo] will contain two contours and thus be represented by /// two [PathMetric] objects. /// /// This iterable does not memoize. Callers who need to traverse the list /// multiple times, or who need to randomly access elements of the list, should /// use [toList] on this object. class PathMetrics extends collection.IterableBase { PathMetrics._(Path path, bool forceClosed) : _iterator = PathMetricIterator._(_PathMeasure(path as _NativePath, forceClosed)); final Iterator _iterator; @override Iterator get iterator => _iterator; } /// Used by [PathMetrics] to track iteration from one segment of a path to the /// next for measurement. class PathMetricIterator implements Iterator { PathMetricIterator._(this._pathMeasure); PathMetric? _pathMetric; final _PathMeasure _pathMeasure; @override PathMetric get current { final PathMetric? currentMetric = _pathMetric; if (currentMetric == null) { throw RangeError( 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".', ); } return currentMetric; } @override bool moveNext() { if (_pathMeasure._nextContour()) { _pathMetric = PathMetric._(_pathMeasure); return true; } _pathMetric = null; return false; } } /// Utilities for measuring a [Path] and extracting sub-paths. /// /// Iterate over the object returned by [Path.computeMetrics] to obtain /// [PathMetric] objects. Callers that want to randomly access elements or /// iterate multiple times should use `path.computeMetrics().toList()`, since /// [PathMetrics] does not memoize. /// /// Once created, the metrics are only valid for the path as it was specified /// when [Path.computeMetrics] was called. If additional contours are added or /// any contours are updated, the metrics need to be recomputed. Previously /// created metrics will still refer to a snapshot of the path at the time they /// were computed, rather than to the actual metrics for the new mutations to /// the path. class PathMetric { PathMetric._(this._measure) : length = _measure.length(_measure.currentContourIndex), isClosed = _measure.isClosed(_measure.currentContourIndex), contourIndex = _measure.currentContourIndex; /// Return the total length of the current contour. /// /// The length may be calculated from an approximation of the geometry /// originally added. For this reason, it is not recommended to rely on /// this property for mathematically correct lengths of common shapes. final double length; /// Whether the contour is closed. /// /// Returns true if the contour ends with a call to [Path.close] (which may /// have been implied when using methods like [Path.addRect]) or if /// `forceClosed` was specified as true in the call to [Path.computeMetrics]. /// Returns false otherwise. final bool isClosed; /// The zero-based index of the contour. /// /// [Path] objects are made up of zero or more contours. The first contour is /// created once a drawing command (e.g. [Path.lineTo]) is issued. A /// [Path.moveTo] command after a drawing command may create a new contour, /// although it may not if optimizations are applied that determine the move /// command did not actually result in moving the pen. /// /// This property is only valid with reference to its original iterator and /// the contours of the path at the time the path's metrics were computed. If /// additional contours were added or existing contours updated, this metric /// will be invalid for the current state of the path. final int contourIndex; final _PathMeasure _measure; /// Computes the position of the current contour at the given offset, and the /// angle of the path at that point. /// /// For example, calling this method with a distance of 1.41 for a line from /// 0.0,0.0 to 2.0,2.0 would give a point 1.0,1.0 and the angle 45 degrees /// (but in radians). /// /// Returns null if the contour has zero [length]. /// /// The distance is clamped to the [length] of the current contour. Tangent? getTangentForOffset(double distance) { return _measure.getTangentForOffset(contourIndex, distance); } /// Given a start and end distance, return the intervening segment(s). /// /// `start` and `end` are clamped to legal values (0..[length]) /// Begin the segment with a moveTo if `startWithMoveTo` is true. Path extractPath(double start, double end, {bool startWithMoveTo = true}) { return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); } @override String toString() => 'PathMetric(length: $length, isClosed: $isClosed, contourIndex: $contourIndex)'; } base class _PathMeasure extends NativeFieldWrapperClass1 { _PathMeasure(_NativePath path, bool forceClosed) { _constructor(path, forceClosed); } @Native, Bool)>(symbol: 'PathMeasure::Create') external void _constructor(_NativePath path, bool forceClosed); double length(int contourIndex) { assert( contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.', ); return _length(contourIndex); } @Native, Int32)>(symbol: 'PathMeasure::getLength', isLeaf: true) external double _length(int contourIndex); Tangent? getTangentForOffset(int contourIndex, double distance) { assert( contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.', ); final Float32List posTan = _getPosTan(contourIndex, distance); // first entry == 0 indicates that Skia returned false if (posTan[0] == 0.0) { return null; } else { return Tangent(Offset(posTan[1], posTan[2]), Offset(posTan[3], posTan[4])); } } @Native, Int32, Double)>(symbol: 'PathMeasure::getPosTan') external Float32List _getPosTan(int contourIndex, double distance); Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo = true}) { assert( contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.', ); final path = _NativePath._(); _extractPath(path, contourIndex, start, end, startWithMoveTo); return path; } @Native, Handle, Int32, Double, Double, Bool)>( symbol: 'PathMeasure::getSegment', ) external void _extractPath( Path outPath, int contourIndex, double start, double end, bool startWithMoveTo, ); bool isClosed(int contourIndex) { assert( contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.', ); return _isClosed(contourIndex); } @Native, Int32)>(symbol: 'PathMeasure::isClosed', isLeaf: true) external bool _isClosed(int contourIndex); // Move to the next contour in the path. // // A path can have a next contour if [Path.moveTo] was called after drawing began. // Return true if one exists, or false. bool _nextContour() { final bool next = _nativeNextContour(); if (next) { currentContourIndex++; } return next; } @Native)>(symbol: 'PathMeasure::nextContour', isLeaf: true) external bool _nativeNextContour(); /// The index of the current contour in the list of contours in the path. /// /// [nextContour] will increment this to the zero based index. int currentContourIndex = -1; } /// Styles to use for blurs in [MaskFilter] objects. // These enum values must be kept in sync with DlBlurStyle. enum BlurStyle { // These mirror DlBlurStyle and must be kept in sync. /// Fuzzy inside and outside. This is useful for painting shadows that are /// offset from the shape that ostensibly is casting the shadow. normal, /// Solid inside, fuzzy outside. This corresponds to drawing the shape, and /// additionally drawing the blur. This can make objects appear brighter, /// maybe even as if they were fluorescent. solid, /// Nothing inside, fuzzy outside. This is useful for painting shadows for /// partially transparent shapes, when they are painted separately but without /// an offset, so that the shadow doesn't paint below the shape. outer, /// Fuzzy inside, nothing outside. This can make shapes appear to be lit from /// within. inner, } /// A mask filter to apply to shapes as they are painted. A mask filter is a /// function that takes a bitmap of color pixels, and returns another bitmap of /// color pixels. /// /// Instances of this class are used with [Paint.maskFilter] on [Paint] objects. class MaskFilter { /// Creates a mask filter that takes the shape being drawn and blurs it. /// /// This is commonly used to approximate shadows. /// /// The `style` argument controls the kind of effect to draw; see [BlurStyle]. /// /// The `sigma` argument controls the size of the effect. It is the standard /// deviation of the Gaussian blur to apply. The value must be greater than /// zero. The sigma corresponds to very roughly half the radius of the effect /// in pixels. /// /// A blur is an expensive operation and should therefore be used sparingly. /// /// The arguments must not be null. /// /// See also: /// /// * [Canvas.drawShadow], which is a more efficient way to draw shadows. const MaskFilter.blur(this._style, this._sigma); final BlurStyle _style; final double _sigma; // The type of MaskFilter class to create for flutter::DisplayList. // These constants must be kept in sync with MaskFilterType in paint.cc. static const int _TypeNone = 0; // null static const int _TypeBlur = 1; // DlBlurMaskFilter @override bool operator ==(Object other) { return other is MaskFilter && other._style == _style && other._sigma == _sigma; } @override int get hashCode => Object.hash(_style, _sigma); @override String toString() => 'MaskFilter.blur($_style, ${_sigma.toStringAsFixed(1)})'; } abstract class _ColorTransform { Color transform(Color color, ColorSpace resultColorSpace); } class _IdentityColorTransform implements _ColorTransform { const _IdentityColorTransform(); @override Color transform(Color color, ColorSpace resultColorSpace) => color; } class _ClampTransform implements _ColorTransform { const _ClampTransform(this.child); final _ColorTransform child; @override Color transform(Color color, ColorSpace resultColorSpace) { return Color.from( alpha: clampDouble(color.a, 0, 1), red: clampDouble(color.r, 0, 1), green: clampDouble(color.g, 0, 1), blue: clampDouble(color.b, 0, 1), colorSpace: resultColorSpace, ); } } // sRGB standard constants for transfer functions. // See https://en.wikipedia.org/wiki/SRGB. const double _kSrgbGamma = 2.4; const double _kSrgbLinearThreshold = 0.04045; const double _kSrgbLinearSlope = 12.92; const double _kSrgbEncodedOffset = 0.055; const double _kSrgbEncodedDivisor = 1.055; const double _kSrgbLinearToEncodedThreshold = 0.0031308; /// sRGB electro-optical transfer function (gamma decode to linear). double _srgbEOTF(double v) { if (v <= _kSrgbLinearThreshold) { return v / _kSrgbLinearSlope; } return math.pow((v + _kSrgbEncodedOffset) / _kSrgbEncodedDivisor, _kSrgbGamma).toDouble(); } /// sRGB opto-electronic transfer function (linear to gamma encode). double _srgbOETF(double v) { if (v <= _kSrgbLinearToEncodedThreshold) { return v * _kSrgbLinearSlope; } return _kSrgbEncodedDivisor * math.pow(v, 1.0 / _kSrgbGamma).toDouble() - _kSrgbEncodedOffset; } /// Extended versions that handle negative values by mirroring. double _srgbEOTFExtended(double v) { return v < 0.0 ? -_srgbEOTF(-v) : _srgbEOTF(v); } double _srgbOETFExtended(double v) { return v < 0.0 ? -_srgbOETF(-v) : _srgbOETF(v); } /// Display P3 to sRGB 3x3 matrix in linear space. /// M = sRGB_XYZ_to_RGB * P3_RGB_to_XYZ const List _kP3ToSrgbLinear = [ 1.2249401, -0.2249402, 0.0, -0.0420569, 1.0420571, 0.0, -0.0196376, -0.0786507, 1.0982884, ]; /// sRGB to Display P3 3x3 matrix in linear space (inverse of [_kP3ToSrgbLinear]). const List _kSrgbToP3Linear = [ 0.8224622, 0.1775380, 0.0, 0.0331942, 0.9668058, 0.0, 0.0170806, 0.0723974, 0.9105220, ]; /// Converts Display P3 (gamma-encoded) to extended sRGB (gamma-encoded). /// Pipeline: EOTF(decode) -> 3x3 matrix -> OETF(encode). class _P3ToSrgbTransform implements _ColorTransform { const _P3ToSrgbTransform(); @override Color transform(Color color, ColorSpace resultColorSpace) { final double rLin = _srgbEOTFExtended(color.r); final double gLin = _srgbEOTFExtended(color.g); final double bLin = _srgbEOTFExtended(color.b); final double rOut = _kP3ToSrgbLinear[0] * rLin + _kP3ToSrgbLinear[1] * gLin + _kP3ToSrgbLinear[2] * bLin; final double gOut = _kP3ToSrgbLinear[3] * rLin + _kP3ToSrgbLinear[4] * gLin + _kP3ToSrgbLinear[5] * bLin; final double bOut = _kP3ToSrgbLinear[6] * rLin + _kP3ToSrgbLinear[7] * gLin + _kP3ToSrgbLinear[8] * bLin; return Color.from( alpha: color.a, red: _srgbOETFExtended(rOut), green: _srgbOETFExtended(gOut), blue: _srgbOETFExtended(bOut), colorSpace: resultColorSpace, ); } } /// Converts sRGB (gamma-encoded) to Display P3 (gamma-encoded). /// Pipeline: EOTF(decode) -> 3x3 matrix -> OETF(encode). class _SrgbToP3Transform implements _ColorTransform { const _SrgbToP3Transform(); @override Color transform(Color color, ColorSpace resultColorSpace) { final double rLin = _srgbEOTFExtended(color.r); final double gLin = _srgbEOTFExtended(color.g); final double bLin = _srgbEOTFExtended(color.b); final double rOut = _kSrgbToP3Linear[0] * rLin + _kSrgbToP3Linear[1] * gLin + _kSrgbToP3Linear[2] * bLin; final double gOut = _kSrgbToP3Linear[3] * rLin + _kSrgbToP3Linear[4] * gLin + _kSrgbToP3Linear[5] * bLin; final double bOut = _kSrgbToP3Linear[6] * rLin + _kSrgbToP3Linear[7] * gLin + _kSrgbToP3Linear[8] * bLin; return Color.from( alpha: color.a, red: _srgbOETFExtended(rOut), green: _srgbOETFExtended(gOut), blue: _srgbOETFExtended(bOut), colorSpace: resultColorSpace, ); } } _ColorTransform _getColorTransform(ColorSpace source, ColorSpace destination) { switch (source) { case ColorSpace.sRGB: switch (destination) { case ColorSpace.sRGB: return const _IdentityColorTransform(); case ColorSpace.extendedSRGB: return const _IdentityColorTransform(); case ColorSpace.displayP3: return const _SrgbToP3Transform(); } case ColorSpace.extendedSRGB: switch (destination) { case ColorSpace.sRGB: return const _ClampTransform(_IdentityColorTransform()); case ColorSpace.extendedSRGB: return const _IdentityColorTransform(); case ColorSpace.displayP3: return const _ClampTransform(_SrgbToP3Transform()); } case ColorSpace.displayP3: switch (destination) { case ColorSpace.sRGB: return const _ClampTransform(_P3ToSrgbTransform()); case ColorSpace.extendedSRGB: return const _P3ToSrgbTransform(); case ColorSpace.displayP3: return const _IdentityColorTransform(); } } } /// A description of a color filter to apply when drawing a shape or compositing /// a layer with a particular [Paint]. A color filter is a function that takes /// two colors, and outputs one color. When applied during compositing, it is /// independently applied to each pixel of the layer being drawn before the /// entire layer is merged with the destination. /// /// Instances of this class are used with [Paint.colorFilter] on [Paint] /// objects. class ColorFilter implements ImageFilter { /// Creates a color filter that applies the blend mode given as the second /// argument. The source color is the one given as the first argument, and the /// destination color is the one from the layer being composited. /// /// The output of this filter is then composited into the background according /// to the [Paint.blendMode], using the output of this filter as the source /// and the background as the destination. const ColorFilter.mode(Color color, BlendMode blendMode) : _color = color, _blendMode = blendMode, _matrix = null, _type = _kTypeMode; /// Construct a color filter from a 4x5 row-major matrix. The matrix is /// interpreted as a 5x5 matrix, where the fifth row is the identity /// configuration. /// /// Every pixel's color value, represented as an `[R, G, B, A]`, is matrix /// multiplied to create a new color: /// /// | R' | | a00 a01 a02 a03 a04 | | R | /// | G' | | a10 a11 a12 a13 a14 | | G | /// | B' | = | a20 a21 a22 a23 a24 | * | B | /// | A' | | a30 a31 a32 a33 a34 | | A | /// | 1 | | 0 0 0 0 1 | | 1 | /// /// The matrix is in row-major order and the translation column is specified /// in unnormalized, 0...255, space. For example, the identity matrix is: /// /// ```dart /// const ColorFilter identity = ColorFilter.matrix([ /// 1, 0, 0, 0, 0, /// 0, 1, 0, 0, 0, /// 0, 0, 1, 0, 0, /// 0, 0, 0, 1, 0, /// ]); /// ``` /// /// ## Examples /// /// An inversion color matrix: /// /// ```dart /// const ColorFilter invert = ColorFilter.matrix([ /// -1, 0, 0, 0, 255, /// 0, -1, 0, 0, 255, /// 0, 0, -1, 0, 255, /// 0, 0, 0, 1, 0, /// ]); /// ``` /// /// A sepia-toned color matrix (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#sepiaEquivalent)): /// /// ```dart /// const ColorFilter sepia = ColorFilter.matrix([ /// 0.393, 0.769, 0.189, 0, 0, /// 0.349, 0.686, 0.168, 0, 0, /// 0.272, 0.534, 0.131, 0, 0, /// 0, 0, 0, 1, 0, /// ]); /// ``` /// /// A greyscale color filter (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#grayscaleEquivalent)): /// /// ```dart /// const ColorFilter greyscale = ColorFilter.matrix([ /// 0.2126, 0.7152, 0.0722, 0, 0, /// 0.2126, 0.7152, 0.0722, 0, 0, /// 0.2126, 0.7152, 0.0722, 0, 0, /// 0, 0, 0, 1, 0, /// ]); /// ``` const ColorFilter.matrix(List matrix) : _color = null, _blendMode = null, _matrix = matrix, _type = _kTypeMatrix; /// Construct a color filter that applies the sRGB gamma curve to the RGB /// channels. const ColorFilter.linearToSrgbGamma() : _color = null, _blendMode = null, _matrix = null, _type = _kTypeLinearToSrgbGamma; /// Creates a color filter that applies the inverse of the sRGB gamma curve /// to the RGB channels. const ColorFilter.srgbToLinearGamma() : _color = null, _blendMode = null, _matrix = null, _type = _kTypeSrgbToLinearGamma; /// Creates a color filter that applies the given saturation to the RGB /// channels. factory ColorFilter.saturation(double saturation) { const rLuminance = 0.2126; const gLuminance = 0.7152; const bLuminance = 0.0722; final double invSat = 1 - saturation; return ColorFilter.matrix([ // dart format off invSat * rLuminance + saturation, invSat * gLuminance, invSat * bLuminance, 0, 0, invSat * rLuminance, invSat * gLuminance + saturation, invSat * bLuminance, 0, 0, invSat * rLuminance, invSat * gLuminance, invSat * bLuminance + saturation, 0, 0, 0, 0, 0, 1, 0, // dart format on ]); } final Color? _color; final BlendMode? _blendMode; final List? _matrix; final int _type; // The type of DlColorFilter class to create. static const int _kTypeMode = 1; // MakeModeFilter static const int _kTypeMatrix = 2; // MakeMatrixFilterRowMajor255 static const int _kTypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma static const int _kTypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma // DlColorImageFilter @override _ImageFilter _toNativeImageFilter() => _ImageFilter.fromColorFilter(this); _ColorFilter? _toNativeColorFilter() { switch (_type) { case _kTypeMode: if (_color == null || _blendMode == null) { return null; } return _ColorFilter.mode(this); case _kTypeMatrix: final List? matrix = _matrix; if (matrix == null) { return null; } assert(matrix.length == 20, 'Color Matrix must have 20 entries.'); return _ColorFilter.matrix(this); case _kTypeLinearToSrgbGamma: return _ColorFilter.linearToSrgbGamma(this); case _kTypeSrgbToLinearGamma: return _ColorFilter.srgbToLinearGamma(this); default: throw StateError('Unknown mode $_type for ColorFilter.'); } } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is ColorFilter && other._type == _type && _listEquals(other._matrix, _matrix) && other._color == _color && other._blendMode == _blendMode; } @override int get hashCode { final List? matrix = _matrix; return Object.hash(_color, _blendMode, matrix == null ? null : Object.hashAll(matrix), _type); } @override String get debugShortDescription { switch (_type) { case _kTypeMode: return 'ColorFilter.mode($_color, $_blendMode)'; case _kTypeMatrix: return 'ColorFilter.matrix($_matrix)'; case _kTypeLinearToSrgbGamma: return 'ColorFilter.linearToSrgbGamma()'; case _kTypeSrgbToLinearGamma: return 'ColorFilter.srgbToLinearGamma()'; default: return 'unknow ColorFilter'; } } @override String toString() { switch (_type) { case _kTypeMode: return 'ColorFilter.mode($_color, $_blendMode)'; case _kTypeMatrix: return 'ColorFilter.matrix($_matrix)'; case _kTypeLinearToSrgbGamma: return 'ColorFilter.linearToSrgbGamma()'; case _kTypeSrgbToLinearGamma: return 'ColorFilter.srgbToLinearGamma()'; default: return "Unknown ColorFilter type. This is an error. If you're seeing this, please file an issue at https://github.com/flutter/flutter/issues/new."; } } } /// A [ColorFilter] that is backed by a native DlColorFilter. /// /// This is a private class, rather than being the implementation of the public /// ColorFilter, because we want ColorFilter to be const constructible and /// efficiently comparable, so that widgets can check for ColorFilter equality to /// avoid repainting. base class _ColorFilter extends NativeFieldWrapperClass1 { _ColorFilter.mode(this.creator) : assert(creator._type == ColorFilter._kTypeMode) { _constructor(); _initMode(creator._color!.value, creator._blendMode!.index); } _ColorFilter.matrix(this.creator) : assert(creator._type == ColorFilter._kTypeMatrix) { _constructor(); _initMatrix(Float32List.fromList(creator._matrix!)); } _ColorFilter.linearToSrgbGamma(this.creator) : assert(creator._type == ColorFilter._kTypeLinearToSrgbGamma) { _constructor(); _initLinearToSrgbGamma(); } _ColorFilter.srgbToLinearGamma(this.creator) : assert(creator._type == ColorFilter._kTypeSrgbToLinearGamma) { _constructor(); _initSrgbToLinearGamma(); } /// The original Dart object that created the native wrapper, which retains /// the values used for the filter. final ColorFilter creator; @Native(symbol: 'ColorFilter::Create') external void _constructor(); @Native, Int32, Int32)>(symbol: 'ColorFilter::initMode', isLeaf: true) external void _initMode(int color, int blendMode); @Native, Handle)>(symbol: 'ColorFilter::initMatrix') external void _initMatrix(Float32List matrix); @Native)>(symbol: 'ColorFilter::initLinearToSrgbGamma', isLeaf: true) external void _initLinearToSrgbGamma(); @Native)>(symbol: 'ColorFilter::initSrgbToLinearGamma', isLeaf: true) external void _initSrgbToLinearGamma(); } /// A filter operation to apply to a raster image. /// /// See also: /// /// * [BackdropFilter], a widget that applies [ImageFilter] to its rendering. /// * [ClipRect], a widget that limits the area affected by the [ImageFilter] /// when used with [BackdropFilter]. /// * [ImageFiltered], a widget that applies [ImageFilter] to its children. /// * [SceneBuilder.pushBackdropFilter], which is the low-level API for using /// this class as a backdrop filter. /// * [SceneBuilder.pushImageFilter], which is the low-level API for using /// this class as a child layer filter. abstract class ImageFilter { // This class is not meant to be extended; this constructor prevents extension. ImageFilter._(); // ignore: unused_element /// Creates an image filter that applies a Gaussian blur. /// /// The `sigma_x` and `sigma_y` are the standard deviation of the Gaussian /// kernel in the X direction and the Y direction, respectively. /// /// The `tile_mode` defines the behavior of sampling pixels at the edges when /// performing a standard, unbounded blur. /// /// The `bounds` argument is optional and enables "bounded blur" mode. When /// `bounds` is non-null, the image filter substitutes transparent black for /// any sample it reads from outside the defined bounding rectangle. The final /// weighted sum is then divided by the total weight of the non-transparent samples /// (the effective alpha), resulting in opaque output. /// /// The bounded mode prevents color bleeding from content adjacent to the /// bounds into the blurred area, and is typically used when the blur must be /// strictly contained within a clipped region, such as for iOS-style frosted /// glass effects. /// /// The `bounds` rectangle is specified in the canvas's current coordinate /// space and is affected by the current transform; consequently, the bounds /// may not be axis-aligned in the final canvas coordinates. factory ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0, TileMode? tileMode, Rect? bounds, }) { return _GaussianBlurImageFilter( sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode, bounds: bounds, ); } /// Creates an image filter that dilates each input pixel's channel values /// to the max value within the given radii along the x and y axes. factory ImageFilter.dilate({double radiusX = 0.0, double radiusY = 0.0}) { return _DilateImageFilter(radiusX: radiusX, radiusY: radiusY); } /// Create a filter that erodes each input pixel's channel values /// to the minimum channel value within the given radii along the x and y axes. factory ImageFilter.erode({double radiusX = 0.0, double radiusY = 0.0}) { return _ErodeImageFilter(radiusX: radiusX, radiusY: radiusY); } /// Creates an image filter that applies a matrix transformation. /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. factory ImageFilter.matrix( Float64List matrix4, { FilterQuality filterQuality = FilterQuality.medium, }) { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } return _MatrixImageFilter(data: Float64List.fromList(matrix4), filterQuality: filterQuality); } /// Composes the `inner` filter with `outer`, to combine their effects. /// /// Creates a single [ImageFilter] that when applied, has the same effect as /// subsequently applying `inner` and `outer`, i.e., /// result = outer(inner(source)). factory ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) { return _ComposeImageFilter(innerFilter: inner, outerFilter: outer); } /// Creates an image filter from a [FragmentShader]. /// /// > [!WARNING] /// > This API is only supported when using the Impeller rendering engine. /// > On other backends, an [UnsupportedError] will be thrown. /// /// > To check at runtime whether this API is supported, use [isShaderFilterSupported]. /// /// Example usage: /// /// ```dart /// if (ui.ImageFilter.isShaderFilterSupported) { /// // Use the filter... /// } /// ``` /// /// The fragment shader provided here has additional requirements to be used /// by the engine for filtering. The first uniform value must be a vec2, this /// will be set by the engine to the size of the bound texture. There must /// also be at least one sampler2D uniform, the first of which will be set by /// the engine to contain the filter input. /// /// When Impeller uses the OpenGL(ES) backend, the y-axis direction is /// reversed. Custom fragment shaders must invert the y-axis on /// GLES or they will render upside-down. /// /// For example, the following is a valid fragment shader that can be used /// with this API. Note that the uniform names are not required to have any /// particular value. /// /// ```glsl /// #include /// /// uniform vec2 u_size; /// uniform float u_time; /// /// uniform sampler2D u_texture_input; /// /// out vec4 frag_color; /// /// void main() { /// vec2 uv = FlutterFragCoord().xy / u_size; /// // Reverse y axis for OpenGL backend. /// #ifdef IMPELLER_TARGET_OPENGLES /// uv.y = 1.0 - uv.y /// #endif /// frag_color = texture(u_texture_input, uv) * u_time; /// /// } /// /// ``` factory ImageFilter.shader(FragmentShader shader) { if (!_impellerEnabled) { throw UnsupportedError('ImageFilter.shader only supported with Impeller rendering engine.'); } final bool invalidFloats = shader._floats.length < 2; final bool invalidSampler = !shader._validateImageFilter(); if (invalidFloats || invalidSampler) { final buffer = StringBuffer( 'ImageFilter.shader requires that the first uniform is a vec2 and at ' 'least one sampler uniform is present.\n', ); if (invalidFloats) { buffer.write('The shader has fewer than two float uniforms.\n'); } if (invalidSampler) { buffer.write('The shader is missing a sampler uniform.\n'); } throw StateError(buffer.toString()); } return _FragmentShaderImageFilter(shader); } /// Whether [ImageFilter.shader] is supported on the current backend. /// /// > [!WARNING] /// > This property will only return true when the Impeller rendering engine is enabled. /// > Attempting to create an [ImageFilter.shader] when this property is `false` will throw an [UnsupportedError]. static bool get isShaderFilterSupported => _impellerEnabled; // Converts this to a native DlImageFilter. See the comments of this method in // subclasses for the exact type of DlImageFilter this method converts to. _ImageFilter _toNativeImageFilter(); /// The description text to show when the filter is part of a composite /// [ImageFilter] created using [ImageFilter.compose]. String get debugShortDescription => toString(); } class _MatrixImageFilter implements ImageFilter { _MatrixImageFilter({required this.data, required this.filterQuality}); final Float64List data; final FilterQuality filterQuality; // MakeMatrixFilterRowMajor255 late final _ImageFilter nativeFilter = _ImageFilter.matrix(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; @override String get debugShortDescription => 'matrix($data, $filterQuality)'; @override String toString() => 'ImageFilter.matrix($data, $filterQuality)'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _MatrixImageFilter && other.filterQuality == filterQuality && _listEquals(other.data, data); } @override int get hashCode => Object.hash(filterQuality, Object.hashAll(data)); } class _GaussianBlurImageFilter implements ImageFilter { _GaussianBlurImageFilter({ required this.sigmaX, required this.sigmaY, required this.tileMode, this.bounds, }); final double sigmaX; final double sigmaY; final TileMode? tileMode; final Rect? bounds; // MakeBlurFilter late final _ImageFilter nativeFilter = _ImageFilter.blur(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; String get _modeString { switch (tileMode) { case TileMode.clamp: return 'clamp'; case TileMode.mirror: return 'mirror'; case TileMode.repeated: return 'repeated'; case TileMode.decal: return 'decal'; case null: return 'unspecified'; } } @override String get debugShortDescription => 'blur($sigmaX, $sigmaY, $_modeString${_boundsString()})'; String _boundsString() => bounds == null ? '' : ', bounds: $bounds'; @override String toString() => 'ImageFilter.blur($sigmaX, $sigmaY, $_modeString${_boundsString()})'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _GaussianBlurImageFilter && other.sigmaX == sigmaX && other.sigmaY == sigmaY && other.bounds == bounds && other.tileMode == tileMode; } @override int get hashCode => Object.hash(sigmaX, sigmaY, bounds, tileMode); } class _DilateImageFilter implements ImageFilter { _DilateImageFilter({required this.radiusX, required this.radiusY}); final double radiusX; final double radiusY; late final _ImageFilter nativeFilter = _ImageFilter.dilate(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; @override String get debugShortDescription => 'dilate($radiusX, $radiusY)'; @override String toString() => 'ImageFilter.dilate($radiusX, $radiusY)'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _DilateImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override int get hashCode => Object.hash(radiusX, radiusY); } class _ErodeImageFilter implements ImageFilter { _ErodeImageFilter({required this.radiusX, required this.radiusY}); final double radiusX; final double radiusY; late final _ImageFilter nativeFilter = _ImageFilter.erode(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; @override String get debugShortDescription => 'erode($radiusX, $radiusY)'; @override String toString() => 'ImageFilter.erode($radiusX, $radiusY)'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _ErodeImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override int get hashCode => Object.hash(radiusX, radiusY); } class _ComposeImageFilter implements ImageFilter { _ComposeImageFilter({required this.innerFilter, required this.outerFilter}); final ImageFilter innerFilter; final ImageFilter outerFilter; // DlComposeImageFilter late final _ImageFilter nativeFilter = _ImageFilter.composed(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; @override String get debugShortDescription => '${innerFilter.debugShortDescription} -> ${outerFilter.debugShortDescription}'; @override String toString() => 'ImageFilter.compose(source -> $debugShortDescription -> result)'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _ComposeImageFilter && other.innerFilter == innerFilter && other.outerFilter == outerFilter; } @override int get hashCode => Object.hash(innerFilter, outerFilter); } class _FragmentShaderImageFilter implements ImageFilter { _FragmentShaderImageFilter(this.shader); final FragmentShader shader; late final _ImageFilter nativeFilter = _ImageFilter.shader(this); @override _ImageFilter _toNativeImageFilter() => nativeFilter; @override String get debugShortDescription => 'shader'; @override String toString() => 'ImageFilter.shader(Shader#${shader.hashCode})'; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is _FragmentShaderImageFilter && other.shader == shader && _equals(nativeFilter, other.nativeFilter); } @Native(symbol: 'ImageFilter::equals') external static bool _equals(_ImageFilter a, _ImageFilter b); @override int get hashCode => shader.hashCode; } /// An [ImageFilter] that is backed by a native DlImageFilter. /// /// This is a private class, rather than being the implementation of the public /// ImageFilter, because we want ImageFilter to be efficiently comparable, so that /// widgets can check for ImageFilter equality to avoid repainting. base class _ImageFilter extends NativeFieldWrapperClass1 { /// Creates an image filter that applies a Gaussian blur. _ImageFilter.blur(_GaussianBlurImageFilter filter) : creator = filter { _constructor(); final Rect bounds = filter.bounds ?? Rect.zero; _initBlur( filter.sigmaX, filter.sigmaY, filter.tileMode?.index ?? -1, filter.bounds != null, bounds.left, bounds.top, bounds.right, bounds.bottom, ); } /// Creates an image filter that dilates each input pixel's channel values /// to the max value within the given radii along the x and y axes. _ImageFilter.dilate(_DilateImageFilter filter) : creator = filter { _constructor(); _initDilate(filter.radiusX, filter.radiusY); } /// Create a filter that erodes each input pixel's channel values /// to the minimum channel value within the given radii along the x and y axes. _ImageFilter.erode(_ErodeImageFilter filter) : creator = filter { _constructor(); _initErode(filter.radiusX, filter.radiusY); } /// Creates an image filter that applies a matrix transformation. /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. _ImageFilter.matrix(_MatrixImageFilter filter) : creator = filter { if (filter.data.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } _constructor(); _initMatrix(filter.data, filter.filterQuality.index); } /// Converts a color filter to an image filter. _ImageFilter.fromColorFilter(ColorFilter filter) : creator = filter { _constructor(); final _ColorFilter? nativeFilter = filter._toNativeColorFilter(); _initColorFilter(nativeFilter); } /// Composes `_innerFilter` with `_outerFilter`. _ImageFilter.composed(_ComposeImageFilter filter) : creator = filter { _constructor(); final _ImageFilter nativeFilterInner = filter.innerFilter._toNativeImageFilter(); final _ImageFilter nativeFilterOuter = filter.outerFilter._toNativeImageFilter(); _initComposed(nativeFilterOuter, nativeFilterInner); } _ImageFilter.shader(_FragmentShaderImageFilter filter) : creator = filter { _constructor(); _initShader(filter.shader); } @Native(symbol: 'ImageFilter::Create') external void _constructor(); @Native< Void Function(Pointer, Double, Double, Int32, Bool, Double, Double, Double, Double) >(symbol: 'ImageFilter::initBlur', isLeaf: true) external void _initBlur( double sigmaX, double sigmaY, int tileMode, bool bounded, double boundsLeft, double boundsTop, double boundsRight, double boundsBottom, ); @Native, Double, Double)>( symbol: 'ImageFilter::initDilate', isLeaf: true, ) external void _initDilate(double radiusX, double radiusY); @Native, Double, Double)>( symbol: 'ImageFilter::initErode', isLeaf: true, ) external void _initErode(double radiusX, double radiusY); @Native, Handle, Int32)>(symbol: 'ImageFilter::initMatrix') external void _initMatrix(Float64List matrix4, int filterQuality); @Native, Pointer)>(symbol: 'ImageFilter::initColorFilter') external void _initColorFilter(_ColorFilter? colorFilter); @Native, Pointer, Pointer)>( symbol: 'ImageFilter::initComposeFilter', ) external void _initComposed(_ImageFilter outerFilter, _ImageFilter innerFilter); @Native, Pointer)>(symbol: 'ImageFilter::initShader') external void _initShader(FragmentShader shader); /// The original Dart object that created the native wrapper, which retains /// the values used for the filter. final ImageFilter creator; } /// Base class for objects such as [Gradient] and [ImageShader] which /// correspond to shaders as used by [Paint.shader]. base class Shader extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @pragma('vm:entry-point') Shader._(); bool _debugDisposed = false; /// Whether [dispose] has been called. /// /// This must only be used when asserts are enabled. Otherwise, it will throw. bool get debugDisposed { late bool disposed; assert(() { disposed = _debugDisposed; return true; }()); return disposed; } /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// /// The underlying memory allocated by this object will be retained beyond /// this call if it is still needed by another object that has not been /// disposed. For example, a [Picture] that has not been disposed that /// refers to an [ImageShader] may keep its underlying resources alive. /// /// Classes that override this method must call `super.dispose()`. void dispose() { assert(() { assert(!_debugDisposed, 'A Shader cannot be disposed more than once.'); _debugDisposed = true; return true; }()); } } /// Defines how to handle areas outside the defined bounds of a gradient or image filter. /// /// ## For Gradients /// /// Gradients are defined with some specific bounds creating an inner area and an outer area, and `TileMode` controls how colors /// are determined for areas outside these bounds: /// /// - **Linear gradients**: The inner area is the area between two points /// (typically referred to as `start` and `end` in the gradient API), or more precisely, /// it's the area between the parallel lines that are orthogonal to the line drawn between the two points. /// Colors outside this area are determined by the `TileMode`. /// /// - **Radial gradients**: The inner area is the disc defined by a center and radius. /// Colors outside this disc are determined by the `TileMode`. /// /// - **Sweep gradients**: The inner area is the angular sector between `startAngle` /// and `endAngle`. Colors outside this sector are determined by the `TileMode`. /// /// ## For Image Filters /// /// When applying filters (like blur) that sample colors from outside an image's bounds, /// `TileMode` defines how those out-of-bounds samples are determined: /// /// - It controls what color values are used when the filter needs to sample /// from areas outside the original image. /// - This is particularly important for effects like blurring near image edges. /// /// See also: /// /// * [painting.Gradient], the superclass for [LinearGradient] and /// [RadialGradient], as used by [BoxDecoration] et al, which works in /// relative coordinates and can create a [Shader] representing the gradient /// for a particular [Rect] on demand. /// * [dart:ui.Gradient], the low-level class used when dealing with the /// [Paint.shader] property directly, with its [Gradient.linear] and /// [Gradient.radial] constructors. /// * [dart:ui.ImageFilter.blur], an ImageFilter that may sometimes need to /// read samples from outside an image to combine with the pixels near the /// edge of the image. // These enum values must be kept in sync with DlTileMode. enum TileMode { /// Samples beyond the edge are clamped to the nearest color in the defined inner area. /// /// For gradients, this means the region outside the inner area is painted with /// the color at the end of the color stop list closest to that region. /// /// For sweep gradients specifically, the entire area outside the angular sector /// defined by [startAngle] and [endAngle] will be painted with the color at the /// end of the color stop list closest to that region. /// /// An image filter will substitute the nearest edge pixel for any samples taken from /// outside its source image. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png) clamp, /// Samples beyond the edge are repeated from the far end of the defined area. /// /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then /// repeated from 1.0 to 2.0, 2.0 to 3.0, and so forth (and for linear gradients, similarly /// from -1.0 to 0.0, -2.0 to -1.0, etc). /// /// For sweep gradients, the gradient pattern is repeated in the same direction /// (clockwise) for angles beyond [endAngle] and before [startAngle]. /// /// An image filter will treat its source image as if it were tiled across the enlarged /// sample space from which it reads, each tile in the same orientation as the base image. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png) repeated, /// Samples beyond the edge are mirrored back and forth across the defined area. /// /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then /// repeated backwards from 2.0 to 1.0, then forwards from 2.0 to 3.0, then backwards /// again from 4.0 to 3.0, and so forth (and for linear gradients, similarly in the /// negative direction). /// /// For sweep gradients, the gradient pattern is mirrored back and forth as the angle /// increases beyond [endAngle] or decreases below [startAngle]. /// /// An image filter will treat its source image as tiled in an alternating forwards and /// backwards or upwards and downwards direction across the sample space from which /// it is reading. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png) mirror, /// Samples beyond the edge are treated as transparent black. /// /// A gradient will render transparency over any region that is outside the circle of a /// radial gradient, outside the parallel lines that define the inner area of a linear /// gradient, or outside the angular sector of a sweep gradient. /// /// For sweep gradients, only the sector between [startAngle] and [endAngle] will be /// painted; all other areas will be transparent. /// /// An image filter will substitute transparent black for any sample it must read from /// outside its source image. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png) decal, } Float32List _encodeWideColorList(List colors) { final int colorCount = colors.length; final result = Float32List(colorCount * 4); for (var i = 0; i < colorCount; i++) { final Color colorXr = colors[i].withValues(colorSpace: ColorSpace.extendedSRGB); result[i * 4 + 0] = colorXr.a; result[i * 4 + 1] = colorXr.r; result[i * 4 + 2] = colorXr.g; result[i * 4 + 3] = colorXr.b; } return result; } Int32List _encodeColorList(List colors) { final int colorCount = colors.length; final result = Int32List(colorCount); for (var i = 0; i < colorCount; i++) { result[i] = colors[i].value; } return result; } Float32List _encodePointList(List points) { final int pointCount = points.length; final result = Float32List(pointCount * 2); for (var i = 0; i < pointCount; i++) { final int xIndex = i * 2; final int yIndex = xIndex + 1; final Offset point = points[i]; assert(_offsetIsValid(point)); result[xIndex] = point.dx; result[yIndex] = point.dy; } return result; } Float32List _encodeTwoPoints(Offset pointA, Offset pointB) { assert(_offsetIsValid(pointA)); assert(_offsetIsValid(pointB)); final result = Float32List(4); result[0] = pointA.dx; result[1] = pointA.dy; result[2] = pointB.dx; result[3] = pointB.dy; return result; } /// A shader (as used by [Paint.shader]) that renders a color gradient. /// /// There are several types of gradients, represented by the various constructors /// on this class. /// /// See also: /// /// * [Gradient](https://api.flutter.dev/flutter/painting/Gradient-class.html), the class in the [painting] library. /// base class Gradient extends Shader { /// Creates a linear gradient from `from` to `to`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and /// `color` must therefore only have two entries). Stop values less than 0.0 /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded /// down to 1.0. Each stop value must be greater than or equal to the previous /// stop value. Stop values that do not meet this criteria will be rounded up /// to the previous stop value. /// /// The behavior before `from` and after `to` is described by the `tileMode` /// argument. For details, see the [TileMode] enum. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png) /// /// If `from`, `to`, `colors`, or `tileMode` are null, or if `colors` or /// `colorStops` contain null values, this constructor will throw a /// [NoSuchMethodError]. /// /// If `matrix4` is provided, the gradient fill will be transformed by the /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must /// be a column-major matrix packed into a list of 16 values. Gradient.linear( Offset from, Offset to, List colors, [ List? colorStops, TileMode tileMode = TileMode.clamp, Float64List? matrix4, ]) : assert(_offsetIsValid(from)), assert(_offsetIsValid(to)), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); final Float32List endPointsBuffer = _encodeTwoPoints(from, to); final Float32List colorsBuffer = _encodeWideColorList(colors); final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); _constructor(); _initLinear(endPointsBuffer, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); } /// Creates a radial gradient centered at `center` that ends at `radius` /// distance from the center. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and /// `color` must therefore only have two entries). Stop values less than 0.0 /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded /// down to 1.0. Each stop value must be greater than or equal to the previous /// stop value. Stop values that do not meet this criteria will be rounded up /// to the previous stop value. /// /// The behavior before and after the radius is described by the `tileMode` /// argument. For details, see the [TileMode] enum. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png) /// /// If `center`, `radius`, `colors`, or `tileMode` are null, or if `colors` or /// `colorStops` contain null values, this constructor will throw a /// [NoSuchMethodError]. /// /// If `matrix4` is provided, the gradient fill will be transformed by the /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must /// be a column-major matrix packed into a list of 16 values. /// /// If `focal` is provided and not equal to `center` and `focalRadius` is /// provided and not equal to 0.0, the generated shader will be a two point /// conical radial gradient, with `focal` being the center of the focal /// circle and `focalRadius` being the radius of that circle. If `focal` is /// provided and not equal to `center`, at least one of the two offsets must /// not be equal to [Offset.zero]. Gradient.radial( Offset center, double radius, List colors, [ List? colorStops, TileMode tileMode = TileMode.clamp, Float64List? matrix4, Offset? focal, double focalRadius = 0.0, ]) : assert(_offsetIsValid(center)), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); final Float32List colorsBuffer = _encodeWideColorList(colors); // If focal is null or focal radius is null, this should be treated as a regular radial gradient // If focal == center and the focal radius is 0.0, it's still a regular radial gradient if (focal == null || (focal == center && focalRadius == 0.0)) { _constructor(); _initRadial( center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4, ); } else { assert( center != Offset.zero || focal != Offset.zero, ); // will result in exception(s) in Skia side _constructor(); _initConical( focal.dx, focal.dy, focalRadius, center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4, ); } } /// Creates a sweep gradient centered at `center` that starts at `startAngle` /// and ends at `endAngle`. /// /// `startAngle` and `endAngle` should be provided in radians, with zero /// radians being the horizontal line to the right of the `center` and with /// positive angles going clockwise around the `center`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `colors[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied /// (and `colors` must therefore only have two entries). Stop values less than /// 0.0 will be rounded up to 0.0 and stop values greater than 1.0 will be /// rounded down to 1.0. Each stop value must be greater than or equal to the /// previous stop value. Stop values that do not meet this criteria will be /// rounded up to the previous stop value. /// /// The `startAngle` and `endAngle` parameters define the angular sector to be /// painted. Angles are measured in radians clockwise from the positive x-axis. /// Values outside the range `[0, 2Ï€]` are normalized to this range using modulo /// arithmetic. The gradient is only painted in the sector between `startAngle` /// and `endAngle`. The `tileMode` determines how the gradient behaves outside /// this sector. /// /// The `tileMode` argument specifies how the gradient should handle areas /// outside the angular sector defined by `startAngle` and `endAngle`: /// /// The behavior before `startAngle` and after `endAngle` is described by the /// `tileMode` argument. For details, see the [TileMode] enum. /// /// * [TileMode.clamp]: The edge colors are extended to infinity. /// * [TileMode.mirror]: The gradient is repeated, alternating direction each time. /// * [TileMode.repeated]: The gradient is repeated in the same direction. /// * [TileMode.decal]: Only the colors within the gradient's angular sector are /// drawn, with transparent black elsewhere. /// /// The [colorStops] argument must have the same number of values as [colors], /// if specified. It specifies the position of each color stop between 0.0 and /// 1.0. If it is null, a uniform distribution is assumed. The stop values must /// be in ascending order. A stop value of 0.0 corresponds to [startAngle], and /// a stop value of 1.0 corresponds to [endAngle]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png) /// /// If `center`, `colors`, `tileMode`, `startAngle`, or `endAngle` are null, /// or if `colors` or `colorStops` contain null values, this constructor will /// throw a [NoSuchMethodError]. /// /// If `matrix4` is provided, the gradient fill will be transformed by the /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must /// be a column-major matrix packed into a list of 16 values. Gradient.sweep( Offset center, List colors, [ List? colorStops, TileMode tileMode = TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, Float64List? matrix4, ]) : assert(_offsetIsValid(center)), assert(startAngle < endAngle), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); final Float32List colorsBuffer = _encodeWideColorList(colors); final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); _constructor(); _initSweep( center.dx, center.dy, colorsBuffer, colorStopsBuffer, tileMode.index, startAngle, endAngle, matrix4, ); } @Native(symbol: 'Gradient::Create') external void _constructor(); @Native, Handle, Handle, Handle, Int32, Handle)>( symbol: 'Gradient::initLinear', ) external void _initLinear( Float32List endPoints, Float32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4, ); @Native, Double, Double, Double, Handle, Handle, Int32, Handle)>( symbol: 'Gradient::initRadial', ) external void _initRadial( double centerX, double centerY, double radius, Float32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4, ); @Native< Void Function( Pointer, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32, Handle, ) >(symbol: 'Gradient::initTwoPointConical') external void _initConical( double startX, double startY, double startRadius, double endX, double endY, double endRadius, Float32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4, ); @Native< Void Function(Pointer, Double, Double, Handle, Handle, Int32, Double, Double, Handle) >(symbol: 'Gradient::initSweep') external void _initSweep( double centerX, double centerY, Float32List colors, Float32List? colorStops, int tileMode, double startAngle, double endAngle, Float64List? matrix, ); static void _validateColorStops(List colors, List? colorStops) { if (colorStops == null) { if (colors.length != 2) { throw ArgumentError('"colors" must have length 2 if "colorStops" is omitted.'); } } else { if (colors.length != colorStops.length) { throw ArgumentError('"colors" and "colorStops" arguments must have equal length.'); } } } } /// A shader (as used by [Paint.shader]) that tiles an image. base class ImageShader extends Shader { /// Creates an image-tiling shader. /// /// The first argument specifies the image to render. The /// [decodeImageFromList] function can be used to decode an image from bytes /// into the form expected here. (In production code, starting from /// [instantiateImageCodec] may be preferable.) /// /// The second and third arguments specify the [TileMode] for the x direction /// and y direction respectively. [TileMode.repeated] can be used for tiling /// images. /// /// The fourth argument gives the matrix to apply to the effect. The /// expression `Matrix4.identity().storage` creates a [Float64List] /// prepopulated with the identity matrix. /// /// All the arguments are required and must not be null, except for /// [filterQuality]. If [filterQuality] is not specified at construction time /// it will be deduced from the environment where it is used, such as from /// [Paint.filterQuality]. @pragma('vm:entry-point') ImageShader( Image image, TileMode tmx, TileMode tmy, Float64List matrix4, { FilterQuality? filterQuality, }) : assert(!image.debugDisposed), super._() { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } _constructor(); final String? error = _initWithImage( image._image, tmx.index, tmy.index, filterQuality?.index ?? -1, matrix4, ); if (error != null) { throw Exception(error); } } @override void dispose() { super.dispose(); _dispose(); } @Native(symbol: 'ImageShader::Create') external void _constructor(); @Native, Pointer, Int32, Int32, Int32, Handle)>( symbol: 'ImageShader::initWithImage', ) external String? _initWithImage( _Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4, ); /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'ImageShader::dispose') external void _dispose(); } typedef _UniformFloatInfo = ({int index, int size}); /// An instance of [FragmentProgram] creates [Shader] objects (as used by /// [Paint.shader]). /// /// For more information, see the website /// [documentation]( https://docs.flutter.dev/development/ui/advanced/shaders). base class FragmentProgram extends NativeFieldWrapperClass1 { @pragma('vm:entry-point') FragmentProgram._fromAsset(String assetKey) { _constructor(); final String result = _initFromAsset(assetKey); if (result.isNotEmpty) { throw Exception(result); } assert(() { _debugName = assetKey; return true; }()); } String? _debugName; final List> _shaders = >[]; /// Creates a fragment program from the asset with key [assetKey]. /// /// The asset must be a file produced as the output of the `impellerc` /// compiler. The constructed object should then be reused via the /// [fragmentShader] method to create [Shader] objects that can be used by /// [Paint.shader]. static Future fromAsset(String assetKey) { // The flutter tool converts all asset keys with spaces into URI // encoded paths (replacing ' ' with '%20', for example). We perform // the same encoding here so that users can load assets with the same // key they have written in the pubspec. final String encodedKey = Uri(path: Uri.encodeFull(assetKey)).path; final FragmentProgram? program = _shaderRegistry[encodedKey]; if (program != null) { return Future.value(program); } return Future.microtask(() { final program = FragmentProgram._fromAsset(encodedKey); _shaderRegistry[encodedKey] = program; return program; }); } // This is a cache of shaders that have been loaded by // FragmentProgram.fromAsset. It holds a strong reference to theFragmentPrograms // The native engine will retain the resources associated with this shader // program (PSO variants) until shutdown, so maintaining a strong reference // here ensures we do not perform extra work if the dart object is continually // re-initialized. static final Map _shaderRegistry = {}; // This is called on hot reload when a shader has changed. static void _reinitializeShader(String assetKey) { // If a shader for the asset isn't already registered, then there's no // need to reinitialize it. The new shader will be loaded and initialized // the next time the program access it. final FragmentProgram? program = _shaderRegistry[assetKey]; if (program == null) { return; } final String result = program._initFromAsset(assetKey); if (result.isNotEmpty) { throw result; // ignore: only_throw_errors } // Update all the named bindings. program._shaders.removeWhere((WeakReference shaderReference) { final FragmentShader? shader = shaderReference.target; if (shader == null) { return true; } shader._reinitialize(); shader._slots.removeWhere((WeakReference slotReference) { final UniformFloatSlot? slot = slotReference.target; if (slot == null) { return true; } if (!program._hasUniform(slot.name)) { return true; } final _UniformFloatInfo info = program._getUniformFloatInfo(slot.name); slot._shaderIndex = info.index + slot.index; return false; }); shader._samplers.removeWhere((WeakReference ref) { final ImageSamplerSlot? slot = ref.target; if (slot == null) { return true; } slot._shaderIndex = program._getImageSamplerIndex(slot.name); return false; }); return false; }); } bool _hasUniform(String name) { return _uniformInfo.any((dynamic entryDynamic) { final entry = entryDynamic! as Map; return entry['name'] == name || (entry['type'] == 'Struct' && (entry['struct_field_names']! as List).contains(name)); }); } int _getImageSamplerIndex(String name) { var index = 0; var found = false; for (final Object? entryDynamic in _uniformInfo) { final entry = entryDynamic! as Map; if (entry['name'] == name) { if (entry['type'] != 'SampledImage') { throw ArgumentError('Uniform "$name" is not an image sampler.'); } found = true; break; } else if (entry['type'] == 'SampledImage') { index += 1; } } if (!found) { if (_hasUniform(name)) { throw ArgumentError('Uniform "$name" is not an image sampler.'); } else { throw ArgumentError('No uniform named "$name".'); } } return index; } _UniformFloatInfo _getUniformFloatInfo(String name) { var offset = 0; var sizeInFloats = 0; var found = false; const sizeOfFloat = 4; for (final Object? entryDynamic in _uniformInfo) { final entry = entryDynamic! as Map; if (found) { break; } if (entry['type'] == 'Struct') { final elementNames = entry['struct_field_names']! as List; final elementSizes = entry['struct_field_bytes']! as List; for (var i = 0; i < elementNames.length; ++i) { final elementName = elementNames[i]! as String; final elementSize = elementSizes[i]! as int; sizeInFloats = elementSize ~/ sizeOfFloat; if (elementName == name) { found = true; break; } offset += sizeInFloats; } } else { sizeInFloats = (entry['size'] as int? ?? 0) ~/ sizeOfFloat; if (entry['name'] == name) { found = true; break; } offset += sizeInFloats; } } if (!found) { throw ArgumentError('No uniform named "$name".'); } return (index: offset, size: sizeInFloats); } @pragma('vm:entry-point') late int _uniformFloatCount; @pragma('vm:entry-point') late int _samplerCount; @pragma('vm:entry-point') late List _uniformInfo; @Native(symbol: 'FragmentProgram::Create') external void _constructor(); @Native, Handle)>(symbol: 'FragmentProgram::initFromAsset') external String _initFromAsset(String assetKey); /// Returns a fresh instance of [FragmentShader]. FragmentShader fragmentShader() { final result = FragmentShader._(this, debugName: _debugName); _shaders.removeWhere((WeakReference ref) => ref.target == null); _shaders.add(WeakReference(result)); return result; } } /// A binding into a uniform defined in a shader. Used now to restrict the types /// of UniformArrays that can be created. sealed class UniformType {} /// A binding to a uniform of type float. Calling [set] on this object updates /// a float uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformFloat('uColor', 0).set(1.0); /// shader.getUniformFloat('uColor', 1).set(0.0); /// shader.getUniformFloat('uColor', 2).set(0.0); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformFloat] - How [UniformFloatSlot] instances are acquired. /// base class UniformFloatSlot extends UniformType { UniformFloatSlot._(this._shader, this.name, this.index, this._shaderIndex); /// Set the float value of the bound uniform. void set(double val) { _shader.setFloat(_shaderIndex, val); } /// VisibleForTesting: This is the index one would use with /// [FragmentShader.setFloat] for this uniform. int get shaderIndex { return _shaderIndex; } final FragmentShader _shader; int _shaderIndex; /// The name of the bound uniform. final String name; /// The offset into the bound uniform. For example, 1 for `.y` or 2 for `.b`. final int index; } /// A binding to a uniform of type vec2. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformVec2('uSize').set(100, 100); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformVec2] - How [UniformVec2Slot] instances are acquired. /// base class UniformVec2Slot extends UniformType { UniformVec2Slot._(this._xSlot, this._ySlot); /// Set the float value of the bound uniform. void set(double x, double y) { _xSlot.set(x); _ySlot.set(y); } final UniformFloatSlot _xSlot, _ySlot; } /// A binding to a uniform of type vec3. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader, double time) { /// shader.getUniformVec3('uScaledTime').set(time, time*0.1, time*0.01); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformVec3] - How [UniformVec3Slot] instances are acquired. /// base class UniformVec3Slot extends UniformType { UniformVec3Slot._(this._xSlot, this._ySlot, this._zSlot); /// Set the float value of the bound uniform. void set(double x, double y, double z) { _xSlot.set(x); _ySlot.set(y); _zSlot.set(z); } final UniformFloatSlot _xSlot, _ySlot, _zSlot; } /// A binding to a uniform of type vec4. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformVec4('uColor').set(1.0, 0.0, 1.0, 1.0); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformVec4] - How [UniformVec4Slot] instances are acquired. /// base class UniformVec4Slot extends UniformType { UniformVec4Slot._(this._xSlot, this._ySlot, this._zSlot, this._wSlot); /// Set the float value of the bound uniform. void set(double x, double y, double z, double w) { _xSlot.set(x); _ySlot.set(y); _zSlot.set(z); _wSlot.set(w); } final UniformFloatSlot _xSlot, _ySlot, _zSlot, _wSlot; } /// A binding to a uniform of type mat2. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat2('uIdentity').set( /// 1.0, 0.0, /// 0.0, 1.0 /// ); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformMat2] - How [UniformMat2Slot] instances are acquired. /// base class UniformMat2Slot extends UniformType { UniformMat2Slot._(this._m00, this._m01, this._m10, this._m11); /// Set the float value of the matrix in row-major order. void set(double m00, double m01, double m10, double m11) { _m00.set(m00); _m01.set(m01); _m10.set(m10); _m11.set(m11); } final UniformFloatSlot _m00, _m01, _m10, _m11; } /// A binding to a uniform of type mat3. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat3('uIdentity').set( /// 1.0, 0.0, 0.0, /// 0.0, 1.0, 0.0, /// 0.0, 0.0, 1.0 /// ); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformMat3] - How [UniformMat3Slot] instances are acquired. /// base class UniformMat3Slot extends UniformType { UniformMat3Slot._( this._m00, this._m01, this._m02, this._m10, this._m11, this._m12, this._m20, this._m21, this._m22, ); /// Set the float value of the matrix in row-major order. void set( double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22, ) { _m00.set(m00); _m01.set(m01); _m02.set(m02); _m10.set(m10); _m11.set(m11); _m12.set(m12); _m20.set(m20); _m21.set(m21); _m22.set(m22); } final UniformFloatSlot _m00, _m01, _m02, _m10, _m11, _m12, _m20, _m21, _m22; } /// A binding to a uniform of type mat4. Calling [set] on this object updates /// the uniform's value. /// /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat4('uIdentity').set( /// 1.0, 0.0, 0.0, 0.0, /// 0.0, 1.0, 0.0, 0.0, /// 0.0, 0.0, 1.0, 0.0, /// 0.0, 0.0, 0.0, 1.0 /// ); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformMat4] - How [UniformMat4Slot] instances are acquired. /// base class UniformMat4Slot extends UniformType { UniformMat4Slot._( this._m00, this._m01, this._m02, this._m03, this._m10, this._m11, this._m12, this._m13, this._m20, this._m21, this._m22, this._m23, this._m30, this._m31, this._m32, this._m33, ); /// Set the float value of the matrix in row-major order. void set( double m00, double m01, double m02, double m03, double m10, double m11, double m12, double m13, double m20, double m21, double m22, double m23, double m30, double m31, double m32, double m33, ) { _m00.set(m00); _m01.set(m01); _m02.set(m02); _m03.set(m03); _m10.set(m10); _m11.set(m11); _m12.set(m12); _m13.set(m13); _m20.set(m20); _m21.set(m21); _m22.set(m22); _m23.set(m23); _m30.set(m30); _m31.set(m31); _m32.set(m32); _m33.set(m33); } final UniformFloatSlot _m00, _m01, _m02, _m03, _m10, _m11, _m12, _m13, _m20, _m21, _m22, _m23, _m30, _m31, _m32, _m33; } /// An array of bindings to uniforms of the same type T. Access elements via [] and /// set them individually. /// Example: /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray colors = shader.getUniformVec4Array('uColorArray'); /// colors[0].set(1.0, 0.0, 1.0, 0.3); /// } /// ``` /// /// See also: /// [FragmentShader.getUniformFloatArray] - How [UniformArray] instances are acquired. /// class UniformArray { UniformArray._(this._elements); /// Access an element of the UniformArray. T operator [](int index) { return _elements[index]; } /// The number of Uniforms in the UniformArray. int get length => _elements.length; final List _elements; } /// A binding to a shader's image sampler. Calling [set] on this object updates /// a sampler's bound image. base class ImageSamplerSlot { ImageSamplerSlot._(this._shader, this.name, this._shaderIndex); final FragmentShader _shader; int _shaderIndex; /// Set the [Image] value for the bound sampler associated with this slot. void set(Image val) { _shader.setImageSampler(_shaderIndex, val); } /// VisibleForTesting: This is the index one would use with /// [FragmentShader.setImageSampler] for this sampler. int get shaderIndex => _shaderIndex; /// The name of the bound uniform. final String name; } /// A [Shader] generated from a [FragmentProgram]. /// /// Instances of this class can be obtained from the /// [FragmentProgram.fragmentShader] method. The float uniforms list is /// initialized to the size expected by the shader and is zero-filled. Uniforms /// of float type can then be set by calling [setFloat]. Sampler uniforms are /// set by calling [setImageSampler]. /// /// A [FragmentShader] can be re-used, and this is an efficient way to avoid /// allocating and re-initializing the uniform buffer and samplers. However, /// if two [FragmentShader] objects with different float uniforms or samplers /// are required to exist simultaneously, they must be obtained from two /// different calls to [FragmentProgram.fragmentShader]. base class FragmentShader extends Shader { FragmentShader._(this._program, {String? debugName}) : _debugName = debugName, super._() { _floats = _constructor(_program, _program._uniformFloatCount, _program._samplerCount); } final FragmentProgram _program; final String? _debugName; static final Float32List _kEmptyFloat32List = Float32List(0); Float32List _floats = _kEmptyFloat32List; final List> _slots = >[]; final List> _samplers = >[]; void _reinitialize() { _floats = _constructor(_program, _program._uniformFloatCount, _program._samplerCount); } List _getSlotsForUniform(String name, int size) { final _UniformFloatInfo info = _program._getUniformFloatInfo(name); if (info.size != size) { throw ArgumentError('Uniform `$name` has size ${info.size}, not size $size.'); } final slots = List.generate( size, (i) => UniformFloatSlot._(this, name, i, info.index + i), ); _slots.removeWhere((WeakReference ref) => ref.target == null); _slots.addAll(slots.map((slot) => WeakReference(slot))); return slots; } /// Sets the float uniform at [index] to [value]. /// /// All uniforms defined in a fragment shader that are not samplers must be /// set through this method. This includes floats and vec2, vec3, and vec4. /// The correct index for each uniform is determined by the order of the /// uniforms as defined in the fragment program, ignoring any samplers. For /// data types that are composed of multiple floats such as a vec4, more than /// one call to [setFloat] is required. /// /// For example, given the following uniforms in a fragment program: /// /// ```glsl /// uniform float uScale; /// uniform sampler2D uTexture; /// uniform vec2 uMagnitude; /// uniform vec4 uColor; /// ``` /// /// Then the corresponding Dart code to correctly initialize these uniforms /// is: /// /// ```dart /// void updateShader(ui.FragmentShader shader, Color color, ui.Image image) { /// shader.setFloat(0, 23); // uScale /// shader.setFloat(1, 114); // uMagnitude x /// shader.setFloat(2, 83); // uMagnitude y /// /// // Convert color to premultiplied opacity. /// shader.setFloat(3, color.r * color.a); // uColor r /// shader.setFloat(4, color.g * color.a); // uColor g /// shader.setFloat(5, color.b * color.a); // uColor b /// shader.setFloat(6, color.a); // uColor a /// /// // initialize sampler uniform. /// shader.setImageSampler(0, image); /// } /// ``` /// /// Note how the indexes used does not count the `sampler2D` uniform. This /// uniform will be set separately with [setImageSampler], with the index starting /// over at 0. /// /// Any float uniforms that are left uninitialized will default to `0`. void setFloat(int index, double value) { assert(!debugDisposed, 'Tried to accesss uniforms on a disposed Shader: $this'); _floats[index] = value; } /// Access the float binding for uniform named [name] with optional offset /// [index]. Example [index] values: 1 for 'foo.y', 2 for 'foo.b'. /// /// Example: /// /// ```glsl /// uniform float uScale; /// uniform sampler2D uTexture; /// uniform vec2 uMagnitude; /// uniform vec4 uColor; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformFloat('uScale'); /// shader.getUniformFloat('uMagnitude', 0); /// shader.getUniformFloat('uMagnitude', 1); /// shader.getUniformFloat('uColor', 0); /// shader.getUniformFloat('uColor', 1); /// shader.getUniformFloat('uColor', 2); /// shader.getUniformFloat('uColor', 3); /// } /// ``` UniformFloatSlot getUniformFloat(String name, [int? index]) { index ??= 0; final _UniformFloatInfo info = _program._getUniformFloatInfo(name); IndexError.check(index, info.size, message: 'Index `$index` out of bounds for `$name`.'); final result = UniformFloatSlot._(this, name, index, info.index + index); _slots.removeWhere((WeakReference ref) => ref.target == null); _slots.add(WeakReference(result)); return result; } /// Access the float binding for a vec2 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform float uScale; /// uniform vec2 uMagnitude; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformFloat('uScale'); /// shader.getUniformVec2('uMagnitude'); /// } /// ``` UniformVec2Slot getUniformVec2(String name) { final List slots = _getSlotsForUniform(name, 2); return UniformVec2Slot._(slots[0], slots[1]); } /// Access the float binding for a vec3 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform float uScale; /// uniform vec3 uScaledTime; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformFloat('uScale'); /// shader.getUniformVec3('uScaledTime'); /// } /// ``` UniformVec3Slot getUniformVec3(String name) { final List slots = _getSlotsForUniform(name, 3); return UniformVec3Slot._(slots[0], slots[1], slots[2]); } /// Access the float binding for a vec4 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform float uScale; /// uniform vec4 uColor; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformFloat('uScale'); /// shader.getUniformVec4('uColor'); /// } /// ``` UniformVec4Slot getUniformVec4(String name) { final List slots = _getSlotsForUniform(name, 4); return UniformVec4Slot._(slots[0], slots[1], slots[2], slots[3]); } /// Access the float binding for a mat2 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat2 uIdentity; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat2('uIdentity'); /// } /// ``` UniformMat2Slot getUniformMat2(String name) { final List slots = _getSlotsForUniform(name, 4); return UniformMat2Slot._(slots[0], slots[1], slots[2], slots[3]); } /// Access the float binding for a mat3 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat3 uIdentity; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat3('uIdentity'); /// } /// ``` UniformMat3Slot getUniformMat3(String name) { final List slots = _getSlotsForUniform(name, 9); return UniformMat3Slot._( slots[0], slots[1], slots[2], slots[3], slots[4], slots[5], slots[6], slots[7], slots[8], ); } /// Access the float binding for a mat4 uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat4 uIdentity; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// shader.getUniformMat4('uIdentity'); /// } /// ``` UniformMat4Slot getUniformMat4(String name) { final List slots = _getSlotsForUniform(name, 16); return UniformMat4Slot._( slots[0], slots[1], slots[2], slots[3], slots[4], slots[5], slots[6], slots[7], slots[8], slots[9], slots[10], slots[11], slots[12], slots[13], slots[14], slots[15], ); } UniformArray _getUniformArray( String name, int elementSize, T Function(List slots) elementFactory, ) { final _UniformFloatInfo info = _program._getUniformFloatInfo(name); if (info.size % elementSize != 0) { throw ArgumentError( 'Uniform size (${info.size}) for "$name" is not a multiple of $elementSize.', ); } final int numElements = info.size ~/ elementSize; final elements = List.generate(numElements, (i) { final slots = List.generate( info.size, (j) => UniformFloatSlot._(this, name, j, info.index + i * elementSize + j), ); _slots.addAll(slots.map((slot) => WeakReference(slot))); return elementFactory(slots); }); _slots.removeWhere((WeakReference ref) => ref.target == null); return UniformArray._(elements); } /// Access the binding for a float[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform float[10] uValues; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray values = shader.getUniformFloatArray('uValues'); /// values[2].set(1.0); /// } /// ``` UniformArray getUniformFloatArray(String name) { return _getUniformArray(name, 1, (components) => components.first); } /// Access the binding for a vec2[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform vec2[10] uPositions; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray positions = shader.getUniformVec2Array('uPositions'); /// positions[2].set(6.0, 7.0); /// } /// ``` UniformArray getUniformVec2Array(String name) { return _getUniformArray( name, 2, // 2 floats per element (components) => UniformVec2Slot._( components[0], components[1], ), // Create Vec2 from two UniformFloat components ); } /// Access the binding for a vec3[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform vec3[10] uColors; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray colors = shader.getUniformVec3Array('uColors'); /// colors[0].set(1.0, 0.0, 1.0); /// } /// ``` UniformArray getUniformVec3Array(String name) { return _getUniformArray( name, 3, // 3 floats per element (components) => UniformVec3Slot._(components[0], components[1], components[2]), // Create Vec3 ); } /// Access the binding for a vec4[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform vec4[10] uColors; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray colors = shader.getUniformVec4Array('uColors'); /// colors[0].set(1.0, 0.0, 1.0, 0.5); /// } /// ``` UniformArray getUniformVec4Array(String name) { return _getUniformArray( name, 4, // 4 floats per element (components) => UniformVec4Slot._( components[0], components[1], components[2], components[3], ), // Create Vec4 ); } /// Access the binding for a mat2[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat2[10] uMatricies; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray mats = shader.getUniformMat2Array('uMatricies'); /// mats[0].set( /// 1.0, 0.0, /// 1.0, 0.5 /// ); /// } /// ``` UniformArray getUniformMat2Array(String name) { return _getUniformArray( name, 4, (components) => UniformMat2Slot._(components[0], components[1], components[2], components[3]), ); } /// Access the binding for a mat3[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat3[10] uMatricies; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray mats = shader.getUniformMat3Array('uMatricies'); /// mats[0].set( /// 1.0, 0.0, 0.0, /// 1.0, 0.5, 0.0, /// 1.0, 0.3, 1.2 /// ); /// } /// ``` UniformArray getUniformMat3Array(String name) { return _getUniformArray( name, 9, (components) => UniformMat3Slot._( components[0], components[1], components[2], components[3], components[4], components[5], components[6], components[7], components[8], ), ); } /// Access the binding for a mat4[] uniform named [name]. /// /// Example: /// /// ```glsl /// uniform mat4[10] uMatricies; /// ``` /// /// ```dart /// void updateShader(ui.FragmentShader shader) { /// final ui.UniformArray mats = shader.getUniformMat4Array('uMatricies'); /// mats[0].set( /// 1.0, 0.0, 0.0, 1.0, /// 1.0, 0.5, 0.0, 0.4, /// 1.0, 0.3, 1.2, 0.2, /// 0.0, 0.0, 1.0, 0.3, /// ); /// } /// ``` UniformArray getUniformMat4Array(String name) { return _getUniformArray( name, 16, // 4 floats per element (components) => UniformMat4Slot._( components[0], components[1], components[2], components[3], components[4], components[5], components[6], components[7], components[8], components[9], components[10], components[11], components[12], components[13], components[14], components[15], ), ); } /// Access the [ImageSamplerSlot] binding associated with the sampler named /// [name]. /// /// The index provided to setImageSampler is the index of the sampler uniform /// defined in the fragment program, excluding all non-sampler uniforms. ImageSamplerSlot getImageSampler(String name) { final int index = _program._getImageSamplerIndex(name); final slot = ImageSamplerSlot._(this, name, index); _samplers.removeWhere((WeakReference ref) => ref.target == null); _samplers.add(WeakReference(slot)); return slot; } /// Sets the sampler uniform at [index] to [image]. /// /// The index provided to setImageSampler is the index of the sampler uniform defined /// in the fragment program, excluding all non-sampler uniforms. /// /// The optional [filterQuality] argument may be provided to set the quality level used to sample /// the image. By default, it is set to [FilterQuality.none]. /// /// All the sampler uniforms that a shader expects must be provided or the /// results will be undefined. void setImageSampler(int index, Image image, {FilterQuality filterQuality = FilterQuality.none}) { assert(!debugDisposed, 'Tried to access uniforms on a disposed Shader: $this'); assert(!image.debugDisposed, 'Image has been disposed'); _setImageSampler(index, image._image, filterQuality.index); } /// Releases the native resources held by the [FragmentShader]. /// /// After this method is called, calling methods on the shader, or attaching /// it to a [Paint] object will fail with an exception. Calling [dispose] /// twice will also result in an exception being thrown. @override void dispose() { super.dispose(); _floats = _kEmptyFloat32List; _dispose(); } @Native(symbol: 'ReusableFragmentShader::Create') external Float32List _constructor( FragmentProgram program, int floatUniforms, int samplerUniforms, ); @Native, Handle, Handle, Int32)>( symbol: 'ReusableFragmentShader::SetImageSampler', ) external void _setImageSampler(int index, _Image sampler, int filterQualityIndex); @Native)>(symbol: 'ReusableFragmentShader::ValidateSamplers') external bool _validateSamplers(); @Native)>(symbol: 'ReusableFragmentShader::ValidateImageFilter') external bool _validateImageFilter(); @Native)>(symbol: 'ReusableFragmentShader::Dispose') external void _dispose(); } /// Defines how a list of points is interpreted when drawing a set of triangles. /// /// Used by [Canvas.drawVertices]. // These enum values must be kept in sync with DlVertexMode. enum VertexMode { /// Draw each sequence of three points as the vertices of a triangle. triangles, /// Draw each sliding window of three points as the vertices of a triangle. triangleStrip, /// Draw the first point and each sliding window of two points as the vertices /// of a triangle. /// /// This mode is not natively supported by most backends, and is instead /// implemented by unrolling the points into the equivalent /// [VertexMode.triangles], which is generally more efficient. triangleFan, } /// A set of vertex data used by [Canvas.drawVertices]. /// /// Vertex data consists of a series of points in the canvas coordinate space. /// Based on the [VertexMode], these points are interpreted either as /// independent triangles ([VertexMode.triangles]), as a sliding window of /// points forming a chain of triangles each sharing one side with the next /// ([VertexMode.triangleStrip]), or as a fan of triangles with a single shared /// point ([VertexMode.triangleFan]). /// /// Each point can be associated with a color. Each triangle is painted as a /// gradient that blends between the three colors at the three points of that /// triangle. If no colors are specified, transparent black is assumed for all /// the points. /// /// These colors are then blended with the [Paint] specified in the call to /// [Canvas.drawVertices]. This paint is either a solid color ([Paint.color]), /// or a bitmap, specified using a shader ([Paint.shader]), typically either a /// gradient ([Gradient]) or image ([ImageFilter]). The bitmap uses the same /// coordinate space as the canvas (in the case of an [ImageFilter], this is /// notably different than the coordinate space of the source image; the source /// image is tiled according to the filter's configuration, and the image that /// is sampled when painting the triangles is the infinite one after all the /// repeating is applied.) /// /// Each point in the [Vertices] is associated with a specific point on this /// image. Each triangle is painted by sampling points from this image by /// interpolating between the three points of the image corresponding to the /// three points of the triangle. /// /// The [Vertices.new] constructor configures all this using lists of [Offset] /// and [Color] objects. The [Vertices.raw] constructor instead uses /// [Float32List], [Int32List], and [Uint16List] objects, which more closely /// corresponds to the data format used internally and therefore reduces some of /// the conversion overhead. The raw constructor is useful if the data is coming /// from another source (e.g. a file) and can therefore be parsed directly into /// the underlying representation. base class Vertices extends NativeFieldWrapperClass1 { /// Creates a set of vertex data for use with [Canvas.drawVertices]. /// /// The `mode` parameter describes how the points should be interpreted: as /// independent triangles ([VertexMode.triangles]), as a sliding window of /// points forming a chain of triangles each sharing one side with the next /// ([VertexMode.triangleStrip]), or as a fan of triangles with a single /// shared point ([VertexMode.triangleFan]). /// /// The `positions` parameter provides the points in the canvas space that /// will be use to draw the triangles. /// /// The `colors` parameter, if specified, provides the color for each point in /// `positions`. Each triangle is painted as a gradient that blends between /// the three colors at the three points of that triangle. (These colors are /// then blended with the [Paint] specified in the call to /// [Canvas.drawVertices].) /// /// The `textureCoordinates` parameter, if specified, provides the points in /// the [Paint] image to sample for the corresponding points in `positions`. /// /// If the `colors` or `textureCoordinates` parameters are specified, they must /// be the same length as `positions`. /// /// The `indices` parameter specifies the order in which the points should be /// painted. If it is omitted (or present but empty), the points are processed /// in the order they are given in `positions`, as if the `indices` was a list /// from 0 to n-1, where _n_ is the number of entries in `positions`. The /// `indices` parameter, if present and non-empty, must have at least three /// entries, but may be of any length beyond this. Indicies may refer to /// offsets in the positions array multiple times, or may skip positions /// entirely. /// /// If the `indices` parameter is specified, all values in the list must be /// valid index values for `positions`. /// /// The `mode` and `positions` parameters must not be null. /// /// This constructor converts its parameters into [dart:typed_data] lists /// (e.g. using [Float32List]s for the coordinates) before sending them to the /// Flutter engine. If the data provided to this constructor is not already in /// [List] form, consider using the [Vertices.raw] constructor instead to /// avoid converting the data twice. Vertices( VertexMode mode, List positions, { List? colors, List? textureCoordinates, List? indices, }) { if (colors != null && colors.length != positions.length) { throw ArgumentError('"positions" and "colors" lengths must match.'); } if (textureCoordinates != null && textureCoordinates.length != positions.length) { throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); } assert(() { if (indices != null) { for (var index = 0; index < indices.length; index += 1) { if (indices[index] >= positions.length) { throw ArgumentError( '"indices" values must be valid indices in the positions list ' '(i.e. numbers in the range 0..${positions.length - 1}), ' 'but indices[$index] is ${indices[index]}, which is too big.', ); } } } return true; }()); final Float32List encodedPositions = _encodePointList(positions); final Float32List? encodedTextureCoordinates = (textureCoordinates != null) ? _encodePointList(textureCoordinates) : null; final Int32List? encodedColors = colors != null ? _encodeColorList(colors) : null; final Uint16List? encodedIndices = indices != null ? Uint16List.fromList(indices) : null; if (!_init( this, mode.index, encodedPositions, encodedTextureCoordinates, encodedColors, encodedIndices, )) { throw ArgumentError('Invalid configuration for vertices.'); } } /// Creates a set of vertex data for use with [Canvas.drawVertices], using the /// encoding expected by the Flutter engine. /// /// The `mode` parameter describes how the points should be interpreted: as /// independent triangles ([VertexMode.triangles]), as a sliding window of /// points forming a chain of triangles each sharing one side with the next /// ([VertexMode.triangleStrip]), or as a fan of triangles with a single /// shared point ([VertexMode.triangleFan]). /// /// The `positions` parameter provides the points in the canvas space that /// will be use to draw the triangles. Each point is represented as two /// numbers in the list, the first giving the x coordinate and the second /// giving the y coordinate. (As a result, the list must have an even number /// of entries.) /// /// The `colors` parameter, if specified, provides the color for each point in /// `positions`. Each color is represented as ARGB with 8 bit color channels /// (like [Color.value]'s internal representation), and the list, if /// specified, must therefore be half the length of `positions`. Each triangle /// is painted as a gradient that blends between the three colors at the three /// points of that triangle. (These colors are then blended with the [Paint] /// specified in the call to [Canvas.drawVertices].) /// /// The `textureCoordinates` parameter, if specified, provides the points in /// the [Paint] image to sample for the corresponding points in `positions`. /// Each point is represented as two numbers in the list, the first giving the /// x coordinate and the second giving the y coordinate. This list, if /// specified, must be the same length as `positions`. /// /// The `indices` parameter specifies the order in which the points should be /// painted. If it is omitted (or present but empty), the points are processed /// in the order they are given in `positions`, as if the `indices` was a list /// from 0 to n-2, where _n_ is the number of pairs in `positions` (i.e. half /// the length of `positions`). The `indices` parameter, if present and /// non-empty, must have at least three entries, but may be of any length /// beyond this. Indicies may refer to offsets in the positions array multiple /// times, or may skip positions entirely. /// /// If the `indices` parameter is specified, all values in the list must be /// valid index values for pairs in `positions`. For example, if there are 12 /// numbers in `positions` (representing 6 coordinates), the `indicies` must /// be numbers in the range 0..5 inclusive. /// /// The `mode` and `positions` parameters must not be null. Vertices.raw( VertexMode mode, Float32List positions, { Int32List? colors, Float32List? textureCoordinates, Uint16List? indices, }) { if (positions.length % 2 != 0) { throw ArgumentError( '"positions" must have an even number of entries (each coordinate is an x,y pair).', ); } if (colors != null && colors.length * 2 != positions.length) { throw ArgumentError('"positions" and "colors" lengths must match.'); } if (textureCoordinates != null && textureCoordinates.length != positions.length) { throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); } assert(() { if (indices != null) { for (var index = 0; index < indices.length; index += 1) { if (indices[index] * 2 >= positions.length) { throw ArgumentError( '"indices" values must be valid indices in the positions list ' '(i.e. numbers in the range 0..${positions.length ~/ 2 - 1}), ' 'but indices[$index] is ${indices[index]}, which is too big.', ); } } } return true; }()); if (!_init(this, mode.index, positions, textureCoordinates, colors, indices)) { throw ArgumentError('Invalid configuration for vertices.'); } } @Native(symbol: 'Vertices::init') external static bool _init( Vertices outVertices, int mode, Float32List positions, Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, ); /// Release the resources used by this object. The object is no longer usable /// after this method is called. void dispose() { assert(!_disposed); assert(() { _disposed = true; return true; }()); _dispose(); } /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'Vertices::dispose') external void _dispose(); bool _disposed = false; /// Whether this reference to the underlying vertex data is [dispose]d. /// /// This only returns a valid value if asserts are enabled, and must not be /// used otherwise. bool get debugDisposed { bool? disposed; assert(() { disposed = _disposed; return true; }()); return disposed ?? (throw StateError('Vertices.debugDisposed is only available when asserts are enabled.')); } } /// Defines how a list of points is interpreted when drawing a set of points. /// /// Used by [Canvas.drawPoints] and [Canvas.drawRawPoints]. // These enum values must be kept in sync with DlPointMode. enum PointMode { /// Draw each point separately. /// /// If the [Paint.strokeCap] is [StrokeCap.round], then each point is drawn /// as a circle with the diameter of the [Paint.strokeWidth], filled as /// described by the [Paint] (ignoring [Paint.style]). /// /// Otherwise, each point is drawn as an axis-aligned square with sides of /// length [Paint.strokeWidth], filled as described by the [Paint] (ignoring /// [Paint.style]). points, /// Draw each sequence of two points as a line segment. /// /// If the number of points is odd, then the last point is ignored. /// /// The lines are stroked as described by the [Paint] (ignoring /// [Paint.style]). lines, /// Draw the entire sequence of points as one line. /// /// The lines are stroked as described by the [Paint] (ignoring /// [Paint.style]). polygon, } /// Defines how a new clip region should be merged with the existing clip /// region. /// /// Used by [Canvas.clipRect]. enum ClipOp { /// Subtract the new region from the existing region. difference, /// Intersect the new region from the existing region. intersect, } /// An interface for recording graphical operations. /// /// [Canvas] objects are used in creating [Picture] objects, which can /// themselves be used with a [SceneBuilder] to build a [Scene]. In /// normal usage, however, this is all handled by the framework. /// /// A canvas has a current transformation matrix which is applied to all /// operations. Initially, the transformation matrix is the identity transform. /// It can be modified using the [translate], [scale], [rotate], [skew], /// and [transform] methods. /// /// A canvas also has a current clip region which is applied to all operations. /// Initially, the clip region is infinite. It can be modified using the /// [clipRect], [clipRRect], and [clipPath] methods. /// /// The current transform and clip can be saved and restored using the stack /// managed by the [save], [saveLayer], and [restore] methods. /// /// ## Use with the Flutter framework /// /// The Flutter framework's [RendererBinding] provides a hook for creating /// [Canvas] objects ([RendererBinding.createCanvas]) that allows tests to hook /// into the scene creation logic. When creating a [Canvas] that will be used /// with a [PictureLayer] as part of the [Scene] in the context of the Flutter /// framework, consider calling [RendererBinding.createCanvas] instead of /// calling the [Canvas.new] constructor directly. /// /// This does not apply when using a canvas to generate a bitmap for other /// purposes, e.g. for generating a PNG image using [Picture.toImage]. abstract class Canvas { /// Creates a canvas for recording graphical operations into the /// given picture recorder. /// /// Graphical operations that affect pixels entirely outside the given /// `cullRect` might be discarded by the implementation. However, the /// implementation might draw outside these bounds if, for example, a command /// draws partially inside and outside the `cullRect`. To ensure that pixels /// outside a given region are discarded, consider using a [clipRect]. The /// `cullRect` is optional; by default, all operations are kept. /// /// To end the recording, call [PictureRecorder.endRecording] on the /// given recorder. factory Canvas(PictureRecorder recorder, [Rect? cullRect]) = _NativeCanvas; /// Saves a copy of the current transform and clip on the save stack. /// /// Call [restore] to pop the save stack. /// /// See also: /// /// * [saveLayer], which does the same thing but additionally also groups the /// commands done until the matching [restore]. void save(); /// Saves a copy of the current transform and clip on the save stack, and then /// creates a new group which subsequent calls will become a part of. When the /// save stack is later popped, the group will be flattened into a layer and /// have the given `paint`'s [Paint.colorFilter] and [Paint.blendMode] /// applied. /// /// This lets you create composite effects, for example making a group of /// drawing commands semi-transparent. Without using [saveLayer], each part of /// the group would be painted individually, so where they overlap would be /// darker than where they do not. By using [saveLayer] to group them /// together, they can be drawn with an opaque color at first, and then the /// entire group can be made transparent using the [saveLayer]'s paint. /// /// Call [restore] to pop the save stack and apply the paint to the group. /// /// ## Using saveLayer with clips /// /// When a rectangular clip operation (from [clipRect]) is not axis-aligned /// with the raster buffer, or when the clip operation is not rectilinear /// (e.g. because it is a rounded rectangle clip created by [clipRRect] or an /// arbitrarily complicated path clip created by [clipPath]), the edge of the /// clip needs to be anti-aliased. /// /// If two draw calls overlap at the edge of such a clipped region, without /// using [saveLayer], the first drawing will be anti-aliased with the /// background first, and then the second will be anti-aliased with the result /// of blending the first drawing and the background. On the other hand, if /// [saveLayer] is used immediately after establishing the clip, the second /// drawing will cover the first in the layer, and thus the second alone will /// be anti-aliased with the background when the layer is clipped and /// composited (when [restore] is called). /// /// For example, this [CustomPainter.paint] method paints a clean white /// rounded rectangle: /// /// ```dart /// void paint(Canvas canvas, Size size) { /// Rect rect = Offset.zero & size; /// canvas.save(); /// canvas.clipRRect(RRect.fromRectXY(rect, 100.0, 100.0)); /// canvas.saveLayer(rect, Paint()); /// canvas.drawPaint(Paint()..color = Colors.red); /// canvas.drawPaint(Paint()..color = Colors.white); /// canvas.restore(); /// canvas.restore(); /// } /// ``` /// /// On the other hand, this one renders a red outline, the result of the red /// paint being anti-aliased with the background at the clip edge, then the /// white paint being similarly anti-aliased with the background _including /// the clipped red paint_: /// /// ```dart /// void paint(Canvas canvas, Size size) { /// // (this example renders poorly, prefer the example above) /// Rect rect = Offset.zero & size; /// canvas.save(); /// canvas.clipRRect(RRect.fromRectXY(rect, 100.0, 100.0)); /// canvas.drawPaint(Paint()..color = Colors.red); /// canvas.drawPaint(Paint()..color = Colors.white); /// canvas.restore(); /// } /// ``` /// /// This point is moot if the clip only clips one draw operation. For example, /// the following paint method paints a pair of clean white rounded /// rectangles, even though the clips are not done on a separate layer: /// /// ```dart /// void paint(Canvas canvas, Size size) { /// canvas.save(); /// canvas.clipRRect(RRect.fromRectXY(Offset.zero & (size / 2.0), 50.0, 50.0)); /// canvas.drawPaint(Paint()..color = Colors.white); /// canvas.restore(); /// canvas.save(); /// canvas.clipRRect(RRect.fromRectXY(size.center(Offset.zero) & (size / 2.0), 50.0, 50.0)); /// canvas.drawPaint(Paint()..color = Colors.white); /// canvas.restore(); /// } /// ``` /// /// (Incidentally, rather than using [clipRRect] and [drawPaint] to draw /// rounded rectangles like this, prefer the [drawRRect] method. These /// examples are using [drawPaint] as a proxy for "complicated draw operations /// that will get clipped", to illustrate the point.) /// /// ## Performance considerations /// /// Generally speaking, [saveLayer] is relatively expensive. /// /// There are a several different hardware architectures for GPUs (graphics /// processing units, the hardware that handles graphics), but most of them /// involve batching commands and reordering them for performance. When layers /// are used, they cause the rendering pipeline to have to switch render /// target (from one layer to another). Render target switches can flush the /// GPU's command buffer, which typically means that optimizations that one /// could get with larger batching are lost. Render target switches also /// generate a lot of memory churn because the GPU needs to copy out the /// current frame buffer contents from the part of memory that's optimized for /// writing, and then needs to copy it back in once the previous render target /// (layer) is restored. /// /// See also: /// /// * [save], which saves the current state, but does not create a new layer /// for subsequent commands. /// * [BlendMode], which discusses the use of [Paint.blendMode] with /// [saveLayer]. void saveLayer(Rect? bounds, Paint paint); /// Pops the current save stack, if there is anything to pop. /// Otherwise, does nothing. /// /// Use [save] and [saveLayer] to push state onto the stack. /// /// If the state was pushed with [saveLayer], then this call will also /// cause the new layer to be composited into the previous layer. void restore(); /// Restores the save stack to a previous level as might be obtained from [getSaveCount]. /// If [count] is less than 1, the stack is restored to its initial state. /// If [count] is greater than the current [getSaveCount] then nothing happens. /// /// Use [save] and [saveLayer] to push state onto the stack. /// /// If any of the state stack levels restored by this call were pushed with /// [saveLayer], then this call will also cause those layers to be composited /// into their previous layers. void restoreToCount(int count); /// Returns the number of items on the save stack, including the /// initial state. This means it returns 1 for a clean canvas, and /// that each call to [save] and [saveLayer] increments it, and that /// each matching call to [restore] decrements it. /// /// This number cannot go below 1. int getSaveCount(); /// Add a translation to the current transform, shifting the coordinate space /// horizontally by the first argument and vertically by the second argument. void translate(double dx, double dy); /// Add an axis-aligned scale to the current transform, scaling by the first /// argument in the horizontal direction and the second in the vertical /// direction. /// /// If [sy] is unspecified, [sx] will be used for the scale in both /// directions. void scale(double sx, [double? sy]); /// Add a rotation to the current transform. The argument is in radians clockwise. void rotate(double radians); /// Add an axis-aligned skew to the current transform, with the first argument /// being the horizontal skew in rise over run units clockwise around the /// origin, and the second argument being the vertical skew in rise over run /// units clockwise around the origin. void skew(double sx, double sy); /// Multiply the current transform by the specified 4⨉4 transformation matrix /// specified as a list of values in column-major order. void transform(Float64List matrix4); /// Returns the current transform including the combined result of all transform /// methods executed since the creation of this [Canvas] object, and respecting the /// save/restore history. /// /// Methods that can change the current transform include [translate], [scale], /// [rotate], [skew], and [transform]. The [restore] method can also modify /// the current transform by restoring it to the same value it had before its /// associated [save] or [saveLayer] call. Float64List getTransform(); /// Reduces the clip region to the intersection of the current clip and the /// given rectangle. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/clip_rect.png) /// /// If [doAntiAlias] is true, then the clip will be anti-aliased. /// /// If multiple draw commands intersect with the clip boundary, this can result /// in incorrect blending at the clip boundary. See [saveLayer] for a /// discussion of how to address that. /// /// Use [ClipOp.difference] to subtract the provided rectangle from the /// current clip. void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); /// Reduces the clip region to the intersection of the current clip and the /// given rounded rectangle. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/clip_rrect.png) /// /// If [doAntiAlias] is true, then the clip will be anti-aliased. /// /// If multiple draw commands intersect with the clip boundary, this can result /// in incorrect blending at the clip boundary. See [saveLayer] for a /// discussion of how to address that and some examples of using [clipRRect]. void clipRRect(RRect rrect, {bool doAntiAlias = true}); /// Reduces the clip region to the intersection of the current clip and the /// given rounded superellipse. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/clip_rsuperellipse.png) /// /// If [doAntiAlias] is true, then the clip will be anti-aliased. /// /// If multiple draw commands intersect with the clip boundary, this can result /// in incorrect blending at the clip boundary. See [saveLayer] for a /// discussion of how to address that and some examples of using [clipRSuperellipse]. void clipRSuperellipse(RSuperellipse rsuperellipse, {bool doAntiAlias = true}); /// Reduces the clip region to the intersection of the current clip and the /// given [Path]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/clip_path.png) /// /// If [doAntiAlias] is true, then the clip will be anti-aliased. /// /// If multiple draw commands intersect with the clip boundary, this can result /// in incorrect blending at the clip boundary. See [saveLayer] for a /// discussion of how to address that. void clipPath(Path path, {bool doAntiAlias = true}); /// Returns the conservative bounds of the combined result of all clip methods /// executed within the current save stack of this [Canvas] object, as measured /// in the local coordinate space under which rendering operations are currently /// performed. /// /// The combined clip results are rounded out to an integer pixel boundary before /// they are transformed back into the local coordinate space which accounts for /// the pixel roundoff in rendering operations, particularly when antialiasing. /// Because the [Picture] may eventually be rendered into a scene within the /// context of transforming widgets or layers, the result may thus be overly /// conservative due to premature rounding. Using the [getDestinationClipBounds] /// method combined with the external transforms and rounding in the true device /// coordinate system will produce more accurate results, but this value may /// provide a more convenient approximation to compare rendering operations to /// the established clip. /// /// {@template dart.ui.canvas.conservativeClipBounds} /// The conservative estimate of the bounds is based on intersecting the bounds /// of each clip method that was executed with [ClipOp.intersect] and potentially /// ignoring any clip method that was executed with [ClipOp.difference]. The /// [ClipOp] argument is only present on the [clipRect] method. /// /// To understand how the bounds estimate can be conservative, consider the /// following two clip method calls: /// /// ```dart /// void draw(Canvas canvas) { /// canvas.clipPath(Path() /// ..addRect(const Rect.fromLTRB(10, 10, 20, 20)) /// ..addRect(const Rect.fromLTRB(80, 80, 100, 100))); /// canvas.clipPath(Path() /// ..addRect(const Rect.fromLTRB(80, 10, 100, 20)) /// ..addRect(const Rect.fromLTRB(10, 80, 20, 100))); /// // ... /// } /// ``` /// /// After executing both of those calls there is no area left in which to draw /// because the two paths have no overlapping regions. But, in this case, /// [getLocalClipBounds] would return a rectangle from `10, 10` to `100, 100` because it /// only intersects the bounds of the two path objects to obtain its conservative /// estimate. /// /// The clip bounds are not affected by the bounds of any enclosing /// [saveLayer] call as the engine does not currently guarantee the strict /// enforcement of those bounds during rendering. /// /// Methods that can change the current clip include [clipRect], [clipRRect], /// and [clipPath]. The [restore] method can also modify the current clip by /// restoring it to the same value it had before its associated [save] or /// [saveLayer] call. /// {@endtemplate} Rect getLocalClipBounds(); /// Returns the conservative bounds of the combined result of all clip methods /// executed within the current save stack of this [Canvas] object, as measured /// in the destination coordinate space in which the [Picture] will be rendered. /// /// Unlike [getLocalClipBounds], the bounds are not rounded out to an integer /// pixel boundary as the Destination coordinate space may not represent pixels /// if the [Picture] being constructed will be further transformed when it is /// rendered or added to a scene. In order to determine the true pixels being /// affected, those external transforms should be applied first before rounding /// out the result to integer pixel boundaries. Most typically, [Picture] objects /// are rendered in a scene with a scale transform representing the Device Pixel /// Ratio. /// /// {@macro dart.ui.canvas.conservativeClipBounds} Rect getDestinationClipBounds(); /// Paints the given [Color] onto the canvas, applying the given /// [BlendMode], with the given color being the source and the background /// being the destination. void drawColor(Color color, BlendMode blendMode); /// Draws a line between the given points using the given paint. The line is /// stroked, the value of the [Paint.style] is ignored for this call. /// /// The `p1` and `p2` arguments are interpreted as offsets from the origin. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_line.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_line_dark.png#gh-dark-mode-only) void drawLine(Offset p1, Offset p2, Paint paint); /// Fills the canvas with the given [Paint]. /// /// To fill the canvas with a solid color and blend mode, consider /// [drawColor] instead. void drawPaint(Paint paint); /// Draws a rectangle with the given [Paint]. Whether the rectangle is filled /// or stroked (or both) is controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rect.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rect_dark.png#gh-dark-mode-only) void drawRect(Rect rect, Paint paint); /// Draws a rounded rectangle with the given [Paint]. Whether the rectangle is /// filled or stroked (or both) is controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rrect.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rrect_dark.png#gh-dark-mode-only) void drawRRect(RRect rrect, Paint paint); /// Draws a shape consisting of the difference between two rounded rectangles /// with the given [Paint]. Whether this shape is filled or stroked (or both) /// is controlled by [Paint.style]. /// /// This shape is almost but not quite entirely unlike an annulus. void drawDRRect(RRect outer, RRect inner, Paint paint); /// Draws a rounded superellipse with the given [Paint]. The shape is filled, /// and the value of the [Paint.style] is ignored for this call. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rsuperellipse.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_rsuperellipse.png#gh-dark-mode-only) void drawRSuperellipse(RSuperellipse rsuperellipse, Paint paint); /// Draws an axis-aligned oval that fills the given axis-aligned rectangle /// with the given [Paint]. Whether the oval is filled or stroked (or both) is /// controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_oval.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_oval_dark.png#gh-dark-mode-only) void drawOval(Rect rect, Paint paint); /// Draws a circle centered at the point given by the first argument and /// that has the radius given by the second argument, with the [Paint] given in /// the third argument. Whether the circle is filled or stroked (or both) is /// controlled by [Paint.style]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_circle.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_circle_dark.png#gh-dark-mode-only) void drawCircle(Offset c, double radius, Paint paint); /// Draw an arc scaled to fit inside the given rectangle. /// /// It starts from `startAngle` radians around the oval up to /// `startAngle` + `sweepAngle` radians around the oval, with zero radians /// being the point on the right hand side of the oval that crosses the /// horizontal line that intersects the center of the rectangle and with positive /// angles going clockwise around the oval. If `useCenter` is true, the arc is /// closed back to the center, forming a circle sector. Otherwise, the arc is /// not closed, forming a circle segment. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_draw_arc.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/canvas_draw_arc_dark.png#gh-dark-mode-only) /// /// This method is optimized for drawing arcs and should be faster than [Path.arcTo]. void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint); /// Draws the given [Path] with the given [Paint]. /// /// Whether this shape is filled or stroked (or both) is controlled by /// [Paint.style]. If the path is filled, then sub-paths within it are /// implicitly closed (see [Path.close]). /// /// When drawing simple shapes (such as rectangles, ovals, or rounded /// rectangles), prefer using methods such as [drawRect], [drawOval], or /// [drawRRect] over [drawPath]. Methods that draw simple shapes are generally /// more efficient than drawing a [Path]. void drawPath(Path path, Paint paint); /// Draws the given [Image] into the canvas with its top-left corner at the /// given [Offset]. The image is composited into the canvas using the given [Paint]. void drawImage(Image image, Offset offset, Paint paint); /// Draws the subset of the given image described by the `src` argument into /// the canvas in the axis-aligned rectangle given by the `dst` argument. /// /// This might sample from outside the `src` rect by up to half the width of /// an applied filter. /// /// Multiple calls to this method with different arguments (from the same /// image) can be batched into a single call to [drawAtlas] to improve /// performance. void drawImageRect(Image image, Rect src, Rect dst, Paint paint); /// Draws the given [Image] into the canvas using the given [Paint]. /// /// The image is drawn in nine portions described by splitting the image by /// drawing two horizontal lines and two vertical lines, where the `center` /// argument describes the rectangle formed by the four points where these /// four lines intersect each other. (This forms a 3-by-3 grid of regions, /// the center region being described by the `center` argument.) /// /// The four regions in the corners are drawn, without scaling, in the four /// corners of the destination rectangle described by `dst`. The remaining /// five regions are drawn by stretching them to fit such that they exactly /// cover the destination rectangle while maintaining their relative /// positions. void drawImageNine(Image image, Rect center, Rect dst, Paint paint); /// Draw the given picture onto the canvas. To create a picture, see /// [PictureRecorder]. void drawPicture(Picture picture); /// Draws the text in the given [Paragraph] into this canvas at the given /// [Offset]. /// /// The [Paragraph] object must have had [Paragraph.layout] called on it /// first. /// /// To align the text, set the `textAlign` on the [ParagraphStyle] object /// passed to the [ParagraphBuilder.new] constructor. For more details see /// [TextAlign] and the discussion at [ParagraphStyle.new]. /// /// If the text is left aligned or justified, the left margin will be at the /// position specified by the `offset` argument's [Offset.dx] coordinate. /// /// If the text is right aligned or justified, the right margin will be at the /// position described by adding the [ParagraphConstraints.width] given to /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate. /// /// If the text is centered, the centering axis will be at the position /// described by adding half of the [ParagraphConstraints.width] given to /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate. void drawParagraph(Paragraph paragraph, Offset offset); /// Draws a sequence of points according to the given [PointMode]. /// /// The `points` argument is interpreted as offsets from the origin. /// /// The `paint` is used for each point ([PointMode.points]) or line /// ([PointMode.lines] or [PointMode.polygon]), ignoring [Paint.style]. /// /// See also: /// /// * [drawRawPoints], which takes `points` as a [Float32List] rather than a /// [List]. void drawPoints(PointMode pointMode, List points, Paint paint); /// Draws a sequence of points according to the given [PointMode]. /// /// The `points` argument is interpreted as a list of pairs of floating point /// numbers, where each pair represents an x and y offset from the origin. /// /// The `paint` is used for each point ([PointMode.points]) or line /// ([PointMode.lines] or [PointMode.polygon]), ignoring [Paint.style]. /// /// See also: /// /// * [drawPoints], which takes `points` as a [List] rather than a /// [List]. void drawRawPoints(PointMode pointMode, Float32List points, Paint paint); /// Draws a set of [Vertices] onto the canvas as one or more triangles. /// /// The [Paint.color] property specifies the default color to use for the /// triangles. /// /// The [Paint.shader] property, if set, overrides the color entirely, /// replacing it with the colors from the specified [ImageShader], [Gradient], /// or other shader. /// /// The `blendMode` parameter is used to control how the colors in the /// `vertices` are combined with the colors in the `paint`. If there are no /// colors specified in `vertices` then the `blendMode` has no effect. If /// there are colors in the `vertices`, then the color taken from the /// [Paint.shader] or [Paint.color] in the `paint` is blended with the colors /// specified in the `vertices` using the `blendMode` parameter. For the /// purposes of this blending, the colors from the `paint` parameter are /// considered the source, and the colors from the `vertices` are considered /// the destination. [BlendMode.dst] ignores the `paint` and uses only the /// colors of the `vertices`; [BlendMode.src] ignores the colors of the /// `vertices` and uses only the colors in the `paint`. /// /// All parameters must not be null. /// /// See also: /// * [Vertices.new], which creates a set of vertices to draw on the canvas. /// * [Vertices.raw], which creates the vertices using typed data lists /// rather than unencoded lists. /// * [paint], Image shaders can be used to draw images on a triangular mesh. void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint); /// Draws many parts of an image - the [atlas] - onto the canvas. /// /// This method allows for optimization when you want to draw many parts of an /// image onto the canvas, such as when using sprites or zooming. It is more efficient /// than using multiple calls to [drawImageRect] and provides more functionality /// to individually transform each image part by a separate rotation or scale and /// blend or modulate those parts with a solid color. /// /// The method takes a list of [Rect] objects that each define a piece of the /// [atlas] image to be drawn independently. Each [Rect] is associated with an /// [RSTransform] entry in the [transforms] list which defines the location, /// rotation, and (uniform) scale with which to draw that portion of the image. /// Each [Rect] can also be associated with an optional [Color] which will be /// composed with the associated image part using the [blendMode] before blending /// the result onto the canvas. The full operation can be broken down as: /// /// - Blend each rectangular portion of the image specified by an entry in the /// [rects] argument with its associated entry in the [colors] list using the /// [blendMode] argument (if a color is specified). In this part of the operation, /// the image part will be considered the source of the operation and the associated /// color will be considered the destination. /// - Blend the result from the first step onto the canvas using the translation, /// rotation, and scale properties expressed in the associated entry in the /// [transforms] list using the properties of the [Paint] object. /// /// If the first stage of the operation which blends each part of the image with /// a color is needed, then both the [colors] and [blendMode] arguments must /// not be null and there must be an entry in the [colors] list for each /// image part. If that stage is not needed, then the [colors] argument can /// be either null or an empty list and the [blendMode] argument may also be null. /// /// The optional [cullRect] argument can provide an estimate of the bounds of the /// coordinates rendered by all components of the atlas to be compared against /// the clip to quickly reject the operation if it does not intersect. /// /// An example usage to render many sprites from a single sprite atlas with no /// rotations or scales: /// /// ```dart /// class Sprite { /// Sprite(this.index, this.center); /// int index; /// Offset center; /// } /// /// class MyPainter extends CustomPainter { /// MyPainter(this.spriteAtlas, this.allSprites); /// /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image /// ui.Image spriteAtlas; /// List allSprites; /// /// @override /// void paint(Canvas canvas, Size size) { /// Paint paint = Paint(); /// canvas.drawAtlas(spriteAtlas, [ /// for (final Sprite sprite in allSprites) /// RSTransform.fromComponents( /// rotation: 0.0, /// scale: 1.0, /// // Center of the sprite relative to its rect /// anchorX: 5.0, /// anchorY: 5.0, /// // Location at which to draw the center of the sprite /// translateX: sprite.center.dx, /// translateY: sprite.center.dy, /// ), /// ], [ /// for (final Sprite sprite in allSprites) /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0), /// ], null, null, null, paint); /// } /// /// // ... /// } /// ``` /// /// Another example usage which renders sprites with an optional opacity and rotation: /// /// ```dart /// class Sprite { /// Sprite(this.index, this.center, this.alpha, this.rotation); /// int index; /// Offset center; /// int alpha; /// double rotation; /// } /// /// class MyPainter extends CustomPainter { /// MyPainter(this.spriteAtlas, this.allSprites); /// /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image /// ui.Image spriteAtlas; /// List allSprites; /// /// @override /// void paint(Canvas canvas, Size size) { /// Paint paint = Paint(); /// canvas.drawAtlas(spriteAtlas, [ /// for (final Sprite sprite in allSprites) /// RSTransform.fromComponents( /// rotation: sprite.rotation, /// scale: 1.0, /// // Center of the sprite relative to its rect /// anchorX: 5.0, /// anchorY: 5.0, /// // Location at which to draw the center of the sprite /// translateX: sprite.center.dx, /// translateY: sprite.center.dy, /// ), /// ], [ /// for (final Sprite sprite in allSprites) /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0), /// ], [ /// for (final Sprite sprite in allSprites) /// Colors.white.withAlpha(sprite.alpha), /// ], BlendMode.srcIn, null, paint); /// } /// /// // ... /// } /// ``` /// /// The length of the [transforms] and [rects] lists must be equal and /// if the [colors] argument is not null then it must either be empty or /// have the same length as the other two lists. /// /// See also: /// /// * [drawRawAtlas], which takes its arguments as typed data lists rather /// than objects. void drawAtlas( Image atlas, List transforms, List rects, List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint, ); /// Draws many parts of an image - the [atlas] - onto the canvas. /// /// This method allows for optimization when you want to draw many parts of an /// image onto the canvas, such as when using sprites or zooming. It is more efficient /// than using multiple calls to [drawImageRect] and provides more functionality /// to individually transform each image part by a separate rotation or scale and /// blend or modulate those parts with a solid color. It is also more efficient /// than [drawAtlas] as the data in the arguments is already packed in a format /// that can be directly used by the rendering code. /// /// A full description of how this method uses its arguments to draw onto the /// canvas can be found in the description of the [drawAtlas] method. /// /// The [rstTransforms] argument is interpreted as a list of four-tuples, with /// each tuple being ([RSTransform.scos], [RSTransform.ssin], /// [RSTransform.tx], [RSTransform.ty]). /// /// The [rects] argument is interpreted as a list of four-tuples, with each /// tuple being ([Rect.left], [Rect.top], [Rect.right], [Rect.bottom]). /// /// The [colors] argument, which can be null, is interpreted as a list of /// 32-bit colors, with the same packing as [Color.value]. If the [colors] /// argument is not null then the [blendMode] argument must also not be null. /// /// An example usage to render many sprites from a single sprite atlas with no rotations /// or scales: /// /// ```dart /// class Sprite { /// Sprite(this.index, this.center); /// int index; /// Offset center; /// } /// /// class MyPainter extends CustomPainter { /// MyPainter(this.spriteAtlas, this.allSprites); /// /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image /// ui.Image spriteAtlas; /// List allSprites; /// /// @override /// void paint(Canvas canvas, Size size) { /// // For best advantage, these lists should be cached and only specific /// // entries updated when the sprite information changes. This code is /// // illustrative of how to set up the data and not a recommendation for /// // optimal usage. /// Float32List rectList = Float32List(allSprites.length * 4); /// Float32List transformList = Float32List(allSprites.length * 4); /// for (int i = 0; i < allSprites.length; i++) { /// Sprite sprite = allSprites[i]; /// final double rectX = sprite.index * 10.0; /// rectList[i * 4 + 0] = rectX; /// rectList[i * 4 + 1] = 0.0; /// rectList[i * 4 + 2] = rectX + 10.0; /// rectList[i * 4 + 3] = 10.0; /// /// // This example sets the RSTransform values directly for a common case of no /// // rotations or scales and just a translation to position the atlas entry. For /// // more complicated transforms one could use the RSTransform class to compute /// // the necessary values or do the same math directly. /// transformList[i * 4 + 0] = 1.0; /// transformList[i * 4 + 1] = 0.0; /// transformList[i * 4 + 2] = sprite.center.dx - 5.0; /// transformList[i * 4 + 3] = sprite.center.dy - 5.0; /// } /// Paint paint = Paint(); /// canvas.drawRawAtlas(spriteAtlas, transformList, rectList, null, null, null, paint); /// } /// /// // ... /// } /// ``` /// /// Another example usage which renders sprites with an optional opacity and rotation: /// /// ```dart /// class Sprite { /// Sprite(this.index, this.center, this.alpha, this.rotation); /// int index; /// Offset center; /// int alpha; /// double rotation; /// } /// /// class MyPainter extends CustomPainter { /// MyPainter(this.spriteAtlas, this.allSprites); /// /// // assume spriteAtlas contains N 10x10 sprites side by side in a (N*10)x10 image /// ui.Image spriteAtlas; /// List allSprites; /// /// @override /// void paint(Canvas canvas, Size size) { /// // For best advantage, these lists should be cached and only specific /// // entries updated when the sprite information changes. This code is /// // illustrative of how to set up the data and not a recommendation for /// // optimal usage. /// Float32List rectList = Float32List(allSprites.length * 4); /// Float32List transformList = Float32List(allSprites.length * 4); /// Int32List colorList = Int32List(allSprites.length); /// for (int i = 0; i < allSprites.length; i++) { /// Sprite sprite = allSprites[i]; /// final double rectX = sprite.index * 10.0; /// rectList[i * 4 + 0] = rectX; /// rectList[i * 4 + 1] = 0.0; /// rectList[i * 4 + 2] = rectX + 10.0; /// rectList[i * 4 + 3] = 10.0; /// /// // This example uses an RSTransform object to compute the necessary values for /// // the transform using a factory helper method because the sprites contain /// // rotation values which are not trivial to work with. But if the math for the /// // values falls out from other calculations on the sprites then the values could /// // possibly be generated directly from the sprite update code. /// final RSTransform transform = RSTransform.fromComponents( /// rotation: sprite.rotation, /// scale: 1.0, /// // Center of the sprite relative to its rect /// anchorX: 5.0, /// anchorY: 5.0, /// // Location at which to draw the center of the sprite /// translateX: sprite.center.dx, /// translateY: sprite.center.dy, /// ); /// transformList[i * 4 + 0] = transform.scos; /// transformList[i * 4 + 1] = transform.ssin; /// transformList[i * 4 + 2] = transform.tx; /// transformList[i * 4 + 3] = transform.ty; /// /// // This example computes the color value directly, but one could also compute /// // an actual Color object and use its Color.value getter for the same result. /// // Since we are using BlendMode.srcIn, only the alpha component matters for /// // these colors which makes this a simple shift operation. /// colorList[i] = sprite.alpha << 24; /// } /// Paint paint = Paint(); /// canvas.drawRawAtlas(spriteAtlas, transformList, rectList, colorList, BlendMode.srcIn, null, paint); /// } /// /// // ... /// } /// ``` /// /// See also: /// /// * [drawAtlas], which takes its arguments as objects rather than typed /// data lists. void drawRawAtlas( Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint, ); /// Draws a shadow for a [Path] representing the given material elevation. /// /// The `transparentOccluder` argument should be true if the occluding object /// is not opaque. /// /// The arguments must not be null. void drawShadow(Path path, Color color, double elevation, bool transparentOccluder); } base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _NativeCanvas(PictureRecorder recorder, [Rect? cullRect]) { if (recorder.isRecording) { throw ArgumentError('"recorder" must not already be associated with another Canvas.'); } _recorder = recorder as _NativePictureRecorder; _recorder!._canvas = this; cullRect ??= Rect.largest; _constructor(_recorder!, cullRect.left, cullRect.top, cullRect.right, cullRect.bottom); } @Native, Double, Double, Double, Double)>( symbol: 'Canvas::Create', ) external void _constructor( _NativePictureRecorder recorder, double left, double top, double right, double bottom, ); // The underlying DlCanvas is owned by the DisplayListBuilder used to create this Canvas. // The Canvas holds a reference to the PictureRecorder to prevent the recorder from being // garbage collected until PictureRecorder.endRecording is called. _NativePictureRecorder? _recorder; @override @Native)>(symbol: 'Canvas::save', isLeaf: true) external void save(); static Rect _sorted(Rect rect) { if (rect.isEmpty) { rect = Rect.fromLTRB( math.min(rect.left, rect.right), math.min(rect.top, rect.bottom), math.max(rect.left, rect.right), math.max(rect.top, rect.bottom), ); } return rect; } @override void saveLayer(Rect? bounds, Paint paint) { if (bounds == null) { _saveLayerWithoutBounds(paint._objects, paint._data); } else { assert(_rectIsValid(bounds)); _saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint._objects, paint._data); } } @Native, Handle, Handle)>(symbol: 'Canvas::saveLayerWithoutBounds') external void _saveLayerWithoutBounds(List? paintObjects, ByteData paintData); @Native, Double, Double, Double, Double, Handle, Handle)>( symbol: 'Canvas::saveLayer', ) external void _saveLayer( double left, double top, double right, double bottom, List? paintObjects, ByteData paintData, ); @override @Native)>(symbol: 'Canvas::restore', isLeaf: true) external void restore(); @override @Native, Int32)>(symbol: 'Canvas::restoreToCount', isLeaf: true) external void restoreToCount(int count); @override @Native)>(symbol: 'Canvas::getSaveCount', isLeaf: true) external int getSaveCount(); @override @Native, Double, Double)>(symbol: 'Canvas::translate', isLeaf: true) external void translate(double dx, double dy); @override void scale(double sx, [double? sy]) => _scale(sx, sy ?? sx); @Native, Double, Double)>(symbol: 'Canvas::scale', isLeaf: true) external void _scale(double sx, double sy); @override @Native, Double)>(symbol: 'Canvas::rotate', isLeaf: true) external void rotate(double radians); @override @Native, Double, Double)>(symbol: 'Canvas::skew', isLeaf: true) external void skew(double sx, double sy); @override void transform(Float64List matrix4) { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } _transform(matrix4); } @Native, Handle)>(symbol: 'Canvas::transform') external void _transform(Float64List matrix4); @override Float64List getTransform() { final matrix4 = Float64List(16); _getTransform(matrix4); return matrix4; } @Native, Handle)>(symbol: 'Canvas::getTransform') external void _getTransform(Float64List matrix4); @override void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}) { assert(_rectIsValid(rect)); rect = _sorted(rect); // Even if rect is still empty - which implies it has a zero dimension - // we still need to perform the clipRect operation as it will effectively // nullify any further rendering until the next restore call. _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias); } @Native, Double, Double, Double, Double, Int32, Bool)>( symbol: 'Canvas::clipRect', isLeaf: true, ) external void _clipRect( double left, double top, double right, double bottom, int clipOp, bool doAntiAlias, ); @override void clipRRect(RRect rrect, {bool doAntiAlias = true}) { assert(_rrectIsValid(rrect)); _clipRRect(rrect._getValue32(), doAntiAlias); } @Native, Handle, Bool)>(symbol: 'Canvas::clipRRect') external void _clipRRect(Float32List rrect, bool doAntiAlias); @override void clipRSuperellipse(RSuperellipse rsuperellipse, {bool doAntiAlias = true}) { assert(_rsuperellipseIsValid(rsuperellipse)); _clipRSuperellipse(rsuperellipse._native(), doAntiAlias); } @Native, Pointer, Bool)>(symbol: 'Canvas::clipRSuperellipse') external void _clipRSuperellipse(_NativeRSuperellipse rsuperellipse, bool doAntiAlias); @override void clipPath(Path path, {bool doAntiAlias = true}) { _clipPath(path as _NativePath, doAntiAlias); } @Native, Pointer, Bool)>(symbol: 'Canvas::clipPath') external void _clipPath(_NativePath path, bool doAntiAlias); @override Rect getLocalClipBounds() { final bounds = Float64List(4); _getLocalClipBounds(bounds); return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); } @Native, Handle)>(symbol: 'Canvas::getLocalClipBounds') external void _getLocalClipBounds(Float64List bounds); @override Rect getDestinationClipBounds() { final bounds = Float64List(4); _getDestinationClipBounds(bounds); return Rect.fromLTRB(bounds[0], bounds[1], bounds[2], bounds[3]); } @Native, Handle)>(symbol: 'Canvas::getDestinationClipBounds') external void _getDestinationClipBounds(Float64List bounds); @override void drawColor(Color color, BlendMode blendMode) { _drawColor(color.value, blendMode.index); } @Native, Uint32, Int32)>(symbol: 'Canvas::drawColor', isLeaf: true) external void _drawColor(int color, int blendMode); @override void drawLine(Offset p1, Offset p2, Paint paint) { assert(_offsetIsValid(p1)); assert(_offsetIsValid(p2)); _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data); } @Native, Double, Double, Double, Double, Handle, Handle)>( symbol: 'Canvas::drawLine', ) external void _drawLine( double x1, double y1, double x2, double y2, List? paintObjects, ByteData paintData, ); @override void drawPaint(Paint paint) { _drawPaint(paint._objects, paint._data); } @Native, Handle, Handle)>(symbol: 'Canvas::drawPaint') external void _drawPaint(List? paintObjects, ByteData paintData); @override void drawRect(Rect rect, Paint paint) { assert(_rectIsValid(rect)); rect = _sorted(rect); if (paint.style != PaintingStyle.fill || !rect.isEmpty) { _drawRect(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); } } @Native, Double, Double, Double, Double, Handle, Handle)>( symbol: 'Canvas::drawRect', ) external void _drawRect( double left, double top, double right, double bottom, List? paintObjects, ByteData paintData, ); @override void drawRRect(RRect rrect, Paint paint) { assert(_rrectIsValid(rrect)); _drawRRect(rrect._getValue32(), paint._objects, paint._data); } @Native, Handle, Handle, Handle)>(symbol: 'Canvas::drawRRect') external void _drawRRect(Float32List rrect, List? paintObjects, ByteData paintData); @override void drawDRRect(RRect outer, RRect inner, Paint paint) { assert(_rrectIsValid(outer)); assert(_rrectIsValid(inner)); _drawDRRect(outer._getValue32(), inner._getValue32(), paint._objects, paint._data); } @Native, Handle, Handle, Handle, Handle)>( symbol: 'Canvas::drawDRRect', ) external void _drawDRRect( Float32List outer, Float32List inner, List? paintObjects, ByteData paintData, ); @override void drawRSuperellipse(RSuperellipse rsuperellipse, Paint paint) { assert(_rsuperellipseIsValid(rsuperellipse)); _drawRSuperellipse(rsuperellipse._native(), paint._objects, paint._data); } @Native, Pointer, Handle, Handle)>( symbol: 'Canvas::drawRSuperellipse', ) external void _drawRSuperellipse( _NativeRSuperellipse rsuperellipse, List? paintObjects, ByteData paintData, ); @override void drawOval(Rect rect, Paint paint) { assert(_rectIsValid(rect)); rect = _sorted(rect); if (paint.style != PaintingStyle.fill || !rect.isEmpty) { _drawOval(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); } } @Native, Double, Double, Double, Double, Handle, Handle)>( symbol: 'Canvas::drawOval', ) external void _drawOval( double left, double top, double right, double bottom, List? paintObjects, ByteData paintData, ); @override void drawCircle(Offset c, double radius, Paint paint) { assert(_offsetIsValid(c)); _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data); } @Native, Double, Double, Double, Handle, Handle)>( symbol: 'Canvas::drawCircle', ) external void _drawCircle( double x, double y, double radius, List? paintObjects, ByteData paintData, ); @override void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) { assert(_rectIsValid(rect)); _drawArc( rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, useCenter, paint._objects, paint._data, ); } @Native< Void Function( Pointer, Double, Double, Double, Double, Double, Double, Bool, Handle, Handle, ) >(symbol: 'Canvas::drawArc') external void _drawArc( double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool useCenter, List? paintObjects, ByteData paintData, ); @override void drawPath(Path path, Paint paint) { _drawPath(path as _NativePath, paint._objects, paint._data); } @Native, Pointer, Handle, Handle)>(symbol: 'Canvas::drawPath') external void _drawPath(_NativePath path, List? paintObjects, ByteData paintData); @override void drawImage(Image image, Offset offset, Paint paint) { assert(!image.debugDisposed); assert(_offsetIsValid(offset)); final String? error = _drawImage( image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index, ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } @Native, Pointer, Double, Double, Handle, Handle, Int32)>( symbol: 'Canvas::drawImage', ) external String? _drawImage( _Image image, double x, double y, List? paintObjects, ByteData paintData, int filterQualityIndex, ); @override void drawImageRect(Image image, Rect src, Rect dst, Paint paint) { assert(!image.debugDisposed); assert(_rectIsValid(src)); assert(_rectIsValid(dst)); final String? error = _drawImageRect( image._image, src.left, src.top, src.right, src.bottom, dst.left, dst.top, dst.right, dst.bottom, paint._objects, paint._data, paint.filterQuality.index, ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } @Native< Handle Function( Pointer, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32, ) >(symbol: 'Canvas::drawImageRect') external String? _drawImageRect( _Image image, double srcLeft, double srcTop, double srcRight, double srcBottom, double dstLeft, double dstTop, double dstRight, double dstBottom, List? paintObjects, ByteData paintData, int filterQualityIndex, ); @override void drawImageNine(Image image, Rect center, Rect dst, Paint paint) { assert(!image.debugDisposed); assert(_rectIsValid(center)); assert(_rectIsValid(dst)); final String? error = _drawImageNine( image._image, center.left, center.top, center.right, center.bottom, dst.left, dst.top, dst.right, dst.bottom, paint._objects, paint._data, paint.filterQuality.index, ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } @Native< Handle Function( Pointer, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32, ) >(symbol: 'Canvas::drawImageNine') external String? _drawImageNine( _Image image, double centerLeft, double centerTop, double centerRight, double centerBottom, double dstLeft, double dstTop, double dstRight, double dstBottom, List? paintObjects, ByteData paintData, int filterQualityIndex, ); @override void drawPicture(Picture picture) { assert(!picture.debugDisposed); _drawPicture(picture as _NativePicture); } @Native, Pointer)>(symbol: 'Canvas::drawPicture') external void _drawPicture(_NativePicture picture); @override void drawParagraph(Paragraph paragraph, Offset offset) { final nativeParagraph = paragraph as _NativeParagraph; assert(!nativeParagraph.debugDisposed); assert(_offsetIsValid(offset)); assert(!nativeParagraph._needsLayout); nativeParagraph._paint(this, offset.dx, offset.dy); } @override void drawPoints(PointMode pointMode, List points, Paint paint) { _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points)); } @override void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) { if (points.length % 2 != 0) { throw ArgumentError('"points" must have an even number of values.'); } _drawPoints(paint._objects, paint._data, pointMode.index, points); } @Native, Handle, Handle, Int32, Handle)>(symbol: 'Canvas::drawPoints') external void _drawPoints( List? paintObjects, ByteData paintData, int pointMode, Float32List points, ); @override void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { assert(!vertices.debugDisposed); _drawVertices(vertices, blendMode.index, paint._objects, paint._data); } @Native, Pointer, Int32, Handle, Handle)>( symbol: 'Canvas::drawVertices', ) external void _drawVertices( Vertices vertices, int blendMode, List? paintObjects, ByteData paintData, ); @override void drawAtlas( Image atlas, List transforms, List rects, List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint, ) { assert(!atlas.debugDisposed); assert(colors == null || colors.isEmpty || blendMode != null); final int rectCount = rects.length; if (transforms.length != rectCount) { throw ArgumentError('"transforms" and "rects" lengths must match.'); } if (colors != null && colors.isNotEmpty && colors.length != rectCount) { throw ArgumentError( 'If non-null, "colors" length must match that of "transforms" and "rects".', ); } final rstTransformBuffer = Float32List(rectCount * 4); final rectBuffer = Float32List(rectCount * 4); for (var i = 0; i < rectCount; i++) { final int index0 = i * 4; final int index1 = index0 + 1; final int index2 = index0 + 2; final int index3 = index0 + 3; final RSTransform rstTransform = transforms[i]; final Rect rect = rects[i]; assert(_rectIsValid(rect)); rstTransformBuffer[index0] = rstTransform.scos; rstTransformBuffer[index1] = rstTransform.ssin; rstTransformBuffer[index2] = rstTransform.tx; rstTransformBuffer[index3] = rstTransform.ty; rectBuffer[index0] = rect.left; rectBuffer[index1] = rect.top; rectBuffer[index2] = rect.right; rectBuffer[index3] = rect.bottom; } final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); final Float32List? cullRectBuffer = cullRect?._getValue32(); final int qualityIndex = paint.filterQuality.index; final String? error = _drawAtlas( paint._objects, paint._data, qualityIndex, atlas._image, rstTransformBuffer, rectBuffer, colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer, ); if (error != null) { throw PictureRasterizationException._(error, stack: atlas._debugStack); } } @override void drawRawAtlas( Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, BlendMode? blendMode, Rect? cullRect, Paint paint, ) { assert(colors == null || blendMode != null); final int rectCount = rects.length; if (rstTransforms.length != rectCount) { throw ArgumentError('"rstTransforms" and "rects" lengths must match.'); } if (rectCount % 4 != 0) { throw ArgumentError('"rstTransforms" and "rects" lengths must be a multiple of four.'); } if (colors != null && colors.length * 4 != rectCount) { throw ArgumentError( 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".', ); } final int qualityIndex = paint.filterQuality.index; final String? error = _drawAtlas( paint._objects, paint._data, qualityIndex, atlas._image, rstTransforms, rects, colors, (blendMode ?? BlendMode.src).index, cullRect?._getValue32(), ); if (error != null) { throw PictureRasterizationException._(error, stack: atlas._debugStack); } } @Native< Handle Function( Pointer, Handle, Handle, Int32, Pointer, Handle, Handle, Handle, Int32, Handle, ) >(symbol: 'Canvas::drawAtlas') external String? _drawAtlas( List? paintObjects, ByteData paintData, int filterQualityIndex, _Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, int blendMode, Float32List? cullRect, ); @override void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { _drawShadow(path as _NativePath, color.value, elevation, transparentOccluder); } @Native, Pointer, Uint32, Double, Bool)>( symbol: 'Canvas::drawShadow', ) external void _drawShadow( _NativePath path, int color, double elevation, bool transparentOccluder, ); @override String toString() => 'Canvas(recording: ${_recorder != null})'; } /// Signature for [Picture] lifecycle events. typedef PictureEventCallback = void Function(Picture picture); /// An object representing a sequence of recorded graphical operations. /// /// To create a [Picture], use a [PictureRecorder]. /// /// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via /// the [SceneBuilder.addPicture] method. A [Picture] can also be /// drawn into a [Canvas], using the [Canvas.drawPicture] method. abstract class Picture { /// A callback that is invoked to report a picture creation. /// /// It's preferred to use [MemoryAllocations] in flutter/foundation.dart /// than to use [onCreate] directly because [MemoryAllocations] /// allows multiple callbacks. static PictureEventCallback? onCreate; /// A callback that is invoked to report the picture disposal. /// /// It's preferred to use [MemoryAllocations] in flutter/foundation.dart /// than to use [onDispose] directly because [MemoryAllocations] /// allows multiple callbacks. static PictureEventCallback? onDispose; /// Creates an image from this picture. /// /// The returned image will be `width` pixels wide and `height` pixels high. /// The picture is rasterized within the 0 (left), 0 (top), `width` (right), /// `height` (bottom) bounds. Content outside these bounds is clipped. Future toImage(int width, int height); /// Synchronously creates a handle to an image of this picture. /// /// {@template dart.ui.painting.Picture.toImageSync} /// The returned image will be [width] pixels wide and [height] pixels high. /// The picture is rasterized within the 0 (left), 0 (top), [width] (right), /// [height] (bottom) bounds. Content outside these bounds is clipped. /// /// The image object is created and returned synchronously, but is rasterized /// asynchronously. If the rasterization fails, an exception will be thrown /// when the image is drawn to a [Canvas]. /// /// If a GPU context is available, this image will be created as GPU resident /// and not copied back to the host. This means the image will be more /// efficient to draw. /// /// If no GPU context is available, the image will be rasterized on the CPU. /// /// The [targetFormat] argument specifies the pixel format of the returned /// [Image]. If [TargetPixelFormat.dontCare] is specified, the pixel format /// will be chosen automatically based on the GPU capabilities. /// {@endtemplate} Image toImageSync( int width, int height, { TargetPixelFormat targetFormat = TargetPixelFormat.dontCare, }); /// Release the resources used by this object. The object is no longer usable /// after this method is called. void dispose(); /// Whether this reference to the underlying picture is [dispose]d. /// /// This only returns a valid value if asserts are enabled, and must not be /// used otherwise. bool get debugDisposed; /// Returns the approximate number of bytes allocated for this object. /// /// The actual size of this picture may be larger, particularly if it contains /// references to image or other large objects. int get approximateBytesUsed; } base class _NativePicture extends NativeFieldWrapperClass1 implements Picture { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a [Picture], use a [PictureRecorder]. _NativePicture._(); @override Future toImage(int width, int height) { assert(!_disposed); if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } return _futurize( (_Callback callback) => _toImage(width, height, (_Image? image) { if (image == null) { callback(null); } else { callback(Image._(image, image.width, image.height)); } }), ); } @Native, Uint32, Uint32, Handle)>(symbol: 'Picture::toImage') external String? _toImage(int width, int height, void Function(_Image?) callback); @override Image toImageSync( int width, int height, { TargetPixelFormat targetFormat = TargetPixelFormat.dontCare, }) { assert(!_disposed); if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } final image = _Image._(); _toImageSync(width, height, targetFormat.index, image); return Image._(image, image.width, image.height); } @Native, Uint32, Uint32, Int32, Handle)>( symbol: 'Picture::toImageSync', ) external void _toImageSync(int width, int height, int targetFormat, _Image outImage); @override void dispose() { assert(!_disposed); assert(() { _disposed = true; return true; }()); Picture.onDispose?.call(this); _dispose(); } /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'Picture::dispose') external void _dispose(); bool _disposed = false; @override bool get debugDisposed { bool? disposed; assert(() { disposed = _disposed; return true; }()); return disposed ?? (throw StateError('Picture.debugDisposed is only available when asserts are enabled.')); } @override @Native)>(symbol: 'Picture::GetAllocationSize', isLeaf: true) external int get approximateBytesUsed; @override String toString() => 'Picture'; } /// Records a [Picture] containing a sequence of graphical operations. /// /// To begin recording, construct a [Canvas] to record the commands. /// To end recording, use the [PictureRecorder.endRecording] method. /// /// ## Use with the Flutter framework /// /// The Flutter framework's [RendererBinding] provides a hook for creating /// [PictureRecorder] objects ([RendererBinding.createPictureRecorder]) that /// allows tests to hook into the scene creation logic. When creating a /// [PictureRecorder] and [Canvas] that will be used with a [PictureLayer] as /// part of the [Scene] in the context of the Flutter framework, consider /// calling [RendererBinding.createPictureRecorder] instead of calling the /// [PictureRecorder.new] constructor directly. /// /// This does not apply when using a canvas to generate a bitmap for other /// purposes, e.g. for generating a PNG image using [Picture.toImage]. abstract class PictureRecorder { /// Creates a new idle PictureRecorder. To associate it with a /// [Canvas] and begin recording, pass this [PictureRecorder] to the /// [Canvas] constructor. factory PictureRecorder() = _NativePictureRecorder; /// Whether this object is currently recording commands. /// /// Specifically, this returns true if a [Canvas] object has been /// created to record commands and recording has not yet ended via a /// call to [endRecording], and false if either this /// [PictureRecorder] has not yet been associated with a [Canvas], /// or the [endRecording] method has already been called. bool get isRecording; /// Finishes recording graphical operations. /// /// Returns a picture containing the graphical operations that have been /// recorded thus far. After calling this function, both the picture recorder /// and the canvas objects are invalid and cannot be used further. Picture endRecording(); } base class _NativePictureRecorder extends NativeFieldWrapperClass1 implements PictureRecorder { _NativePictureRecorder() { _constructor(); } @Native(symbol: 'PictureRecorder::Create') external void _constructor(); @override bool get isRecording => _canvas != null; @override Picture endRecording() { if (_canvas == null) { throw StateError('PictureRecorder did not start recording.'); } final picture = _NativePicture._(); _endRecording(picture); _canvas!._recorder = null; _canvas = null; // We invoke the handler here, not in the Picture constructor, because we want // [picture.approximateBytesUsed] to be available for the handler. Picture.onCreate?.call(picture); return picture; } @Native, Handle)>(symbol: 'PictureRecorder::endRecording') external void _endRecording(_NativePicture outPicture); _NativeCanvas? _canvas; @override String toString() => 'PictureRecorder(recording: $isRecording)'; } /// A single shadow. /// /// Multiple shadows are stacked together in a [TextStyle]. class Shadow { /// Construct a shadow. /// /// The default shadow is a black shadow with zero offset and zero blur. /// Default shadows should be completely covered by the casting element, /// and not be visible. /// /// Transparency should be adjusted through the [color] alpha. /// /// Shadow order matters due to compositing multiple translucent objects not /// being commutative. const Shadow({ this.color = const Color(_kColorDefault), this.offset = Offset.zero, this.blurRadius = 0.0, }) : assert(blurRadius >= 0.0, 'Text shadow blur radius should be non-negative.'); static const int _kColorDefault = 0xFF000000; // Constants for shadow encoding. static const int _kBytesPerShadow = 16; static const int _kColorOffset = 0 << 2; static const int _kXOffset = 1 << 2; static const int _kYOffset = 2 << 2; static const int _kBlurOffset = 3 << 2; /// Color that the shadow will be drawn with. /// /// The shadows are shapes composited directly over the base canvas, and do not /// represent optical occlusion. final Color color; /// The displacement of the shadow from the casting element. /// /// Positive x/y offsets will shift the shadow to the right and down, while /// negative offsets shift the shadow to the left and up. The offsets are /// relative to the position of the element that is casting it. final Offset offset; /// The standard deviation of the Gaussian to convolve with the shadow's shape. final double blurRadius; /// Converts a blur radius in pixels to sigmas. /// /// See the sigma argument to [MaskFilter.blur]. /// // See SkBlurMask::ConvertRadiusToSigma(). // static double convertRadiusToSigma(double radius) { return radius > 0 ? radius * 0.57735 + 0.5 : 0; } /// The [blurRadius] in sigmas instead of logical pixels. /// /// See the sigma argument to [MaskFilter.blur]. double get blurSigma => convertRadiusToSigma(blurRadius); /// Create the [Paint] object that corresponds to this shadow description. /// /// The [offset] is not represented in the [Paint] object. /// To honor this as well, the shape should be translated by [offset] before /// being filled using this [Paint]. /// /// This class does not provide a way to disable shadows to avoid /// inconsistencies in shadow blur rendering, primarily as a method of /// reducing test flakiness. [toPaint] should be overridden in subclasses to /// provide this functionality. Paint toPaint() { return Paint() ..color = color ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma); } /// Returns a new shadow with its [offset] and [blurRadius] scaled by the given /// factor. Shadow scale(double factor) { return Shadow(color: color, offset: offset * factor, blurRadius: blurRadius * factor); } /// Linearly interpolate between two shadows. /// /// If either shadow is null, this function linearly interpolates from /// a shadow that matches the other shadow in color but has a zero /// offset and a zero blurRadius. /// /// {@template dart.ui.shadow.lerp} /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. /// {@endtemplate} static Shadow? lerp(Shadow? a, Shadow? b, double t) { if (b == null) { if (a == null) { return null; } else { return a.scale(1.0 - t); } } else { if (a == null) { return b.scale(t); } else { return Shadow( color: Color.lerp(a.color, b.color, t)!, offset: Offset.lerp(a.offset, b.offset, t)!, blurRadius: _lerpDouble(a.blurRadius, b.blurRadius, t), ); } } } /// Linearly interpolate between two lists of shadows. /// /// If the lists differ in length, excess items are lerped with null. /// /// {@macro dart.ui.shadow.lerp} static List? lerpList(List? a, List? b, double t) { if (a == null && b == null) { return null; } a ??= []; b ??= []; final result = []; final int commonLength = math.min(a.length, b.length); for (var i = 0; i < commonLength; i += 1) { result.add(Shadow.lerp(a[i], b[i], t)!); } for (var i = commonLength; i < a.length; i += 1) { result.add(a[i].scale(1.0 - t)); } for (var i = commonLength; i < b.length; i += 1) { result.add(b[i].scale(t)); } return result; } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } return other is Shadow && other.color == color && other.offset == offset && other.blurRadius == blurRadius; } @override int get hashCode => Object.hash(color, offset, blurRadius); // Serialize [shadows] into ByteData. The format is a single uint_32_t at // the beginning indicating the number of shadows, followed by _kBytesPerShadow // bytes for each shadow. static ByteData _encodeShadows(List? shadows) { if (shadows == null) { return ByteData(0); } final int byteCount = shadows.length * _kBytesPerShadow; final shadowsData = ByteData(byteCount); var shadowOffset = 0; for (var shadowIndex = 0; shadowIndex < shadows.length; ++shadowIndex) { final Shadow shadow = shadows[shadowIndex]; shadowOffset = shadowIndex * _kBytesPerShadow; shadowsData.setInt32( _kColorOffset + shadowOffset, shadow.color.value ^ Shadow._kColorDefault, _kFakeHostEndian, ); shadowsData.setFloat32(_kXOffset + shadowOffset, shadow.offset.dx, _kFakeHostEndian); shadowsData.setFloat32(_kYOffset + shadowOffset, shadow.offset.dy, _kFakeHostEndian); final double blurSigma = Shadow.convertRadiusToSigma(shadow.blurRadius); shadowsData.setFloat32(_kBlurOffset + shadowOffset, blurSigma, _kFakeHostEndian); } return shadowsData; } @override String toString() => 'TextShadow($color, $offset, $blurRadius)'; } /// A handle to a read-only byte buffer that is managed by the engine. /// /// The creator of this object is responsible for calling [dispose] when it is /// no longer needed. base class ImmutableBuffer extends NativeFieldWrapperClass1 { ImmutableBuffer._(this._length); /// Creates a copy of the data from a [Uint8List] suitable for internal use /// in the engine. static Future fromUint8List(Uint8List list) { final instance = ImmutableBuffer._(list.length); return _futurize((_Callback callback) { return instance._init(list, callback); }).then((_) => instance); } /// Create a buffer from the asset with key [assetKey]. /// /// Throws an [Exception] if the asset does not exist. static Future fromAsset(String assetKey) { // The flutter tool converts all asset keys with spaces into URI // encoded paths (replacing ' ' with '%20', for example). We perform // the same encoding here so that users can load assets with the same // key they have written in the pubspec. final String encodedKey = Uri(path: Uri.encodeFull(assetKey)).path; final instance = ImmutableBuffer._(0); return _futurize((_Callback callback) { return instance._initFromAsset(encodedKey, callback); }).then((int length) { if (length == -1) { throw Exception('Asset not found'); } return instance.._length = length; }); } /// Create a buffer from the file with [path]. /// /// Throws an [Exception] if the asset does not exist. static Future fromFilePath(String path) { final instance = ImmutableBuffer._(0); return _futurize((_Callback callback) { return instance._initFromFile(path, callback); }).then((int length) { if (length == -1) { throw Exception('Could not load file at $path.'); } return instance.._length = length; }); } @Native(symbol: 'ImmutableBuffer::init') external String? _init(Uint8List list, _Callback callback); @Native(symbol: 'ImmutableBuffer::initFromAsset') external String? _initFromAsset(String assetKey, _Callback callback); @Native(symbol: 'ImmutableBuffer::initFromFile') external String? _initFromFile(String assetKey, _Callback callback); /// The length, in bytes, of the underlying data. int get length => _length; int _length; bool _debugDisposed = false; /// Whether [dispose] has been called. /// /// This must only be used when asserts are enabled. Otherwise, it will throw. bool get debugDisposed { late bool disposed; assert(() { disposed = _debugDisposed; return true; }()); return disposed; } /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// /// The underlying memory allocated by this object will be retained beyond /// this call if it is still needed by another object that has not been /// disposed. For example, an [ImageDescriptor] that has not been disposed /// may still retain a reference to the memory from this buffer even if it /// has been disposed. Freeing that memory requires disposing all resources /// that may still hold it. void dispose() { assert(() { assert(!_debugDisposed); _debugDisposed = true; return true; }()); _dispose(); } /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'ImmutableBuffer::dispose') external void _dispose(); } /// A descriptor of data that can be turned into an [Image] via a [Codec]. /// /// Use this class to determine the height, width, and byte size of image data /// before decoding it. abstract class ImageDescriptor { /// Creates an image descriptor from raw image pixels. /// /// The `pixels` parameter is the pixel data. They are packed in bytes in the /// order described by `pixelFormat`, then grouped in rows, from left to right, /// then top to bottom. /// /// The `rowBytes` parameter is the number of bytes consumed by each row of /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied /// by the number of bytes per pixel in the provided `format`. // Not async because there's no expensive work to do here. factory ImageDescriptor.raw( ImmutableBuffer buffer, { required int width, required int height, int? rowBytes, required PixelFormat pixelFormat, }) = _NativeImageDescriptor.raw; /// Creates an image descriptor from encoded data in a supported format. static Future encoded(ImmutableBuffer buffer) { final descriptor = _NativeImageDescriptor._(); return _futurize((_Callback callback) { return descriptor._initEncoded(buffer, callback); }).then((_) => descriptor); } /// The width, in pixels, of the image. /// /// On the Web, this is only supported for [raw] images. int get width; /// The height, in pixels, of the image. /// /// On the Web, this is only supported for [raw] images. int get height; /// The number of bytes per pixel in the image. /// /// On web, this is only supported for [raw] images. int get bytesPerPixel; /// Release the resources used by this object. The object is no longer usable /// after this method is called. /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). void dispose(); /// Creates a [Codec] object which is suitable for decoding the data in the /// buffer to an [Image]. /// /// If only one of targetWidth or targetHeight are specified, the other /// dimension will be scaled according to the aspect ratio of the supplied /// dimension. /// /// If either targetWidth or targetHeight is less than or equal to zero, it /// will be treated as if it is null. Future instantiateCodec({ int? targetWidth, int? targetHeight, TargetPixelFormat targetFormat = TargetPixelFormat.dontCare, }); } base class _NativeImageDescriptor extends NativeFieldWrapperClass1 implements ImageDescriptor { _NativeImageDescriptor._(); /// Creates an image descriptor from raw image pixels. /// /// The `pixels` parameter is the pixel data. They are packed in bytes in the /// order described by `pixelFormat`, then grouped in rows, from left to right, /// then top to bottom. /// /// The `rowBytes` parameter is the number of bytes consumed by each row of /// pixels in the data buffer. If unspecified, it defaults to `width` multiplied /// by the number of bytes per pixel in the provided `format`. // Not async because there's no expensive work to do here. _NativeImageDescriptor.raw( ImmutableBuffer buffer, { required int width, required int height, int? rowBytes, required PixelFormat pixelFormat, }) { _width = width; _height = height; // We only support 4 byte pixel formats in the PixelFormat enum. _bytesPerPixel = 4; _initRaw(this, buffer, width, height, rowBytes ?? -1, pixelFormat.index); } @Native, Handle)>(symbol: 'ImageDescriptor::initEncoded') external String? _initEncoded(ImmutableBuffer buffer, _Callback callback); @Native( symbol: 'ImageDescriptor::initRaw', ) external static void _initRaw( ImageDescriptor outDescriptor, ImmutableBuffer buffer, int width, int height, int rowBytes, int pixelFormat, ); int? _width; @Native)>(symbol: 'ImageDescriptor::width', isLeaf: true) external int _getWidth(); @override int get width => _width ??= _getWidth(); int? _height; @Native)>(symbol: 'ImageDescriptor::height', isLeaf: true) external int _getHeight(); @override int get height => _height ??= _getHeight(); int? _bytesPerPixel; @Native)>(symbol: 'ImageDescriptor::bytesPerPixel', isLeaf: true) external int _getBytesPerPixel(); @override int get bytesPerPixel => _bytesPerPixel ??= _getBytesPerPixel(); @override @Native)>(symbol: 'ImageDescriptor::dispose') external void dispose(); @override Future instantiateCodec({ int? targetWidth, int? targetHeight, TargetPixelFormat targetFormat = TargetPixelFormat.dontCare, }) async { if (targetWidth != null && targetWidth <= 0) { targetWidth = null; } if (targetHeight != null && targetHeight <= 0) { targetHeight = null; } if (targetWidth == null && targetHeight == null) { targetWidth = width; targetHeight = height; } else if (targetWidth == null && targetHeight != null) { targetWidth = (targetHeight * (width / height)).round(); } else if (targetHeight == null && targetWidth != null) { targetHeight = targetWidth ~/ (width / height); } assert(targetWidth != null); assert(targetHeight != null); final Codec codec = _NativeCodec._(); _instantiateCodec(codec, targetWidth!, targetHeight!, targetFormat.index); return codec; } @Native, Handle, Int32, Int32, Int32)>( symbol: 'ImageDescriptor::instantiateCodec', ) external void _instantiateCodec( Codec outCodec, int targetWidth, int targetHeight, int targetFormat, ); @override String toString() => 'ImageDescriptor(width: ${_width ?? '?'}, height: ${_height ?? '?'}, bytes per pixel: ${_bytesPerPixel ?? '?'})'; } /// Generic callback signature, used by [_futurize]. typedef _Callback = void Function(T result); /// Generic callback signature, used by [_futurizeWithError]. typedef _CallbackWithError = void Function(T result, String? error); /// Signature for a method that receives a [_Callback]. /// /// Return value should be null on success, and a string error message on /// failure. typedef _Callbacker = String? Function(_Callback callback); /// Signature for a method that receives a [_CallbackWithError]. /// See also: [_Callbacker] typedef _CallbackerWithError = String? Function(_CallbackWithError callback); // Converts a method that receives a value-returning callback to a method that // returns a Future. // // Return a [String] to cause an [Exception] to be synchronously thrown with // that string as a message. // // If the callback is called with null, the future completes with an error. // // Example usage: // // ```dart // typedef IntCallback = void Function(int result); // // String? _doSomethingAndCallback(IntCallback callback) { // Timer(const Duration(seconds: 1), () { callback(1); }); // } // // Future doSomething() { // return _futurize(_doSomethingAndCallback); // } // ``` // // This function is private and so not directly tested. Instead, an exact copy of it // has been inlined into the test at lib/ui/fixtures/ui_test.dart. if you change this // function, then you must update the test. // // TODO(ianh): We should either automate the code duplication or just make it public. Future _futurize(_Callbacker callbacker) { final completer = Completer.sync(); // If the callback synchronously throws an error, then synchronously // rethrow that error instead of adding it to the completer. This // prevents the Zone from receiving an uncaught exception. var isSync = true; final String? error = callbacker((T? t) { if (t == null) { if (isSync) { throw Exception('operation failed'); } else { completer.completeError(Exception('operation failed')); } } else { completer.complete(t); } }); isSync = false; if (error != null) { throw Exception(error); } return completer.future; } /// A variant of `_futurize` that can communicate specific errors. Future _futurizeWithError(_CallbackerWithError callbacker) { final completer = Completer.sync(); // If the callback synchronously throws an error, then synchronously // rethrow that error instead of adding it to the completer. This // prevents the Zone from receiving an uncaught exception. var isSync = true; final String? error = callbacker((T? t, String? error) { if (t != null) { completer.complete(t); } else { if (isSync) { throw Exception(error ?? 'operation failed'); } else { completer.completeError(Exception(error ?? 'operation failed')); } } }); isSync = false; if (error != null) { throw Exception(error); } return completer.future; } /// An exception thrown by [Canvas.drawImage] and related methods when drawing /// an [Image] created via [Picture.toImageSync] that is in an invalid state. /// /// This exception may be thrown if the requested image dimensions exceeded the /// maximum 2D texture size allowed by the GPU, or if no GPU surface or context /// was available for rasterization at request time. class PictureRasterizationException implements Exception { const PictureRasterizationException._(this.message, {this.stack}); /// A string containing details about the failure. final String message; /// If available, the stack trace at the time [Picture.toImageSync] was called. final StackTrace? stack; @override String toString() { final buffer = StringBuffer('Failed to rasterize a picture: $message.'); if (stack != null) { buffer.writeln(); buffer.writeln('The callstack when the image was created was:'); buffer.writeln(stack!.toString()); } return buffer.toString(); } } ¤@32MJPQPNQ>N&@"B:R%Z,A^%X,?:\-QQLK€‡>0 W(=88PHQ7 MH+NPQQMLQWO+(&'%PN(FM%@QP?U>QI(P?P!P 'FM%'&CK =cYKJ%'&M0CH=<:O'%'&%&..M_6DPlKMDLK(LLKJMNQJ# 9O"9/3Q"&79-99.89-@DGKN+J I&% ?YI (I680IH8<&.1P-82I680Q-81>= AKT>3@2221/HCJM?FMJKLNPMQ-O MNNE566( "+,+K:: ><>=( CFLGKOK@<>','% =?>+ (1KML+ M+3; #",( ; €¥3MJQMONPKDPKQLNMGKOFNMJRMQA'NAQMQOOKOFD<] ?O;[?KB[=NQN-_ >)N_O+ HMM?MHL] MNJ:MRL] OSO@MQM^ TNJ;RLM^ O NPL4K_ ONPG4K_ P4M[?OL&NQNQOJ\JOOQ)Q`BHJ HK&LM/QL&POQO"^PHJ"I/QNQLM/_9GP+ NI^ OI_ ;LM/bSLM/a I*PIN-LM/a9GO, NBa9 IOI8bLOI8a NOOLMHO$`(QIIP&[P)O1QIP-bBOQIIP5]6NN QIP-bOKX;]GUTVTN;YMHK9)V(DFE8B/J(KMIK0O4 P N='RPK-$5C<@QdN2;JPpP% CE%OqP%N *MDL ;=6iHP NQ9 5gHP N N2gHP N :<KFF'DFG> $R KQKMYU,Y1@X[O$ QNUM[?M] NSR)PIdbCDITENBFH GK>NPHPQ#N)/KKONOPQH!4,+(*)**)%+)+1.+40,B<@>@@>6B>@LF@RJBABD!.C&++TFG P*#D809 F1HHMQOW6<aCEDK`8ALBQQ!M!'O4JM;#:BD(R#%>:O==D"FA02R#%BAQK:iggLQ O,I?T%%C87KNKMNEN$iiiLQ JI I'>KP>$D!! [G &RHH M2RYQRTL(M$Z+%F:6<82H0d %*`  8G%Q!I"(F . F;G-P  ;!I"(. AH+D?LJ1G!J 9#) <#)%,=U &10(1,=;/A3=9KCJK238M,3MN89"MMK5 0"+  & # :BNMHJ^  YP;&L- HVB!  PNLMIQ _E?NP3 OO3 PGO;G 9M LG ,96@NOOGHNLOGnO&O2( !>KB!'?KC!(DBM1QKKO -,7#DN _5LEOGMPOJLU-.<LO,ON3L-3 (NQ"@6T 1OOQLLNLN)HQ6 8?J PN&6 !"4K+CD=<0"##9 + EN$+QNC +LFMJPOJM9 *5IN"OPUIM07! >/M)HL H8&/4%(DQA0$P#K%LI9 ;T  JY% +3* #a 9>D (,O3*,.OPNM+6/POMN*6 <IONGJ-^9,=NKLQ?KJPOLQOH<JJP< #?46H,D(G*$&& D & , >ILI:_4Q=.* M7JQOO+8 BQQ9 $O&4NKAOO&NN7 KNK5 K9NT=4NKAIQP#PIQIOL:K%XJ H2EC?F+?C(D9m$ (MO#(0>E$5#H-G #G2-C@quFCGE NEGmqREGEZFDEG%mqFFDEGF(NC"C$G@GDGD.EEFCBQOEOID'EOL!OE4ONCJCNM!GB.RNH=?AFDD lppt@RO%G4BCM.6FMN+BF?=MQ+IEAEDPMHE@/@0'5PMK?QORMJNELPN:I QgO1LHOOJ8KMI4PMPCMMOPONO4QNSQK:J(NE9 G& D ATZ, ^, 9$6 ^, 9$6 I'O I/W Y[ Y%c QO Q%W R __ !$$Z]  !)$](%  <IP  <IP A SY 52R= "$J. 720YG C$(N ;`BP'` J$(T BH$IP-* M M #*`/ $"(W< (&"S? +>E%aJ ;- B^1&-EU,=HSD-QR0PONOONE9HQLFFLOJP!@-X( 2O;+# 3!c\u  '0?HKN$NOLPN>B36IJI%NFP+PGNO9NPO9P'OM5F2AIBDN] W;5%UB$ +K"a-D +KC; R[F` +K"CN'$ +K$^-+U*,Z&LA 5<8MC NL. MQC P MPQK4QONPO&M3FK*6 #T 3 Q!=;# F3% >)')($3+ .(*+9E$"]E-"a@%2%23(0   S0   J:6 >444^^^$&%$A:6 >444^^^$&%$P2&2#-"C&2#>>&-#2QONL1I +MQDQN-;JGM(33333OL@ !> €‡=#### €‰A&&&& 0L('N('J6*(rrrr05DII M)4 (. K*$5$5B #,# 7"( *c %9/$2$2% 9/$2$2€œ>PKR;VAZ9/E/EN&@ e5S1d*d*3OO(MO(L+R-<MCP-NOQV9LPPMO?%MFMCDRLBDNJ8 >bJO7%ZH7MAZ4 OPMP.IB,MN-)2,& < 6c:@+#O3 ID+/H[u?QM'J922I%!D 7 G D #,*0/ H8B 7 `G ] #,0"""$ E2FD 7 D A #,` 51EC 7 C @ #,_ 53O!!F 7 &V Z #,+,* =:+D 7 0 H #,2"3H@ '?PSD;:2HF$3IVV9N @  ^39'=9&<ZE_=G.SZ4N&F55J)Q! P#MJIJEN Z€�3J[mAWAUSYGM-QDJP*JH'MN;ZRJSS9X ccb QX^,RHZ]ffe SXXVXCZVddc @ZYCW4Y ccb 7(.)U####1(()!4(.)#$#=""!HRv%5OQJOQQP"O6ccdfKFMS@(%#<-DC=,[K OQJOQQP"N6ccdfQFMS@LNJNO%('<-=,Ce\D 75 PKN;OREQMPN-QRSRQMIPADVMS1QQRR5bbceOP#MS@(#'&<-C=,= P$`$  - `#R X 0\>(&*" @ W*# K%H5J*P>04 ZEIEM>EIJ-HB52>-9SMNQ[=5KN,@<1&S=R= / P5 K3 F "5:&(O+7:#/K 4I:87:'LL8:75% " D% 80#D \34$BU9Q*O  2444X2N0)>2 %PN  24V1/0!)N  ?KV1<0+1N  2?V1I059N  2. V1A<=1N  2. V1< ON  2. V1<  T  2d)`,"-2&PB M0>9'%35HPN&MMNI9+]["@,\_[FHU3 @UO4NNKNP*G" N Q+113;;;;'* MUJ+UME" 4*11---- @H^NU9@ 4*- 0G2@ 4*. 0G<@ 4*) 0GF@ 4, 0GF@ 4, 0G@ 4, 0H;=H(V 695T Q$U(=! 4d EH<# 4h$ C. !)9< 4b& C. !e< 4b+ C. !)<# 4c  C. e<# 4c  C. ) <# 4c  C. ") NPK2<8X:6XCd@M!eT=@BOL; "e%@7U\%_(SQ#=H NQK>A8OFKLP&MNOLOOMMPOMMJM"OEMQQP#7GNMNK/M)QNO=OOR)PQQLNKKN*>LQQL' &?KWWB4!NIM  FP/W^ ! BQ/NMNK/MJLOQOGQQO7OOQI9PQQPJLQ-NPO.>%%] CKWWB8!NNM  TBb%P# K%EJN bN<<"NJ9MO :FB 6B J 89 5GDAIPIPI5M?&KPNOJ3KGCIHPOMCGNH,Q5QQOILQNHNJM$NLPP%NIQNNPH-J-(@)79 ONN-C(@79 QFJ-T9a9 KIQ8$?NNQKONKKQQP"H-@?ID\MR?NP""CEG5%PP(OJF'UQLO)#P;'SVRNP-NVDRH)JYNWDRHP:N"_DRHXRNVDRH)7QRSTSROOSSOQ8RTOBN'!9=:< QL[TFM4POLQRSPRPPRTS 5#CL4OHNko4/EP;ko)QBlp,PP&E:PCtsDMQ#ko)KRO#mq9<@MJTQP,osX^5II,HKQ,)NW;OLOLIDGNNMMCNMNGGG%LK LM>OLQMMJLM:EGFJPJEPMFJNLJLIQINLHPKKOMJ:'QL'RJDQYST8PQORPTLQPUW0TNDSOKNUTQIQ).6Y!/!74*=F..4C'  YD.6Y!/!74*+=F..4C44,  IK3N"DQYSTRR7PHQ@+NJLMP[).6Y!/POQEJ8+6)'0(Y[ZB,,?? !^  YD.6Y!/POQEJ>8+6)'0([W[]KF);D,,7755ZZXG/ !n  MMP&WN> ]4\Q%\\E% N#))))  .< $ebZY!! Q _+ W c1 ?_. X( ^, ( >M1 %P4 Z MNBXV  ;"2SA S2>bZ 81Y? #!FV9 $#'F\? 5.b6 5 HY ! -UM * >\Y ! -"B]` ;""XI"! E2NH)$(! * >\Y ! :BQ"! _   ! *Ac^ <"$&!Na!! E"*!N  %#! H""*!N  %#! &$-V6 ;;,$+8 KY M"KGe! K%KO$! ""C(*KMU ;3* %%%6""6644&%'(F$A8&*N 1(-N^<f 8& *N   !! XSN" B.?H4GBB?KB!)AKC!**OOI0B7OOIPN-PIOMNBAP#FNIQ2 PJ6 1%4P? ^U A%4<6H$X  #K%D  a ^) "GEE&KMHNJK0KG!AG6;DG7E</KPE`D  + D(SG%Y: C<KKAO.U0$*+''*0R#?NLBR23-s64<3<M=O'DIO"CR [0IE$&MJKLNPMDO 8! 37B 9H#N";0/7'7! #" "( >LR:=-M25&4 \\OX CGO>#NA52-:9>EIJ-H+1<,)19=+15:)TD]M\L4)Q! P#MJMMN K%LKO!9PRNSA=&#KC328 *;+;27P#K%NKMN(#A`9PRNSA=&ENbSF(!#Y +Z .a$ C L #A 357>?>6!"*OF1# x50>H8J CAUOML 4;=0UV,V4)GD=,-@ CF)GD=; 6IONPP5;E5R MG)dart:ui/painting.dartÀSGÀfdÀh³ÀRõÀSÀSÚÀdÙÀSÀS'ÀS5ÀeÀU¯ÀR«ÀR¯ÀR°ÀSîÀTÀT—ìÀT�ÀT±ÀVÝÀX+ÀXOÀX]ÀT�ÀUÌÀUßÀUÕÀUÚ;org-dartlang-sdk:///flutter/lib/ui/platform_dispatcher.dartÀ // Copyright 2013 The Flutter Authors. 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.ui; /// Signature of callbacks that have no arguments and return no data. typedef VoidCallback = void Function(); /// Signature for [PlatformDispatcher.onBeginFrame]. /// /// The `duration` argument is the point at which the current frame interval /// began, expressed as a duration since some epoch. The epoch in all /// frames will be the same, but it may not match [DateTime]'s epoch. /// /// For any two frames `a` and `b` such that the frame number of `a` is less /// than the frame number of `b`, the duration argument for `a` will be less /// than or equal to the duration argument for `b`. typedef FrameCallback = void Function(Duration duration); /// Signature for [PlatformDispatcher.onReportTimings]. /// /// {@template dart.ui.TimingsCallback.list} /// The callback takes a list of [FrameTiming] because it may not be /// immediately triggered after each frame. Instead, Flutter tries to batch /// frames together and send all their timings at once to decrease the /// overhead (as this is available in the release mode). The list is sorted in /// ascending order of time (earliest frame first). The timing of any frame /// will be sent within about 1 second (100ms if in the profile/debug mode) /// even if there are no later frames to batch. The timing of the first frame /// will be sent immediately without batching. /// {@endtemplate} typedef TimingsCallback = void Function(List timings); /// Signature for [PlatformDispatcher.onPointerDataPacket]. typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); /// Signature for [PlatformDispatcher.onKeyData]. /// /// The callback should return true if the key event has been handled by the /// framework and should not be propagated further. typedef KeyDataCallback = bool Function(KeyData data); /// Signature for [PlatformDispatcher.onSemanticsActionEvent]. typedef SemanticsActionEventCallback = void Function(SemanticsActionEvent action); /// Signature for responses to platform messages. /// /// Used as a parameter to [PlatformDispatcher.sendPlatformMessage] and /// [PlatformDispatcher.onPlatformMessage]. typedef PlatformMessageResponseCallback = void Function(ByteData? data); /// Deprecated. Migrate to [ChannelBuffers.setListener] instead. /// /// Signature for [PlatformDispatcher.onPlatformMessage]. @Deprecated( 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); // Signature for _setNeedsReportTimings. typedef _SetNeedsReportTimingsFunc = void Function(bool value); /// Signature for [PlatformDispatcher.onError]. /// /// If this method returns false, the engine may use some fallback method to /// provide information about the error. /// /// After calling this method, the process or the VM may terminate. Some severe /// unhandled errors may not be able to call this method either, such as Dart /// compilation errors or process terminating errors. typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace); // A gesture setting value that indicates it has not been set by the engine. const double _kUnsetGestureSetting = -1.0; // A display corner radius value that indicates it has not been set by the engine. const double _kUnsetDisplayCornerRadius = -1.0; // A message channel to receive KeyData from the platform. // // See embedder.cc::kFlutterKeyDataChannel for more information. const String _kFlutterKeyDataChannel = 'flutter/keydata'; @pragma('vm:entry-point') ByteData? _wrapUnmodifiableByteData(ByteData? byteData) => byteData?.asUnmodifiableView(); /// A token that represents a root isolate. class RootIsolateToken { RootIsolateToken._(this._token); /// An enumeration representing the root isolate (0 if not a root isolate). final int _token; /// The token for the root isolate that is executing this Dart code. If this /// Dart code is not executing on a root isolate [instance] will be null. static final RootIsolateToken? instance = () { final int token = __getRootIsolateToken(); return token == 0 ? null : RootIsolateToken._(token); }(); @Native(symbol: 'PlatformConfigurationNativeApi::GetRootIsolateToken') external static int __getRootIsolateToken(); } /// Platform event dispatcher singleton. /// /// The most basic interface to the host operating system's interface. /// /// This is the central entry point for platform messages and configuration /// events from the platform. /// /// It exposes the core scheduler API, the input event callback, the graphics /// drawing API, and other such core services. /// /// It manages the list of the application's [views] as well as the /// [configuration] of various platform attributes. /// /// Consider avoiding static references to this singleton through /// [PlatformDispatcher.instance] and instead prefer using a binding for /// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`. /// See [PlatformDispatcher.instance] for more information about why this is /// preferred. class PlatformDispatcher { /// Private constructor, since only dart:ui is supposed to create one of /// these. Use [instance] to access the singleton. PlatformDispatcher._() { _setNeedsReportTimings = _nativeSetNeedsReportTimings; } /// The [PlatformDispatcher] singleton. /// /// Consider avoiding static references to this singleton through /// [PlatformDispatcher.instance] and instead prefer using a binding for /// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`. /// /// Static access of this object means that Flutter has few, if any options to /// fake or mock the given object in tests. Even in cases where Dart offers /// special language constructs to forcefully shadow such properties, those /// mechanisms would only be reasonable for tests and they would not be /// reasonable for a future of Flutter where we legitimately want to select an /// appropriate implementation at runtime. /// /// The only place that `WidgetsBinding.instance.platformDispatcher` is /// inappropriate is if access to these APIs is required before the binding is /// initialized by invoking `runApp()` or /// `WidgetsFlutterBinding.instance.ensureInitialized()`. In that case, it is /// necessary (though unfortunate) to use the [PlatformDispatcher.instance] /// object statically. static PlatformDispatcher get instance => _instance; static final PlatformDispatcher _instance = PlatformDispatcher._(); _PlatformConfiguration _configuration = const _PlatformConfiguration(); /// Called when the platform configuration changes. /// /// The engine invokes this callback in the same zone in which the callback /// was set. VoidCallback? get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; VoidCallback? _onPlatformConfigurationChanged; Zone _onPlatformConfigurationChangedZone = Zone.root; set onPlatformConfigurationChanged(VoidCallback? callback) { _onPlatformConfigurationChanged = callback; _onPlatformConfigurationChangedZone = Zone.current; } /// The current list of displays. /// /// If any of their configurations change, [onMetricsChanged] will be called. /// /// To get the display for a [FlutterView], use [FlutterView.display]. /// /// Platforms may limit what information is available to the application with /// regard to secondary displays and/or displays that do not have an active /// application window. /// /// Presently, on Android and Web this collection will only contain the /// display that the current window is on. On iOS, it will only contains the /// main display on the phone or tablet. On Desktops other than Linux, it will /// contain only a main display with a valid refresh rate but invalid size and /// device pixel ratio values. // TODO(dnfield): Update these docs when https://github.com/flutter/flutter/issues/125939 // and https://github.com/flutter/flutter/issues/125938 are resolved. Iterable get displays => _displays.values; final Map _displays = {}; /// The current list of views, including top level platform windows used by /// the application. /// /// If any of their configurations change, [onMetricsChanged] will be called. Iterable get views => _views.values; final Map _views = {}; /// Returns the [FlutterView] with the provided ID if one exists, or null /// otherwise. FlutterView? view({required int id}) => _views[id]; /// The [FlutterView] provided by the engine if the platform is unable to /// create windows, or, for backwards compatibility. /// /// If the platform provides an implicit view, it can be used to bootstrap /// the framework. This is common for platforms designed for single-view /// applications like mobile devices with a single display. /// /// Applications and libraries must not rely on this property being set /// as it may be null depending on the engine's configuration. Instead, /// consider using [View.of] to lookup the [FlutterView] the current /// [BuildContext] is drawing into. /// /// While the properties on the referenced [FlutterView] may change, /// the reference itself is guaranteed to never change over the lifetime /// of the application: if this property is null at startup, it will remain /// so throughout the entire lifetime of the application. If it points to a /// specific [FlutterView], it will continue to point to the same view until /// the application is shut down (although the engine may replace or remove /// the underlying backing surface of the view at its discretion). /// /// See also: /// /// * [View.of], for accessing the current view. /// * [PlatformDispatcher.views] for a list of all [FlutterView]s provided /// by the platform. FlutterView? get implicitView { final FlutterView? result = _views[_implicitViewId]; // Make sure [implicitView] agrees with `_implicitViewId`. assert( (result != null) == (_implicitViewId != null), (_implicitViewId != null) ? 'The implicit view ID is $_implicitViewId, but the implicit view does not exist.' : 'The implicit view ID is null, but the implicit view exists.', ); // Make sure [implicitView] never chages. assert(() { if (_debugRecordedLastImplicitView) { assert( identical(_debugLastImplicitView, result), 'The implicitView has changed:\n' 'Last: $_debugLastImplicitView\nCurrent: $result', ); } else { _debugLastImplicitView = result; _debugRecordedLastImplicitView = true; } return true; }()); return result; } FlutterView? _debugLastImplicitView; bool _debugRecordedLastImplicitView = false; /// A callback that is invoked whenever the [ViewConfiguration] of any of the /// [views] changes. /// /// For example when the device is rotated or when the application is resized /// (e.g. when showing applications side-by-side on Android), /// `onMetricsChanged` is called. /// /// The engine invokes this callback in the same zone in which the callback /// was set. /// /// The framework registers with this callback and updates the layout /// appropriately. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. VoidCallback? get onMetricsChanged => _onMetricsChanged; VoidCallback? _onMetricsChanged; Zone _onMetricsChangedZone = Zone.root; set onMetricsChanged(VoidCallback? callback) { _onMetricsChanged = callback; _onMetricsChangedZone = Zone.current; } // Called from the engine, via hooks.dart // // Adds a new view with the specific view configuration. // // The implicit view must be added before [implicitView] is first called, // which is typically the main function. void _addView(int id, _ViewConfiguration viewConfiguration) { assert(!_views.containsKey(id), 'View ID $id already exists.'); _views[id] = FlutterView._(id, this, viewConfiguration); _invoke(onMetricsChanged, _onMetricsChangedZone); } // Called from the engine, via hooks.dart // // Removes the specific view. // // The target view must must exist. The implicit view must not be removed, // or an assertion will be triggered. void _removeView(int id) { assert(id != _implicitViewId, 'The implicit view #$id can not be removed.'); if (id == _implicitViewId) { return; } assert(_views.containsKey(id), 'View ID $id does not exist.'); _views.remove(id); _invoke(onMetricsChanged, _onMetricsChangedZone); } void _sendViewFocusEvent(ViewFocusEvent event) { _invoke1(onViewFocusChange, _onViewFocusChangeZone, event); } /// Opaque engine identifier for the engine running current isolate. Can be used /// in native code to retrieve the engine instance. /// The identifier is valid while the isolate is running. int? get engineId => _engineId; int? _engineId; // Called from the engine, via hooks.dart. // // Updates the available displays. void _updateDisplays(List displays) { _displays.clear(); for (final display in displays) { _displays[display.id] = display; } _invoke(onMetricsChanged, _onMetricsChangedZone); } // Called from the engine, via hooks.dart // // Updates the metrics of the window with the given id. void _updateWindowMetrics(int viewId, _ViewConfiguration viewConfiguration) { assert(_views.containsKey(viewId), 'View $viewId does not exist.'); _views[viewId]!._viewConfiguration = viewConfiguration; _invoke(onMetricsChanged, _onMetricsChangedZone); } /// A callback invoked immediately after the focus is transitioned across [FlutterView]s. /// /// When the platform moves the focus from one [FlutterView] to another, this /// callback is invoked indicating the new view that has focus and the direction /// in which focus was received. For example, if focus is moved to the [FlutterView] /// with ID 2 in the forward direction (could be the result of pressing tab) /// the callback receives a [ViewFocusEvent] with [ViewFocusState.focused] and /// [ViewFocusDirection.forward]. /// /// Typically, receivers of this event respond by moving the focus to the first /// focusable widget inside the [FlutterView] with ID 2. If a view receives /// focus in the backward direction (could be the result of pressing shift + tab), /// typically the last focusable widget inside that view is focused. /// /// The platform may remove focus from a [FlutterView]. For example, on the web, /// the browser can move focus to another element, or to the browser's built-in UI. /// On desktop, the operating system can switch to another window (e.g. using Alt + Tab on Windows). /// In scenarios like these, [onViewFocusChange] will be invoked with [ViewFocusState.unfocused] and /// [ViewFocusDirection.undefined]. /// /// Receivers typically respond to this event by removing all focus indications /// from the app. /// /// Apps can also programmatically request to move the focus to a desired /// [FlutterView] by calling [requestViewFocusChange]. /// /// The callback is invoked in the same zone in which the callback was set. /// /// See also: /// /// * [requestViewFocusChange] to programmatically instruct the platform to move focus to a different [FlutterView]. /// * [ViewFocusState] for a list of allowed focus transitions. /// * [ViewFocusDirection] for a list of allowed focus directions. /// * [ViewFocusEvent], which is the event object provided to the callback. ViewFocusChangeCallback? get onViewFocusChange => _onViewFocusChange; ViewFocusChangeCallback? _onViewFocusChange; // ignore: unused_field, field will be used when platforms other than web use these focus APIs. Zone _onViewFocusChangeZone = Zone.root; set onViewFocusChange(ViewFocusChangeCallback? callback) { _onViewFocusChange = callback; _onViewFocusChangeZone = Zone.current; } /// Requests a focus change of the [FlutterView] with ID [viewId]. /// /// If an app would like to request the engine to move focus, in forward direction, /// to the [FlutterView] with ID 1 it should call this method with [ViewFocusState.focused] /// and [ViewFocusDirection.forward]. /// /// There is no need to call this method if the view in question already has /// focus as it won't have any effect. /// /// A call to this method will lead to the engine calling [onViewFocusChange] /// if the request is successfully fulfilled. /// /// See also: /// /// * [onViewFocusChange], a callback to subscribe to view focus change events. void requestViewFocusChange({ required int viewId, required ViewFocusState state, required ViewFocusDirection direction, }) { _requestViewFocusChange(viewId, state.index, direction.index); } @Native( symbol: 'PlatformConfigurationNativeApi::RequestViewFocusChange', ) external static void _requestViewFocusChange(int viewId, int state, int direction); /// A callback invoked when any view begins a frame. /// /// A callback that is invoked to notify the application that it is an /// appropriate time to provide a scene using the [SceneBuilder] API and the /// [FlutterView.render] method. /// /// When possible, this is driven by the hardware VSync signal of the attached /// screen with the highest VSync rate. This is only called if /// [PlatformDispatcher.scheduleFrame] has been called since the last time /// this callback was invoked. FrameCallback? get onBeginFrame => _onBeginFrame; FrameCallback? _onBeginFrame; Zone _onBeginFrameZone = Zone.root; set onBeginFrame(FrameCallback? callback) { _onBeginFrame = callback; _onBeginFrameZone = Zone.current; } // Called from the engine, via hooks.dart void _beginFrame(int microseconds) { _invoke1(onBeginFrame, _onBeginFrameZone, Duration(microseconds: microseconds)); } /// A callback that is invoked for each frame after [onBeginFrame] has /// completed and after the microtask queue has been drained. /// /// This can be used to implement a second phase of frame rendering that /// happens after any deferred work queued by the [onBeginFrame] phase. VoidCallback? get onDrawFrame => _onDrawFrame; VoidCallback? _onDrawFrame; Zone _onDrawFrameZone = Zone.root; set onDrawFrame(VoidCallback? callback) { _onDrawFrame = callback; _onDrawFrameZone = Zone.current; } // Called from the engine, via hooks.dart void _drawFrame() { _invoke(onDrawFrame, _onDrawFrameZone); } /// A callback that is invoked when pointer data is available. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// See also: /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; PointerDataPacketCallback? _onPointerDataPacket; Zone _onPointerDataPacketZone = Zone.root; set onPointerDataPacket(PointerDataPacketCallback? callback) { _onPointerDataPacket = callback; _onPointerDataPacketZone = Zone.current; } // Called from the engine, via hooks.dart void _dispatchPointerDataPacket(ByteData packet) { if (onPointerDataPacket != null) { _invoke1( onPointerDataPacket, _onPointerDataPacketZone, _unpackPointerDataPacket(packet), ); } } // This value must match kPointerDataFieldCount in pointer_data.cc. (The // pointer_data.cc also lists other locations that must be kept consistent.) static const int _kPointerDataFieldCount = 36; static PointerDataPacket _unpackPointerDataPacket(ByteData packet) { const int kStride = Int64List.bytesPerElement; const int kBytesPerPointerData = _kPointerDataFieldCount * kStride; final int length = packet.lengthInBytes ~/ kBytesPerPointerData; assert(length * kBytesPerPointerData == packet.lengthInBytes); final data = []; for (var i = 0; i < length; ++i) { int offset = i * _kPointerDataFieldCount; data.add( PointerData( // The unpacking code must match the struct in pointer_data.h. embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian), timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], device: packet.getInt64(kStride * offset++, _kFakeHostEndian), pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), panX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), panY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), panDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian), rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), viewId: packet.getInt64(kStride * offset++, _kFakeHostEndian), ), ); assert(offset == (i + 1) * _kPointerDataFieldCount); } return PointerDataPacket(data: data); } static ChannelCallback _keyDataListener(KeyDataCallback onKeyData, Zone zone) => (ByteData? packet, PlatformMessageResponseCallback callback) { _invoke1( (KeyData keyData) { final bool handled = onKeyData(keyData); final response = Uint8List(1); response[0] = handled ? 1 : 0; callback(response.buffer.asByteData()); }, zone, _unpackKeyData(packet!), ); }; /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// The callback should return true if the key event has been handled by the /// framework and should not be propagated further. KeyDataCallback? get onKeyData => _onKeyData; KeyDataCallback? _onKeyData; set onKeyData(KeyDataCallback? callback) { _onKeyData = callback; if (callback != null) { channelBuffers.setListener(_kFlutterKeyDataChannel, _keyDataListener(callback, Zone.current)); } else { channelBuffers.clearListener(_kFlutterKeyDataChannel); } } // If this value changes, update the encoding code in the following files: // // * key_data.h (kKeyDataFieldCount) // * KeyData.java (KeyData.FIELD_COUNT) static const int _kKeyDataFieldCount = 6; // The packet structure is described in `key_data_packet.h`. static KeyData _unpackKeyData(ByteData packet) { const int kStride = Int64List.bytesPerElement; var offset = 0; final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); final String? character = charDataSize == 0 ? null : utf8.decoder.convert( packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize), ); final keyData = KeyData( timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), character: character, synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0, ); return keyData; } /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use /// [onReportTimings] directly because [SchedulerBinding.addTimingsCallback] /// allows multiple callbacks. /// /// This can be used to see if the application has missed frames (through /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high /// latencies (through [FrameTiming.totalSpan]). /// /// Unlike [Timeline], the timing information here is available in the release /// mode (additional to the profile and the debug mode). Hence this can be /// used to monitor the application's performance in the wild. /// /// {@macro dart.ui.TimingsCallback.list} /// /// If this is null, no additional work will be done. If this is not null, /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. TimingsCallback? get onReportTimings => _onReportTimings; TimingsCallback? _onReportTimings; Zone _onReportTimingsZone = Zone.root; set onReportTimings(TimingsCallback? callback) { if ((callback == null) != (_onReportTimings == null)) { _setNeedsReportTimings(callback != null); } _onReportTimings = callback; _onReportTimingsZone = Zone.current; } late _SetNeedsReportTimingsFunc _setNeedsReportTimings; void _nativeSetNeedsReportTimings(bool value) => __nativeSetNeedsReportTimings(value); @Native(symbol: 'PlatformConfigurationNativeApi::SetNeedsReportTimings') external static void __nativeSetNeedsReportTimings(bool value); // Called from the engine, via hooks.dart void _reportTimings(List timings) { assert(timings.length % FrameTiming._dataLength == 0); final frameTimings = []; for (var i = 0; i < timings.length; i += FrameTiming._dataLength) { frameTimings.add(FrameTiming._(timings.sublist(i, i + FrameTiming._dataLength))); } _invoke1(onReportTimings, _onReportTimingsZone, frameTimings); } /// Sends a message to a platform-specific plugin. /// /// The `name` parameter determines which plugin receives the message. The /// `data` parameter contains the message payload and is typically UTF-8 /// encoded JSON but can be arbitrary data. If the plugin replies to the /// message, `callback` will be called with the response. /// /// The framework invokes [callback] in the same zone in which this method was /// called. void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { final String? error = _sendPlatformMessage( name, _zonedPlatformMessageResponseCallback(callback), data, ); if (error != null) { throw Exception(error); } } String? _sendPlatformMessage( String name, PlatformMessageResponseCallback? callback, ByteData? data, ) => __sendPlatformMessage(name, callback, data); @Native( symbol: 'PlatformConfigurationNativeApi::SendPlatformMessage', ) external static String? __sendPlatformMessage( String name, PlatformMessageResponseCallback? callback, ByteData? data, ); /// Sends a message to a platform-specific plugin via a [SendPort]. /// /// This operates similarly to [sendPlatformMessage] but is used when sending /// messages from background isolates. The [port] parameter allows Flutter to /// know which isolate to send the result to. The [name] parameter is the name /// of the channel communication will happen on. The [data] parameter is the /// payload of the message. The [identifier] parameter is a unique integer /// assigned to the message. void sendPortPlatformMessage(String name, ByteData? data, int identifier, SendPort port) { final String? error = _sendPortPlatformMessage(name, identifier, port.nativePort, data); if (error != null) { throw Exception(error); } } String? _sendPortPlatformMessage(String name, int identifier, int port, ByteData? data) => __sendPortPlatformMessage(name, identifier, port, data); @Native( symbol: 'PlatformConfigurationNativeApi::SendPortPlatformMessage', ) external static String? __sendPortPlatformMessage( String name, int identifier, int port, ByteData? data, ); /// Registers the current isolate with the isolate identified with by the /// [token]. This is required if platform channels are to be used on a /// background isolate. void registerBackgroundIsolate(RootIsolateToken token) { DartPluginRegistrant.ensureInitialized(); __registerBackgroundIsolate(token._token); } @Native(symbol: 'PlatformConfigurationNativeApi::RegisterBackgroundIsolate') external static void __registerBackgroundIsolate(int rootIsolateId); /// Informs the engine whether the framework is generating a semantics tree. /// /// Only framework knows when semantics tree should be generated. It uses this /// method to notify the engine whether the framework will generate a semantics tree. /// /// In the case where platforms want to enable semantics, e.g. when /// assistive technologies are enabled, it notifies framework through /// [onSemanticsEnabledChanged]. /// /// After this has been set to true, platforms are expected to prepare for accepting /// semantics update sent via [FlutterView.updateSemantics]. When this is set to false, platforms /// may dispose any resources associated with processing semantics as no further /// semantics updates will be sent via [FlutterView.updateSemantics]. /// /// One must call this method with true before sending update through [updateSemantics]. void setSemanticsTreeEnabled(bool enabled) => _setSemanticsTreeEnabled(enabled); @Native(symbol: 'PlatformConfigurationNativeApi::SetSemanticsTreeEnabled') external static void _setSemanticsTreeEnabled(bool update); /// Deprecated. Migrate to [ChannelBuffers.setListener] instead. /// /// Called whenever this platform dispatcher receives a message from a /// platform-specific plugin. /// /// The `name` parameter determines which plugin sent the message. The `data` /// parameter is the payload and is typically UTF-8 encoded JSON but can be /// arbitrary data. /// /// Message handlers must call the function given in the `callback` parameter. /// If the handler does not need to respond, the handler should pass null to /// the callback. /// /// The framework invokes this callback in the same zone in which the callback /// was set. @Deprecated( 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; PlatformMessageCallback? _onPlatformMessage; Zone _onPlatformMessageZone = Zone.root; @Deprecated( 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) set onPlatformMessage(PlatformMessageCallback? callback) { _onPlatformMessage = callback; _onPlatformMessageZone = Zone.current; } /// Called by [_dispatchPlatformMessage]. void _respondToPlatformMessage(int responseId, ByteData? data) => __respondToPlatformMessage(responseId, data); @Native( symbol: 'PlatformConfigurationNativeApi::RespondToPlatformMessage', ) external static void __respondToPlatformMessage(int responseId, ByteData? data); /// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback( PlatformMessageResponseCallback? callback, ) { if (callback == null) { return null; } // Store the zone in which the callback is being registered. final Zone registrationZone = Zone.current; return (ByteData? data) { registrationZone.runUnaryGuarded(callback, data); }; } /// Send a message to the framework using the [ChannelBuffers]. /// /// This method constructs the appropriate callback to respond /// with the given `responseId`. It should only be called for messages /// from the platform. void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { if (name == ChannelBuffers.kControlChannelName) { try { channelBuffers.handleMessage(data!); } finally { _respondToPlatformMessage(responseId, null); } } else if (onPlatformMessage != null) { _invoke3( onPlatformMessage, _onPlatformMessageZone, name, data, (ByteData? responseData) { _respondToPlatformMessage(responseId, responseData); }, ); } else { channelBuffers.push(name, data, (ByteData? responseData) { _respondToPlatformMessage(responseId, responseData); }); } } /// Set the debug name associated with this platform dispatcher's root /// isolate. /// /// Normally debug names are automatically generated from the Dart port, entry /// point, and source file. For example: `main.dart$main-1234`. /// /// This can be combined with flutter tools `--isolate-filter` flag to debug /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. /// Note that this does not rename any child isolates of the root. void setIsolateDebugName(String name) => _setIsolateDebugName(name); @Native(symbol: 'PlatformConfigurationNativeApi::SetIsolateDebugName') external static void _setIsolateDebugName(String name); /// Requests the Dart VM to adjusts the GC heuristics based on the requested `performance_mode`. /// /// This operation is a no-op of web. The request to change a performance may be ignored by the /// engine or not resolve in a predictable way. /// /// See [DartPerformanceMode] for more information on individual performance modes. void requestDartPerformanceMode(DartPerformanceMode mode) { _requestDartPerformanceMode(mode.index); } @Native(symbol: 'PlatformConfigurationNativeApi::RequestDartPerformanceMode') external static int _requestDartPerformanceMode(int mode); /// The embedder can specify data that the isolate can request synchronously /// on launch. This accessor fetches that data. /// /// This data is persistent for the duration of the Flutter application and is /// available even after isolate restarts. Because of this lifecycle, the size /// of this data must be kept to a minimum. /// /// For asynchronous communication between the embedder and isolate, a /// platform channel may be used. ByteData? getPersistentIsolateData() => _getPersistentIsolateData(); @Native(symbol: 'PlatformConfigurationNativeApi::GetPersistentIsolateData') external static ByteData? _getPersistentIsolateData(); /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and /// [onDrawFrame] callbacks be invoked. /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. /// * [scheduleWarmUpFrame], which should only be used to schedule warm up /// frames. void scheduleFrame() => _scheduleFrame(); @Native(symbol: 'PlatformConfigurationNativeApi::ScheduleFrame') external static void _scheduleFrame(); /// Schedule a frame to run as soon as possible, rather than waiting for the /// engine to request a frame in response to a system "Vsync" signal. /// /// The application can call this method as soon as it starts up so that the /// first frame (which is likely to be quite expensive) can start a few extra /// milliseconds earlier. Using it in other situations might lead to /// unintended results, such as screen tearing. Depending on platforms and /// situations, the warm up frame might or might not be actually rendered onto /// the screen. /// /// For more introduction to the warm up frame, see /// [SchedulerBinding.scheduleWarmUpFrame]. /// /// This method uses the provided callbacks as the begin frame callback and /// the draw frame callback instead of [onBeginFrame] and [onDrawFrame]. /// /// See also: /// /// * [SchedulerBinding.scheduleWarmUpFrame], which uses this method, and /// introduces the warm up frame in more details. /// * [scheduleFrame], which schedules the frame at the next appropriate /// opportunity and should be used to render regular frames. void scheduleWarmUpFrame({required VoidCallback beginFrame, required VoidCallback drawFrame}) { // We use timers here to ensure that microtasks flush in between. Timer.run(beginFrame); Timer.run(() { drawFrame(); _endWarmUpFrame(); }); } @Native(symbol: 'PlatformConfigurationNativeApi::EndWarmUpFrame') external static void _endWarmUpFrame(); /// Additional accessibility features that may be enabled by the platform. AccessibilityFeatures get accessibilityFeatures => _configuration.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] /// changes. /// /// The framework invokes this callback in the same zone in which the callback /// was set. VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; VoidCallback? _onAccessibilityFeaturesChanged; Zone _onAccessibilityFeaturesChangedZone = Zone.root; set onAccessibilityFeaturesChanged(VoidCallback? callback) { _onAccessibilityFeaturesChanged = callback; _onAccessibilityFeaturesChangedZone = Zone.current; } // Called from the engine, via hooks.dart void _updateAccessibilityFeatures(int values) { final newFeatures = AccessibilityFeatures._(values); final _PlatformConfiguration previousConfiguration = _configuration; if (newFeatures == previousConfiguration.accessibilityFeatures) { return; } _configuration = previousConfiguration.copyWith(accessibilityFeatures: newFeatures); _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); _invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); } /// Change the retained semantics data about this platform dispatcher. /// /// If [semanticsEnabled] is true, the user has requested that this function /// be called whenever the semantic content of this platform dispatcher /// changes. /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. @Deprecated(''' In a multi-view world, the platform dispatcher can no longer provide apis to update semantics since each view will host its own semantics tree. Semantics updates must be passed to an individual [FlutterView]. To update semantics, use PlatformDispatcher.instance.views to get a [FlutterView] and call `updateSemantics`. ''') void updateSemantics(SemanticsUpdate update) => _updateSemantics(_implicitViewId!, update as _NativeSemanticsUpdate); @Native)>( symbol: 'PlatformConfigurationNativeApi::UpdateSemantics', ) external static void _updateSemantics(int viewId, _NativeSemanticsUpdate update); /// The system-reported default locale of the device. /// /// This establishes the language and formatting conventions that application /// should, if possible, use to render their user interface. /// /// This is the first locale selected by the user and is the user's primary /// locale (the locale the device UI is displayed in) /// /// This is equivalent to `locales.first`, except that it will provide an /// undefined (using the language tag "und") non-null locale if the [locales] /// list has not been set or is empty. Locale get locale => locales.isEmpty ? const Locale.fromSubtags() : locales.first; /// Sets the locale for the application in engine. /// /// This is typically called by framework to set the locale based on which /// locale the Flutter app actually uses. void setApplicationLocale(Locale locale) => _setApplicationLocale(locale.toLanguageTag()); @Native(symbol: 'PlatformConfigurationNativeApi::SetApplicationLocale') external static void _setApplicationLocale(String locale); /// The full system-reported supported locales of the device. /// /// This establishes the language and formatting conventions that application /// should, if possible, use to render their user interface. /// /// The list is ordered in order of priority, with lower-indexed locales being /// preferred over higher-indexed ones. The first element is the primary /// [locale]. /// /// The [onLocaleChanged] callback is called whenever this value changes. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. List get locales => _configuration.locales; /// Performs the platform-native locale resolution. /// /// Each platform may return different results. /// /// If the platform fails to resolve a locale, then this will return null. /// /// This method returns synchronously and is a direct call to /// platform specific APIs without invoking method channels. Locale? computePlatformResolvedLocale(List supportedLocales) { final supportedLocalesData = []; for (final locale in supportedLocales) { supportedLocalesData.add(locale.languageCode); supportedLocalesData.add(locale.countryCode); supportedLocalesData.add(locale.scriptCode); } final List result = _computePlatformResolvedLocale(supportedLocalesData); if (result.isNotEmpty) { return Locale.fromSubtags( languageCode: result[0], countryCode: result[1] == '' ? null : result[1], scriptCode: result[2] == '' ? null : result[2], ); } return null; } List _computePlatformResolvedLocale(List supportedLocalesData) => __computePlatformResolvedLocale(supportedLocalesData); @Native( symbol: 'PlatformConfigurationNativeApi::ComputePlatformResolvedLocale', ) external static List __computePlatformResolvedLocale(List supportedLocalesData); /// A callback that is invoked whenever [locale] changes value. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onLocaleChanged => _onLocaleChanged; VoidCallback? _onLocaleChanged; Zone _onLocaleChangedZone = Zone.root; set onLocaleChanged(VoidCallback? callback) { _onLocaleChanged = callback; _onLocaleChangedZone = Zone.current; } // Called from the engine, via hooks.dart void _updateLocales(List locales) { const stringsPerLocale = 4; final int numLocales = locales.length ~/ stringsPerLocale; final _PlatformConfiguration previousConfiguration = _configuration; final newLocales = []; var localesDiffer = numLocales != previousConfiguration.locales.length; for (var localeIndex = 0; localeIndex < numLocales; localeIndex++) { final String countryCode = locales[localeIndex * stringsPerLocale + 1]; final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; newLocales.add( Locale.fromSubtags( languageCode: locales[localeIndex * stringsPerLocale], countryCode: countryCode.isEmpty ? null : countryCode, scriptCode: scriptCode.isEmpty ? null : scriptCode, ), ); if (!localesDiffer && newLocales[localeIndex] != previousConfiguration.locales[localeIndex]) { localesDiffer = true; } } if (!localesDiffer) { return; } _configuration = previousConfiguration.copyWith(locales: newLocales); _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); _invoke(onLocaleChanged, _onLocaleChangedZone); } // Called from the engine, via hooks.dart String _localeClosure() => locale.toString(); /// The lifecycle state immediately after dart isolate initialization. /// /// This property will not be updated as the lifecycle changes. /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup with /// any buffered lifecycle state events. String get initialLifecycleState { _initialLifecycleStateAccessed = true; return _initialLifecycleState; } late String _initialLifecycleState; /// Tracks if the initial state has been accessed. Once accessed, we will stop /// updating the [initialLifecycleState], as it is not the preferred way to /// access the state. bool _initialLifecycleStateAccessed = false; // Called from the engine, via hooks.dart void _updateInitialLifecycleState(String state) { // We do not update the state if the state has already been used to initialize // the lifecycleState. if (!_initialLifecycleStateAccessed) { _initialLifecycleState = state; } } /// The setting indicating whether time should always be shown in the 24-hour /// format. /// /// This option is used by [showTimePicker]. bool get alwaysUse24HourFormat => _configuration.alwaysUse24HourFormat; /// The system-suggested height of the text, as a multiple of the font size. /// /// This value takes precedence over any text height specified at the /// application level. For example, at framework level, in the [TextStyle] /// for [Text], [SelectableText], and [EditableText] widgets, this value /// overrides the existing value of [TextStyle.height] and [StrutStyle.height]. /// /// Returns null when no override has been set by the system. /// /// If this value changes, [onMetricsChanged] will be called. double? get lineHeightScaleFactorOverride => _configuration.lineHeightScaleFactorOverride; /// The system-suggested amount of additional space (in logical pixels) /// to add between each letter. /// /// A negative value can be used to bring the letters closer. /// /// This value takes precedence over any text letter spacing specified at the /// application level. For example, at framework level, in the [TextStyle] /// for [Text], [SelectableText], and [EditableText] widgets, this value /// overrides the existing value of [TextStyle.letterSpacing]. /// /// Returns null when no override has been set by the system. /// /// If this value changes, [onMetricsChanged] will be called. double? get letterSpacingOverride => _configuration.letterSpacingOverride; /// The system-suggested amount of additional space (in logical pixels) /// to add between each sequence of white-space (i.e. between each word). /// /// A negative value can be used to bring the words closer. /// /// This value takes precedence over any text word spacing specified at the /// application level. For example, at framework level, in the [TextStyle] /// for [Text], [SelectableText], and [EditableText] widgets, this value /// overrides the existing value of [TextStyle.wordSpacing]. /// /// Returns null when no override has been set by the system. /// /// If this value changes, [onMetricsChanged] will be called. double? get wordSpacingOverride => _configuration.wordSpacingOverride; /// The system-suggested amount of additional space (in logical pixels) /// to add following each paragraph in text. /// /// This value takes precedence over any text paragraph spacing specified at /// the application level. /// /// Returns null when no override has been set by the system. /// /// If this value changes, [onMetricsChanged] will be called. double? get paragraphSpacingOverride => _configuration.paragraphSpacingOverride; /// The system-reported text scale. /// /// This establishes the text scaling factor to use when rendering text, /// according to the user's platform preferences. /// /// The [onTextScaleFactorChanged] callback is called whenever this value /// changes. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. double get textScaleFactor => _configuration.textScaleFactor; /// A callback that is invoked whenever [textScaleFactor] changes value. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; VoidCallback? _onTextScaleFactorChanged; Zone _onTextScaleFactorChangedZone = Zone.root; set onTextScaleFactorChanged(VoidCallback? callback) { _onTextScaleFactorChanged = callback; _onTextScaleFactorChangedZone = Zone.current; } /// Whether the spell check service is supported on the current platform. /// /// This option is used by [EditableTextState] to define its /// [SpellCheckConfiguration] when a default spell check service /// is requested. bool get nativeSpellCheckServiceDefined => _nativeSpellCheckServiceDefined; bool _nativeSpellCheckServiceDefined = false; /// Whether showing system context menu is supported on the current platform. /// /// This option is used by [AdaptiveTextSelectionToolbar] to decide whether /// to show system context menu, or to fallback to the default Flutter context /// menu. bool get supportsShowingSystemContextMenu => _supportsShowingSystemContextMenu; bool _supportsShowingSystemContextMenu = false; /// Whether briefly displaying the characters as you type in obscured text /// fields is enabled in system settings. /// /// See also: /// /// * [EditableText.obscureText], which when set to true hides the text in /// the text field. bool get brieflyShowPassword => _brieflyShowPassword; bool _brieflyShowPassword = true; /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. Brightness get platformBrightness => _configuration.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; VoidCallback? _onPlatformBrightnessChanged; Zone _onPlatformBrightnessChangedZone = Zone.root; set onPlatformBrightnessChanged(VoidCallback? callback) { _onPlatformBrightnessChanged = callback; _onPlatformBrightnessChangedZone = Zone.current; } /// The setting indicating the current system font of the host platform. String? get systemFontFamily => _configuration.systemFontFamily; /// A callback that is invoked whenever [systemFontFamily] changes value. /// /// The framework invokes this callback in the same zone in which the callback /// was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onSystemFontFamilyChanged => _onSystemFontFamilyChanged; VoidCallback? _onSystemFontFamilyChanged; Zone _onSystemFontFamilyChangedZone = Zone.root; set onSystemFontFamilyChanged(VoidCallback? callback) { _onSystemFontFamilyChanged = callback; _onSystemFontFamilyChangedZone = Zone.current; } // Called from the engine, via hooks.dart void _updateUserSettingsData(String jsonData) { final data = json.decode(jsonData) as Map; if (data.isEmpty) { return; } final double textScaleFactor = (data['textScaleFactor']! as num).toDouble(); final alwaysUse24HourFormat = data['alwaysUse24HourFormat']! as bool; final nativeSpellCheckServiceDefined = data['nativeSpellCheckServiceDefined'] as bool?; if (nativeSpellCheckServiceDefined != null) { _nativeSpellCheckServiceDefined = nativeSpellCheckServiceDefined; } else { _nativeSpellCheckServiceDefined = false; } final supportsShowingSystemContextMenu = data['supportsShowingSystemContextMenu'] as bool?; if (supportsShowingSystemContextMenu != null) { _supportsShowingSystemContextMenu = supportsShowingSystemContextMenu; } else { _supportsShowingSystemContextMenu = false; } // This field is optional. final brieflyShowPassword = data['brieflyShowPassword'] as bool?; if (brieflyShowPassword != null) { _brieflyShowPassword = brieflyShowPassword; } final Brightness platformBrightness = switch (data['platformBrightness']) { 'dark' => Brightness.dark, 'light' => Brightness.light, final Object? value => throw StateError('$value is not a valid platformBrightness.'), }; final systemFontFamily = data['systemFontFamily'] as String?; final configurationId = data['configurationId'] as int?; final _PlatformConfiguration previousConfiguration = _configuration; final platformBrightnessChanged = previousConfiguration.platformBrightness != platformBrightness; final textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; final alwaysUse24HourFormatChanged = previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; final systemFontFamilyChanged = previousConfiguration.systemFontFamily != systemFontFamily; if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged && !systemFontFamilyChanged && configurationId == null) { return; } _configuration = previousConfiguration.copyWith( textScaleFactor: textScaleFactor, alwaysUse24HourFormat: alwaysUse24HourFormat, platformBrightness: platformBrightness, systemFontFamily: systemFontFamily, configurationId: configurationId, ); _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); if (textScaleFactorChanged) { _cachedFontSizes = null; _invoke(onTextScaleFactorChanged, _onTextScaleFactorChangedZone); } if (platformBrightnessChanged) { _invoke(onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); } if (systemFontFamilyChanged) { _invoke(onSystemFontFamilyChanged, _onSystemFontFamilyChangedZone); } } /// Whether the user has requested that updateSemantics be called when the /// semantic contents of a view changes. /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. bool get semanticsEnabled => _configuration.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; VoidCallback? _onSemanticsEnabledChanged; Zone _onSemanticsEnabledChangedZone = Zone.root; set onSemanticsEnabledChanged(VoidCallback? callback) { _onSemanticsEnabledChanged = callback; _onSemanticsEnabledChangedZone = Zone.current; } // Called from the engine, via hooks.dart void _updateSemanticsEnabled(bool enabled) { final _PlatformConfiguration previousConfiguration = _configuration; if (previousConfiguration.semanticsEnabled == enabled) { return; } _configuration = previousConfiguration.copyWith(semanticsEnabled: enabled); _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); _invoke(onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); } /// A callback that is invoked whenever the user requests an action to be /// performed on a semantics node. /// /// This callback is used when the user expresses the action they wish to /// perform based on the semantics node supplied by updateSemantics. /// /// The framework invokes this callback in the same zone in which the /// callback was set. SemanticsActionEventCallback? get onSemanticsActionEvent => _onSemanticsActionEvent; SemanticsActionEventCallback? _onSemanticsActionEvent; Zone _onSemanticsActionEventZone = Zone.root; set onSemanticsActionEvent(SemanticsActionEventCallback? callback) { _onSemanticsActionEvent = callback; _onSemanticsActionEventZone = Zone.current; } // Called from the engine via hooks.dart. void _updateFrameData(int frameNumber) { final FrameData previous = _frameData; if (previous.frameNumber == frameNumber) { return; } _frameData = FrameData._(frameNumber: frameNumber); _invoke(onFrameDataChanged, _onFrameDataChangedZone); } /// The [FrameData] object for the current frame. FrameData get frameData => _frameData; FrameData _frameData = const FrameData._(); /// A callback that is invoked when the window updates the [FrameData]. VoidCallback? get onFrameDataChanged => _onFrameDataChanged; VoidCallback? _onFrameDataChanged; Zone _onFrameDataChangedZone = Zone.root; set onFrameDataChanged(VoidCallback? callback) { _onFrameDataChanged = callback; _onFrameDataChangedZone = Zone.current; } // Called from the engine, via hooks.dart void _dispatchSemanticsAction(int viewId, int nodeId, int action, ByteData? args) { _invoke1( onSemanticsActionEvent, _onSemanticsActionEventZone, SemanticsActionEvent( type: SemanticsAction.fromIndex(action)!, nodeId: nodeId, viewId: viewId, arguments: args, ), ); } ErrorCallback? _onError; Zone? _onErrorZone; /// A callback that is invoked when an unhandled error occurs in the root /// isolate. /// /// This callback must return `true` if it has handled the error. Otherwise, /// it must return `false` and a fallback mechanism such as printing to stderr /// will be used, as configured by the specific platform embedding via /// `Settings::unhandled_exception_callback`. /// /// The VM or the process may exit or become unresponsive after calling this /// callback. The callback will not be called for exceptions that cause the VM /// or process to terminate or become unresponsive before the callback can be /// invoked. /// /// This callback is not directly invoked by errors in child isolates of the /// root isolate. Programs that create new isolates must listen for errors on /// those isolates and forward the errors to the root isolate. ErrorCallback? get onError => _onError; set onError(ErrorCallback? callback) { _onError = callback; _onErrorZone = Zone.current; } bool _dispatchError(Object error, StackTrace stackTrace) { if (_onError == null) { return false; } assert(_onErrorZone != null); if (identical(_onErrorZone, Zone.current)) { return _onError!(error, stackTrace); } else { try { return _onErrorZone!.runBinary(_onError!, error, stackTrace); } catch (e, s) { _onErrorZone!.handleUncaughtError(e, s); return false; } } } /// The route or path that the embedder requested when the application was /// launched. /// /// This will be the string "`/`" if no particular route was requested. /// /// ## Android /// /// On Android, calling /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) /// will set this value. The value must be set sufficiently early, i.e. before /// the [runApp] call is executed in Dart, for this to have any effect on the /// framework. The `createFlutterView` method in your `FlutterActivity` /// subclass is a suitable time to set the value. The application's /// `AndroidManifest.xml` file must also be updated to have a suitable /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). /// /// ## iOS /// /// On iOS, calling /// [`FlutterViewController.setInitialRoute`](/ios-embedder/interface_flutter_view_controller.html#a7f269c2da73312f856d42611cc12a33f) /// will set this value. The value must be set sufficiently early, i.e. before /// the [runApp] call is executed in Dart, for this to have any effect on the /// framework. The `application:didFinishLaunchingWithOptions:` method is a /// suitable time to set this value. /// /// See also: /// /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. String get defaultRouteName => _defaultRouteName(); @Native(symbol: 'PlatformConfigurationNativeApi::DefaultRouteName') external static String _defaultRouteName(); /// Computes the scaled font size from the given `unscaledFontSize`, according /// to the user's platform preferences. /// /// Many platforms allow users to scale text globally for better readability. /// Given the font size the app developer specified in logical pixels, this /// method converts it to the preferred font size (also in logical pixels) that /// accounts for platform-wide text scaling. The return value is always /// non-negative. /// /// The scaled value of the same font size input may change if the user changes /// the text scaling preference (in system settings for example). The /// [onTextScaleFactorChanged] callback can be used to monitor such changes. /// /// Instead of directly calling this method, applications should typically use /// [MediaQuery.textScalerOf] to retrive the scaled font size in a widget tree, /// so text in the app resizes properly when the text scaling preference /// changes. double scaleFontSize(double unscaledFontSize) { assert(unscaledFontSize >= 0); assert(unscaledFontSize.isFinite); if (textScaleFactor == 1.0) { return unscaledFontSize; } final int unscaledFloor = unscaledFontSize.floor(); final int unscaledCeil = unscaledFontSize.ceil(); if (unscaledFloor == unscaledCeil) { // No need to interpolate if the input value is an integer. return _scaleAndMemoize(unscaledFloor) ?? unscaledFontSize * textScaleFactor; } assert( unscaledCeil - unscaledFloor == 1, 'Unexpected interpolation range: $unscaledFloor - $unscaledCeil.', ); return switch ((_scaleAndMemoize(unscaledFloor), _scaleAndMemoize(unscaledCeil))) { (null, _) || (_, null) => unscaledFontSize * textScaleFactor, (final double lower, final double upper) => lower + (upper - lower) * (unscaledFontSize - unscaledFloor), }; } // The cache is cleared when the text scale factor changes. Map? _cachedFontSizes; // This method returns null if an error is encountered. double? _scaleAndMemoize(int unscaledFontSize) { final int? configurationId = _configuration.configurationId; if (configurationId == null) { // The platform uses linear scaling, or the platform hasn't sent us a // configuration yet. return null; } final double? cachedValue = _cachedFontSizes?[unscaledFontSize]; if (cachedValue != null) { assert(cachedValue >= 0); return cachedValue; } final double unscaledFontSizeDouble = unscaledFontSize.toDouble(); final double fontSize = PlatformDispatcher._getScaledFontSize( unscaledFontSizeDouble, configurationId, ); if (fontSize >= 0) { return (_cachedFontSizes ??= {})[unscaledFontSize] = fontSize; } switch (fontSize) { case -1: // Invalid configuration id. This error can be unrecoverable as the // _getScaledFontSize function can be destructive. assert(false, 'Flutter Error: incorrect configuration id: $configurationId.'); case final double errorCode: assert(false, 'Unknown error: GetScaledFontSize failed with $errorCode.'); } return null; } // Calls the platform's text scaling implementation to scale the given // `unscaledFontSize`. // // The `configurationId` parameter tells the embedder which platform // configuration to use for computing the scaled font size. When the user // changes the platform configuration, the configuration data will first be // made available on the platform thread before being dispatched asynchronously // to the Flutter UI thread. Since this call is synchronous, without this // identifier, it could call into the embber who's using a newer configuration // that Flutter has not received yet. The `configurationId` parameter must be // the lastest configuration id received from the platform // (`_configuration.configurationId`). Using an incorrect id could result in // an unrecoverable error. // // Currently this is only implemented on newer versions of Android (SDK level // 34, using the `TypedValue#applyDimension` API). Platforms that do not have // the capability will never send a `configurationId` to [PlatformDispatcher], // and should not call this method. This method returns -1 when the specified // configurationId does not match any configuration. @Native(symbol: 'PlatformConfigurationNativeApi::GetScaledFontSize') external static double _getScaledFontSize(double unscaledFontSize, int configurationId); } /// A color specified in the operating system UI color palette. /// /// As of the current release, system colors are supported on web only. To check /// if the current platform supports system colors, use the static /// [platformProvidesSystemColors] field. If the field is `false`, other /// functions in this class will throw [UnsupportedError]. /// /// This class is typically used in conjunction with /// [AccessibilityFeatures.highContrast]. In particular, on Windows, when a user /// enables high-contrast mode, they may also pick specific colors that should /// be used by application user interfaces. While it is common for applications /// to use custom color themes and design languages, in high-contrast mode it is /// recommended that widgets use system-specified colors to make content more /// legible for users. /// /// The "light" system colors are available through [SystemColor.light], and the "dark" system /// colors are available through [SystemColor.dark]. /// /// Example: /// /// ```dart /// import 'dart:ui'; /// /// Color getSystemAccentColor() { /// Color? systemAccentColor; /// if (SystemColor.platformProvidesSystemColors) { /// if (PlatformDispatcher.instance.platformBrightness == Brightness.light) { /// systemAccentColor = SystemColor.light.accentColor.value; /// } else { /// systemAccentColor = SystemColor.dark.accentColor.value; /// } /// } /// /// return systemAccentColor ?? const Color(0xFF007AFF); /// } /// ``` /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors /// * https://developer.mozilla.org/en-US/docs/Web/CSS/system-color /// * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors final class SystemColor { /// Creates an instance of a system color. /// /// [name] is the name of the color. System colors provided by [SystemColorPalette], such as /// [SystemColorPalette.accentColor] and [SystemColorPalette.buttonText], use standard names /// defined by the [W3C CSS specification](https://drafts.csswg.org/css-color/#css-system-colors). /// /// [value] is the color value, if this color name is supported, and null if /// it's unsupported. const SystemColor({required this.name, this.value}); /// Standard system color name, as defined by W3C CSS specification. /// /// System color names in Flutter are case-sensitive. This is so that color /// names can be easily used as [Map] keys. This is in contrast to CSS, where /// system color names are not case-sensitive. That is, specifying /// `background-color: aCcEnTcOlOr` is equivalent to specifying /// `background-color: AccentColor`. /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors final String name; /// The color value used for the color named [name], if supported. /// /// If [isSupported] is false, the [value] is null. If [isSupported] is true, /// the [value] is not null. final Color? value; /// Returns true if the current platform provides the system color with the /// given [name]. /// /// See also: /// /// * [platformProvidesSystemColors], which returns whether the current /// platform provides system colors. bool get isSupported => value != null; /// Returns true if the current platform provides system colors. /// /// As of the current release, system colors are supported on web only. /// /// See also: /// /// * [isSupported], which returns whether a specific color is supported. static bool get platformProvidesSystemColors => false; /// A palette of system colors for light mode. static final SystemColorPalette light = SystemColorPalette._(Brightness.light); /// A palette of system colors for dark mode. static final SystemColorPalette dark = SystemColorPalette._(Brightness.dark); } /// A palette of system colors specified in the operating system for a given [brightness]. /// /// The getters in this class, such as [accentColor] and [buttonText], provide standard system /// colors defined by the [W3C CSS specification](https://drafts.csswg.org/css-color/#css-system-colors). final class SystemColorPalette { SystemColorPalette._(this.brightness); /// The brightness mode for which this palette is defined. final Brightness brightness; static UnsupportedError _systemColorUnsupportedError() { return UnsupportedError('SystemColor not supported on the current platform.'); } /// Returns system color named "AccentColor". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get accentColor => throw _systemColorUnsupportedError(); /// Returns system color named "AccentColorText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get accentColorText => throw _systemColorUnsupportedError(); /// Returns system color named "ActiveText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get activeText => throw _systemColorUnsupportedError(); /// Returns system color named "ButtonBorder". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get buttonBorder => throw _systemColorUnsupportedError(); /// Returns system color named "ButtonFace". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get buttonFace => throw _systemColorUnsupportedError(); /// Returns system color named "ButtonText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get buttonText => throw _systemColorUnsupportedError(); /// Returns system color named "Canvas". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get canvas => throw _systemColorUnsupportedError(); /// Returns system color named "CanvasText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get canvasText => throw _systemColorUnsupportedError(); /// Returns system color named "Field". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get field => throw _systemColorUnsupportedError(); /// Returns system color named "FieldText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get fieldText => throw _systemColorUnsupportedError(); /// Returns system color named "GrayText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get grayText => throw _systemColorUnsupportedError(); /// Returns system color named "Highlight". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get highlight => throw _systemColorUnsupportedError(); /// Returns system color named "HighlightText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get highlightText => throw _systemColorUnsupportedError(); /// Returns system color named "LinkText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get linkText => throw _systemColorUnsupportedError(); /// Returns system color named "Mark". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get mark => throw _systemColorUnsupportedError(); /// Returns system color named "MarkText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get markText => throw _systemColorUnsupportedError(); /// Returns system color named "SelectedItem". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get selectedItem => throw _systemColorUnsupportedError(); /// Returns system color named "SelectedItemText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get selectedItemText => throw _systemColorUnsupportedError(); /// Returns system color named "VisitedText". /// /// See also: /// /// * https://drafts.csswg.org/css-color/#css-system-colors SystemColor get visitedText => throw _systemColorUnsupportedError(); } /// Configuration of the platform. /// /// Immutable class (but can't use @immutable in dart:ui) class _PlatformConfiguration { const _PlatformConfiguration({ this.accessibilityFeatures = const AccessibilityFeatures._(0), this.alwaysUse24HourFormat = false, this.semanticsEnabled = false, this.platformBrightness = Brightness.light, this.textScaleFactor = 1.0, this.locales = const [], this.defaultRouteName, this.systemFontFamily, this.configurationId, this.lineHeightScaleFactorOverride, this.letterSpacingOverride, this.wordSpacingOverride, this.paragraphSpacingOverride, }); _PlatformConfiguration copyWith({ AccessibilityFeatures? accessibilityFeatures, bool? alwaysUse24HourFormat, bool? semanticsEnabled, Brightness? platformBrightness, double? textScaleFactor, List? locales, String? defaultRouteName, String? systemFontFamily, int? configurationId, }) { return _PlatformConfiguration( accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, platformBrightness: platformBrightness ?? this.platformBrightness, textScaleFactor: textScaleFactor ?? this.textScaleFactor, locales: locales ?? this.locales, defaultRouteName: defaultRouteName ?? this.defaultRouteName, systemFontFamily: systemFontFamily ?? this.systemFontFamily, configurationId: configurationId ?? this.configurationId, lineHeightScaleFactorOverride: lineHeightScaleFactorOverride, letterSpacingOverride: letterSpacingOverride, wordSpacingOverride: wordSpacingOverride, paragraphSpacingOverride: paragraphSpacingOverride, ); } /// Additional accessibility features that may be enabled by the platform. final AccessibilityFeatures accessibilityFeatures; /// The setting indicating whether time should always be shown in the 24-hour /// format. final bool alwaysUse24HourFormat; /// Whether the user has requested that updateSemantics be called when the /// semantic contents of a view changes. final bool semanticsEnabled; /// The setting indicating the current brightness mode of the host platform. /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. final Brightness platformBrightness; /// The system-reported text scale. final double textScaleFactor; /// The full system-reported supported locales of the device. final List locales; /// The route or path that the embedder requested when the application was /// launched. final String? defaultRouteName; /// The system-reported default font family. final String? systemFontFamily; /// A unique identifier for this [_PlatformConfiguration]. /// /// This unique identifier is optionally assigned by the platform embedder. /// Dart code that runs on the Flutter UI thread and synchronously invokes /// platform APIs can use this identifier to tell the embedder to use the /// configuration that matches the current [_PlatformConfiguration] in /// dart:ui. See the [_getScaledFontSize] function for an example. /// /// This field's nullability also indicates whether the platform supports /// nonlinear text scaling (as it's the only feature that requires synchronous /// invocation of platform APIs). This field is always null if the platform /// does not use nonlinear text scaling, or when dart:ui has not received any /// configuration updates from the embedder yet. The _getScaledFontSize /// function should not be called in either case. final int? configurationId; /// The system-reported height of the text, as a multiple of the font size. final double? lineHeightScaleFactorOverride; /// The system-reported amount of additional space (in logical pixels) /// to add between each letter. /// /// A negative value can be used to bring the letters closer. final double? letterSpacingOverride; /// The system-reported amount of additional space (in logical pixels) /// to add between each sequence of white-space (i.e. between each word). /// /// A negative value can be used to bring the words closer. final double? wordSpacingOverride; /// The system-reported amount of additional space (in logical pixels) /// to add between each paragraph in text. final double? paragraphSpacingOverride; } /// An immutable view configuration. class _ViewConfiguration { const _ViewConfiguration({ this.devicePixelRatio = 1.0, this.size = Size.zero, this.viewInsets = ViewPadding.zero, this.viewPadding = ViewPadding.zero, this.systemGestureInsets = ViewPadding.zero, this.padding = ViewPadding.zero, this.gestureSettings = const GestureSettings(), this.displayFeatures = const [], this.displayId = 0, this.viewConstraints = const ViewConstraints(maxWidth: 0, maxHeight: 0), this.displayCornerRadii, }); /// The identifier for a display for this view, in /// [PlatformDispatcher._displays]. final int displayId; /// The sizing constraints for this view in physical pixels. final ViewConstraints viewConstraints; /// The pixel density of the output surface. final double devicePixelRatio; /// The size requested for the view in physical pixels. final Size size; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but over which the operating system will likely /// place system UI, such as the keyboard, that fully obscures any content. /// /// The relationship between this [viewInsets], [viewPadding], and [padding] /// are described in more detail in the documentation for [FlutterView]. final ViewPadding viewInsets; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but which may be partially obscured by system /// UI (such as the system notification area), or physical intrusions in /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// /// Unlike [padding], this value does not change relative to [viewInsets]. /// For example, on an iPhone X, it will not change in response to the soft /// keyboard being visible or hidden, whereas [padding] will. /// /// The relationship between this [viewInsets], [viewPadding], and [padding] /// are described in more detail in the documentation for [FlutterView]. final ViewPadding viewPadding; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but where the operating system will consume /// input gestures for the sake of system navigation. /// /// For example, an operating system might use the vertical edges of the /// screen, where swiping inwards from the edges takes users backward /// through the history of screens they previously visited. final ViewPadding systemGestureInsets; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but which may be partially obscured by system /// UI (such as the system notification area), or physical intrusions in /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// /// The relationship between this [viewInsets], [viewPadding], and [padding] /// are described in more detail in the documentation for [FlutterView]. final ViewPadding padding; /// Additional configuration for touch gestures performed on this view. /// /// For example, the touch slop defined in physical pixels may be provided /// by the gesture settings and should be preferred over the framework /// touch slop constant. final GestureSettings gestureSettings; /// Areas of the display that are obstructed by hardware features. /// /// This list is populated only on Android. If the device has no display /// features, this list is empty. /// /// The coordinate space in which the [DisplayFeature.bounds] are defined spans /// across the screens currently in use. This means that the space between the screens /// is virtually part of the Flutter view space, with the [DisplayFeature.bounds] /// of the display feature as an obstructed area. The [DisplayFeature.type] can /// be used to determine if this display feature obstructs the screen or not. /// For example, [DisplayFeatureType.hinge] and [DisplayFeatureType.cutout] both /// obstruct the display, while [DisplayFeatureType.fold] is a crease in the display. /// /// Folding [DisplayFeature]s like the [DisplayFeatureType.hinge] and /// [DisplayFeatureType.fold] also have a [DisplayFeature.state] which can be /// used to determine the posture the device is in. final List displayFeatures; /// The radii of the display corners in physical pixels. /// /// This is currently populated only on Android API 31+. On earlier Android /// versions, iOS, and other platforms, this value is `null`. final DisplayCornerRadii? displayCornerRadii; @override String toString() { return '$runtimeType[size: $size]'; } } /// Various important time points in the lifetime of a frame. /// /// [FrameTiming] records a timestamp of each phase for performance analysis. enum FramePhase { /// The timestamp of the vsync signal given by the operating system. /// /// See also [FrameTiming.vsyncOverhead]. vsyncStart, /// When the UI thread starts building a frame. /// /// See also [FrameTiming.buildDuration]. buildStart, /// When the UI thread finishes building a frame. /// /// See also [FrameTiming.buildDuration]. buildFinish, /// When the raster thread starts rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterStart, /// When the raster thread finishes rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterFinish, /// When the raster thread finished rasterizing a frame in wall-time. /// /// This is useful for correlating time raster finish time with the system /// clock to integrate with other profiling tools. rasterFinishWallTime, } enum _FrameTimingInfo { /// The number of engine layers cached in the raster cache during the frame. layerCacheCount, /// The number of bytes used to cache engine layers during the frame. layerCacheBytes, /// The number of picture layers cached in the raster cache during the frame. pictureCacheCount, /// The number of bytes used to cache pictures during the frame. pictureCacheBytes, /// The frame number of the frame. frameNumber, } /// Time-related performance metrics of a frame. /// /// If you're using the whole Flutter framework, please use /// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using /// [PlatformDispatcher.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If /// [SchedulerBinding] is unavailable, then see [PlatformDispatcher.onReportTimings] /// for how to get this. /// /// The metrics in debug mode (`flutter run` without any flags) may be very /// different from those in profile and release modes due to the debug overhead. /// Therefore it's recommended to only monitor and analyze performance metrics /// in profile and release modes. class FrameTiming { /// Construct [FrameTiming] with raw timestamps in microseconds. /// /// This constructor is used for unit test only. Real [FrameTiming]s should /// be retrieved from [PlatformDispatcher.onReportTimings]. /// /// If the [frameNumber] is not provided, it defaults to `-1`. factory FrameTiming({ required int vsyncStart, required int buildStart, required int buildFinish, required int rasterStart, required int rasterFinish, required int rasterFinishWallTime, int layerCacheCount = 0, int layerCacheBytes = 0, int pictureCacheCount = 0, int pictureCacheBytes = 0, int frameNumber = -1, }) { return FrameTiming._([ vsyncStart, buildStart, buildFinish, rasterStart, rasterFinish, rasterFinishWallTime, layerCacheCount, layerCacheBytes, pictureCacheCount, pictureCacheBytes, frameNumber, ]); } /// Construct [FrameTiming] with raw timestamps in microseconds. /// /// List [timestamps] must have the same number of elements as /// [FramePhase.values]. /// /// This constructor is usually only called by the Flutter engine, or a test. /// To get the [FrameTiming] of your app, see [PlatformDispatcher.onReportTimings]. FrameTiming._(this._data) : assert(_data.length == _dataLength); static final int _dataLength = FramePhase.values.length + _FrameTimingInfo.values.length; /// This is a raw timestamp in microseconds from some epoch. The epoch in all /// [FrameTiming] is the same, but it may not match [DateTime]'s epoch. int timestampInMicroseconds(FramePhase phase) => _data[phase.index]; Duration _rawDuration(FramePhase phase) => Duration(microseconds: _data[phase.index]); int _rawInfo(_FrameTimingInfo info) => _data[FramePhase.values.length + info.index]; /// The duration to build the frame on the UI thread. /// /// The build starts approximately when [PlatformDispatcher.onBeginFrame] is /// called. The [Duration] in the [PlatformDispatcher.onBeginFrame] callback /// is exactly the `Duration(microseconds: /// timestampInMicroseconds(FramePhase.buildStart))`. /// /// The build finishes when [FlutterView.render] is called. /// /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} /// To ensure smooth animations of X fps, this should not exceed 1000/X /// milliseconds. /// {@endtemplate} /// {@template dart.ui.FrameTiming.fps_milliseconds} /// That's about 16ms for 60fps, and 8ms for 120fps. /// {@endtemplate} Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); /// The duration to rasterize the frame on the raster thread. /// /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} /// {@macro dart.ui.FrameTiming.fps_milliseconds} Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); /// The duration between receiving the vsync signal and starting building the /// frame. Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); /// The timespan between vsync start and raster finish. /// /// To achieve the lowest latency on an X fps display, this should not exceed /// 1000/X milliseconds. /// {@macro dart.ui.FrameTiming.fps_milliseconds} /// /// See also [vsyncOverhead], [buildDuration] and [rasterDuration]. Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); /// The number of layers stored in the raster cache during the frame. /// /// See also [layerCacheBytes], [pictureCacheCount] and [pictureCacheBytes]. int get layerCacheCount => _rawInfo(_FrameTimingInfo.layerCacheCount); /// The number of bytes of image data used to cache layers during the frame. /// /// See also [layerCacheCount], [layerCacheMegabytes], [pictureCacheCount] and [pictureCacheBytes]. int get layerCacheBytes => _rawInfo(_FrameTimingInfo.layerCacheBytes); /// The number of megabytes of image data used to cache layers during the frame. /// /// See also [layerCacheCount], [layerCacheBytes], [pictureCacheCount] and [pictureCacheBytes]. double get layerCacheMegabytes => layerCacheBytes / 1024.0 / 1024.0; /// The number of pictures stored in the raster cache during the frame. /// /// See also [layerCacheCount], [layerCacheBytes] and [pictureCacheBytes]. int get pictureCacheCount => _rawInfo(_FrameTimingInfo.pictureCacheCount); /// The number of bytes of image data used to cache pictures during the frame. /// /// See also [layerCacheCount], [layerCacheBytes], [pictureCacheCount] and [pictureCacheMegabytes]. int get pictureCacheBytes => _rawInfo(_FrameTimingInfo.pictureCacheBytes); /// The number of megabytes of image data used to cache pictures during the frame. /// /// See also [layerCacheCount], [layerCacheBytes], [pictureCacheCount] and [pictureCacheBytes]. double get pictureCacheMegabytes => pictureCacheBytes / 1024.0 / 1024.0; /// The frame key associated with this frame measurement. int get frameNumber => _data.last; final List _data; // some elements in microseconds, some in bytes, some are counts String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; @override String toString() { return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, ' 'rasterDuration: ${_formatMS(rasterDuration)}, ' 'vsyncOverhead: ${_formatMS(vsyncOverhead)}, ' 'totalSpan: ${_formatMS(totalSpan)}, ' 'layerCacheCount: $layerCacheCount, ' 'layerCacheBytes: $layerCacheBytes, ' 'pictureCacheCount: $pictureCacheCount, ' 'pictureCacheBytes: $pictureCacheBytes, ' 'frameNumber: ${_data.last})'; } } /// States that an application can be in once it is running. /// /// States not supported on a platform will be synthesized by the framework when /// transitioning between states which are supported, so that all /// implementations share the same state machine. /// /// The initial value for the state is the [detached] state, updated to the /// current state (usually [resumed]) as soon as the first lifecycle update is /// received from the platform. /// /// For historical and name collision reasons, Flutter's application state names /// do not correspond one to one with the state names on all platforms. On /// Android, for instance, when the OS calls /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()), /// Flutter will enter the [inactive] state, but when Android calls /// [`Activity.onStop`](https://developer.android.com/reference/android/app/Activity#onStop()), /// Flutter enters the [paused] state. See the individual state's documentation /// for descriptions of what they mean on each platform. /// /// The current application state can be obtained from /// [SchedulerBinding.instance.lifecycleState], and changes to the state can be /// observed by creating an [AppLifecycleListener], or by using a /// [WidgetsBindingObserver] by overriding the /// [WidgetsBindingObserver.didChangeAppLifecycleState] method. /// /// Applications should not rely on always receiving all possible notifications. /// /// For example, if the application is killed with a task manager, a kill /// signal, the user pulls the power from the device, or there is a rapid /// unscheduled disassembly of the device, no notification will be sent before /// the application is suddenly terminated, and some states may be skipped. /// /// See also: /// /// * [AppLifecycleListener], an object used observe the lifecycle state that /// provides state transition callbacks. /// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state /// from the widgets layer. /// * iOS's [UIKit activity /// lifecycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc) /// documentation. /// * Android's [activity /// lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle) /// documentation. /// * macOS's [AppKit activity /// lifecycle](https://developer.apple.com/documentation/appkit/nsapplicationdelegate?language=objc) /// documentation. enum AppLifecycleState { /// The application is still hosted by a Flutter engine but is detached from /// any host views. /// /// The application defaults to this state before it initializes, and can be /// in this state (applicable on Android, iOS, and web) after all views have been /// detached. /// /// When the application is in this state, the engine is running without a /// view. /// /// This state is only entered on iOS, Android, and web, although on all platforms /// it is the default state before the application begins running. detached, /// On all platforms, this state indicates that the application is in the /// default running mode for a running application that has input focus and is /// visible. /// /// On Android, this state corresponds to the Flutter host view having focus /// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean)) /// was called with true) while in Android's "resumed" state. It is possible /// for the Flutter app to be in the [inactive] state while still being in /// Android's /// ["onResume"](https://developer.android.com/guide/components/activities/activity-lifecycle) /// state if the app has lost focus /// ([`Activity.onWindowFocusChanged`](https://developer.android.com/reference/android/app/Activity#onWindowFocusChanged(boolean)) /// was called with false), but hasn't had /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()) /// called on it. /// /// On iOS and macOS, this corresponds to the app running in the foreground /// active state. resumed, /// At least one view of the application is visible, but none have input /// focus. The application is otherwise running normally. /// /// On non-web desktop platforms, this corresponds to an application that is /// not in the foreground, but still has visible windows. /// /// On the web, this corresponds to an application that is running in a /// window or tab that does not have input focus. /// /// On iOS and macOS, this state corresponds to the Flutter host view running in the /// foreground inactive state. Apps transition to this state when in a phone /// call, when responding to a TouchID request, when entering the app switcher /// or the control center, or when the UIViewController hosting the Flutter /// app is transitioning. /// /// On Android, this corresponds to the Flutter host view running in Android's /// paused state (i.e. /// [`Activity.onPause`](https://developer.android.com/reference/android/app/Activity#onPause()) /// has been called), or in Android's "resumed" state (i.e. /// [`Activity.onResume`](https://developer.android.com/reference/android/app/Activity#onResume()) /// has been called) but does not have window focus. Examples of when apps /// transition to this state include when the app is partially obscured or /// another activity is focused, a app running in a split screen that isn't /// the current app, an app interrupted by a phone call, a picture-in-picture /// app, a system dialog, another view. It will also be inactive when the /// notification window shade is down, or the application switcher is visible. /// /// On Android and iOS, apps in this state should assume that they may be /// [hidden] and [paused] at any time. inactive, /// All views of an application are hidden, either because the application is /// about to be paused (on iOS and Android), or because it has been minimized /// or placed on a desktop that is no longer visible (on non-web desktop), or /// is running in a window or tab that is no longer visible (on the web). /// /// On iOS and Android, in order to keep the state machine the same on all /// platforms, a transition to this state is synthesized before the [paused] /// state is entered when coming from [inactive], and before the [inactive] /// state is entered when coming from [paused]. This allows cross-platform /// implementations that want to know when an app is conceptually "hidden" to /// only write one handler. hidden, /// The application is not currently visible to the user, and not responding /// to user input. /// /// When the application is in this state, the engine will not call the /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] /// callbacks. /// /// This state is only entered on iOS and Android. paused, } /// The possible responses to a request to exit the application. /// /// The request is typically responded to by creating an [AppLifecycleListener] /// and supplying an [AppLifecycleListener.onExitRequested] callback, or by /// overriding [WidgetsBindingObserver.didRequestAppExit]. enum AppExitResponse { /// Exiting the application can proceed. exit, /// Cancel the exit: do not exit the application. cancel, } /// The type of application exit to perform when calling /// [ServicesBinding.exitApplication]. enum AppExitType { /// Requests that the application start an orderly exit, sending a request /// back to the framework through the [WidgetsBinding]. If that responds /// with [AppExitResponse.exit], then proceed with the same steps as a /// [required] exit. If that responds with [AppExitResponse.cancel], then the /// exit request is canceled and the application continues executing normally. cancelable, /// A non-cancelable orderly exit request. The engine will shut down the /// engine and call the native UI toolkit's exit API. /// /// If you need an even faster and more dangerous exit, then call `dart:io`'s /// `exit()` directly, and even the native toolkit's exit API won't be called. /// This is quite dangerous, though, since it's possible that the engine will /// crash because it hasn't been properly shut down, causing the app to crash /// on exit. required, } /// A representation of distances for each of the four edges of a rectangle, /// used to encode the view insets and padding that applications should place /// around their user interface, as exposed by [FlutterView.viewInsets] and /// [FlutterView.padding]. View insets and padding are preferably read via /// [MediaQuery.of]. /// /// For a generic class that represents distances around a rectangle, see the /// [EdgeInsets] class. /// /// See also: /// /// * [WidgetsBindingObserver], for a widgets layer mechanism to receive /// notifications when the padding changes. /// * [MediaQuery.of], for the preferred mechanism for accessing these values. /// * [Scaffold], which automatically applies the padding in material design /// applications. class ViewPadding { const ViewPadding._({ required this.left, required this.top, required this.right, required this.bottom, }); /// The distance from the left edge to the first unpadded pixel, in physical pixels. final double left; /// The distance from the top edge to the first unpadded pixel, in physical pixels. final double top; /// The distance from the right edge to the first unpadded pixel, in physical pixels. final double right; /// The distance from the bottom edge to the first unpadded pixel, in physical pixels. final double bottom; /// A view padding that has zeros for each edge. static const ViewPadding zero = ViewPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); @override String toString() { return 'ViewPadding(left: $left, top: $top, right: $right, bottom: $bottom)'; } } /// Deprecated. Will be removed in a future version of Flutter. /// /// Use [ViewPadding] instead. @Deprecated( 'Use ViewPadding instead. ' 'This feature was deprecated after v3.8.0-14.0.pre.', ) typedef WindowPadding = ViewPadding; /// Immutable layout constraints for [FlutterView]s. /// /// Similar to [BoxConstraints], a [Size] respects a [ViewConstraints] if, and /// only if, all of the following relations hold: /// /// * [minWidth] <= [Size.width] <= [maxWidth] /// * [minHeight] <= [Size.height] <= [maxHeight] /// /// The constraints themselves must satisfy these relations: /// /// * 0.0 <= [minWidth] <= [maxWidth] <= [double.infinity] /// * 0.0 <= [minHeight] <= [maxHeight] <= [double.infinity] /// /// For each constraint, [double.infinity] is a legal value. /// /// For a generic class that represents these kind of constraints, see the /// [BoxConstraints] class. class ViewConstraints { /// Creates view constraints with the given constraints. const ViewConstraints({ this.minWidth = 0.0, this.maxWidth = double.infinity, this.minHeight = 0.0, this.maxHeight = double.infinity, }); /// Creates view constraints that is respected only by the given size. ViewConstraints.tight(Size size) : minWidth = size.width, maxWidth = size.width, minHeight = size.height, maxHeight = size.height; /// The minimum width that satisfies the constraints. final double minWidth; /// The maximum width that satisfies the constraints. /// /// Might be [double.infinity]. final double maxWidth; /// The minimum height that satisfies the constraints. final double minHeight; /// The maximum height that satisfies the constraints. /// /// Might be [double.infinity]. final double maxHeight; /// Whether the given size satisfies the constraints. bool isSatisfiedBy(Size size) { return (minWidth <= size.width) && (size.width <= maxWidth) && (minHeight <= size.height) && (size.height <= maxHeight); } /// Whether there is exactly one size that satisfies the constraints. bool get isTight => minWidth >= maxWidth && minHeight >= maxHeight; /// Scales each constraint parameter by the inverse of the given factor. ViewConstraints operator /(double factor) { return ViewConstraints( minWidth: minWidth / factor, maxWidth: maxWidth / factor, minHeight: minHeight / factor, maxHeight: maxHeight / factor, ); } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is ViewConstraints && other.minWidth == minWidth && other.maxWidth == maxWidth && other.minHeight == minHeight && other.maxHeight == maxHeight; } @override int get hashCode => Object.hash(minWidth, maxWidth, minHeight, maxHeight); @override String toString() { if (minWidth == double.infinity && minHeight == double.infinity) { return 'ViewConstraints(biggest)'; } if (minWidth == 0 && maxWidth == double.infinity && minHeight == 0 && maxHeight == double.infinity) { return 'ViewConstraints(unconstrained)'; } String describe(double min, double max, String dim) { if (min == max) { return '$dim=${min.toStringAsFixed(1)}'; } return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}'; } final String width = describe(minWidth, maxWidth, 'w'); final String height = describe(minHeight, maxHeight, 'h'); return 'ViewConstraints($width, $height)'; } } /// Area of the display that may be obstructed by a hardware feature. /// /// This is populated only on Android. /// /// The [bounds] are measured in logical pixels. On devices with two screens the /// coordinate system starts with (0,0) in the top-left corner of the left or top screen /// and expands to include both screens and the visual space between them. /// /// The [type] describes the behaviour and if [DisplayFeature] obstructs the display. /// For example, [DisplayFeatureType.hinge] and [DisplayFeatureType.cutout] both obstruct the display, /// while [DisplayFeatureType.fold] does not. /// /// ![Device with a hinge display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_hinge.png) /// /// ![Device with a fold display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_fold.png) /// /// ![Device with a cutout display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_cutout.png) /// /// The [state] contains information about the posture for foldable features /// ([DisplayFeatureType.hinge] and [DisplayFeatureType.fold]). The posture is /// the shape of the display, for example [DisplayFeatureState.postureFlat] or /// [DisplayFeatureState.postureHalfOpened]. For [DisplayFeatureType.cutout], /// the state is not used and has the [DisplayFeatureState.unknown] value. class DisplayFeature { // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs const DisplayFeature({required this.bounds, required this.type, required this.state}) : assert( !identical(type, DisplayFeatureType.cutout) || identical(state, DisplayFeatureState.unknown), ); /// The area of the flutter view occupied by this display feature, measured in logical pixels. /// /// On devices with two screens, the Flutter view spans from the top-left corner /// of the left or top screen to the bottom-right corner of the right or bottom screen, /// including the visual area occupied by any display feature. Bounds of display /// features are reported in this coordinate system. /// /// For example, on a dual screen device in portrait mode: /// /// * [Rect.left] gives you the size of left screen, in logical pixels. /// * [Rect.right] gives you the size of the left screen + the hinge width. final Rect bounds; /// Type of display feature, e.g. hinge, fold, cutout. final DisplayFeatureType type; /// Posture of display feature, which is populated only for folds and hinges. /// /// For cutouts, this is [DisplayFeatureState.unknown] final DisplayFeatureState state; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is DisplayFeature && bounds == other.bounds && type == other.type && state == other.state; } @override int get hashCode => Object.hash(bounds, type, state); @override String toString() { return 'DisplayFeature(rect: $bounds, type: $type, state: $state)'; } } /// Type of [DisplayFeature], describing the [DisplayFeature] behaviour and if /// it obstructs the display. /// /// Some types of [DisplayFeature], like [DisplayFeatureType.fold], can be /// reported without actually impeding drawing on the screen. They are useful /// for knowing where the display is bent or has a crease. The /// [DisplayFeature.bounds] can be 0-width in such cases. /// /// The shape formed by the screens for types [DisplayFeatureType.fold] and /// [DisplayFeatureType.hinge] is called the posture and is exposed in /// [DisplayFeature.state]. For example, the [DisplayFeatureState.postureFlat] posture /// means the screens form a flat surface. /// /// ![Device with a hinge display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_hinge.png) /// /// ![Device with a fold display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_fold.png) /// /// ![Device with a cutout display feature](https://flutter.github.io/assets-for-api-docs/assets/hardware/display_feature_cutout.png) enum DisplayFeatureType { /// [DisplayFeature] type is new and not yet known to Flutter. unknown, /// A fold in the flexible screen without a physical gap. /// /// The bounds for this display feature type indicate where the display makes a crease. fold, /// A physical separation with a hinge that allows two display panels to fold. hinge, /// A non-displaying area of the screen, usually housing cameras or sensors. cutout, } /// State of the display feature, which contains information about the posture /// for foldable features. /// /// The posture is the shape made by the parts of the flexible screen or /// physical screen panels. They are inspired by and similar to /// [Android Postures](https://developer.android.com/guide/topics/ui/foldables#postures). /// /// * For [DisplayFeatureType.fold]s & [DisplayFeatureType.hinge]s, the state is /// the posture. /// * For [DisplayFeatureType.cutout]s, the state is not used and has the /// [DisplayFeatureState.unknown] value. enum DisplayFeatureState { /// The display feature is a [DisplayFeatureType.cutout] or this state is new /// and not yet known to Flutter. unknown, /// The foldable device is completely open. /// /// The screen space that is presented to the user is flat. postureFlat, /// Fold angle is in an intermediate position between opened and closed state. /// /// There is a non-flat angle between parts of the flexible screen or between /// physical screen panels such that the screens start to face each other. postureHalfOpened, } /// A set of radii for the four corners of the display. /// /// This class represents the curvature of the display corners. The radii values /// are defined in physical pixels. /// /// If a corner is square (not rounded), the radius is 0.0. /// /// This is currently populated only on Android API 31+. class DisplayCornerRadii { /// Creates a [DisplayCornerRadii]. const DisplayCornerRadii({ required this.topLeft, required this.topRight, required this.bottomRight, required this.bottomLeft, }) : assert(topLeft >= 0), assert(topRight >= 0), assert(bottomRight >= 0), assert(bottomLeft >= 0); /// The radius of the top-left corner of the display, in physical pixels. final double topLeft; /// The radius of the top-right corner of the display, in physical pixels. final double topRight; /// The radius of the bottom-right corner of the display, in physical pixels. final double bottomRight; /// The radius of the bottom-left corner of the display, in physical pixels. final double bottomLeft; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is DisplayCornerRadii && topLeft == other.topLeft && topRight == other.topRight && bottomRight == other.bottomRight && bottomLeft == other.bottomLeft; } @override int get hashCode => Object.hash(topLeft, topRight, bottomRight, bottomLeft); @override String toString() { return 'DisplayCornerRadii(topLeft: $topLeft, topRight: $topRight, ' 'bottomRight: $bottomRight, bottomLeft: $bottomLeft)'; } } /// An identifier used to select a user's language and formatting preferences. /// /// This represents a [Unicode Language /// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) /// (i.e. without Locale extensions), except variants are not supported. /// /// Locales are canonicalized according to the "preferred value" entries in the /// [IANA Language Subtag /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). /// For example, `const Locale('he')` and `const Locale('iw')` are equal and /// both have the [languageCode] `he`, because `iw` is a deprecated language /// subtag that was replaced by the subtag `he`. /// /// See also: /// /// * [PlatformDispatcher.locale], which specifies the system's currently selected /// [Locale]. class Locale { /// Creates a new Locale object. The first argument is the /// primary language subtag, the second is the region (also /// referred to as 'country') subtag. /// /// For example: /// /// ```dart /// const Locale swissFrench = Locale('fr', 'CH'); /// const Locale canadianFrench = Locale('fr', 'CA'); /// ``` /// /// The primary language subtag must not be null. The region subtag is /// optional. When there is no region/country subtag, the parameter should /// be omitted or passed `null` instead of an empty-string. /// /// The subtag values are _case sensitive_ and must be one of the valid /// subtags according to CLDR supplemental data: /// [language](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml), /// [region](https://github.com/unicode-org/cldr/blob/master/common/validity/region.xml). The /// primary language subtag must be at least two and at most eight lowercase /// letters, but not four letters. The region subtag must be two /// uppercase letters or three digits. See the [Unicode Language /// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) /// specification. /// /// Validity is not checked by default, but some methods may throw away /// invalid data. /// /// See also: /// /// * [Locale.fromSubtags], which also allows a [scriptCode] to be /// specified. const Locale(this._languageCode, [this._countryCode]) : assert(_languageCode != ''), scriptCode = null; /// Creates a new Locale object. /// /// The keyword arguments specify the subtags of the Locale. /// /// The subtag values are _case sensitive_ and must be valid subtags according /// to CLDR supplemental data: /// [language](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml), /// [script](https://github.com/unicode-org/cldr/blob/master/common/validity/script.xml) and /// [region](https://github.com/unicode-org/cldr/blob/master/common/validity/region.xml) for /// each of languageCode, scriptCode and countryCode respectively. /// /// The [languageCode] subtag is optional. When there is no language subtag, /// the parameter should be omitted or set to "und". When not supplied, the /// [languageCode] defaults to "und", an undefined language code. /// /// The [countryCode] subtag is optional. When there is no country subtag, /// the parameter should be omitted or passed `null` instead of an empty-string. /// /// Validity is not checked by default, but some methods may throw away /// invalid data. const Locale.fromSubtags({String languageCode = 'und', this.scriptCode, String? countryCode}) : assert(languageCode != ''), _languageCode = languageCode, assert(scriptCode != ''), assert(countryCode != ''), _countryCode = countryCode; /// The primary language subtag for the locale. /// /// This must not be null. It may be 'und', representing 'undefined'. /// /// This is expected to be string registered in the [IANA Language Subtag /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) /// with the type "language". The string specified must match the case of the /// string in the registry. /// /// Language subtags that are deprecated in the registry and have a preferred /// code are changed to their preferred code. For example, `const /// Locale('he')` and `const Locale('iw')` are equal, and both have the /// [languageCode] `he`, because `iw` is a deprecated language subtag that was /// replaced by the subtag `he`. /// /// This must be a valid Unicode Language subtag as listed in [Unicode CLDR /// supplemental /// data](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml). /// /// See also: /// /// * [Locale.fromSubtags], which describes the conventions for creating /// [Locale] objects. String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; final String _languageCode; // This map is generated by //flutter/tools/gen_locale.dart // Mappings generated for language subtag registry as of 2019-02-27. static const Map _deprecatedLanguageSubtagMap = { 'in': 'id', // Indonesian; deprecated 1989-01-01 'iw': 'he', // Hebrew; deprecated 1989-01-01 'ji': 'yi', // Yiddish; deprecated 1989-01-01 'jw': 'jv', // Javanese; deprecated 2001-08-13 'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22 'aam': 'aas', // Aramanik; deprecated 2015-02-12 'adp': 'dz', // Adap; deprecated 2015-02-12 'aue': 'ktz', // Ç‚KxʼauÇ�ʼein; deprecated 2015-02-12 'ayx': 'nun', // Ayi (China); deprecated 2011-08-16 'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30 'bjd': 'drl', // Bandjigali; deprecated 2012-08-12 'ccq': 'rki', // Chaungtha; deprecated 2012-08-12 'cjr': 'mom', // Chorotega; deprecated 2010-03-11 'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12 'cmk': 'xch', // Chimakum; deprecated 2010-03-11 'coy': 'pij', // Coyaima; deprecated 2016-05-30 'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30 'drh': 'khk', // Darkhat; deprecated 2010-03-11 'drw': 'prs', // Darwazi; deprecated 2010-03-11 'gav': 'dev', // Gabutamon; deprecated 2010-03-11 'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12 'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30 'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12 'guv': 'duz', // Gey; deprecated 2016-05-30 'hrr': 'jal', // Horuru; deprecated 2012-08-12 'ibi': 'opa', // Ibilo; deprecated 2012-08-12 'ilw': 'gal', // Talur; deprecated 2013-09-10 'jeg': 'oyb', // Jeng; deprecated 2017-02-23 'kgc': 'tdf', // Kasseng; deprecated 2016-05-30 'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12 'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12 'krm': 'bmf', // Krim; deprecated 2017-02-23 'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30 'kvs': 'gdj', // Kunggara; deprecated 2016-05-30 'kwq': 'yam', // Kwak; deprecated 2015-02-12 'kxe': 'tvd', // Kakihum; deprecated 2015-02-12 'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30 'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30 'lii': 'raq', // Lingkhim; deprecated 2015-02-12 'lmm': 'rmx', // Lamam; deprecated 2014-02-28 'meg': 'cir', // Mea; deprecated 2013-09-10 'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11 'mwj': 'vaj', // Maligo; deprecated 2015-02-12 'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11 'nad': 'xny', // Nijadali; deprecated 2016-05-30 'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08 'nnx': 'ngv', // Ngong; deprecated 2015-02-12 'nts': 'pij', // Natagaimas; deprecated 2016-05-30 'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12 'pcr': 'adx', // Panang; deprecated 2013-09-10 'pmc': 'huw', // Palumata; deprecated 2016-05-30 'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12 'ppa': 'bfy', // Pao; deprecated 2016-05-30 'ppr': 'lcq', // Piru; deprecated 2013-09-10 'pry': 'prt', // Pray 3; deprecated 2016-05-30 'puz': 'pub', // Purum Naga; deprecated 2014-02-28 'sca': 'hle', // Sansu; deprecated 2012-08-12 'skk': 'oyb', // Sok; deprecated 2017-02-23 'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30 'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30 'thx': 'oyb', // The; deprecated 2015-02-12 'tie': 'ras', // Tingal; deprecated 2011-08-16 'tkk': 'twm', // Takpa; deprecated 2011-08-16 'tlw': 'weo', // South Wemale; deprecated 2012-08-12 'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30 'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30 'tnf': 'prs', // Tangshewi; deprecated 2010-03-11 'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12 'uok': 'ema', // Uokha; deprecated 2015-02-12 'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30 'xia': 'acn', // Xiandao; deprecated 2013-09-10 'xkh': 'waw', // Karahawyana; deprecated 2016-05-30 'xsj': 'suj', // Subi; deprecated 2015-02-12 'ybd': 'rki', // Yangbye; deprecated 2012-08-12 'yma': 'lrr', // Yamphe; deprecated 2012-08-12 'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12 'yos': 'zom', // Yos; deprecated 2013-09-10 'yuu': 'yug', // Yugh; deprecated 2014-02-28 }; /// The script subtag for the locale. /// /// This may be null, indicating that there is no specified script subtag. /// /// This must be a valid Unicode Language Identifier script subtag as listed /// in [Unicode CLDR supplemental /// data](https://github.com/unicode-org/cldr/blob/master/common/validity/script.xml). /// /// See also: /// /// * [Locale.fromSubtags], which describes the conventions for creating /// [Locale] objects. final String? scriptCode; /// The region subtag for the locale. /// /// This may be null, indicating that there is no specified region subtag. /// /// This is expected to be string registered in the [IANA Language Subtag /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) /// with the type "region". The string specified must match the case of the /// string in the registry. /// /// Region subtags that are deprecated in the registry and have a preferred /// code are changed to their preferred code. For example, `const Locale('de', /// 'DE')` and `const Locale('de', 'DD')` are equal, and both have the /// [countryCode] `DE`, because `DD` is a deprecated language subtag that was /// replaced by the subtag `DE`. /// /// See also: /// /// * [Locale.fromSubtags], which describes the conventions for creating /// [Locale] objects. String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; final String? _countryCode; // This map is generated by //flutter/tools/gen_locale.dart // Mappings generated for language subtag registry as of 2019-02-27. static const Map _deprecatedRegionSubtagMap = { 'BU': 'MM', // Burma; deprecated 1989-12-05 'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30 'FX': 'FR', // Metropolitan France; deprecated 1997-07-14 'TP': 'TL', // East Timor; deprecated 2002-05-20 'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14 'ZR': 'CD', // Zaire; deprecated 1997-07-14 }; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other is! Locale) { return false; } final String? thisCountryCode = countryCode; final String? otherCountryCode = other.countryCode; return other.languageCode == languageCode && other.scriptCode == scriptCode // scriptCode cannot be '' && (other.countryCode == thisCountryCode // Treat '' as equal to null. || otherCountryCode != null && otherCountryCode.isEmpty && thisCountryCode == null || thisCountryCode != null && thisCountryCode.isEmpty && other.countryCode == null); } @override int get hashCode => Object.hash(languageCode, scriptCode, countryCode == '' ? null : countryCode); static Locale? _cachedLocale; static String? _cachedLocaleString; /// Returns a string representing the locale. /// /// This identifier happens to be a valid Unicode Locale Identifier using /// underscores as separator, however it is intended to be used for debugging /// purposes only. For parsable results, use [toLanguageTag] instead. @keepToString @override String toString() { if (!identical(_cachedLocale, this)) { _cachedLocale = this; _cachedLocaleString = _rawToString('_'); } return _cachedLocaleString!; } /// Returns a syntactically valid Unicode BCP47 Locale Identifier. /// /// Some examples of such identifiers: "en", "es-419", "hi-Deva-IN" and /// "zh-Hans-CN". See http://www.unicode.org/reports/tr35/ for technical /// details. String toLanguageTag() => _rawToString('-'); String _rawToString(String separator) { final out = StringBuffer(languageCode); if (scriptCode != null && scriptCode!.isNotEmpty) { out.write('$separator$scriptCode'); } final String? countryCode = _countryCode; if (countryCode != null && countryCode.isNotEmpty) { out.write('$separator${this.countryCode}'); } return out.toString(); } } /// Various performance modes for tuning the Dart VM's GC performance. /// /// For the editor of this enum, please keep the order in sync with `Dart_PerformanceMode` /// in [dart_api.h](https://github.com/dart-lang/sdk/blob/main/runtime/include/dart_api.h#L1302). enum DartPerformanceMode { /// This is the default mode that the Dart VM is in. balanced, /// Optimize for low latency, at the expense of throughput and memory overhead /// by performing work in smaller batches (requiring more overhead) or by /// delaying work (requiring more memory). An embedder should not remain in /// this mode indefinitely. latency, /// Optimize for high throughput, at the expense of latency and memory overhead /// by performing work in larger batches with more intervening growth. throughput, /// Optimize for low memory, at the expensive of throughput and latency by more /// frequently performing work. memory, } /// An event to request a [SemanticsAction] of [type] to be performed on the /// [SemanticsNode] identified by [nodeId] owned by the [FlutterView] identified /// by [viewId]. /// /// Used by [SemanticsBinding.performSemanticsAction]. class SemanticsActionEvent { /// Creates a [SemanticsActionEvent]. const SemanticsActionEvent({ required this.type, required this.viewId, required this.nodeId, this.arguments, }); /// The type of action to be performed. final SemanticsAction type; /// The id of the [FlutterView] the [SemanticsNode] identified by [nodeId] is /// associated with. final int viewId; /// The id of the [SemanticsNode] on which the action is to be performed. final int nodeId; /// Optional arguments for the action. final Object? arguments; static const Object _noArgumentPlaceholder = Object(); /// Create a clone of the [SemanticsActionEvent] but with provided parameters /// replaced. SemanticsActionEvent copyWith({ SemanticsAction? type, int? viewId, int? nodeId, Object? arguments = _noArgumentPlaceholder, }) { return SemanticsActionEvent( type: type ?? this.type, viewId: viewId ?? this.viewId, nodeId: nodeId ?? this.nodeId, arguments: arguments == _noArgumentPlaceholder ? this.arguments : arguments, ); } } /// Signature for [PlatformDispatcher.onViewFocusChange]. typedef ViewFocusChangeCallback = void Function(ViewFocusEvent viewFocusEvent); /// An event for the engine to communicate view focus changes to the app. /// /// This value will be typically passed to the [PlatformDispatcher.onViewFocusChange] /// callback. final class ViewFocusEvent { /// Creates a [ViewFocusChange]. const ViewFocusEvent({required this.viewId, required this.state, required this.direction}); /// The ID of the [FlutterView] that experienced a focus change. final int viewId; /// The state focus changed to. final ViewFocusState state; /// The direction focus changed to. final ViewFocusDirection direction; @override String toString() { return 'ViewFocusEvent(viewId: $viewId, state: $state, direction: $direction)'; } } /// Represents the focus state of a given [FlutterView]. /// /// When focus is lost, the view's focus state changes to [ViewFocusState.unfocused]. /// /// When focus is gained, the view's focus state changes to [ViewFocusState.focused]. /// /// Valid transitions within a view are: /// /// - [ViewFocusState.focused] to [ViewFocusState.unfocused]. /// - [ViewFocusState.unfocused] to [ViewFocusState.focused]. /// /// See also: /// /// * [ViewFocusDirection], that specifies the focus direction. /// * [ViewFocusEvent], that conveys information about a [FlutterView] focus change. enum ViewFocusState { /// Specifies that a view does not have platform focus. unfocused, /// Specifies that a view has platform focus. focused, } /// Represents the direction in which the focus transitioned across [FlutterView]s. /// /// See also: /// /// * [ViewFocusState], that specifies the current focus state of a [FlutterView]. /// * [ViewFocusEvent], that conveys information about a [FlutterView] focus change. enum ViewFocusDirection { /// Indicates the focus transition did not have a direction. /// /// This is typically associated with focus being programmatically requested or /// when focus is lost. undefined, /// Indicates the focus transition was performed in a forward direction. /// /// This is typically result of the user pressing tab. forward, /// Indicates the focus transition was performed in a backward direction. /// /// This is typically result of the user pressing shift + tab. backward, } Œ¼JJG&GKNNONE3M"9? 5 ^K.,5,= )/ '/PP@$NHJ8;;#*1"*,;L+@D=6, M(Q!C63PS6<"-%1&'6,:PH<6\PSWOQ$RNUGSVgg&RL9NyDGPH/b+=#+EV^(O)P0S #+C/FV7IO#QAM!4 &.&,'_I@KJ1%,%,,AQMN3-A%-,5'#"* KO1G3HEC"'0IMd_a_ITNNSSJPSMPPMPIPPNNPIOQQIINNJMI ;*SE5++4 # =QO60-e =M(+,?33U0 _ aXHGPINO!LM3QMA,MJP-<%)3<0!):Y`B,+;*HXC5MKK<Qe0 7  /44C1/FPPQOM]]]?CI PNQOQ6;H/+6;=#+,D4*HSKDQ/A08BAIO6 -5,D #? A= IQBOUEG`:cb2V>-c=O2QQ.I$Ga9Q*KN,T)OHOPGMQ6.NKM7LBbFU*M[KQW18?08,29IFYRRIOJO/NJOP2L0?T8P?N8LP)U5M,]a=@P?QKLK*662M@?I.-543V!!98 U=$MdBQK09")0!),. ?I#LINMAA> eJR4,0IBQ+%+#&QN/,4S+&P/JOHMKR@@]J"@PMKA@@MJL>NMK?@@IJ/O@@S&K4LK*@KQK0K+29*2L?CN0PNQ R2M,N8$OJJNQK0Q.5<-5KCLQK0M,3:+3,2@QJ\2H /`4L 1F'2P!#\B=I&H])N`&#)$#5(4.*(R"H%N#JM+M@OHM,3:+3,/I=PRHL%LGHW90G(0,++/8:4).J?%,3$,,V$#2 LOQI0OQPOPA*)!="1+ `1MJsQPJFIk €ˆQPN'5J%6Y.Q*PNRJRHOQRK2#'"86)BT )IXD2F>&:3A#LE GCRL;W#SIGLNRLQP=OPPQP7e[@QCI;5QOPQN_5  # 6RCB ;>FN-__eO7GNPEB'@EPNL+)CJN91R0P[_j!)=;S0@G4@K/@F1@H/@F/@F+@B/@F*@A.@E-@D.@E2@I-@D)@@-@D1@H5@L0@G#:!C(#0 %( #$2!$#RRCI@(CC@D40:M5P$M+OJ'& @M"/"=NMLIELQNPJ4N/I"@'IL>%I-*%!()1%45M5&?)/!:OQNOK OOKOMN@OK!OM8KH>)OOKOOKJMI)EK$RYTRPSXHP6.;N@0 (>NG,2,4,9-;-HM5OHPC%1A' CAPVC\PJGYW8OO-8>BJ77 R@?4!TP Q:P4FSHOIOfISbGJMMQfMUbK<%ZQ G97/..22'=QB2LO QK-bD`P97PB/@QJJOLN+O€ƒ_gOOTM UE LQO€…OMa&€…-cN K<O<J4WOQNQc>eMMNPLQL) PPPLMONMP OJM5 APL;+4 9'MKIPQK8PQPP MNLKNJ/PNWVXY3_ R@ 8%5O2/2=;==K;%&I#88"99"8"'$&$HFK.##%% #",'&&(& M G)'(/:1KQPM8Q$<9&! LMPO #",*$&,( O I?O(SIPcMM1T=>(58 IM>J3``OCCUJF8##?Q!`__EONDMSJ`"$ !"2HLdPPDJQ#N[L[>GT5123>50889766;54<446@;5032214B71@514<;520>3;5427335;013720;:03295;6@2;48143@01(MO$YL(MLdNNQIP#LW>GR0E>5;0 #"1812>_^ e &0LPH +/!EJK/*,8*.92G[b7 QLN RIR" MQ7(*PL)9P"0!%%S:PJV#^C"&& T9VV)>>BW: 0 TUW?R K9 LA  dart:ui/platform_dispatcher.dartÀdÙÀh³—ìÀ[PÀcÀc3À\sÀ[ùÀcAÀ\ À[ƒÀfdÀ[—À[ËÀ[ÙÀ[çÀ\/À\?À\…À\³À\ÃÀe/org-dartlang-sdk:///flutter/lib/ui/pointer.dartÀCR// Copyright 2013 The Flutter Authors. 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.ui; /// How the pointer has changed since the last report. enum PointerChange { /// The input from the pointer is no longer directed towards this receiver. cancel, /// The device has started tracking the pointer. /// /// For example, the pointer might be hovering above the device, having not yet /// made contact with the surface of the device. add, /// The device is no longer tracking the pointer. /// /// For example, the pointer might have drifted out of the device's hover /// detection range or might have been disconnected from the system entirely. remove, /// The pointer has moved with respect to the device while not in contact with /// the device. hover, /// The pointer has made contact with the device. down, /// The pointer has moved with respect to the device while in contact with the /// device. move, /// The pointer has stopped making contact with the device. up, /// A pan/zoom has started on this pointer. /// /// This type of event will always have kind [PointerDeviceKind.trackpad]. panZoomStart, /// The pan/zoom on this pointer has updated. /// /// This type of event will always have kind [PointerDeviceKind.trackpad]. panZoomUpdate, /// The pan/zoom on this pointer has ended. /// /// This type of event will always have kind [PointerDeviceKind.trackpad]. panZoomEnd, } /// The kind of pointer device. enum PointerDeviceKind { /// A touch-based pointer device. /// /// The most common case is a touch screen. /// /// When the user is operating with a trackpad on iOS, clicking will also /// dispatch events with kind [touch] if /// `UIApplicationSupportsIndirectInputEvents` is not present in `Info.plist` /// or returns NO. /// /// See also: /// /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc). touch, /// A mouse-based pointer device. /// /// The most common case is a mouse on the desktop or Web. /// /// When the user is operating with a trackpad on iOS, moving the pointing /// cursor will also dispatch events with kind [mouse], and clicking will /// dispatch events with kind [mouse] if /// `UIApplicationSupportsIndirectInputEvents` is not present in `Info.plist` /// or returns NO. /// /// See also: /// /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc). mouse, /// A pointer device with a stylus. stylus, /// A pointer device with a stylus that has been inverted. invertedStylus, /// Gestures from a trackpad. /// /// A trackpad here is defined as a touch-based pointer device with an /// indirect surface (the user operates the screen by touching something that /// is not the screen). /// /// When the user makes zoom, pan, scroll or rotate gestures with a physical /// trackpad, supporting platforms dispatch events with kind [trackpad]. /// /// Events with kind [trackpad] can only have a [PointerChange] of `add`, /// `remove`, and pan-zoom related values. /// /// Some platforms don't support (or don't fully support) trackpad /// gestures, and might convert trackpad gestures into fake pointer events /// that simulate dragging. These events typically have kind [touch] or /// [mouse] instead of [trackpad]. This includes (but is not limited to) Web, /// and iOS when `UIApplicationSupportsIndirectInputEvents` isn't present in /// `Info.plist` or returns NO. /// /// Moving the pointing cursor or clicking with a trackpad typically triggers /// [touch] or [mouse] events, but never triggers [trackpad] events. /// /// See also: /// /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc). trackpad, /// An unknown pointer device. unknown, } /// The kind of pointer signal event. enum PointerSignalKind { /// The event is not associated with a pointer signal. none, /// A pointer-generated scroll (e.g., mouse wheel or trackpad scroll). scroll, /// A pointer-generated scroll-inertia cancel. scrollInertiaCancel, /// A pointer-generated scale event (e.g. trackpad pinch). scale, /// An unknown pointer signal kind. unknown, } /// A function that implements the [PointerData.respond] method. typedef PointerDataRespondCallback = void Function({bool allowPlatformDefault}); /// Information about the state of a pointer. class PointerData { /// Creates an object that represents the state of a pointer. const PointerData({ this.viewId = 0, this.embedderId = 0, this.timeStamp = Duration.zero, this.change = PointerChange.cancel, this.kind = PointerDeviceKind.touch, this.signalKind, this.device = 0, this.pointerIdentifier = 0, this.physicalX = 0.0, this.physicalY = 0.0, this.physicalDeltaX = 0.0, this.physicalDeltaY = 0.0, this.buttons = 0, this.obscured = false, this.synthesized = false, this.pressure = 0.0, this.pressureMin = 0.0, this.pressureMax = 0.0, this.distance = 0.0, this.distanceMax = 0.0, this.size = 0.0, this.radiusMajor = 0.0, this.radiusMinor = 0.0, this.radiusMin = 0.0, this.radiusMax = 0.0, this.orientation = 0.0, this.tilt = 0.0, this.platformData = 0, this.scrollDeltaX = 0.0, this.scrollDeltaY = 0.0, this.panX = 0.0, this.panY = 0.0, this.panDeltaX = 0.0, this.panDeltaY = 0.0, this.scale = 0.0, this.rotation = 0.0, PointerDataRespondCallback? onRespond, }) : _onRespond = onRespond; /// The ID of the [FlutterView] this [PointerEvent] originated from. final int viewId; /// Unique identifier that ties the [PointerEvent] to the embedder /// event that created it. /// it. /// /// No two pointer events can have the same [embedderId]. This is different /// from [pointerIdentifier] - used for hit-testing, whereas [embedderId] is /// used to identify the platform event. final int embedderId; /// Time of event dispatch, relative to an arbitrary timeline. final Duration timeStamp; /// How the pointer has changed since the last report. final PointerChange change; /// The kind of input device for which the event was generated. final PointerDeviceKind kind; /// The kind of signal for a pointer signal event. final PointerSignalKind? signalKind; /// Unique identifier for the pointing device, reused across interactions. final int device; /// Unique identifier for the pointer. /// /// This field changes for each new pointer down event. Framework uses this /// identifier to determine hit test result. final int pointerIdentifier; /// X coordinate of the position of the pointer, in physical pixels in the /// global coordinate space. final double physicalX; /// Y coordinate of the position of the pointer, in physical pixels in the /// global coordinate space. final double physicalY; /// The distance of pointer movement on X coordinate in physical pixels. final double physicalDeltaX; /// The distance of pointer movement on Y coordinate in physical pixels. final double physicalDeltaY; /// Bit field using the *Button constants (primaryMouseButton, /// secondaryStylusButton, etc). For example, if this has the value 6 and the /// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an /// upside-down stylus with both its primary and secondary buttons pressed. final int buttons; /// Set if an application from a different security domain is in any way /// obscuring this application's window. (Aspirational; not currently /// implemented.) final bool obscured; /// Set if this pointer data was synthesized by pointer data packet converter. /// pointer data packet converter will synthesize additional pointer datas if /// the input sequence of pointer data is illegal. /// /// For example, a down pointer data will be synthesized if the converter receives /// a move pointer data while the pointer is not previously down. final bool synthesized; /// The pressure of the touch as a number ranging from 0.0, indicating a touch /// with no discernible pressure, to 1.0, indicating a touch with "normal" /// pressure, and possibly beyond, indicating a stronger touch. For devices /// that do not detect pressure (e.g. mice), returns 1.0. final double pressure; /// The minimum value that [pressure] can return for this pointer. For devices /// that do not detect pressure (e.g. mice), returns 1.0. This will always be /// a number less than or equal to 1.0. final double pressureMin; /// The maximum value that [pressure] can return for this pointer. For devices /// that do not detect pressure (e.g. mice), returns 1.0. This will always be /// a greater than or equal to 1.0. final double pressureMax; /// The distance of the detected object from the input surface (e.g. the /// distance of a stylus or finger from a touch screen), in arbitrary units on /// an arbitrary (not necessarily linear) scale. If the pointer is down, this /// is 0.0 by definition. final double distance; /// The maximum value that a distance can return for this pointer. If this /// input device cannot detect "hover touch" input events, then this will be /// 0.0. final double distanceMax; /// The area of the screen being pressed, scaled to a value between 0 and 1. /// The value of size can be used to determine fat touch events. This value /// is only set on Android, and is a device specific approximation within /// the range of detectable values. So, for example, the value of 0.1 could /// mean a touch with the tip of the finger, 0.2 a touch with full finger, /// and 0.3 the full palm. final double size; /// The radius of the contact ellipse along the major axis, in logical pixels. final double radiusMajor; /// The radius of the contact ellipse along the minor axis, in logical pixels. final double radiusMinor; /// The minimum value that could be reported for radiusMajor and radiusMinor /// for this pointer, in logical pixels. final double radiusMin; /// The minimum value that could be reported for radiusMajor and radiusMinor /// for this pointer, in logical pixels. final double radiusMax; /// For PointerDeviceKind.touch events: /// /// The angle of the contact ellipse, in radius in the range: /// /// -pi/2 < orientation <= pi/2 /// /// ...giving the angle of the major axis of the ellipse with the y-axis /// (negative angles indicating an orientation along the top-left / /// bottom-right diagonal, positive angles indicating an orientation along the /// top-right / bottom-left diagonal, and zero indicating an orientation /// parallel with the y-axis). /// /// For PointerDeviceKind.stylus and PointerDeviceKind.invertedStylus events: /// /// The angle of the stylus, in radians in the range: /// /// -pi < orientation <= pi /// /// ...giving the angle of the axis of the stylus projected onto the input /// surface, relative to the positive y-axis of that surface (thus 0.0 /// indicates the stylus, if projected onto that surface, would go from the /// contact point vertically up in the positive y-axis direction, pi would /// indicate that the stylus would go down in the negative y-axis direction; /// pi/4 would indicate that the stylus goes up and to the right, -pi/2 would /// indicate that the stylus goes to the left, etc). final double orientation; /// For PointerDeviceKind.stylus and PointerDeviceKind.invertedStylus events: /// /// The angle of the stylus, in radians in the range: /// /// 0 <= tilt <= pi/2 /// /// ...giving the angle of the axis of the stylus, relative to the axis /// perpendicular to the input surface (thus 0.0 indicates the stylus is /// orthogonal to the plane of the input surface, while pi/2 indicates that /// the stylus is flat on that surface). final double tilt; /// Opaque platform-specific data associated with the event. final int platformData; /// For events with signalKind of PointerSignalKind.scroll: /// /// The amount to scroll in the x direction, in physical pixels. final double scrollDeltaX; /// For events with signalKind of PointerSignalKind.scroll: /// /// The amount to scroll in the y direction, in physical pixels. final double scrollDeltaY; /// For events with change of PointerChange.panZoomUpdate: /// /// The current panning magnitude of the pan/zoom in the x direction, in /// physical pixels. final double panX; /// For events with change of PointerChange.panZoomUpdate: /// /// The current panning magnitude of the pan/zoom in the y direction, in /// physical pixels. final double panY; /// For events with change of PointerChange.panZoomUpdate: /// /// The difference in panning of the pan/zoom in the x direction since the /// latest panZoomUpdate event, in physical pixels. final double panDeltaX; /// For events with change of PointerChange.panZoomUpdate: /// /// The difference in panning of the pan/zoom in the y direction since the /// last panZoomUpdate event, in physical pixels. final double panDeltaY; /// For events with change of PointerChange.panZoomUpdate: /// /// The current scale of the pan/zoom (unitless), with 1.0 as the initial scale. final double scale; /// For events with change of PointerChange.panZoomUpdate: /// /// The current angle of the pan/zoom in radians, with 0.0 as the initial angle. final double rotation; // An optional function that allows the framework to respond to the event // that triggered this PointerData instance. final PointerDataRespondCallback? _onRespond; /// Method that the framework/app can call to respond to the native event /// that triggered this [PointerData]. /// /// The parameter [allowPlatformDefault] allows the platform to perform the /// default action associated with the native event when it's set to `true`. /// /// This method can be called any number of times, but once `allowPlatformDefault` /// is set to `true`, it can't be set to `false` again. /// /// If `allowPlatformDefault` is never set to `true`, the Flutter engine will /// consume the event, so it won't be seen by the platform. In the web, this /// means that `preventDefault` will be called in the DOM event that triggered /// the `PointerData`. See [Event: preventDefault() method in MDN][EpDmiMDN]. /// /// The implementation of this method is configured through the `onRespond` /// parameter of the [PointerData] constructor. /// /// See also [PointerDataRespondCallback]. /// /// [EpDmiMDN]: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault void respond({required bool allowPlatformDefault}) { if (_onRespond != null) { _onRespond(allowPlatformDefault: allowPlatformDefault); } } @override String toString() => 'PointerData(viewId: $viewId, x: $physicalX, y: $physicalY)'; /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' 'embedderId: $embedderId, ' 'timeStamp: $timeStamp, ' 'change: $change, ' 'kind: $kind, ' 'signalKind: $signalKind, ' 'device: $device, ' 'pointerIdentifier: $pointerIdentifier, ' 'physicalX: $physicalX, ' 'physicalY: $physicalY, ' 'physicalDeltaX: $physicalDeltaX, ' 'physicalDeltaY: $physicalDeltaY, ' 'buttons: $buttons, ' 'synthesized: $synthesized, ' 'pressure: $pressure, ' 'pressureMin: $pressureMin, ' 'pressureMax: $pressureMax, ' 'distance: $distance, ' 'distanceMax: $distanceMax, ' 'size: $size, ' 'radiusMajor: $radiusMajor, ' 'radiusMinor: $radiusMinor, ' 'radiusMin: $radiusMin, ' 'radiusMax: $radiusMax, ' 'orientation: $orientation, ' 'tilt: $tilt, ' 'platformData: $platformData, ' 'scrollDeltaX: $scrollDeltaX, ' 'scrollDeltaY: $scrollDeltaY, ' 'panX: $panX, ' 'panY: $panY, ' 'panDeltaX: $panDeltaX, ' 'panDeltaY: $panDeltaY, ' 'scale: $scale, ' 'rotation: $rotation, ' 'viewId: $viewId' ')'; } } /// A sequence of reports about the state of pointers. class PointerDataPacket { /// Creates a packet of pointer data reports. const PointerDataPacket({this.data = const []}); /// Data about the individual pointers in this packet. /// /// This list might contain multiple pieces of data about the same pointer. final List data; } �Ù.M0M.M $.L+P€Á $=ML+P€Á & = IPOKL-EMJPO"PG€Á ! &9I 1= & AQ.@$() +GE NO+A9B 5'M)N/MMKKAPKNKHQP5UDQMN<QP*QP&KQPMO ONLNMQQO+O+*@%KFQK!P8!MINMOP7P8JKN+?>C>C=K=K=M6=M4=S=SL/0L)NOU:POQPN2-X7> UP$"$2"",,& && &&&""&(((""  70@9N dart:ui/pointer.dartÀ]ÀfdÀh³À]/À]C3org-dartlang-sdk:///flutter/lib/ui/compositing.dartÀ”M// Copyright 2013 The Flutter Authors. 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.ui; /// An opaque object representing a composited scene. /// /// To create a Scene object, use a [SceneBuilder]. /// /// Scene objects can be displayed on the screen using the [FlutterView.render] /// method. abstract class Scene { /// Synchronously creates a handle to an image from this scene. /// /// {@macro dart.ui.painting.Picture.toImageSync} Image toImageSync(int width, int height); /// Creates a raster image representation of the current state of the scene. /// /// This is a slow operation that is performed on a background thread. /// /// Callers must dispose the [Image] when they are done with it. If the result /// will be shared with other methods or classes, [Image.clone] should be used /// and each handle created must be disposed. Future toImage(int width, int height); /// Releases the resources used by this scene. /// /// After calling this function, the scene is cannot be used further. /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). void dispose(); } @pragma('vm:entry-point') base class _NativeScene extends NativeFieldWrapperClass1 implements Scene { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a Scene object, use a [SceneBuilder]. @pragma('vm:entry-point') _NativeScene._(); @override Image toImageSync(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } final image = _Image._(); final String? result = _toImageSync(width, height, image); if (result != null) { throw PictureRasterizationException._(result); } return Image._(image, image.width, image.height); } @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImageSync') external String? _toImageSync(int width, int height, _Image outImage); @override Future toImage(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } return _futurize( (_Callback callback) => _toImage(width, height, (_Image? image) { if (image == null) { callback(null); } else { callback(Image._(image, image.width, image.height)); } }), ); } @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImage') external String? _toImage(int width, int height, _Callback<_Image?> callback); @override @Native)>(symbol: 'Scene::dispose') external void dispose(); @override String toString() => 'Scene'; } // Lightweight wrapper of a native layer object. // // This is used to provide a typed API for engine layers to prevent // incompatible layers from being passed to [SceneBuilder]'s push methods. // For example, this prevents a layer returned from `pushOpacity` from being // passed as `oldLayer` to `pushTransform`. This is achieved by having one // concrete subclass of this class per push method. abstract class _EngineLayerWrapper implements EngineLayer { _EngineLayerWrapper._(EngineLayer nativeLayer) : _nativeLayer = nativeLayer; EngineLayer? _nativeLayer; @override void dispose() { assert(_nativeLayer != null, 'Object disposed'); _nativeLayer!.dispose(); assert(() { _nativeLayer = null; return true; }()); } // Children of this layer. // // Null if this layer has no children. This field is populated only in debug // mode. List<_EngineLayerWrapper>? _debugChildren; // Whether this layer was used as `oldLayer` in a past frame. // // It is illegal to use a layer object again after it is passed as an // `oldLayer` argument. bool _debugWasUsedAsOldLayer = false; bool _debugCheckNotUsedAsOldLayer() { // The hashCode formatting should match shortHash in the framework assert( !_debugWasUsedAsOldLayer, 'Layer $runtimeType#${hashCode.toUnsigned(20).toRadixString(16).padLeft(5, '0')} was previously used as oldLayer.\n' 'Once a layer is used as oldLayer, it may not be used again. Instead, ' 'after calling one of the SceneBuilder.push* methods and passing an oldLayer ' 'to it, use the layer returned by the method as oldLayer in subsequent ' 'frames.', ); return true; } } /// An opaque handle to a transform engine layer. /// /// Instances of this class are created by [SceneBuilder.pushTransform]. /// /// {@template dart.ui.sceneBuilder.oldLayerCompatibility} /// `oldLayer` parameter in [SceneBuilder] methods only accepts objects created /// by the engine. [SceneBuilder] will throw an [AssertionError] if you pass it /// a custom implementation of this class. /// {@endtemplate} class TransformEngineLayer extends _EngineLayerWrapper { TransformEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to an offset engine layer. /// /// Instances of this class are created by [SceneBuilder.pushOffset]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class OffsetEngineLayer extends _EngineLayerWrapper { OffsetEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a clip rect engine layer. /// /// Instances of this class are created by [SceneBuilder.pushClipRect]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ClipRectEngineLayer extends _EngineLayerWrapper { ClipRectEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a clip rounded rect engine layer. /// /// Instances of this class are created by [SceneBuilder.pushClipRRect]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ClipRRectEngineLayer extends _EngineLayerWrapper { ClipRRectEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a clip rounded superellipse engine layer. /// /// Instances of this class are created by [SceneBuilder.pushClipRSuperellipse]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ClipRSuperellipseEngineLayer extends _EngineLayerWrapper { ClipRSuperellipseEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a clip path engine layer. /// /// Instances of this class are created by [SceneBuilder.pushClipPath]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ClipPathEngineLayer extends _EngineLayerWrapper { ClipPathEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to an opacity engine layer. /// /// Instances of this class are created by [SceneBuilder.pushOpacity]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class OpacityEngineLayer extends _EngineLayerWrapper { OpacityEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a color filter engine layer. /// /// Instances of this class are created by [SceneBuilder.pushColorFilter]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ColorFilterEngineLayer extends _EngineLayerWrapper { ColorFilterEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to an image filter engine layer. /// /// Instances of this class are created by [SceneBuilder.pushImageFilter]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ImageFilterEngineLayer extends _EngineLayerWrapper { ImageFilterEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a backdrop filter engine layer. /// /// Instances of this class are created by [SceneBuilder.pushBackdropFilter]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class BackdropFilterEngineLayer extends _EngineLayerWrapper { BackdropFilterEngineLayer._(super.nativeLayer) : super._(); } /// An opaque handle to a shader mask engine layer. /// /// Instances of this class are created by [SceneBuilder.pushShaderMask]. /// /// {@macro dart.ui.sceneBuilder.oldLayerCompatibility} class ShaderMaskEngineLayer extends _EngineLayerWrapper { ShaderMaskEngineLayer._(super.nativeLayer) : super._(); } /// Builds a [Scene] containing the given visuals. /// /// A [Scene] can then be rendered using [FlutterView.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add /// it to the scene using [addPicture]. /// /// ## Use with the Flutter framework /// /// The Flutter framework's [RendererBinding] provides a hook for creating /// [SceneBuilder] objects ([RendererBinding.createSceneBuilder]) that allows /// tests to hook into the scene creation logic. When creating a [SceneBuilder] /// in the context of the Flutter framework, consider calling /// [RendererBinding.createSceneBuilder] instead of calling the /// [SceneBuilder.new] constructor directly. /// /// This does not apply when using the `dart:ui` API directly, without using the /// Flutter framework bindings, `flutter_test` framework, et al. abstract class SceneBuilder { // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs factory SceneBuilder() = _NativeSceneBuilder; /// Pushes a transform operation onto the operation stack. /// /// The objects are transformed by the given matrix before rasterization. /// /// {@template dart.ui.sceneBuilder.oldLayer} /// If `oldLayer` is not null the engine will attempt to reuse the resources /// allocated for the old layer when rendering the new layer. This is purely /// an optimization. It has no effect on the correctness of rendering. /// {@endtemplate} /// /// {@template dart.ui.sceneBuilder.oldLayerVsRetained} /// Passing a layer to [addRetained] or as `oldLayer` argument to a push /// method counts as _usage_. A layer can be used no more than once in a scene. /// For example, it may not be passed simultaneously to two push methods, or /// to a push method and to `addRetained`. /// /// When a layer is passed to [addRetained] all descendant layers are also /// considered as used in this scene. The same single-usage restriction /// applies to descendants. /// /// When a layer is passed as an `oldLayer` argument to a push method, it may /// no longer be used in subsequent frames. If you would like to continue /// reusing the resources associated with the layer, store the layer object /// returned by the push method and use that in the next frame instead of the /// original object. /// {@endtemplate} /// /// See [pop] for details about the operation stack. TransformEngineLayer pushTransform(Float64List matrix4, {TransformEngineLayer? oldLayer}); /// Pushes an offset operation onto the operation stack. /// /// This is equivalent to [pushTransform] with a matrix with only translation. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. OffsetEngineLayer pushOffset(double dx, double dy, {OffsetEngineLayer? oldLayer}); /// Pushes a rectangular clip operation onto the operation stack. /// /// Rasterization outside the given rectangle is discarded. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). ClipRectEngineLayer pushClipRect( Rect rect, { Clip clipBehavior = Clip.antiAlias, ClipRectEngineLayer? oldLayer, }); /// Pushes a rounded-rectangular clip operation onto the operation stack. /// /// Rasterization outside the given rounded rectangle is discarded. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). ClipRRectEngineLayer pushClipRRect( RRect rrect, { Clip clipBehavior = Clip.antiAlias, ClipRRectEngineLayer? oldLayer, }); /// Pushes a rounded-superellipse clip operation onto the operation stack. /// /// Rasterization outside the given rounded superellipse is discarded. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). ClipRSuperellipseEngineLayer pushClipRSuperellipse( RSuperellipse rsuperellipse, { Clip clipBehavior = Clip.antiAlias, ClipRSuperellipseEngineLayer? oldLayer, }); /// Pushes a path clip operation onto the operation stack. /// /// Rasterization outside the given path is discarded. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. See [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). ClipPathEngineLayer pushClipPath( Path path, { Clip clipBehavior = Clip.antiAlias, ClipPathEngineLayer? oldLayer, }); /// Pushes an opacity operation onto the operation stack. /// /// The given alpha value is blended into the alpha value of the objects' /// rasterization. An alpha value of 0 makes the objects entirely invisible. /// An alpha value of 255 has no effect (i.e., the objects retain the current /// opacity). /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. OpacityEngineLayer pushOpacity( int alpha, { Offset? offset = Offset.zero, OpacityEngineLayer? oldLayer, }); /// Pushes a color filter operation onto the operation stack. /// /// The given color is applied to the objects' rasterization using the given /// blend mode. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. ColorFilterEngineLayer pushColorFilter(ColorFilter filter, {ColorFilterEngineLayer? oldLayer}); /// Pushes an image filter operation onto the operation stack. /// /// The given filter is applied to the children's rasterization before compositing them into /// the scene. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. ImageFilterEngineLayer pushImageFilter( ImageFilter filter, { Offset offset = Offset.zero, ImageFilterEngineLayer? oldLayer, }); /// Pushes a backdrop filter operation onto the operation stack. /// /// The given filter is applied to the current contents of the scene as far back as /// the most recent save layer and rendered back to the scene using the indicated /// [blendMode] prior to rasterizing the child layers. /// /// If [backdropId] is provided and not null, then this value is treated /// as a unique identifier for the backdrop. When the first backdrop filter with /// a given id is processed during rasterization, the state of the backdrop is /// recorded and cached. All subsequent backdrop filters with the same identifier /// will apply their filter to the cached backdrop. The correct usage of the /// backdrop id has the benefit of dramatically improving performance for /// applications with multiple backdrop filters. For example, an application /// that uses a backdrop blur filter for each item in a list view should set /// all filters to have the same backdrop id. /// /// If overlapping backdrop filters use the same backdropId, then each filter /// will apply to the backdrop before the overlapping filter components were /// rendered. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. BackdropFilterEngineLayer pushBackdropFilter( ImageFilter filter, { BlendMode blendMode = BlendMode.srcOver, BackdropFilterEngineLayer? oldLayer, int? backdropId, }); /// Pushes a shader mask operation onto the operation stack. /// /// The given shader is applied to the object's rasterization in the given /// rectangle using the given blend mode. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. ShaderMaskEngineLayer pushShaderMask( Shader shader, Rect maskRect, BlendMode blendMode, { ShaderMaskEngineLayer? oldLayer, FilterQuality filterQuality = FilterQuality.low, }); /// Ends the effect of the most recently pushed operation. /// /// Internally the scene builder maintains a stack of operations. Each of the /// operations in the stack applies to each of the objects added to the scene. /// Calling this function removes the most recently added operation from the /// stack. void pop(); /// Add a retained engine layer subtree from previous frames. /// /// All the engine layers that are in the subtree of the retained layer will /// be automatically appended to the current engine layer tree. /// /// Therefore, when implementing a subclass of the [Layer] concept defined in /// the rendering layer of Flutter's framework, once this is called, there's /// no need to call [Layer.addToScene] for its children layers. /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} void addRetained(EngineLayer retainedLayer); /// Adds an object to the scene that displays performance statistics. /// /// Useful during development to assess the performance of the application. /// The enabledOptions controls which statistics are displayed. The bounds /// controls where the statistics are displayed. /// /// enabledOptions is a bit field with the following bits defined: /// - 0x01: displayRasterizerStatistics - show raster thread frame time /// - 0x02: visualizeRasterizerStatistics - graph raster thread frame times /// - 0x04: displayEngineStatistics - show UI thread frame time /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. /// /// The "UI thread" is the thread that includes all the execution of the main /// Dart isolate (the isolate that can call [FlutterView.render]). The UI /// thread frame time is the total time spent executing the /// [PlatformDispatcher.onBeginFrame] callback. The "raster thread" is the /// thread (running on the CPU) that subsequently processes the [Scene] /// provided by the Dart code to turn it into GPU commands and send it to the /// GPU. /// /// See also the [PerformanceOverlayOption] enum in the rendering library. /// for more details. // Values above must match constants in //engine/src/sky/compositor/performance_overlay_layer.h void addPerformanceOverlay(int enabledOptions, Rect bounds); /// Adds a [Picture] to the scene. /// /// The picture is rasterized at the given `offset`. /// /// The rendering _may_ be cached to reduce the cost of painting the picture /// if it is reused in subsequent frames. Whether a picture is cached or not /// depends on the backend implementation. When caching is considered, the /// choice to cache or not cache is a heuristic based on how often the picture /// is being painted and the cost of painting the picture. To disable this /// caching, set `willChangeHint` to true. To force the caching to happen (in /// backends that do caching), set `isComplexHint` to true. When both are set, /// `willChangeHint` prevails. /// /// In general, setting these hints is not very useful. Backends that cache /// pictures only do so for pictures that have been rendered three times /// already; setting `willChangeHint` to true to avoid caching an animating /// picture that changes every frame is therefore redundant, the picture /// wouldn't have been cached anyway. Similarly, backends that cache pictures /// are relatively aggressive about doing so, such that any image complicated /// enough to warrant caching is probably already being cached even without /// `isComplexHint` being set to true. void addPicture( Offset offset, Picture picture, { bool isComplexHint = false, bool willChangeHint = false, }); /// Adds a backend texture to the scene. /// /// The texture is scaled to the given size and rasterized at the given offset. /// /// If `freeze` is true the texture that is added to the scene will not /// be updated with new frames. `freeze` is used when resizing an embedded /// Android view: When resizing an Android view there is a short period during /// which the framework cannot tell if the newest texture frame has the /// previous or new size, to workaround this the framework "freezes" the /// texture just before resizing the Android view and un-freezes it when it is /// certain that a frame with the new size is ready. void addTexture( int textureId, { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, bool freeze = false, FilterQuality filterQuality = FilterQuality.low, }); /// Adds a platform view (e.g an iOS UIView) to the scene. /// /// On iOS this layer splits the current output surface into two surfaces, one for the scene nodes /// preceding the platform view, and one for the scene nodes following the platform view. /// /// ## Performance impact /// /// Adding an additional surface doubles the amount of graphics memory directly used by Flutter /// for output buffers. Quartz might allocated extra buffers for compositing the Flutter surfaces /// and the platform view. /// /// With a platform view in the scene, Quartz has to composite the two Flutter surfaces and the /// embedded UIView. In addition to that, on iOS versions greater than 9, the Flutter frames are /// synchronized with the UIView frames adding additional performance overhead. /// /// The `offset` argument is not used for iOS and Android. void addPlatformView( int viewId, { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, }); /// Finishes building the scene. /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the /// screen with [FlutterView.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. Scene build(); } base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements SceneBuilder { /// Creates an empty [SceneBuilder] object. _NativeSceneBuilder() { _constructor(); } @Native(symbol: 'SceneBuilder::Create') external void _constructor(); // Layers used in this scene. // // The key is the layer used. The value is the description of what the layer // is used for, e.g. "pushOpacity" or "addRetained". final Map _usedLayers = {}; // In debug mode checks that the `layer` is only used once in a given scene. bool _debugCheckUsedOnce(EngineLayer layer, String usage) { assert(() { assert( !_usedLayers.containsKey(layer), 'Layer ${layer.runtimeType} already used.\n' 'The layer is already being used as ${_usedLayers[layer]} in this scene.\n' 'A layer may only be used once in a given scene.', ); _usedLayers[layer] = usage; return true; }()); return true; } bool _debugCheckCanBeUsedAsOldLayer(_EngineLayerWrapper? layer, String methodName) { assert(() { if (layer == null) { return true; } assert(layer._nativeLayer != null, 'Object disposed'); layer._debugCheckNotUsedAsOldLayer(); assert(_debugCheckUsedOnce(layer, 'oldLayer in $methodName')); layer._debugWasUsedAsOldLayer = true; return true; }()); return true; } final List<_EngineLayerWrapper> _layerStack = <_EngineLayerWrapper>[]; // Pushes the `newLayer` onto the `_layerStack` and adds it to the // `_debugChildren` of the current layer in the stack, if any. bool _debugPushLayer(_EngineLayerWrapper newLayer) { assert(() { if (_layerStack.isNotEmpty) { final _EngineLayerWrapper currentLayer = _layerStack.last; currentLayer._debugChildren ??= <_EngineLayerWrapper>[]; currentLayer._debugChildren!.add(newLayer); } _layerStack.add(newLayer); return true; }()); return true; } @override TransformEngineLayer pushTransform(Float64List matrix4, {TransformEngineLayer? oldLayer}) { assert(_matrix4IsValid(matrix4)); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushTransform(engineLayer, matrix4, oldLayer?._nativeLayer); final layer = TransformEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Handle, Handle)>( symbol: 'SceneBuilder::pushTransformHandle', ) external void _pushTransform(EngineLayer layer, Float64List matrix4, EngineLayer? oldLayer); @override OffsetEngineLayer pushOffset(double dx, double dy, {OffsetEngineLayer? oldLayer}) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushOffset(engineLayer, dx, dy, oldLayer?._nativeLayer); final layer = OffsetEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Double, Double, Handle)>( symbol: 'SceneBuilder::pushOffset', ) external void _pushOffset(EngineLayer layer, double dx, double dy, EngineLayer? oldLayer); @override ClipRectEngineLayer pushClipRect( Rect rect, { Clip clipBehavior = Clip.antiAlias, ClipRectEngineLayer? oldLayer, }) { assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushClipRect( engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index, oldLayer?._nativeLayer, ); final layer = ClipRectEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Double, Double, Double, Double, Int32, Handle)>( symbol: 'SceneBuilder::pushClipRect', ) external void _pushClipRect( EngineLayer outEngineLayer, double left, double right, double top, double bottom, int clipBehavior, EngineLayer? oldLayer, ); @override ClipRRectEngineLayer pushClipRRect( RRect rrect, { Clip clipBehavior = Clip.antiAlias, ClipRRectEngineLayer? oldLayer, }) { assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRRect')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushClipRRect(engineLayer, rrect._getValue32(), clipBehavior.index, oldLayer?._nativeLayer); final layer = ClipRRectEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Handle, Int32, Handle)>( symbol: 'SceneBuilder::pushClipRRect', ) external void _pushClipRRect( EngineLayer layer, Float32List rrect, int clipBehavior, EngineLayer? oldLayer, ); @override ClipRSuperellipseEngineLayer pushClipRSuperellipse( RSuperellipse rsuperellipse, { Clip clipBehavior = Clip.antiAlias, ClipRSuperellipseEngineLayer? oldLayer, }) { assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRSuperellipse')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushClipRSuperellipse( engineLayer, rsuperellipse._native(), clipBehavior.index, oldLayer?._nativeLayer, ); final layer = ClipRSuperellipseEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Pointer, Int32, Handle)>( symbol: 'SceneBuilder::pushClipRSuperellipse', ) external void _pushClipRSuperellipse( EngineLayer layer, _NativeRSuperellipse rsuperellipseParam, int clipBehavior, EngineLayer? oldLayer, ); @override ClipPathEngineLayer pushClipPath( Path path, { Clip clipBehavior = Clip.antiAlias, ClipPathEngineLayer? oldLayer, }) { assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipPath')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushClipPath(engineLayer, path as _NativePath, clipBehavior.index, oldLayer?._nativeLayer); final layer = ClipPathEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Pointer, Int32, Handle)>( symbol: 'SceneBuilder::pushClipPath', ) external void _pushClipPath( EngineLayer layer, _NativePath path, int clipBehavior, EngineLayer? oldLayer, ); @override OpacityEngineLayer pushOpacity( int alpha, { Offset? offset = Offset.zero, OpacityEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOpacity')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy, oldLayer?._nativeLayer); final layer = OpacityEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Int32, Double, Double, Handle)>( symbol: 'SceneBuilder::pushOpacity', ) external void _pushOpacity( EngineLayer layer, int alpha, double dx, double dy, EngineLayer? oldLayer, ); @override ColorFilterEngineLayer pushColorFilter(ColorFilter filter, {ColorFilterEngineLayer? oldLayer}) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter')); final _ColorFilter nativeFilter = filter._toNativeColorFilter()!; final EngineLayer engineLayer = _NativeEngineLayer._(); _pushColorFilter(engineLayer, nativeFilter, oldLayer?._nativeLayer); final layer = ColorFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Pointer, Handle)>( symbol: 'SceneBuilder::pushColorFilter', ) external void _pushColorFilter(EngineLayer layer, _ColorFilter filter, EngineLayer? oldLayer); @override ImageFilterEngineLayer pushImageFilter( ImageFilter filter, { Offset offset = Offset.zero, ImageFilterEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushImageFilter')); final _ImageFilter nativeFilter = filter._toNativeImageFilter(); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushImageFilter(engineLayer, nativeFilter, offset.dx, offset.dy, oldLayer?._nativeLayer); final layer = ImageFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Pointer, Double, Double, Handle)>( symbol: 'SceneBuilder::pushImageFilter', ) external void _pushImageFilter( EngineLayer outEngineLayer, _ImageFilter filter, double dx, double dy, EngineLayer? oldLayer, ); @override BackdropFilterEngineLayer pushBackdropFilter( ImageFilter filter, { BlendMode blendMode = BlendMode.srcOver, int? backdropId, BackdropFilterEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushBackdropFilter( engineLayer, filter._toNativeImageFilter(), blendMode.index, backdropId, oldLayer?._nativeLayer, ); final layer = BackdropFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native, Handle, Pointer, Int32, Handle, Handle)>( symbol: 'SceneBuilder::pushBackdropFilter', ) external void _pushBackdropFilter( EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, int? backdropId, EngineLayer? oldLayer, ); @override ShaderMaskEngineLayer pushShaderMask( Shader shader, Rect maskRect, BlendMode blendMode, { ShaderMaskEngineLayer? oldLayer, FilterQuality filterQuality = FilterQuality.low, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushShaderMask')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushShaderMask( engineLayer, shader, maskRect.left, maskRect.right, maskRect.top, maskRect.bottom, blendMode.index, filterQuality.index, oldLayer?._nativeLayer, ); final layer = ShaderMaskEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } @Native< Void Function( Pointer, Handle, Pointer, Double, Double, Double, Double, Int32, Int32, Handle, ) >(symbol: 'SceneBuilder::pushShaderMask') external void _pushShaderMask( EngineLayer engineLayer, Shader shader, double maskRectLeft, double maskRectRight, double maskRectTop, double maskRectBottom, int blendMode, int filterQualityIndex, EngineLayer? oldLayer, ); @override void pop() { if (_layerStack.isNotEmpty) { _layerStack.removeLast(); } _pop(); } @Native)>(symbol: 'SceneBuilder::pop', isLeaf: true) external void _pop(); @override void addRetained(EngineLayer retainedLayer) { assert(retainedLayer is _EngineLayerWrapper); assert(() { final layer = retainedLayer as _EngineLayerWrapper; assert(layer._nativeLayer != null); void recursivelyCheckChildrenUsedOnce(_EngineLayerWrapper parentLayer) { _debugCheckUsedOnce(parentLayer, 'retained layer'); parentLayer._debugCheckNotUsedAsOldLayer(); final List<_EngineLayerWrapper>? children = parentLayer._debugChildren; if (children == null || children.isEmpty) { return; } children.forEach(recursivelyCheckChildrenUsedOnce); } recursivelyCheckChildrenUsedOnce(layer); return true; }()); final wrapper = retainedLayer as _EngineLayerWrapper; _addRetained(wrapper._nativeLayer!); } @Native, Handle)>(symbol: 'SceneBuilder::addRetained') external void _addRetained(EngineLayer retainedLayer); @override void addPerformanceOverlay(int enabledOptions, Rect bounds) { _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); } @Native, Uint64, Double, Double, Double, Double)>( symbol: 'SceneBuilder::addPerformanceOverlay', isLeaf: true, ) external void _addPerformanceOverlay( int enabledOptions, double left, double right, double top, double bottom, ); @override void addPicture( Offset offset, Picture picture, { bool isComplexHint = false, bool willChangeHint = false, }) { assert(!picture.debugDisposed); final int hints = (isComplexHint ? 1 : 0) | (willChangeHint ? 2 : 0); _addPicture(offset.dx, offset.dy, picture as _NativePicture, hints); } @Native, Double, Double, Pointer, Int32)>( symbol: 'SceneBuilder::addPicture', ) external void _addPicture(double dx, double dy, _NativePicture picture, int hints); @override void addTexture( int textureId, { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, bool freeze = false, FilterQuality filterQuality = FilterQuality.low, }) { _addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index); } @Native, Double, Double, Double, Double, Int64, Bool, Int32)>( symbol: 'SceneBuilder::addTexture', isLeaf: true, ) external void _addTexture( double dx, double dy, double width, double height, int textureId, bool freeze, int filterQuality, ); @override void addPlatformView( int viewId, { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, }) { _addPlatformView(offset.dx, offset.dy, width, height, viewId); } @Native, Double, Double, Double, Double, Int64)>( symbol: 'SceneBuilder::addPlatformView', isLeaf: true, ) external void _addPlatformView(double dx, double dy, double width, double height, int viewId); @override Scene build() { final Scene scene = _NativeScene._(); _build(scene); return scene; } @Native, Handle)>(symbol: 'SceneBuilder::build') external void _build(Scene outScene); @override String toString() => 'SceneBuilder'; } „>>4J8::3?@E(&KNP>@-QAm$0=L0OOI:KRO-MJPLNP7];Q-77UD>-7\K$(#LF-7\K&($MI-7\K6#(,=9-7\K$(#<LOP-77"""@O-77bA_-77*!&CVT9KSQTOLOO0PO-770-)?M,-77(%5=PQO @OBPOB7/HNM3EKOCGOPL>MJP Mb?%7OOMQMPQ!NKNKPPN) !+RJMQJKQ7!5=e\bdbcR=!#GC(KZ.A  O7HO>)5T; " W=,E, IEA7$CA4!  ^&G4$I(] $(#'F<6$`*  &($'G _dx >= double.infinity || _dy >= double.infinity; /// Whether both components are finite (neither infinite nor NaN). /// /// See also: /// /// * [isInfinite], which returns true if either component is equal to /// positive infinity. bool get isFinite => _dx.isFinite && _dy.isFinite; /// Less-than operator. Compares an [Offset] or [Size] to another [Offset] or /// [Size], and returns true if both the horizontal and vertical values of the /// left-hand-side operand are smaller than the horizontal and vertical values /// of the right-hand-side operand respectively. Returns false otherwise. /// /// This is a partial ordering. It is possible for two values to be neither /// less, nor greater than, nor equal to, another. bool operator <(OffsetBase other) => _dx < other._dx && _dy < other._dy; /// Less-than-or-equal-to operator. Compares an [Offset] or [Size] to another /// [Offset] or [Size], and returns true if both the horizontal and vertical /// values of the left-hand-side operand are smaller than or equal to the /// horizontal and vertical values of the right-hand-side operand /// respectively. Returns false otherwise. /// /// This is a partial ordering. It is possible for two values to be neither /// less, nor greater than, nor equal to, another. bool operator <=(OffsetBase other) => _dx <= other._dx && _dy <= other._dy; /// Greater-than operator. Compares an [Offset] or [Size] to another [Offset] /// or [Size], and returns true if both the horizontal and vertical values of /// the left-hand-side operand are bigger than the horizontal and vertical /// values of the right-hand-side operand respectively. Returns false /// otherwise. /// /// This is a partial ordering. It is possible for two values to be neither /// less, nor greater than, nor equal to, another. bool operator >(OffsetBase other) => _dx > other._dx && _dy > other._dy; /// Greater-than-or-equal-to operator. Compares an [Offset] or [Size] to /// another [Offset] or [Size], and returns true if both the horizontal and /// vertical values of the left-hand-side operand are bigger than or equal to /// the horizontal and vertical values of the right-hand-side operand /// respectively. Returns false otherwise. /// /// This is a partial ordering. It is possible for two values to be neither /// less, nor greater than, nor equal to, another. bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy; /// Equality operator. Compares an [Offset] or [Size] to another [Offset] or /// [Size], and returns true if the horizontal and vertical values of the /// left-hand-side operand are equal to the horizontal and vertical values of /// the right-hand-side operand respectively. Returns false otherwise. @override bool operator ==(Object other) { return other is OffsetBase && other._dx == _dx && other._dy == _dy; } @override int get hashCode => Object.hash(_dx, _dy); @override String toString() => 'OffsetBase(${_dx.toStringAsFixed(1)}, ${_dy.toStringAsFixed(1)})'; } /// An immutable 2D floating-point offset. /// /// Generally speaking, Offsets can be interpreted in two ways: /// /// 1. As representing a point in Cartesian space a specified distance from a /// separately-maintained origin. For example, the top-left position of /// children in the [RenderBox] protocol is typically represented as an /// [Offset] from the top left of the parent box. /// /// 2. As a vector that can be applied to coordinates. For example, when /// painting a [RenderObject], the parent is passed an [Offset] from the /// screen's origin which it can add to the offsets of its children to find /// the [Offset] from the screen's origin to each of the children. /// /// Because a particular [Offset] can be interpreted as one sense at one time /// then as the other sense at a later time, the same class is used for both /// senses. /// /// See also: /// /// * [Size], which represents a vector describing the size of a rectangle. class Offset extends OffsetBase { /// Creates an offset. The first argument sets [dx], the horizontal component, /// and the second sets [dy], the vertical component. const Offset(super.dx, super.dy); /// Creates an offset from its [direction] and [distance]. /// /// The direction is in radians clockwise from the positive x-axis. /// /// The distance can be omitted, to create a unit vector (distance = 1.0). factory Offset.fromDirection(double direction, [double distance = 1.0]) { return Offset(distance * math.cos(direction), distance * math.sin(direction)); } /// The x component of the offset. /// /// The y component is given by [dy]. double get dx => _dx; /// The y component of the offset. /// /// The x component is given by [dx]. double get dy => _dy; /// The magnitude of the offset. /// /// If you need this value to compare it to another [Offset]'s distance, /// consider using [distanceSquared] instead, since it is cheaper to compute. double get distance => math.sqrt(dx * dx + dy * dy); /// The square of the magnitude of the offset. /// /// This is cheaper than computing the [distance] itself. double get distanceSquared => dx * dx + dy * dy; /// The angle of this offset as radians clockwise from the positive x-axis, in /// the range -[pi] to [pi], assuming positive values of the x-axis go to the /// right and positive values of the y-axis go down. /// /// Zero means that [dy] is zero and [dx] is zero or positive. /// /// Values from zero to [pi]/2 indicate positive values of [dx] and [dy], the /// bottom-right quadrant. /// /// Values from [pi]/2 to [pi] indicate negative values of [dx] and positive /// values of [dy], the bottom-left quadrant. /// /// Values from zero to -[pi]/2 indicate positive values of [dx] and negative /// values of [dy], the top-right quadrant. /// /// Values from -[pi]/2 to -[pi] indicate negative values of [dx] and [dy], /// the top-left quadrant. /// /// When [dy] is zero and [dx] is negative, the [direction] is [pi]. /// /// When [dx] is zero, [direction] is [pi]/2 if [dy] is positive and -[pi]/2 /// if [dy] is negative. /// /// See also: /// /// * [distance], to compute the magnitude of the vector. /// * [Canvas.rotate], which uses the same convention for its angle. double get direction => math.atan2(dy, dx); /// An offset with zero magnitude. /// /// This can be used to represent the origin of a coordinate space. static const Offset zero = Offset(0.0, 0.0); /// An offset with infinite x and y components. /// /// See also: /// /// * [isInfinite], which checks whether either component is infinite. /// * [isFinite], which checks whether both components are finite. // This is included for completeness, because [Size.infinite] exists. static const Offset infinite = Offset(double.infinity, double.infinity); /// Returns a new offset with the x component scaled by `scaleX` and the y /// component scaled by `scaleY`. /// /// If the two scale arguments are the same, consider using the `*` operator /// instead: /// /// ```dart /// Offset a = const Offset(10.0, 10.0); /// Offset b = a * 2.0; // same as: a.scale(2.0, 2.0) /// ``` /// /// If the two arguments are -1, consider using the unary `-` operator /// instead: /// /// ```dart /// Offset a = const Offset(10.0, 10.0); /// Offset b = -a; // same as: a.scale(-1.0, -1.0) /// ``` Offset scale(double scaleX, double scaleY) => Offset(dx * scaleX, dy * scaleY); /// Returns a new offset with translateX added to the x component and /// translateY added to the y component. /// /// If the arguments come from another [Offset], consider using the `+` or `-` /// operators instead: /// /// ```dart /// Offset a = const Offset(10.0, 10.0); /// Offset b = const Offset(10.0, 10.0); /// Offset c = a + b; // same as: a.translate(b.dx, b.dy) /// Offset d = a - b; // same as: a.translate(-b.dx, -b.dy) /// ``` Offset translate(double translateX, double translateY) => Offset(dx + translateX, dy + translateY); /// Unary negation operator. /// /// Returns an offset with the coordinates negated. /// /// If the [Offset] represents an arrow on a plane, this operator returns the /// same arrow but pointing in the reverse direction. Offset operator -() => Offset(-dx, -dy); /// Binary subtraction operator. /// /// Returns an offset whose [dx] value is the left-hand-side operand's [dx] /// minus the right-hand-side operand's [dx] and whose [dy] value is the /// left-hand-side operand's [dy] minus the right-hand-side operand's [dy]. /// /// See also [translate]. Offset operator -(Offset other) => Offset(dx - other.dx, dy - other.dy); /// Binary addition operator. /// /// Returns an offset whose [dx] value is the sum of the [dx] values of the /// two operands, and whose [dy] value is the sum of the [dy] values of the /// two operands. /// /// See also [translate]. Offset operator +(Offset other) => Offset(dx + other.dx, dy + other.dy); /// Multiplication operator. /// /// Returns an offset whose coordinates are the coordinates of the /// left-hand-side operand (an Offset) multiplied by the scalar /// right-hand-side operand (a double). /// /// See also [scale]. Offset operator *(double operand) => Offset(dx * operand, dy * operand); /// Division operator. /// /// Returns an offset whose coordinates are the coordinates of the /// left-hand-side operand (an Offset) divided by the scalar right-hand-side /// operand (a double). /// /// See also [scale]. Offset operator /(double operand) => Offset(dx / operand, dy / operand); /// Integer (truncating) division operator. /// /// Returns an offset whose coordinates are the coordinates of the /// left-hand-side operand (an Offset) divided by the scalar right-hand-side /// operand (a double), rounded towards zero. Offset operator ~/(double operand) => Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); /// Modulo (remainder) operator. /// /// Returns an offset whose coordinates are the remainder of dividing the /// coordinates of the left-hand-side operand (an Offset) by the scalar /// right-hand-side operand (a double). Offset operator %(double operand) => Offset(dx % operand, dy % operand); /// Rectangle constructor operator. /// /// Combines an [Offset] and a [Size] to form a [Rect] whose top-left /// coordinate is the point given by adding this offset, the left-hand-side /// operand, to the origin, and whose size is the right-hand-side operand. /// /// ```dart /// Rect myRect = Offset.zero & const Size(100.0, 100.0); /// // same as: Rect.fromLTWH(0.0, 0.0, 100.0, 100.0) /// ``` Rect operator &(Size other) => Rect.fromLTWH(dx, dy, other.width, other.height); /// Linearly interpolate between two offsets. /// /// If either offset is null, this function interpolates from [Offset.zero]. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Offset? lerp(Offset? a, Offset? b, double t) { if (b == null) { if (a == null) { return null; } else { return a * (1.0 - t); } } else { if (a == null) { return b * t; } else { return Offset(_lerpDouble(a.dx, b.dx, t), _lerpDouble(a.dy, b.dy, t)); } } } /// Compares two Offsets for equality. @override bool operator ==(Object other) { return other is Offset && other.dx == dx && other.dy == dy; } @override int get hashCode => Object.hash(dx, dy); @override String toString() => 'Offset(${dx.toStringAsFixed(1)}, ${dy.toStringAsFixed(1)})'; } /// Holds a 2D floating-point size. /// /// You can think of this as an [Offset] from the origin. class Size extends OffsetBase { /// Creates a [Size] with the given [width] and [height]. const Size(super.width, super.height); /// Creates an instance of [Size] that has the same values as another. // Used by the rendering library's _DebugSize hack. Size.copy(Size source) : super(source.width, source.height); /// Creates a square [Size] whose [width] and [height] are the given dimension. /// /// See also: /// /// * [Size.fromRadius], which is more convenient when the available size /// is the radius of a circle. const Size.square(double dimension) : super(dimension, dimension); /// Creates a [Size] with the given [width] and an infinite [height]. const Size.fromWidth(double width) : super(width, double.infinity); /// Creates a [Size] with the given [height] and an infinite [width]. const Size.fromHeight(double height) : super(double.infinity, height); /// Creates a square [Size] whose [width] and [height] are twice the given /// dimension. /// /// This is a square that contains a circle with the given radius. /// /// See also: /// /// * [Size.square], which creates a square with the given dimension. const Size.fromRadius(double radius) : super(radius * 2.0, radius * 2.0); /// The horizontal extent of this size. double get width => _dx; /// The vertical extent of this size. double get height => _dy; /// The aspect ratio of this size. /// /// This returns the [width] divided by the [height]. /// /// If the [width] is zero, the result will be zero. If the [height] is zero /// (and the [width] is not), the result will be [double.infinity] or /// [double.negativeInfinity] as determined by the sign of [width]. /// /// See also: /// /// * [AspectRatio], a widget for giving a child widget a specific aspect /// ratio. /// * [FittedBox], a widget that (in most modes) attempts to maintain a /// child widget's aspect ratio while changing its size. double get aspectRatio { if (height != 0.0) { return width / height; } if (width > 0.0) { return double.infinity; } if (width < 0.0) { return double.negativeInfinity; } return 0.0; } /// An empty size, one with a zero width and a zero height. static const Size zero = Size(0.0, 0.0); /// A size whose [width] and [height] are infinite. /// /// See also: /// /// * [isInfinite], which checks whether either dimension is infinite. /// * [isFinite], which checks whether both dimensions are finite. static const Size infinite = Size(double.infinity, double.infinity); /// Whether this size encloses a zero area. /// /// Negative areas are considered empty. bool get isEmpty => width <= 0.0 || height <= 0.0; /// Binary subtraction operator for [Size]. /// /// Subtracting a [Size] from a [Size] returns the [Offset] that describes how /// much bigger the left-hand-side operand is than the right-hand-side /// operand. Adding that resulting [Offset] to the [Size] that was the /// right-hand-side operand would return a [Size] equal to the [Size] that was /// the left-hand-side operand. (i.e. if `sizeA - sizeB -> offsetA`, then /// `offsetA + sizeB -> sizeA`) /// /// Subtracting an [Offset] from a [Size] returns the [Size] that is smaller than /// the [Size] operand by the difference given by the [Offset] operand. In other /// words, the returned [Size] has a [width] consisting of the [width] of the /// left-hand-side operand minus the [Offset.dx] dimension of the /// right-hand-side operand, and a [height] consisting of the [height] of the /// left-hand-side operand minus the [Offset.dy] dimension of the /// right-hand-side operand. OffsetBase operator -(OffsetBase other) { if (other is Size) { return Offset(width - other.width, height - other.height); } if (other is Offset) { return Size(width - other.dx, height - other.dy); } throw ArgumentError(other); } /// Binary addition operator for adding an [Offset] to a [Size]. /// /// Returns a [Size] whose [width] is the sum of the [width] of the /// left-hand-side operand, a [Size], and the [Offset.dx] dimension of the /// right-hand-side operand, an [Offset], and whose [height] is the sum of the /// [height] of the left-hand-side operand and the [Offset.dy] dimension of /// the right-hand-side operand. Size operator +(Offset other) => Size(width + other.dx, height + other.dy); /// Multiplication operator. /// /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side /// operand (a [Size]) multiplied by the scalar right-hand-side operand (a /// [double]). Size operator *(double operand) => Size(width * operand, height * operand); /// Division operator. /// /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side /// operand (a [Size]) divided by the scalar right-hand-side operand (a /// [double]). Size operator /(double operand) => Size(width / operand, height / operand); /// Integer (truncating) division operator. /// /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side /// operand (a [Size]) divided by the scalar right-hand-side operand (a /// [double]), rounded towards zero. Size operator ~/(double operand) => Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); /// Modulo (remainder) operator. /// /// Returns a [Size] whose dimensions are the remainder of dividing the /// left-hand-side operand (a [Size]) by the scalar right-hand-side operand (a /// [double]). Size operator %(double operand) => Size(width % operand, height % operand); /// The lesser of the magnitudes of the [width] and the [height]. double get shortestSide => math.min(width.abs(), height.abs()); /// The greater of the magnitudes of the [width] and the [height]. double get longestSide => math.max(width.abs(), height.abs()); // Convenience methods that do the equivalent of calling the similarly named // methods on a Rect constructed from the given origin and this size. /// The offset to the intersection of the top and left edges of the rectangle /// described by the given [Offset] (which is interpreted as the top-left corner) /// and this [Size]. /// /// See also [Rect.topLeft]. Offset topLeft(Offset origin) => origin; /// The offset to the center of the top edge of the rectangle described by the /// given offset (which is interpreted as the top-left corner) and this size. /// /// See also [Rect.topCenter]. Offset topCenter(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy); /// The offset to the intersection of the top and right edges of the rectangle /// described by the given offset (which is interpreted as the top-left corner) /// and this size. /// /// See also [Rect.topRight]. Offset topRight(Offset origin) => Offset(origin.dx + width, origin.dy); /// The offset to the center of the left edge of the rectangle described by the /// given offset (which is interpreted as the top-left corner) and this size. /// /// See also [Rect.centerLeft]. Offset centerLeft(Offset origin) => Offset(origin.dx, origin.dy + height / 2.0); /// The offset to the point halfway between the left and right and the top and /// bottom edges of the rectangle described by the given offset (which is /// interpreted as the top-left corner) and this size. /// /// See also [Rect.center]. Offset center(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy + height / 2.0); /// The offset to the center of the right edge of the rectangle described by the /// given offset (which is interpreted as the top-left corner) and this size. /// /// See also [Rect.centerRight]. Offset centerRight(Offset origin) => Offset(origin.dx + width, origin.dy + height / 2.0); /// The offset to the intersection of the bottom and left edges of the /// rectangle described by the given offset (which is interpreted as the /// top-left corner) and this size. /// /// See also [Rect.bottomLeft]. Offset bottomLeft(Offset origin) => Offset(origin.dx, origin.dy + height); /// The offset to the center of the bottom edge of the rectangle described by /// the given offset (which is interpreted as the top-left corner) and this /// size. /// /// See also [Rect.bottomCenter]. Offset bottomCenter(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy + height); /// The offset to the intersection of the bottom and right edges of the /// rectangle described by the given offset (which is interpreted as the /// top-left corner) and this size. /// /// See also [Rect.bottomRight]. Offset bottomRight(Offset origin) => Offset(origin.dx + width, origin.dy + height); /// Whether the point specified by the given offset (which is assumed to be /// relative to the top left of the size) lies between the left and right and /// the top and bottom edges of a rectangle of this size. /// /// Rectangles include their top and left edges but exclude their bottom and /// right edges. bool contains(Offset offset) { return offset.dx >= 0.0 && offset.dx < width && offset.dy >= 0.0 && offset.dy < height; } /// A [Size] with the [width] and [height] swapped. Size get flipped => Size(height, width); /// Linearly interpolate between two sizes /// /// If either size is null, this function interpolates from [Size.zero]. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Size? lerp(Size? a, Size? b, double t) { if (b == null) { if (a == null) { return null; } else { return a * (1.0 - t); } } else { if (a == null) { return b * t; } else { return Size(_lerpDouble(a.width, b.width, t), _lerpDouble(a.height, b.height, t)); } } } /// Compares two Sizes for equality. // We don't compare the runtimeType because of _DebugSize in the framework. @override bool operator ==(Object other) { return other is Size && other._dx == _dx && other._dy == _dy; } @override int get hashCode => Object.hash(_dx, _dy); @override String toString() => 'Size(${width.toStringAsFixed(1)}, ${height.toStringAsFixed(1)})'; } /// An immutable, 2D, axis-aligned, floating-point rectangle whose coordinates /// are relative to a given origin. /// /// A Rect can be created with one of its constructors or from an [Offset] and a /// [Size] using the `&` operator: /// /// ```dart /// Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0); /// ``` class Rect { /// Construct a rectangle from its left, top, right, and bottom edges. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltrb.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltrb_dark.png#gh-dark-mode-only) const Rect.fromLTRB(this.left, this.top, this.right, this.bottom); /// Construct a rectangle from its left and top edges, its width, and its /// height. /// /// To construct a [Rect] from an [Offset] and a [Size], you can use the /// rectangle constructor operator `&`. See [Offset.&]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltwh.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltwh_dark.png#gh-dark-mode-only) const Rect.fromLTWH(double left, double top, double width, double height) : this.fromLTRB(left, top, left + width, top + height); /// Construct a rectangle that bounds the given circle. /// /// The `center` argument is assumed to be an offset from the origin. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_circle.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_circle_dark.png#gh-dark-mode-only) Rect.fromCircle({required Offset center, required double radius}) : this.fromCenter(center: center, width: radius * 2, height: radius * 2); /// Constructs a rectangle from its center point, width, and height. /// /// The `center` argument is assumed to be an offset from the origin. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_center.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_center_dark.png#gh-dark-mode-only) Rect.fromCenter({required Offset center, required double width, required double height}) : this.fromLTRB( center.dx - width / 2, center.dy - height / 2, center.dx + width / 2, center.dy + height / 2, ); /// Construct the smallest rectangle that encloses the given offsets, treating /// them as vectors from the origin. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_points.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_points_dark.png#gh-dark-mode-only) Rect.fromPoints(Offset a, Offset b) : this.fromLTRB( math.min(a.dx, b.dx), math.min(a.dy, b.dy), math.max(a.dx, b.dx), math.max(a.dy, b.dy), ); Float32List _getValue32() { final result = Float32List(4); result[0] = left; result[1] = top; result[2] = right; result[3] = bottom; return result; } /// The offset of the left edge of this rectangle from the x axis. final double left; /// The offset of the top edge of this rectangle from the y axis. final double top; /// The offset of the right edge of this rectangle from the x axis. final double right; /// The offset of the bottom edge of this rectangle from the y axis. final double bottom; /// The distance between the left and right edges of this rectangle. double get width => right - left; /// The distance between the top and bottom edges of this rectangle. double get height => bottom - top; /// The distance between the upper-left corner and the lower-right corner of /// this rectangle. Size get size => Size(width, height); /// Whether any of the dimensions are `NaN`. bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN; /// A rectangle with left, top, right, and bottom edges all at zero. static const Rect zero = Rect.fromLTRB(0.0, 0.0, 0.0, 0.0); static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h /// A rectangle that covers the entire coordinate space. /// /// This covers the space from -1e9,-1e9 to 1e9,1e9. /// This is the space over which graphics operations are valid. static const Rect largest = Rect.fromLTRB( -_giantScalar, -_giantScalar, _giantScalar, _giantScalar, ); /// Whether any of the coordinates of this rectangle are equal to positive infinity. // included for consistency with Offset and Size bool get isInfinite { return left >= double.infinity || top >= double.infinity || right >= double.infinity || bottom >= double.infinity; } /// Whether all coordinates of this rectangle are finite. bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; /// Whether this rectangle encloses a non-zero area. Negative areas are /// considered empty. bool get isEmpty => left >= right || top >= bottom; /// Returns a new rectangle translated by the given offset. /// /// To translate a rectangle by separate x and y components rather than by an /// [Offset], consider [translate]. Rect shift(Offset offset) { return Rect.fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy); } /// Returns a new rectangle with translateX added to the x components and /// translateY added to the y components. /// /// To translate a rectangle by an [Offset] rather than by separate x and y /// components, consider [shift]. Rect translate(double translateX, double translateY) { return Rect.fromLTRB( left + translateX, top + translateY, right + translateX, bottom + translateY, ); } /// Returns a new rectangle with edges moved outwards by the given delta. Rect inflate(double delta) { return Rect.fromLTRB(left - delta, top - delta, right + delta, bottom + delta); } /// Returns a new rectangle with edges moved inwards by the given delta. Rect deflate(double delta) => inflate(-delta); /// Returns a new rectangle that is the intersection of the given /// rectangle and this rectangle. The two rectangles must overlap /// for this to be meaningful. If the two rectangles do not overlap, /// then the resulting Rect will have a negative width or height. Rect intersect(Rect other) { return Rect.fromLTRB( math.max(left, other.left), math.max(top, other.top), math.min(right, other.right), math.min(bottom, other.bottom), ); } /// Returns a new rectangle which is the bounding box containing this /// rectangle and the given rectangle. Rect expandToInclude(Rect other) { return Rect.fromLTRB( math.min(left, other.left), math.min(top, other.top), math.max(right, other.right), math.max(bottom, other.bottom), ); } /// Whether `other` has a nonzero area of overlap with this rectangle. bool overlaps(Rect other) { if (right <= other.left || other.right <= left) { return false; } if (bottom <= other.top || other.bottom <= top) { return false; } return true; } /// The lesser of the magnitudes of the [width] and the [height] of this /// rectangle. double get shortestSide => math.min(width.abs(), height.abs()); /// The greater of the magnitudes of the [width] and the [height] of this /// rectangle. double get longestSide => math.max(width.abs(), height.abs()); /// The offset to the intersection of the top and left edges of this rectangle. /// /// See also [Size.topLeft]. Offset get topLeft => Offset(left, top); /// The offset to the center of the top edge of this rectangle. /// /// See also [Size.topCenter]. Offset get topCenter => Offset(left + width / 2.0, top); /// The offset to the intersection of the top and right edges of this rectangle. /// /// See also [Size.topRight]. Offset get topRight => Offset(right, top); /// The offset to the center of the left edge of this rectangle. /// /// See also [Size.centerLeft]. Offset get centerLeft => Offset(left, top + height / 2.0); /// The offset to the point halfway between the left and right and the top and /// bottom edges of this rectangle. /// /// See also [Size.center]. Offset get center => Offset(left + width / 2.0, top + height / 2.0); /// The offset to the center of the right edge of this rectangle. /// /// See also [Size.centerRight]. Offset get centerRight => Offset(right, top + height / 2.0); /// The offset to the intersection of the bottom and left edges of this rectangle. /// /// See also [Size.bottomLeft]. Offset get bottomLeft => Offset(left, bottom); /// The offset to the center of the bottom edge of this rectangle. /// /// See also [Size.bottomCenter]. Offset get bottomCenter => Offset(left + width / 2.0, bottom); /// The offset to the intersection of the bottom and right edges of this rectangle. /// /// See also [Size.bottomRight]. Offset get bottomRight => Offset(right, bottom); /// Whether the point specified by the given offset (which is assumed to be /// relative to the origin) lies between the left and right and the top and /// bottom edges of this rectangle. /// /// Rectangles include their top and left edges but exclude their bottom and /// right edges. bool contains(Offset offset) { return offset.dx >= left && offset.dx < right && offset.dy >= top && offset.dy < bottom; } /// Linearly interpolate between two rectangles. /// /// If either rect is null, [Rect.zero] is used as a substitute. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Rect? lerp(Rect? a, Rect? b, double t) { if (b == null) { if (a == null) { return null; } else { final double k = 1.0 - t; return Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k); } } else { if (a == null) { return Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t); } else { return Rect.fromLTRB( _lerpDouble(a.left, b.left, t), _lerpDouble(a.top, b.top, t), _lerpDouble(a.right, b.right, t), _lerpDouble(a.bottom, b.bottom, t), ); } } } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (runtimeType != other.runtimeType) { return false; } return other is Rect && other.left == left && other.top == top && other.right == right && other.bottom == bottom; } @override int get hashCode => Object.hash(left, top, right, bottom); @override String toString() => 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; } /// A radius for either circular or elliptical shapes. class Radius { /// Constructs a circular radius. [x] and [y] will have the same radius value. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/radius_circular.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/radius_circular_dark.png#gh-dark-mode-only) const Radius.circular(double radius) : this.elliptical(radius, radius); /// Constructs an elliptical radius with the given radii. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/radius_elliptical.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/radius_elliptical_dark.png#gh-dark-mode-only) const Radius.elliptical(this.x, this.y); /// The radius value on the horizontal axis. final double x; /// The radius value on the vertical axis. final double y; /// A radius with [x] and [y] values set to zero. /// /// You can use [Radius.zero] with [RRect] or [RSuperellipse] to have /// right-angle corners. static const Radius zero = Radius.circular(0.0); /// Returns this [Radius], with values clamped to the given min and max /// [Radius] values. /// /// The `min` value defaults to `Radius.circular(-double.infinity)`, and /// the `max` value defaults to `Radius.circular(double.infinity)`. Radius clamp({Radius? minimum, Radius? maximum}) { minimum ??= const Radius.circular(-double.infinity); maximum ??= const Radius.circular(double.infinity); return Radius.elliptical( clampDouble(x, minimum.x, maximum.x), clampDouble(y, minimum.y, maximum.y), ); } /// Returns this [Radius], with values clamped to the given min and max /// values in each dimension /// /// The `minimumX` and `minimumY` values default to `-double.infinity`, and /// the `maximumX` and `maximumY` values default to `double.infinity`. Radius clampValues({double? minimumX, double? minimumY, double? maximumX, double? maximumY}) { return Radius.elliptical( clampDouble(x, minimumX ?? -double.infinity, maximumX ?? double.infinity), clampDouble(y, minimumY ?? -double.infinity, maximumY ?? double.infinity), ); } /// Unary negation operator. /// /// Returns a Radius with the distances negated. /// /// Radiuses with negative values aren't geometrically meaningful, but could /// occur as part of expressions. For example, negating a radius of one pixel /// and then adding the result to another radius is equivalent to subtracting /// a radius of one pixel from the other. Radius operator -() => Radius.elliptical(-x, -y); /// Binary subtraction operator. /// /// Returns a radius whose [x] value is the left-hand-side operand's [x] /// minus the right-hand-side operand's [x] and whose [y] value is the /// left-hand-side operand's [y] minus the right-hand-side operand's [y]. Radius operator -(Radius other) => Radius.elliptical(x - other.x, y - other.y); /// Binary addition operator. /// /// Returns a radius whose [x] value is the sum of the [x] values of the /// two operands, and whose [y] value is the sum of the [y] values of the /// two operands. Radius operator +(Radius other) => Radius.elliptical(x + other.x, y + other.y); /// Multiplication operator. /// /// Returns a radius whose coordinates are the coordinates of the /// left-hand-side operand (a radius) multiplied by the scalar /// right-hand-side operand (a double). Radius operator *(double operand) => Radius.elliptical(x * operand, y * operand); /// Division operator. /// /// Returns a radius whose coordinates are the coordinates of the /// left-hand-side operand (a radius) divided by the scalar right-hand-side /// operand (a double). Radius operator /(double operand) => Radius.elliptical(x / operand, y / operand); /// Integer (truncating) division operator. /// /// Returns a radius whose coordinates are the coordinates of the /// left-hand-side operand (a radius) divided by the scalar right-hand-side /// operand (a double), rounded towards zero. Radius operator ~/(double operand) => Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); /// Modulo (remainder) operator. /// /// Returns a radius whose coordinates are the remainder of dividing the /// coordinates of the left-hand-side operand (a radius) by the scalar /// right-hand-side operand (a double). Radius operator %(double operand) => Radius.elliptical(x % operand, y % operand); /// Linearly interpolate between two radii. /// /// If either is null, this function substitutes [Radius.zero] instead. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Radius? lerp(Radius? a, Radius? b, double t) { if (b == null) { if (a == null) { return null; } else { final double k = 1.0 - t; return Radius.elliptical(a.x * k, a.y * k); } } else { if (a == null) { return Radius.elliptical(b.x * t, b.y * t); } else { return Radius.elliptical(_lerpDouble(a.x, b.x, t), _lerpDouble(a.y, b.y, t)); } } } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (runtimeType != other.runtimeType) { return false; } return other is Radius && other.x == x && other.y == y; } @override int get hashCode => Object.hash(x, y); @override String toString() { return x == y ? 'Radius.circular(${x.toStringAsFixed(1)})' : 'Radius.elliptical(${x.toStringAsFixed(1)}, ' '${y.toStringAsFixed(1)})'; } } // The common base class for `RRect` and `RSuperellipse`. abstract class _RRectLike> { const _RRectLike({ required this.left, required this.top, required this.right, required this.bottom, required this.tlRadiusX, required this.tlRadiusY, required this.trRadiusX, required this.trRadiusY, required this.brRadiusX, required this.brRadiusY, required this.blRadiusX, required this.blRadiusY, }) : assert(tlRadiusX >= 0), assert(tlRadiusY >= 0), assert(trRadiusX >= 0), assert(trRadiusY >= 0), assert(brRadiusX >= 0), assert(brRadiusY >= 0), assert(blRadiusX >= 0), assert(blRadiusY >= 0); // Implemented by a subclass to return an object constructed with the given // parameters. // // Used by various methods that construct an object of the same shape. T _create({ required double left, required double top, required double right, required double bottom, required double tlRadiusX, required double tlRadiusY, required double trRadiusX, required double trRadiusY, required double brRadiusX, required double brRadiusY, required double blRadiusX, required double blRadiusY, }); /// The offset of the left edge of this rectangle from the x axis. final double left; /// The offset of the top edge of this rectangle from the y axis. final double top; /// The offset of the right edge of this rectangle from the x axis. final double right; /// The offset of the bottom edge of this rectangle from the y axis. final double bottom; /// The top-left horizontal radius. final double tlRadiusX; /// The top-left vertical radius. final double tlRadiusY; /// The top-left [Radius]. Radius get tlRadius => Radius.elliptical(tlRadiusX, tlRadiusY); /// The top-right horizontal radius. final double trRadiusX; /// The top-right vertical radius. final double trRadiusY; /// The top-right [Radius]. Radius get trRadius => Radius.elliptical(trRadiusX, trRadiusY); /// The bottom-right horizontal radius. final double brRadiusX; /// The bottom-right vertical radius. final double brRadiusY; /// The bottom-right [Radius]. Radius get brRadius => Radius.elliptical(brRadiusX, brRadiusY); /// The bottom-left horizontal radius. final double blRadiusX; /// The bottom-left vertical radius. final double blRadiusY; /// The bottom-left [Radius]. Radius get blRadius => Radius.elliptical(blRadiusX, blRadiusY); /// Returns a clone translated by the given offset. T shift(Offset offset) { return _create( left: left + offset.dx, top: top + offset.dy, right: right + offset.dx, bottom: bottom + offset.dy, tlRadiusX: tlRadiusX, tlRadiusY: tlRadiusY, trRadiusX: trRadiusX, trRadiusY: trRadiusY, blRadiusX: blRadiusX, blRadiusY: blRadiusY, brRadiusX: brRadiusX, brRadiusY: brRadiusY, ); } /// Returns a clone with edges and radii moved outwards by the given /// delta. T inflate(double delta) { return _create( left: left - delta, top: top - delta, right: right + delta, bottom: bottom + delta, tlRadiusX: math.max(0, tlRadiusX + delta), tlRadiusY: math.max(0, tlRadiusY + delta), trRadiusX: math.max(0, trRadiusX + delta), trRadiusY: math.max(0, trRadiusY + delta), blRadiusX: math.max(0, blRadiusX + delta), blRadiusY: math.max(0, blRadiusY + delta), brRadiusX: math.max(0, brRadiusX + delta), brRadiusY: math.max(0, brRadiusY + delta), ); } /// Returns a clone with edges and radii moved inwards by the given delta. T deflate(double delta) => inflate(-delta); /// The distance between the left and right edges of this rectangle. double get width => right - left; /// The distance between the top and bottom edges of this rectangle. double get height => bottom - top; /// The bounding box of this rounded rectangle (the rectangle with no rounded corners). Rect get outerRect => Rect.fromLTRB(left, top, right, bottom); /// The non-rounded rectangle that is constrained by the smaller of the two /// diagonals, with each diagonal traveling through the middle of the curve /// corners. The middle of a corner is the intersection of the curve with its /// respective quadrant bisector. Rect get safeInnerRect { const kInsetFactor = 0.29289321881; // 1-cos(pi/4) final double leftRadius = math.max(blRadiusX, tlRadiusX); final double topRadius = math.max(tlRadiusY, trRadiusY); final double rightRadius = math.max(trRadiusX, brRadiusX); final double bottomRadius = math.max(brRadiusY, blRadiusY); return Rect.fromLTRB( left + leftRadius * kInsetFactor, top + topRadius * kInsetFactor, right - rightRadius * kInsetFactor, bottom - bottomRadius * kInsetFactor, ); } /// The rectangle that would be formed using the axis-aligned intersection of /// the sides of the rectangle, i.e., the rectangle formed from the /// inner-most centers of the ellipses that form the corners. This is the /// intersection of the [wideMiddleRect] and the [tallMiddleRect]. If any of /// the intersections are void, the resulting [Rect] will have negative width /// or height. Rect get middleRect { final double leftRadius = math.max(blRadiusX, tlRadiusX); final double topRadius = math.max(tlRadiusY, trRadiusY); final double rightRadius = math.max(trRadiusX, brRadiusX); final double bottomRadius = math.max(brRadiusY, blRadiusY); return Rect.fromLTRB( left + leftRadius, top + topRadius, right - rightRadius, bottom - bottomRadius, ); } /// The biggest rectangle that is entirely inside the rounded rectangle and /// has the full width of the rounded rectangle. If the rounded rectangle does /// not have an axis-aligned intersection of its left and right side, the /// resulting [Rect] will have negative width or height. Rect get wideMiddleRect { final double topRadius = math.max(tlRadiusY, trRadiusY); final double bottomRadius = math.max(brRadiusY, blRadiusY); return Rect.fromLTRB(left, top + topRadius, right, bottom - bottomRadius); } /// The biggest rectangle that is entirely inside the rounded rectangle and /// has the full height of the rounded rectangle. If the rounded rectangle /// does not have an axis-aligned intersection of its top and bottom side, the /// resulting [Rect] will have negative width or height. Rect get tallMiddleRect { final double leftRadius = math.max(blRadiusX, tlRadiusX); final double rightRadius = math.max(trRadiusX, brRadiusX); return Rect.fromLTRB(left + leftRadius, top, right - rightRadius, bottom); } /// Whether this rounded rectangle encloses a non-zero area. /// Negative areas are considered empty. bool get isEmpty => left >= right || top >= bottom; /// Whether all coordinates of this rounded rectangle are finite. bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; /// Whether this rounded rectangle is a simple rectangle with zero /// corner radii. bool get isRect { return (tlRadiusX == 0.0 || tlRadiusY == 0.0) && (trRadiusX == 0.0 || trRadiusY == 0.0) && (blRadiusX == 0.0 || blRadiusY == 0.0) && (brRadiusX == 0.0 || brRadiusY == 0.0); } /// Whether this rounded rectangle has a side with no straight section. bool get isStadium { return tlRadius == trRadius && trRadius == brRadius && brRadius == blRadius && (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); } /// Whether this rounded rectangle has no side with a straight section. bool get isEllipse { return tlRadius == trRadius && trRadius == brRadius && brRadius == blRadius && width <= 2.0 * tlRadiusX && height <= 2.0 * tlRadiusY; } /// Whether this rounded rectangle would draw as a circle. bool get isCircle => width == height && isEllipse; /// The lesser of the magnitudes of the [width] and the [height] of this /// rounded rectangle. double get shortestSide => math.min(width.abs(), height.abs()); /// The greater of the magnitudes of the [width] and the [height] of this /// rounded rectangle. double get longestSide => math.max(width.abs(), height.abs()); /// Whether any of the dimensions are `NaN`. bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN || trRadiusX.isNaN || trRadiusY.isNaN || tlRadiusX.isNaN || tlRadiusY.isNaN || brRadiusX.isNaN || brRadiusY.isNaN || blRadiusX.isNaN || blRadiusY.isNaN; /// The offset to the point halfway between the left and right and the top and /// bottom edges of this rectangle. Offset get center => Offset(left + width / 2.0, top + height / 2.0); // Returns the minimum between min and scale to which radius1 and radius2 // should be scaled with in order not to exceed the limit. double _getMin(double min, double radius1, double radius2, double limit) { final double sum = radius1 + radius2; if (sum > limit && sum != 0.0) { return math.min(min, limit / sum); } return min; } /// Scales all radii so that on each side their sum will not exceed the size /// of the width/height. /// /// Skia already handles RRects with radii that are too large in this way. /// Therefore, this method is only needed for use cases of [RRect] or /// [RSuperellipse] that require the appropriately scaled radii values. /// /// See the [Skia scaling implementation](https://github.com/google/skia/blob/main/src/core/SkRRect.cpp) /// for more details. T scaleRadii() { var scale = 1.0; scale = _getMin(scale, blRadiusY, tlRadiusY, height); scale = _getMin(scale, tlRadiusX, trRadiusX, width); scale = _getMin(scale, trRadiusY, brRadiusY, height); scale = _getMin(scale, brRadiusX, blRadiusX, width); assert(scale >= 0); if (scale < 1.0) { return _create( top: top, left: left, right: right, bottom: bottom, tlRadiusX: tlRadiusX * scale, tlRadiusY: tlRadiusY * scale, trRadiusX: trRadiusX * scale, trRadiusY: trRadiusY * scale, blRadiusX: blRadiusX * scale, blRadiusY: blRadiusY * scale, brRadiusX: brRadiusX * scale, brRadiusY: brRadiusY * scale, ); } return _create( top: top, left: left, right: right, bottom: bottom, tlRadiusX: tlRadiusX, tlRadiusY: tlRadiusY, trRadiusX: trRadiusX, trRadiusY: trRadiusY, blRadiusX: blRadiusX, blRadiusY: blRadiusY, brRadiusX: brRadiusX, brRadiusY: brRadiusY, ); } // Linearly interpolate between this object and another of the same shape. T _lerpTo(T? b, double t) { assert(runtimeType == T); if (b == null) { final double k = 1.0 - t; return _create( left: left * k, top: top * k, right: right * k, bottom: bottom * k, tlRadiusX: math.max(0, tlRadiusX * k), tlRadiusY: math.max(0, tlRadiusY * k), trRadiusX: math.max(0, trRadiusX * k), trRadiusY: math.max(0, trRadiusY * k), brRadiusX: math.max(0, brRadiusX * k), brRadiusY: math.max(0, brRadiusY * k), blRadiusX: math.max(0, blRadiusX * k), blRadiusY: math.max(0, blRadiusY * k), ); } else { return _create( left: _lerpDouble(left, b.left, t), top: _lerpDouble(top, b.top, t), right: _lerpDouble(right, b.right, t), bottom: _lerpDouble(bottom, b.bottom, t), tlRadiusX: math.max(0, _lerpDouble(tlRadiusX, b.tlRadiusX, t)), tlRadiusY: math.max(0, _lerpDouble(tlRadiusY, b.tlRadiusY, t)), trRadiusX: math.max(0, _lerpDouble(trRadiusX, b.trRadiusX, t)), trRadiusY: math.max(0, _lerpDouble(trRadiusY, b.trRadiusY, t)), brRadiusX: math.max(0, _lerpDouble(brRadiusX, b.brRadiusX, t)), brRadiusY: math.max(0, _lerpDouble(brRadiusY, b.brRadiusY, t)), blRadiusX: math.max(0, _lerpDouble(blRadiusX, b.blRadiusX, t)), blRadiusY: math.max(0, _lerpDouble(blRadiusY, b.blRadiusY, t)), ); } } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (runtimeType != other.runtimeType) { return false; } return other is _RRectLike && other.left == left && other.top == top && other.right == right && other.bottom == bottom && other.tlRadiusX == tlRadiusX && other.tlRadiusY == tlRadiusY && other.trRadiusX == trRadiusX && other.trRadiusY == trRadiusY && other.blRadiusX == blRadiusX && other.blRadiusY == blRadiusY && other.brRadiusX == brRadiusX && other.brRadiusY == brRadiusY; } @override int get hashCode => Object.hash( left, top, right, bottom, tlRadiusX, tlRadiusY, trRadiusX, trRadiusY, blRadiusX, blRadiusY, brRadiusX, brRadiusY, ); String _toString({required String className}) { final rect = '${left.toStringAsFixed(1)}, ' '${top.toStringAsFixed(1)}, ' '${right.toStringAsFixed(1)}, ' '${bottom.toStringAsFixed(1)}'; if (tlRadius == trRadius && trRadius == brRadius && brRadius == blRadius) { if (tlRadius.x == tlRadius.y) { return '$className.fromLTRBR($rect, ${tlRadius.x.toStringAsFixed(1)})'; } return '$className.fromLTRBXY($rect, ${tlRadius.x.toStringAsFixed(1)}, ${tlRadius.y.toStringAsFixed(1)})'; } return '$className.fromLTRBAndCorners(' '$rect, ' 'topLeft: $tlRadius, ' 'topRight: $trRadius, ' 'bottomRight: $brRadius, ' 'bottomLeft: $blRadius' ')'; } } /// An immutable rounded rectangle with the custom radii for all four corners. class RRect extends _RRectLike { /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and the same radii along its horizontal axis and its vertical axis. /// /// Will assert in debug mode if `radiusX` or `radiusY` are negative. const RRect.fromLTRBXY( double left, double top, double right, double bottom, double radiusX, double radiusY, ) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: radiusX, tlRadiusY: radiusY, trRadiusX: radiusX, trRadiusY: radiusY, blRadiusX: radiusX, blRadiusY: radiusY, brRadiusX: radiusX, brRadiusY: radiusY, ); /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and the same radius in each corner. /// /// Will assert in debug mode if the `radius` is negative in either x or y. RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: radius.x, tlRadiusY: radius.y, trRadiusX: radius.x, trRadiusY: radius.y, blRadiusX: radius.x, blRadiusY: radius.y, brRadiusX: radius.x, brRadiusY: radius.y, ); /// Construct a rounded rectangle from its bounding box and the same radii /// along its horizontal axis and its vertical axis. /// /// Will assert in debug mode if `radiusX` or `radiusY` are negative. RRect.fromRectXY(Rect rect, double radiusX, double radiusY) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: radiusX, tlRadiusY: radiusY, trRadiusX: radiusX, trRadiusY: radiusY, blRadiusX: radiusX, blRadiusY: radiusY, brRadiusX: radiusX, brRadiusY: radiusY, ); /// Construct a rounded rectangle from its bounding box and a radius that is /// the same in each corner. /// /// Will assert in debug mode if the `radius` is negative in either x or y. RRect.fromRectAndRadius(Rect rect, Radius radius) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: radius.x, tlRadiusY: radius.y, trRadiusX: radius.x, trRadiusY: radius.y, blRadiusX: radius.x, blRadiusY: radius.y, brRadiusX: radius.x, brRadiusY: radius.y, ); /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and topLeft, topRight, bottomRight, and bottomLeft radii. /// /// The corner radii default to [Radius.zero], i.e. right-angled corners. Will /// assert in debug mode if any of the radii are negative in either x or y. RRect.fromLTRBAndCorners( double left, double top, double right, double bottom, { Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: topLeft.x, tlRadiusY: topLeft.y, trRadiusX: topRight.x, trRadiusY: topRight.y, blRadiusX: bottomLeft.x, blRadiusY: bottomLeft.y, brRadiusX: bottomRight.x, brRadiusY: bottomRight.y, ); /// Construct a rounded rectangle from its bounding box and topLeft, /// topRight, bottomRight, and bottomLeft radii. /// /// The corner radii default to [Radius.zero], i.e. right-angled corners. Will /// assert in debug mode if any of the radii are negative in either x or y. RRect.fromRectAndCorners( Rect rect, { Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: topLeft.x, tlRadiusY: topLeft.y, trRadiusX: topRight.x, trRadiusY: topRight.y, blRadiusX: bottomLeft.x, blRadiusY: bottomLeft.y, brRadiusX: bottomRight.x, brRadiusY: bottomRight.y, ); const RRect._raw({ super.left = 0.0, super.top = 0.0, super.right = 0.0, super.bottom = 0.0, super.tlRadiusX = 0.0, super.tlRadiusY = 0.0, super.trRadiusX = 0.0, super.trRadiusY = 0.0, super.brRadiusX = 0.0, super.brRadiusY = 0.0, super.blRadiusX = 0.0, super.blRadiusY = 0.0, }); @override RRect _create({ required double left, required double top, required double right, required double bottom, required double tlRadiusX, required double tlRadiusY, required double trRadiusX, required double trRadiusY, required double brRadiusX, required double brRadiusY, required double blRadiusX, required double blRadiusY, }) => RRect._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: tlRadiusX, tlRadiusY: tlRadiusY, trRadiusX: trRadiusX, trRadiusY: trRadiusY, blRadiusX: blRadiusX, blRadiusY: blRadiusY, brRadiusX: brRadiusX, brRadiusY: brRadiusY, ); Float32List _getValue32() { final result = Float32List(12); result[0] = left; result[1] = top; result[2] = right; result[3] = bottom; result[4] = tlRadiusX; result[5] = tlRadiusY; result[6] = trRadiusX; result[7] = trRadiusY; result[8] = brRadiusX; result[9] = brRadiusY; result[10] = blRadiusX; result[11] = blRadiusY; return result; } /// A rounded rectangle with all the values set to zero. static const RRect zero = RRect._raw(); /// Whether the point specified by the given offset (which is assumed to be /// relative to the origin) lies inside the rounded rectangle. /// /// This method may allocate (and cache) a copy of the object with normalized /// radii the first time it is called on a particular [RRect] instance. When /// using this method, prefer to reuse existing [RRect]s rather than /// recreating the object each time. bool contains(Offset point) { if (point.dx < left || point.dx >= right || point.dy < top || point.dy >= bottom) { return false; } // outside bounding box final RRect scaled = scaleRadii(); double x; double y; double radiusX; double radiusY; // check whether point is in one of the rounded corner areas // x, y -> translate to ellipse center if (point.dx < left + scaled.tlRadiusX && point.dy < top + scaled.tlRadiusY) { x = point.dx - left - scaled.tlRadiusX; y = point.dy - top - scaled.tlRadiusY; radiusX = scaled.tlRadiusX; radiusY = scaled.tlRadiusY; } else if (point.dx > right - scaled.trRadiusX && point.dy < top + scaled.trRadiusY) { x = point.dx - right + scaled.trRadiusX; y = point.dy - top - scaled.trRadiusY; radiusX = scaled.trRadiusX; radiusY = scaled.trRadiusY; } else if (point.dx > right - scaled.brRadiusX && point.dy > bottom - scaled.brRadiusY) { x = point.dx - right + scaled.brRadiusX; y = point.dy - bottom + scaled.brRadiusY; radiusX = scaled.brRadiusX; radiusY = scaled.brRadiusY; } else if (point.dx < left + scaled.blRadiusX && point.dy > bottom - scaled.blRadiusY) { x = point.dx - left - scaled.blRadiusX; y = point.dy - bottom + scaled.blRadiusY; radiusX = scaled.blRadiusX; radiusY = scaled.blRadiusY; } else { return true; // inside and not within the rounded corner area } x = x / radiusX; y = y / radiusY; // check if the point is outside the unit circle if (x * x + y * y > 1.0) { return false; } return true; } /// Linearly interpolate between two rounded rectangles. /// /// If either is null, this function substitutes [RRect.zero] instead. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static RRect? lerp(RRect? a, RRect? b, double t) { if (a == null) { if (b == null) { return null; } return b._lerpTo(null, 1 - t); } return a._lerpTo(b, t); } @override String toString() { return _toString(className: 'RRect'); } } /// An immutable rounded superellipse. /// /// A rounded superellipse (not to be confused with a standard superellipse) is /// a shape formed by replacing the four curved corners of a superellipse with /// circular arcs. A (standard) superellipse follows the formula x^n + y^n = /// a^n, and while n > 2 gives it rounded corners, they tend to be too sharp and /// pronounced. Replacing them with circular arcs makes the shape feel softer /// and more natural. /// /// Visually, a rounded superellipse looks similar to a typical rounded rectangle /// ([RRect]) but with smoother transitions between the straight edges and /// corners. It closely matches the `RoundedRectangle` shape in SwiftUI with the /// `.continuous` corner style. class RSuperellipse extends _RRectLike { /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and the same radii along its horizontal axis and its vertical axis. /// /// Will assert in debug mode if `radiusX` or `radiusY` are negative. const RSuperellipse.fromLTRBXY( double left, double top, double right, double bottom, double radiusX, double radiusY, ) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: radiusX, tlRadiusY: radiusY, trRadiusX: radiusX, trRadiusY: radiusY, blRadiusX: radiusX, blRadiusY: radiusY, brRadiusX: radiusX, brRadiusY: radiusY, ); /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and the same radius in each corner. /// /// Will assert in debug mode if the `radius` is negative in either x or y. RSuperellipse.fromLTRBR(double left, double top, double right, double bottom, Radius radius) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: radius.x, tlRadiusY: radius.y, trRadiusX: radius.x, trRadiusY: radius.y, blRadiusX: radius.x, blRadiusY: radius.y, brRadiusX: radius.x, brRadiusY: radius.y, ); /// Construct a rounded rectangle from its bounding box and the same radii /// along its horizontal axis and its vertical axis. /// /// Will assert in debug mode if `radiusX` or `radiusY` are negative. RSuperellipse.fromRectXY(Rect rect, double radiusX, double radiusY) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: radiusX, tlRadiusY: radiusY, trRadiusX: radiusX, trRadiusY: radiusY, blRadiusX: radiusX, blRadiusY: radiusY, brRadiusX: radiusX, brRadiusY: radiusY, ); /// Construct a rounded rectangle from its bounding box and a radius that is /// the same in each corner. /// /// Will assert in debug mode if the `radius` is negative in either x or y. RSuperellipse.fromRectAndRadius(Rect rect, Radius radius) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: radius.x, tlRadiusY: radius.y, trRadiusX: radius.x, trRadiusY: radius.y, blRadiusX: radius.x, blRadiusY: radius.y, brRadiusX: radius.x, brRadiusY: radius.y, ); /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and topLeft, topRight, bottomRight, and bottomLeft radii. /// /// The corner radii default to [Radius.zero], i.e. right-angled corners. Will /// assert in debug mode if any of the radii are negative in either x or y. RSuperellipse.fromLTRBAndCorners( double left, double top, double right, double bottom, { Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: topLeft.x, tlRadiusY: topLeft.y, trRadiusX: topRight.x, trRadiusY: topRight.y, blRadiusX: bottomLeft.x, blRadiusY: bottomLeft.y, brRadiusX: bottomRight.x, brRadiusY: bottomRight.y, ); /// Construct a rounded rectangle from its bounding box and topLeft, /// topRight, bottomRight, and bottomLeft radii. /// /// The corner radii default to [Radius.zero], i.e. right-angled corners. Will /// assert in debug mode if any of the radii are negative in either x or y. RSuperellipse.fromRectAndCorners( Rect rect, { Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( top: rect.top, left: rect.left, right: rect.right, bottom: rect.bottom, tlRadiusX: topLeft.x, tlRadiusY: topLeft.y, trRadiusX: topRight.x, trRadiusY: topRight.y, blRadiusX: bottomLeft.x, blRadiusY: bottomLeft.y, brRadiusX: bottomRight.x, brRadiusY: bottomRight.y, ); const RSuperellipse._raw({ super.left = 0.0, super.top = 0.0, super.right = 0.0, super.bottom = 0.0, super.tlRadiusX = 0.0, super.tlRadiusY = 0.0, super.trRadiusX = 0.0, super.trRadiusY = 0.0, super.brRadiusX = 0.0, super.brRadiusY = 0.0, super.blRadiusX = 0.0, super.blRadiusY = 0.0, }); @override RSuperellipse _create({ required double left, required double top, required double right, required double bottom, required double tlRadiusX, required double tlRadiusY, required double trRadiusX, required double trRadiusY, required double brRadiusX, required double brRadiusY, required double blRadiusX, required double blRadiusY, }) => RSuperellipse._raw( top: top, left: left, right: right, bottom: bottom, tlRadiusX: tlRadiusX, tlRadiusY: tlRadiusY, trRadiusX: trRadiusX, trRadiusY: trRadiusY, blRadiusX: blRadiusX, blRadiusY: blRadiusY, brRadiusX: brRadiusX, brRadiusY: brRadiusY, ); _NativeRSuperellipse _native() { return _NativeRSuperellipse(this); } /// Whether the point specified by the given offset (which is assumed to be /// relative to the origin) lies inside the rounded superellipse. bool contains(Offset point) { return _native().contains(point); } /// A rounded rectangle with all the values set to zero. static const RSuperellipse zero = RSuperellipse._raw(); /// Linearly interpolate between two rounded superellipses. /// /// If either is null, this function substitutes [RSuperellipse.zero] instead. /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static RSuperellipse? lerp(RSuperellipse? a, RSuperellipse? b, double t) { if (a == null) { if (b == null) { return null; } return b._lerpTo(null, 1 - t); } return a._lerpTo(b, t); } @override String toString() { return _toString(className: 'RSuperellipse'); } } class _NativeRSuperellipse extends NativeFieldWrapperClass1 { _NativeRSuperellipse(RSuperellipse rsuperellipse) { _constructor( rsuperellipse.left, rsuperellipse.top, rsuperellipse.right, rsuperellipse.bottom, rsuperellipse.tlRadiusX, rsuperellipse.tlRadiusY, rsuperellipse.trRadiusX, rsuperellipse.trRadiusY, rsuperellipse.brRadiusX, rsuperellipse.brRadiusY, rsuperellipse.blRadiusX, rsuperellipse.blRadiusY, ); } @Native< Void Function( Handle, Double, Double, Double, Double, Double, Double, Double, Double, Double, Double, Double, Double, ) >(symbol: 'RSuperellipse::Create') external void _constructor( double left, double top, double right, double bottom, double tlRadiusX, double tlRadiusY, double trRadiusX, double trRadiusY, double brRadiusX, double brRadiusY, double blRadiusX, double blRadiusY, ); bool contains(Offset point) { return _contains(point.dx, point.dy); } @Native, Double, Double)>( symbol: 'RSuperellipse::contains', isLeaf: true, ) external bool _contains(double x, double y); } /// A transform consisting of a translation, a rotation, and a uniform scale. /// /// Used by [Canvas.drawAtlas]. This is a more efficient way to represent these /// simple transformations than a full matrix. // Modeled after Impeller's RSTransform. class RSTransform { /// Creates an RSTransform. /// /// An [RSTransform] expresses the combination of a translation, a rotation /// around a particular point, and a scale factor. /// /// The first argument, `scos`, is the cosine of the rotation, multiplied by /// the scale factor. /// /// The second argument, `ssin`, is the sine of the rotation, multiplied by /// that same scale factor. /// /// The third argument is the x coordinate of the translation, minus the /// `scos` argument multiplied by the x-coordinate of the rotation point, plus /// the `ssin` argument multiplied by the y-coordinate of the rotation point. /// /// The fourth argument is the y coordinate of the translation, minus the `ssin` /// argument multiplied by the x-coordinate of the rotation point, minus the /// `scos` argument multiplied by the y-coordinate of the rotation point. /// /// The [RSTransform.fromComponents] method may be a simpler way to /// construct these values. However, if there is a way to factor out the /// computations of the sine and cosine of the rotation so that they can be /// reused over multiple calls to this constructor, it may be more efficient /// to directly use this constructor instead. RSTransform(double scos, double ssin, double tx, double ty) { _value ..[0] = scos ..[1] = ssin ..[2] = tx ..[3] = ty; } /// Creates an RSTransform from its individual components. /// /// The `rotation` parameter gives the rotation in radians. /// /// The `scale` parameter describes the uniform scale factor. /// /// The `anchorX` and `anchorY` parameters give the coordinate of the point /// around which to rotate. /// /// The `translateX` and `translateY` parameters give the coordinate of the /// offset by which to translate. /// /// This constructor computes the arguments of the [RSTransform.new] /// constructor and then defers to that constructor to actually create the /// object. If many [RSTransform] objects are being created and there is a way /// to factor out the computations of the sine and cosine of the rotation /// (which are computed each time this constructor is called) and reuse them /// over multiple [RSTransform] objects, it may be more efficient to directly /// use the more direct [RSTransform.new] constructor instead. factory RSTransform.fromComponents({ required double rotation, required double scale, required double anchorX, required double anchorY, required double translateX, required double translateY, }) { final double scos = math.cos(rotation) * scale; final double ssin = math.sin(rotation) * scale; final double tx = translateX + -scos * anchorX + ssin * anchorY; final double ty = translateY + -ssin * anchorX - scos * anchorY; return RSTransform(scos, ssin, tx, ty); } final Float32List _value = Float32List(4); /// The cosine of the rotation multiplied by the scale factor. double get scos => _value[0]; /// The sine of the rotation multiplied by that same scale factor. double get ssin => _value[1]; /// The x coordinate of the translation, minus [scos] multiplied by the /// x-coordinate of the rotation point, plus [ssin] multiplied by the /// y-coordinate of the rotation point. double get tx => _value[2]; /// The y coordinate of the translation, minus [ssin] multiplied by the /// x-coordinate of the rotation point, minus [scos] multiplied by the /// y-coordinate of the rotation point. double get ty => _value[3]; } ˆ› <06P8+#NKNK NNKEB*KEOK.EO0(F#LJ*K&HNM<8 S0OMJKLNPMDO 8 O) #@ + U$: <)I6?RM$EHFHIMEIL*(%8OHFMK>&>+6JFG.+5.QIIQL"TSPDPD,A8 CFMQN#NQMNQJN.QJ'&K#JQNDBEAOHPT+QP!QQR JRP"SQL9]SP#\IK&"MPN $]JK&#VNP<O!\6+-KMJKLNPMDO 2 ['N #B - ZO$Q# A InrELK:nrL<:HptDNGHpt[  Q'pt& #EDFGG$G%O(/MG>P;7B-W3&"$#<[J6>P&dL,N$9LTK1DDGD" $&H)%" $&I66KBLAR+B!;S -C"=Q&GD#?U"1E$AV#3NN&O!]3CMJKLNPMDO 2"P P*(,.  #",  = €‰7QosJ<qu+/-4H3JKF598,,JNIaQQ3OPP,4#KILR KLRDA*TDNT.DN0(O#KI*T.JMJKLNPMDO 8"4 4V #",< ) 58*:5NIEDFG&$B'%B*(!B)' B6 "G 11111111M.G$G%ZANNP$7>=?@(&*,PFLOP>=?@NQL;=@ONMQ;>?O?+6D[E5220J# AJ# $#=5KBLA/Q&GL=M*%)OMHJk:9:9&&&&&&&& M //////// ,)/2HHHHHHHH  #"," "(((((((& # 2'&((P&Pq, # O(QJH Q*NW M7H> ON4 Q@QN"#&% ""## G3QN"#&% ""##  $;*NAPOG' X'A+S.-""[/-""^/0""].0"" D5;IMJKLNPMDO 5% *'POMQORKQ 8QJH" Q*N_ M7HF ON< Q@QN$"#&% ""## G3QN$"#&% ""##  #'ND &;:>QMJKLNPMDO M% 2>6 % *9'/NP/)N5ONKQPSOLFKNO0@ =>@NN$GMQLOPA' 44EE,-A E JH*JI*dart:ui/geometry.dart ÀQ!ÀQÀh³ÀQFÀQ�ÀQžÀQŸÀQòÀQÝÀR—ì;org-dartlang-sdk:///flutter/lib/ui/isolate_name_server.dartŒ¾// Copyright 2013 The Flutter Authors. 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.ui; /// Static methods to allow for simple sharing of [SendPort]s across [Isolate]s. /// /// All isolates share a global mapping of names to ports. An isolate can /// register a [SendPort] with a given name using [registerPortWithName]; /// another isolate can then look up that port using [lookupPortByName]. /// /// To create a [SendPort], first create a [ReceivePort], then use /// [ReceivePort.sendPort]. /// /// Since multiple isolates can each obtain the same [SendPort] associated with /// a particular [ReceivePort], the protocol built on top of this mechanism /// should typically consist of a single message. If more elaborate two-way /// communication or multiple-message communication is necessary, it is /// recommended to establish a separate communication channel in that first /// message (e.g. by passing a dedicated [SendPort]). abstract final class IsolateNameServer { /// Looks up the [SendPort] associated with a given name. /// /// Returns null if the name does not exist. To register the name in the first /// place, consider [registerPortWithName]. /// /// The `name` argument must not be null. static SendPort? lookupPortByName(String name) { return _lookupPortByName(name); } /// Registers a [SendPort] with a given name. /// /// Returns true if registration is successful, and false if the name entry /// already existed (in which case the earlier registration is left /// unchanged). To remove a registration, consider [removePortNameMapping]. /// /// Once a port has been registered with a name, it can be obtained from any /// [Isolate] using [lookupPortByName]. /// /// Multiple isolates should avoid attempting to register ports with the same /// name, as there is an inherent race condition in doing so. /// /// The `port` and `name` arguments must not be null. static bool registerPortWithName(SendPort port, String name) { return _registerPortWithName(port, name); } /// Removes a name-to-[SendPort] mapping given its name. /// /// Returns true if the mapping was successfully removed, false if the mapping /// did not exist. To add a registration, consider [registerPortWithName]. /// /// Generally, removing a port name mapping is an inherently racy operation /// (another isolate could have obtained the name just prior to the name being /// removed, and thus would still be able to communicate over the port even /// after it has been removed). /// /// The `name` argument must not be null. static bool removePortNameMapping(String name) { return _removePortNameMapping(name); } @Native(symbol: 'IsolateNameServerNatives::LookupPortByName') external static SendPort? _lookupPortByName(String name); @Native(symbol: 'IsolateNameServerNatives::RegisterPortWithName') external static bool _registerPortWithName(SendPort port, String name); @Native(symbol: 'IsolateNameServerNatives::RemovePortNameMapping') external static bool _removePortNameMapping(String name); } K 'Key Down', up => 'Key Up', repeat => 'Key Repeat', }; } } /// The source device for the key event. /// /// Not all platforms supply an accurate type. // Must match the KeyEventDeviceType enum in ui/window/key_data.h. enum KeyEventDeviceType { /// The device is a keyboard. keyboard, /// The device is a directional pad on something like a television remote /// control or similar. directionalPad, /// The device is a gamepad button gamepad, /// The device is a joystick button joystick, /// The device is a device connected to an HDMI bus. hdmi; // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs String get label { return switch (this) { keyboard => 'Keyboard', directionalPad => 'Directional Pad', gamepad => 'Gamepad', joystick => 'Joystick', hdmi => 'HDMI', }; } } /// Information about a key event. class KeyData { /// Creates an object that represents a key event. const KeyData({ required this.timeStamp, required this.type, required this.physical, required this.logical, required this.character, required this.synthesized, this.deviceType = KeyEventDeviceType.keyboard, }); /// Time of event dispatch, relative to an arbitrary timeline. /// /// For synthesized events, the [timeStamp] might not be the actual time that /// the key press or release happens. final Duration timeStamp; /// The type of the event. final KeyEventType type; /// Describes what type of device (keyboard, directional pad, etc.) this event /// originated from. final KeyEventDeviceType deviceType; /// The key code for the physical key that has changed. final int physical; /// The key code for the logical key that has changed. final int logical; /// Character input from the event. /// /// Ignored for up events. final String? character; /// If [synthesized] is true, this event does not correspond to a native /// event. /// /// Although most of Flutter's keyboard events are transformed from native /// events, some events are not based on native events, and are synthesized /// only to conform Flutter's key event model (as documented in the /// `HardwareKeyboard` class in the framework). /// /// For example, some key downs or ups might be lost when the window loses /// focus. Some platforms provide ways to query whether a key is being held. /// If the embedder detects an inconsistency between its internal record and /// the state returned by the system, the embedder will synthesize a /// corresponding event to synchronize the state without breaking the event /// model. /// /// As another example, macOS treats CapsLock in a special way by sending down /// and up events at the down of alternate presses to indicate the direction /// in which the lock is toggled instead of that the physical key is going. A /// macOS embedder should normalize the behavior by converting a native down /// event into a down event followed immediately by a synthesized up event, /// and the native up event also into a down event followed immediately by a /// synthesized up event. /// /// Synthesized events do not have a trustworthy [timeStamp], and should not /// be processed as if the key actually went down or up at the time of the /// callback. /// /// [KeyRepeatEvent] is never synthesized. final bool synthesized; // Returns the bits that are not included in [valueMask], shifted to the // right. // // For example, if the input is 0x12abcdabcd, then the result is 0x12. // // This is mostly equivalent to a right shift, resolving the problem that // JavaScript only support 32-bit bitwise operations and needs to use division // instead. static int _nonValueBits(int n) { const valueMask = 0x000FFFFFFFF; // `n >> valueMaskWidth` is equivalent to `n / divisorForValueMask`. const int divisorForValueMask = valueMask + 1; const valueMaskWidth = 32; // Equivalent to assert(divisorForValueMask == (1 << valueMaskWidth)). const firstDivisorWidth = 28; assert( divisorForValueMask == (1 << firstDivisorWidth) * (1 << (valueMaskWidth - firstDivisorWidth)), ); // JS only supports up to 2^53 - 1, therefore non-value bits can only // contain (maxSafeIntegerWidth - valueMaskWidth) bits. const maxSafeIntegerWidth = 52; const int nonValueMask = (1 << (maxSafeIntegerWidth - valueMaskWidth)) - 1; if (identical(0, 0.0)) { // Detects if we are on the web. return (n / divisorForValueMask).floor() & nonValueMask; } else { return (n >> valueMaskWidth) & nonValueMask; } } String _logicalToString() { final result = '0x${logical.toRadixString(16)}'; final int planeNum = _nonValueBits(logical) & 0x0FF; final String planeDescription = (() { switch (planeNum) { case 0x000: return ' (Unicode)'; case 0x001: return ' (Unprintable)'; case 0x002: return ' (Flutter)'; case 0x011: return ' (Android)'; case 0x012: return ' (Fuchsia)'; case 0x013: return ' (iOS)'; case 0x014: return ' (macOS)'; case 0x015: return ' (GTK)'; case 0x016: return ' (Windows)'; case 0x017: return ' (Web)'; case 0x018: return ' (GLFW)'; } return ''; })(); return '$result$planeDescription'; } String? _escapeCharacter() { if (character == null) { return ''; } switch (character!) { case '\n': return r'"\n"'; case '\t': return r'"\t"'; case '\r': return r'"\r"'; case '\b': return r'"\b"'; case '\f': return r'"\f"'; default: return '"$character"'; } } String? _quotedCharCode() { if (character == null) { return ''; } final Iterable hexChars = character!.codeUnits.map( (int code) => code.toRadixString(16).padLeft(2, '0'), ); return ' (0x${hexChars.join(' ')})'; } @override String toString() { return 'KeyData(${type.label}, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: ${_logicalToString()}, ' 'character: ${_escapeCharacter()}${_quotedCharCode()}' '${synthesized ? ', synthesized' : ''}'; } /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(type: ${type.label}, ' 'deviceType: ${deviceType.label}, ' 'timeStamp: $timeStamp, ' 'physical: 0x${physical.toRadixString(16)}, ' 'logical: 0x${logical.toRadixString(16)}, ' 'character: ${_escapeCharacter()}, ' 'synthesized: $synthesized' ')'; } } €ù`) then /// that future is awaited in the new isolate, completing the entire /// asynchronous computation, before returning the result. /// /// If [computation] throws, the `Future` returned by this function completes /// with that error. /// /// The [computation] function and its result (or error) must be /// sendable between isolates. Objects that cannot be sent include open /// files and sockets (see [SendPort.send] for details). /// /// This method can only be invoked from the main isolate. /// /// This API is currently experimental. Future runOnPlatformThread(FutureOr Function() computation) { if (!_platformIsolatesEnabled) { throw UnsupportedError('Platform thread isolates are not supported by this platform.'); } if (isRunningOnPlatformThread) { return Future(computation); } final SendPort? sendPort = _platformRunnerSendPort; if (sendPort != null) { return _sendComputation(sendPort, computation); } else { return (_platformRunnerSendPortFuture ??= _spawnPlatformIsolate()).then( (SendPort port) => _sendComputation(port, computation), ); } } SendPort? _platformRunnerSendPort; Future? _platformRunnerSendPortFuture; final Map> _pending = >{}; int _nextId = 0; Future _spawnPlatformIsolate() { final sendPortCompleter = Completer(); final receiver = RawReceivePort()..keepIsolateAlive = false; receiver.handler = (Object? message) { if (message == null) { // This is the platform isolate's onExit handler. // This shouldn't really happen, since Isolate.exit is disabled, the // pause and terminate capabilities aren't provided to the parent // isolate, and errors are fatal is false. But if the isolate does // shutdown unexpectedly, clear the singleton so we can create another. for (final Completer completer in _pending.values) { completer.completeError( RemoteError('PlatformIsolate shutdown unexpectedly', StackTrace.empty.toString()), ); } _pending.clear(); _platformRunnerSendPort = null; _platformRunnerSendPortFuture = null; } else if (message is _PlatformIsolateReadyMessage) { _platformRunnerSendPort = message.computationPort; sendPortCompleter.complete(message.computationPort); } else if (message is _ComputationResult) { final Completer resultCompleter = _pending.remove(message.id)!; final Object? remoteStack = message.remoteStack; final Object? remoteError = message.remoteError; if (remoteStack != null) { if (remoteStack is StackTrace) { // Typed error. resultCompleter.completeError(remoteError!, remoteStack); } else { // onError handler message, uncaught async error. // Both values are strings, so calling `toString` is efficient. final error = RemoteError(remoteError!.toString(), remoteStack.toString()); resultCompleter.completeError(error, error.stackTrace); } } else { resultCompleter.complete(message.result); } } else { // We encountered an error while starting the new isolate. if (!sendPortCompleter.isCompleted) { sendPortCompleter.completeError(IsolateSpawnException('Unable to spawn isolate: $message')); return; } // This shouldn't happen. throw IsolateSpawnException("Internal error: unexpected message: '$message'"); } }; final Isolate parentIsolate = Isolate.current; final SendPort sendPort = receiver.sendPort; try { _nativeSpawn(() => _platformIsolateMain(parentIsolate, sendPort)); } on Object { receiver.close(); rethrow; } return sendPortCompleter.future; } Future _sendComputation(SendPort port, FutureOr Function() computation) { final int id = ++_nextId; final resultCompleter = Completer(); _pending[id] = resultCompleter; port.send(_ComputationRequest(id, computation)); return resultCompleter.future; } void _safeSend(SendPort sendPort, int id, Object? result, Object? error, Object? stackTrace) { try { sendPort.send(_ComputationResult(id, result, error, stackTrace)); } catch (sendError, sendStack) { sendPort.send(_ComputationResult(id, null, sendError, sendStack)); } } void _platformIsolateMain(Isolate parentIsolate, SendPort sendPort) { final computationPort = RawReceivePort(); computationPort.handler = (_ComputationRequest? message) { if (message == null) { // The parent isolate has shutdown. Allow this isolate to shutdown. computationPort.keepIsolateAlive = false; return; } late final FutureOr potentiallyAsyncResult; try { potentiallyAsyncResult = message.computation(); } catch (e, s) { _safeSend(sendPort, message.id, null, e, s); return; } if (potentiallyAsyncResult is Future) { potentiallyAsyncResult.then( (Object? result) { _safeSend(sendPort, message.id, result, null, null); }, onError: (Object? e, Object? s) { _safeSend(sendPort, message.id, null, e, s ?? StackTrace.empty); }, ); } else { _safeSend(sendPort, message.id, potentiallyAsyncResult, null, null); } }; Isolate.current.addOnExitListener(sendPort); parentIsolate.addOnExitListener(computationPort.sendPort); sendPort.send(_PlatformIsolateReadyMessage(computationPort.sendPort)); } @Native(symbol: 'PlatformIsolateNativeApi::Spawn') external void _nativeSpawn(Function entryPoint); /// Whether the current isolate is running on the platform thread. final bool isRunningOnPlatformThread = _isRunningOnPlatformThread(); @Native( symbol: 'PlatformIsolateNativeApi::IsRunningOnPlatformThread', isLeaf: true, ) external bool _isRunningOnPlatformThread(); class _PlatformIsolateReadyMessage { _PlatformIsolateReadyMessage(this.computationPort); final SendPort computationPort; } class _ComputationRequest { _ComputationRequest(this.id, this.computation); final int id; final FutureOr Function() computation; } class _ComputationResult { _ComputationResult(this.id, this.result, this.remoteError, this.remoteStack); final int id; final Object? result; final Object? remoteError; final Object? remoteStack; } €Ã#1K+3?)8KHIND!] &,:9;0O77!)D _handle; @override bool operator ==(Object other) { if (runtimeType != other.runtimeType) { return false; } return other is CallbackHandle && other._handle == _handle; } @override int get hashCode => _handle.hashCode; } /// Functionality for Flutter plugin authors. /// /// See also: /// /// * [IsolateNameServer], which provides utilities for dealing with /// [Isolate]s. abstract final class PluginUtilities { static final Map _forwardCache = {}; static final Map _backwardCache = {}; /// Get a handle to a named top-level or static callback function which can /// be easily passed between isolates. /// /// The `callback` argument must not be null. /// /// Returns a [CallbackHandle] that can be provided to /// [PluginUtilities.getCallbackFromHandle] to retrieve a tear-off of the /// original callback. If `callback` is not a top-level or static function, /// null is returned. static CallbackHandle? getCallbackHandle(Function callback) { return _forwardCache.putIfAbsent(callback, () { final int? handle = _getCallbackHandle(callback); return handle != null ? CallbackHandle.fromRawHandle(handle) : null; }); } /// Get a tear-off of a named top-level or static callback represented by a /// handle. /// /// The `handle` argument must not be null. /// /// If `handle` is not a valid handle returned by /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a /// tear-off of the callback associated with `handle` is returned. static Function? getCallbackFromHandle(CallbackHandle handle) { return _backwardCache.putIfAbsent(handle, () => _getCallbackFromHandle(handle.toRawHandle())); } } J [!Warning] // > When changes are made to this class, the equivalent APIs in // > `lib/ui/semantics/semantics_node.h` and in each of the embedders // > *must* be updated. class SemanticsAction { const SemanticsAction._(this.index, this.name); /// The numerical value for this action. /// /// Each action has one bit set in this bit field. final int index; /// A human-readable name for this flag, used for debugging purposes. final String name; static const int _kTapIndex = 1 << 0; static const int _kLongPressIndex = 1 << 1; static const int _kScrollLeftIndex = 1 << 2; static const int _kScrollRightIndex = 1 << 3; static const int _kScrollUpIndex = 1 << 4; static const int _kScrollDownIndex = 1 << 5; static const int _kIncreaseIndex = 1 << 6; static const int _kDecreaseIndex = 1 << 7; static const int _kShowOnScreenIndex = 1 << 8; static const int _kMoveCursorForwardByCharacterIndex = 1 << 9; static const int _kMoveCursorBackwardByCharacterIndex = 1 << 10; static const int _kSetSelectionIndex = 1 << 11; static const int _kCopyIndex = 1 << 12; static const int _kCutIndex = 1 << 13; static const int _kPasteIndex = 1 << 14; static const int _kDidGainAccessibilityFocusIndex = 1 << 15; static const int _kDidLoseAccessibilityFocusIndex = 1 << 16; static const int _kCustomActionIndex = 1 << 17; static const int _kDismissIndex = 1 << 18; static const int _kMoveCursorForwardByWordIndex = 1 << 19; static const int _kMoveCursorBackwardByWordIndex = 1 << 20; static const int _kSetTextIndex = 1 << 21; static const int _kFocusIndex = 1 << 22; static const int _kScrollToOffsetIndex = 1 << 23; static const int _kExpandIndex = 1 << 24; static const int _kCollapseIndex = 1 << 25; // READ THIS: // - The maximum supported bit index on the web (in JS mode) is 1 << 31. // - If you add an action here, you MUST update the numSemanticsActions value // in testing/dart/semantics_test.dart and // lib/web_ui/test/engine/semantics/semantics_api_test.dart, or tests will // fail. /// The equivalent of a user briefly tapping the screen with the finger /// without moving it. static const SemanticsAction tap = SemanticsAction._(_kTapIndex, 'tap'); /// The equivalent of a user pressing and holding the screen with the finger /// for a few seconds without moving it. static const SemanticsAction longPress = SemanticsAction._(_kLongPressIndex, 'longPress'); /// The equivalent of a user moving their finger across the screen from right /// to left. /// /// This action should be recognized by controls that are horizontally /// scrollable. static const SemanticsAction scrollLeft = SemanticsAction._(_kScrollLeftIndex, 'scrollLeft'); /// The equivalent of a user moving their finger across the screen from left /// to right. /// /// This action should be recognized by controls that are horizontally /// scrollable. static const SemanticsAction scrollRight = SemanticsAction._(_kScrollRightIndex, 'scrollRight'); /// The equivalent of a user moving their finger across the screen from /// bottom to top. /// /// This action should be recognized by controls that are vertically /// scrollable. static const SemanticsAction scrollUp = SemanticsAction._(_kScrollUpIndex, 'scrollUp'); /// The equivalent of a user moving their finger across the screen from top /// to bottom. /// /// This action should be recognized by controls that are vertically /// scrollable. static const SemanticsAction scrollDown = SemanticsAction._(_kScrollDownIndex, 'scrollDown'); /// A request to scroll the scrollable container to a given scroll offset. /// /// The payload of this [SemanticsAction] is a flutter-standard-encoded /// [Float64List] of length 2 containing the target horizontal and vertical /// offsets (in logical pixels) the receiving scrollable container should /// scroll to. /// /// This action is used by iOS Full Keyboard Access to reveal contents that /// are currently not visible in the viewport. static const SemanticsAction scrollToOffset = SemanticsAction._( _kScrollToOffsetIndex, 'scrollToOffset', ); /// A request to increase the value represented by the semantics node. /// /// For example, this action might be recognized by a slider control. static const SemanticsAction increase = SemanticsAction._(_kIncreaseIndex, 'increase'); /// A request to decrease the value represented by the semantics node. /// /// For example, this action might be recognized by a slider control. static const SemanticsAction decrease = SemanticsAction._(_kDecreaseIndex, 'decrease'); /// A request to fully show the semantics node on screen. /// /// For example, this action might be send to a node in a scrollable list that /// is partially off screen to bring it on screen. static const SemanticsAction showOnScreen = SemanticsAction._( _kShowOnScreenIndex, 'showOnScreen', ); /// Move the cursor forward by one character. /// /// This is for example used by the cursor control in text fields. /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._( _kMoveCursorForwardByCharacterIndex, 'moveCursorForwardByCharacter', ); /// Move the cursor backward by one character. /// /// This is for example used by the cursor control in text fields. /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._( _kMoveCursorBackwardByCharacterIndex, 'moveCursorBackwardByCharacter', ); /// Replaces the current text in the text field. /// /// This is for example used by the text editing in voice access. /// /// The action includes a string argument, which is the new text to /// replace. static const SemanticsAction setText = SemanticsAction._(_kSetTextIndex, 'setText'); /// Set the text selection to the given range. /// /// The provided argument is a Map which includes the keys `base` /// and `extent` indicating where the selection within the `value` of the /// semantics node should start and where it should end. Values for both /// keys can range from 0 to length of `value` (inclusive). /// /// Setting `base` and `extent` to the same value will move the cursor to /// that position (without selecting anything). static const SemanticsAction setSelection = SemanticsAction._( _kSetSelectionIndex, 'setSelection', ); /// Copy the current selection to the clipboard. static const SemanticsAction copy = SemanticsAction._(_kCopyIndex, 'copy'); /// Cut the current selection and place it in the clipboard. static const SemanticsAction cut = SemanticsAction._(_kCutIndex, 'cut'); /// Paste the current content of the clipboard. static const SemanticsAction paste = SemanticsAction._(_kPasteIndex, 'paste'); /// Indicates that the node has gained accessibility focus. /// /// This handler is invoked when the node annotated with this handler gains /// the accessibility focus. The accessibility focus is the /// green (on Android with TalkBack) or black (on iOS with VoiceOver) /// rectangle shown on screen to indicate what element an accessibility /// user is currently interacting with. /// /// The accessibility focus is different from the input focus. The input focus /// is usually held by the element that currently responds to keyboard inputs. /// Accessibility focus and input focus can be held by two different nodes! /// /// See also: /// /// * [focus], which controls the input focus. static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._( _kDidGainAccessibilityFocusIndex, 'didGainAccessibilityFocus', ); /// Indicates that the node has lost accessibility focus. /// /// This handler is invoked when the node annotated with this handler /// loses the accessibility focus. The accessibility focus is /// the green (on Android with TalkBack) or black (on iOS with VoiceOver) /// rectangle shown on screen to indicate what element an accessibility /// user is currently interacting with. /// /// The accessibility focus is different from the input focus. The input focus /// is usually held by the element that currently responds to keyboard inputs. /// Accessibility focus and input focus can be held by two different nodes! static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._( _kDidLoseAccessibilityFocusIndex, 'didLoseAccessibilityFocus', ); /// Indicates that the user has invoked a custom accessibility action. /// /// This handler is added automatically whenever a custom accessibility /// action is added to a semantics node. static const SemanticsAction customAction = SemanticsAction._( _kCustomActionIndex, 'customAction', ); /// A request that the node should be dismissed. /// /// A [SnackBar], for example, may have a dismiss action to indicate to the /// user that it can be removed after it is no longer relevant. On Android, /// (with TalkBack) special hint text is spoken when focusing the node and /// a custom action is available in the local context menu. On iOS, /// (with VoiceOver) users can perform a standard gesture to dismiss it. static const SemanticsAction dismiss = SemanticsAction._(_kDismissIndex, 'dismiss'); /// Move the cursor forward by one word. /// /// This is for example used by the cursor control in text fields. /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. static const SemanticsAction moveCursorForwardByWord = SemanticsAction._( _kMoveCursorForwardByWordIndex, 'moveCursorForwardByWord', ); /// Move the cursor backward by one word. /// /// This is for example used by the cursor control in text fields. /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._( _kMoveCursorBackwardByWordIndex, 'moveCursorBackwardByWord', ); /// Move the input focus to the respective widget. /// /// Most commonly, the input focus determines which widget will receive /// keyboard input. Semantics nodes that can receive this action are expected /// to have [SemanticsFlag.isFocusable] set. Examples of such focusable /// widgets include buttons, checkboxes, switches, and text fields. /// /// Upon receiving this action, the corresponding widget must move input focus /// to itself. Doing otherwise is likely to lead to a poor user experience, /// such as user input routed to a wrong widget. Text fields in particular, /// must immediately become editable, opening a virtual keyboard, if needed. /// Buttons must respond to tap/click events from the keyboard. /// /// Widget reaction to this action must be idempotent. It is possible to /// receive this action more than once, or when the widget is already focused. /// /// Focus behavior is specific to the platform and to the assistive technology /// used. Typically on desktop operating systems, such as Windows, macOS, and /// Linux, moving accessibility focus will also move the input focus. On /// mobile it is more common for the accessibility focus to be detached from /// the input focus. In order to synchronize the two, a user takes an explicit /// action (e.g. double-tap to activate). Sometimes this behavior is /// configurable. For example, VoiceOver on macOS can be configured in the /// global OS user settings to either move the input focus together with the /// VoiceOver focus, or to keep the two detached. For this reason, widgets /// should not expect to receive [didGainAccessibilityFocus] and [focus] /// actions to be reported in any particular combination or order. /// /// On the web, the DOM "focus" event is equivalent to /// [SemanticsAction.focus]. Accessibility focus is not observable from within /// the browser. Instead, the browser, based on the platform features and user /// preferences, makes the determination on whether input focus should be /// moved to an element and, if so, fires a DOM "focus" event. This event is /// forwarded to the framework as [SemanticsAction.focus]. For this reason, on /// the web, the engine never sends [didGainAccessibilityFocus]. /// /// On Android input focus is observable as `AccessibilityAction#ACTION_FOCUS` /// and is separate from accessibility focus, which is observed as /// `AccessibilityAction#ACTION_ACCESSIBILITY_FOCUS`. /// /// See also: /// /// * [didGainAccessibilityFocus], which informs the framework about /// accessibility focus ring, such as the TalkBack (Android) and /// VoiceOver (iOS), moving which does not move the input focus. static const SemanticsAction focus = SemanticsAction._(_kFocusIndex, 'focus'); /// A request that the node should be expanded. /// /// For example, this action might be recognized by a dropdown. static const SemanticsAction expand = SemanticsAction._(_kExpandIndex, 'expand'); /// A request that the node should be collapsed. /// /// For example, this action might be recognized by a dropdown. static const SemanticsAction collapse = SemanticsAction._(_kCollapseIndex, 'collapse'); /// The possible semantics actions. /// /// The map's key is the [index] of the action and the value is the action /// itself. static const Map _kActionById = { _kTapIndex: tap, _kLongPressIndex: longPress, _kScrollLeftIndex: scrollLeft, _kScrollRightIndex: scrollRight, _kScrollUpIndex: scrollUp, _kScrollDownIndex: scrollDown, _kScrollToOffsetIndex: scrollToOffset, _kIncreaseIndex: increase, _kDecreaseIndex: decrease, _kShowOnScreenIndex: showOnScreen, _kMoveCursorForwardByCharacterIndex: moveCursorForwardByCharacter, _kMoveCursorBackwardByCharacterIndex: moveCursorBackwardByCharacter, _kSetSelectionIndex: setSelection, _kCopyIndex: copy, _kCutIndex: cut, _kPasteIndex: paste, _kDidGainAccessibilityFocusIndex: didGainAccessibilityFocus, _kDidLoseAccessibilityFocusIndex: didLoseAccessibilityFocus, _kCustomActionIndex: customAction, _kDismissIndex: dismiss, _kMoveCursorForwardByWordIndex: moveCursorForwardByWord, _kMoveCursorBackwardByWordIndex: moveCursorBackwardByWord, _kSetTextIndex: setText, _kFocusIndex: focus, _kExpandIndex: expand, _kCollapseIndex: collapse, }; // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs static List get values => _kActionById.values.toList(growable: false); // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs static SemanticsAction? fromIndex(int index) => _kActionById[index]; @override String toString() => 'SemanticsAction.$name'; } /// An enum to describe the role for a semantics node. /// /// The roles are translated into native accessibility roles in each platform. enum SemanticsRole { /// Does not represent any role. none, /// A tab button. /// /// See also: /// /// * [tabBar], which is the role for containers of tab buttons. tab, /// Contains tab buttons. /// /// See also: /// /// * [tab], which is the role for tab buttons. tabBar, /// The main display for a tab. tabPanel, /// A pop up dialog. dialog, /// An alert dialog. alertDialog, /// A table structure containing data arranged in rows and columns. /// /// See also: /// /// * [cell], [row], [columnHeader] for table related roles. table, /// A cell in a [table] that does not contain column or row header information. /// /// See also: /// /// * [table],[row], [columnHeader] for table related roles. cell, /// A row of [cell]s or or [columnHeader]s in a [table]. /// /// See also: /// /// * [table] ,[cell],[columnHeader] for table related roles. row, /// A cell in a [table] contains header information for a column. /// /// See also: /// /// * [table] ,[cell], [row] for table related roles. columnHeader, /// A control used for dragging across content. /// /// For example, the drag handle of [ReorderableList]. dragHandle, /// A control to cycle through content on tap. /// /// For example, the next and previous month button of a [CalendarDatePicker]. spinButton, /// A input field with a dropdown list box attached. /// /// For example, a [DropdownMenu] comboBox, /// A presentation of [menu] that usually remains visible and is usually /// presented horizontally. /// /// For example, a [MenuBar]. menuBar, /// A permanently visible list of controls or a widget that can be made to /// open and close. /// /// For example, a [MenuAnchor] or [DropdownButton]. menu, /// An item in a dropdown created by [menu] or [menuBar]. /// /// See also: /// /// * [menuItemCheckbox], a menu item with a checkbox. The [menuItemCheckbox] /// can also be used within [menu] and [menuBar]. /// * [menuItemRadio], a menu item with a radio button. This role is used by /// [menu] or [menuBar] as well. menuItem, /// An item with a checkbox in a dropdown created by [menu] or [menuBar]. /// /// See also: /// /// * [menuItem] and [menuItemRadio] for menu related roles. menuItemCheckbox, /// An item with a radio button in a dropdown created by [menu] or [menuBar]. /// /// See also: /// /// * [menuItem] and [menuItemCheckbox] for menu related roles. menuItemRadio, /// A container to display multiple [listItem]s in vertical or horizontal /// layout. /// /// For example, a [LisView] or [Column]. list, /// An item in a [list]. listItem, /// An area that represents a form. form, /// A pop up displayed when hovering over a component to provide contextual /// explanation. tooltip, /// A graphic object that spins to indicate the application is busy. /// /// For example, a [CircularProgressIndicator]. loadingSpinner, /// A graphic object that shows progress with a numeric number. /// /// For example, a [LinearProgressIndicator]. progressBar, /// A keyboard shortcut field that allows the user to enter a combination or /// sequence of keystrokes. /// /// For example, [Shortcuts]. hotKey, /// A group of radio buttons. radioGroup, /// A component to provide advisory information that is not important to /// justify an [alert]. /// /// For example, a loading message for a web page. status, /// A component to provide important and usually time-sensitive information. /// /// The alert role should only be used for information that requires the /// user's immediate attention, for example: /// /// * An invalid value was entered into a form field. /// * The user's login session is about to expire. /// * The connection to the server was lost so local changes will not be /// saved. alert, /// A supporting section that relates to the main content. /// /// The compelementary role is one of landmark roles. This role can be used to /// describe sidebars, or call-out boxes. /// /// For more information, see: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/complementary_role complementary, /// A section for a footer, containing identifying information such as /// copyright information, navigation links and privacy statements. /// /// The contentInfo role is one of landmark roles. For more information, see: /// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/contentinfo_role contentInfo, /// The primary content of a document. /// /// The section consists of content that is directly related to or expands on /// the central topic of a document, or the main function of an application. /// /// This role is one of landmark roles. For more information, see: /// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/main_role main, /// A region of a web page that contains navigation links. /// /// This role is one of landmark roles. For more information, see: /// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/navigation_role navigation, /// A section of content sufficiently important but cannot be descrived by one /// of the other landmark roles, such as main, contentinfo, complementary, or /// navigation. /// /// For more information, see: /// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/region_role region, } /// Describe the type of data for an input field. /// /// This is typically used to complement text fields. enum SemanticsInputType { /// The default for non text field. none, /// Describes a generic text field. text, /// Describes a url text field. url, /// Describes a text field for phone input. phone, /// Describes a text field that act as a search box. search, /// Describes a text field for email input. email, } /// A Boolean value that can be associated with a semantics node. // // When changes are made to this class, the equivalent APIs in // `lib/ui/semantics/semantics_node.h` and in each of the embedders *must* be // updated. If the change affects the visibility of a [SemanticsNode] to // accessibility services, `flutter_test/controller.dart#SemanticsController._importantFlags` // must be updated as well. class SemanticsFlag { const SemanticsFlag._(this.index, this.name); /// The numerical value for this flag. /// /// Each flag has one bit set in this bit field. final int index; /// A human-readable name for this flag, used for debugging purposes. final String name; static const int _kHasCheckedStateIndex = 1 << 0; static const int _kIsCheckedIndex = 1 << 1; static const int _kIsSelectedIndex = 1 << 2; static const int _kIsButtonIndex = 1 << 3; static const int _kIsTextFieldIndex = 1 << 4; static const int _kIsFocusedIndex = 1 << 5; static const int _kHasEnabledStateIndex = 1 << 6; static const int _kIsEnabledIndex = 1 << 7; static const int _kIsInMutuallyExclusiveGroupIndex = 1 << 8; static const int _kIsHeaderIndex = 1 << 9; static const int _kIsObscuredIndex = 1 << 10; static const int _kScopesRouteIndex = 1 << 11; static const int _kNamesRouteIndex = 1 << 12; static const int _kIsHiddenIndex = 1 << 13; static const int _kIsImageIndex = 1 << 14; static const int _kIsLiveRegionIndex = 1 << 15; static const int _kHasToggledStateIndex = 1 << 16; static const int _kIsToggledIndex = 1 << 17; static const int _kHasImplicitScrollingIndex = 1 << 18; static const int _kIsMultilineIndex = 1 << 19; static const int _kIsReadOnlyIndex = 1 << 20; static const int _kIsFocusableIndex = 1 << 21; static const int _kIsLinkIndex = 1 << 22; static const int _kIsSliderIndex = 1 << 23; static const int _kIsKeyboardKeyIndex = 1 << 24; static const int _kIsCheckStateMixedIndex = 1 << 25; static const int _kHasExpandedStateIndex = 1 << 26; static const int _kIsExpandedIndex = 1 << 27; static const int _kHasSelectedStateIndex = 1 << 28; static const int _kHasRequiredStateIndex = 1 << 29; static const int _kIsRequiredIndex = 1 << 30; // READ THIS: if you add a flag here, you MUST update the following: // // - The maximum supported bit index on the web (in JS mode) is 1 << 31. // - Add an appropriately named and documented `static const SemanticsFlag` // field to this class. // - Add the new flag to `_kFlagById` in this file. // - Make changes in lib/web_ui/lib/semantics.dart in the web engine that mirror // the changes in this file (i.e. `_k*Index`, `static const SemanticsFlag`, // `_kFlagById`). // - Increment the `numSemanticsFlags` value in testing/dart/semantics_test.dart // and in lib/web_ui/test/engine/semantics/semantics_api_test.dart. // - Add the new flag to platform-specific enums: // - The `Flag` enum in flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java. // - The `SemanticsFlags` enum in lib/ui/semantics/semantics_node.h. // - The `FlutterSemanticsFlag` enum in shell/platform/embedder/embedder.h. // - If the new flag affects the visibility of a [SemanticsNode] to accessibility services, // update `flutter_test/controller.dart#SemanticsController._importantFlags` // accordingly. // - If the new flag affects focusability of a semantics node, also update the // value of `AccessibilityBridge.FOCUSABLE_FLAGS` in // flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java. /// {@template dart.ui.semantics.hasCheckedState} /// The semantics node has the quality of either being "checked" or "unchecked". /// /// This flag is mutually exclusive with [hasToggledState]. /// /// For example, a checkbox or a radio button widget has checked state. /// /// See also: /// /// * [SemanticsFlag.isChecked], which controls whether the node is "checked" or "unchecked". /// {@endtemplate} static const SemanticsFlag hasCheckedState = SemanticsFlag._( _kHasCheckedStateIndex, 'hasCheckedState', ); /// {@template dart.ui.semantics.isChecked} /// Whether a semantics node that [hasCheckedState] is checked. /// /// If true, the semantics node is "checked". If false, the semantics node is /// "unchecked". /// /// For example, if a checkbox has a visible checkmark, [isChecked] is true. /// /// See also: /// /// * [SemanticsFlag.hasCheckedState], which enables a checked state. /// {@endtemplate} /// static const SemanticsFlag isChecked = SemanticsFlag._(_kIsCheckedIndex, 'isChecked'); /// {@template dart.ui.semantics.isCheckStateMixed} /// Whether a tristate checkbox is in its mixed state. /// /// If this is true, the check box this semantics node represents /// is in a mixed state. /// /// For example, a [Checkbox] with [Checkbox.tristate] set to true /// can have checked, unchecked, or mixed state. /// /// Must be false when the checkbox is either checked or unchecked. /// {@endtemplate} static const SemanticsFlag isCheckStateMixed = SemanticsFlag._( _kIsCheckStateMixedIndex, 'isCheckStateMixed', ); /// {@template dart.ui.semantics.hasSelectedState} /// The semantics node has the quality of either being "selected" or "unselected". /// /// Whether the widget corresponding to this node is currently selected or not /// is determined by the [isSelected] flag. /// /// When this flag is not set, the corresponding widget cannot be selected by /// the user, and the presence or the lack of [isSelected] does not carry any /// meaning. /// {@endtemplate} static const SemanticsFlag hasSelectedState = SemanticsFlag._( _kHasSelectedStateIndex, 'hasSelectedState', ); /// {@template dart.ui.semantics.isSelected} /// Whether a semantics node is selected. /// /// This flag only has meaning in nodes that have [hasSelectedState] flag set. /// /// If true, the semantics node is "selected". If false, the semantics node is /// "unselected". /// /// For example, the active tab in a tab bar has [isSelected] set to true. /// {@endtemplate} static const SemanticsFlag isSelected = SemanticsFlag._(_kIsSelectedIndex, 'isSelected'); /// {@template dart.ui.semantics.isButton} /// Whether the semantic node represents a button. /// /// Platforms have special handling for buttons, for example Android's TalkBack /// and iOS's VoiceOver provides an additional hint when the focused object is /// a button. /// {@endtemplate} static const SemanticsFlag isButton = SemanticsFlag._(_kIsButtonIndex, 'isButton'); /// {@template dart.ui.semantics.isTextField} /// Whether the semantic node represents a text field. /// /// Text fields are announced as such and allow text input via accessibility /// affordances. /// {@endtemplate} static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex, 'isTextField'); /// {@template dart.ui.semantics.isSlider} /// Whether the semantic node represents a slider. /// {@endtemplate} static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex, 'isSlider'); /// {@template dart.ui.semantics.isKeyboardKey} /// Whether the semantic node represents a keyboard key. /// {@endtemplate} static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex, 'isKeyboardKey'); /// {@template dart.ui.semantics.isReadOnly} /// Whether the semantic node is read only. /// /// Only applicable when [isTextField] is true. /// {@endtemplate} static const SemanticsFlag isReadOnly = SemanticsFlag._(_kIsReadOnlyIndex, 'isReadOnly'); /// {@template dart.ui.semantics.isLink} /// Whether the semantic node is an interactive link. /// /// Platforms have special handling for links, for example iOS's VoiceOver /// provides an additional hint when the focused object is a link, as well as /// the ability to parse the links through another navigation menu. /// {@endtemplate} static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex, 'isLink'); /// {@template dart.ui.semantics.isFocusable} /// Whether the semantic node is able to hold the user's focus. /// /// The focused element is usually the current receiver of keyboard inputs. /// {@endtemplate} static const SemanticsFlag isFocusable = SemanticsFlag._(_kIsFocusableIndex, 'isFocusable'); /// {@template dart.ui.semantics.isFocused} /// Whether the semantic node currently holds the user's focus. /// /// The focused element is usually the current receiver of keyboard inputs. /// {@endtemplate} static const SemanticsFlag isFocused = SemanticsFlag._(_kIsFocusedIndex, 'isFocused'); /// {@template dart.ui.semantics.hasEnabledState} /// The semantics node has the quality of either being "enabled" or /// "disabled". /// /// For example, a button can be enabled or disabled and therefore has an /// "enabled" state. Static text is usually neither enabled nor disabled and /// therefore does not have an "enabled" state. /// {@endtemplate} static const SemanticsFlag hasEnabledState = SemanticsFlag._( _kHasEnabledStateIndex, 'hasEnabledState', ); /// {@template dart.ui.semantics.isEnabled} /// Whether a semantic node that [hasEnabledState] is currently enabled. /// /// A disabled element does not respond to user interaction. For example, a /// button that currently does not respond to user interaction should be /// marked as disabled. /// {@endtemplate} static const SemanticsFlag isEnabled = SemanticsFlag._(_kIsEnabledIndex, 'isEnabled'); /// {@template dart.ui.semantics.isInMutuallyExclusiveGroup} /// Whether a semantic node is in a mutually exclusive group. /// /// For example, a radio button is in a mutually exclusive group because /// only one radio button in that group can be marked as [isChecked]. /// {@endtemplate} static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._( _kIsInMutuallyExclusiveGroupIndex, 'isInMutuallyExclusiveGroup', ); /// {@template dart.ui.semantics.isHeader} /// Whether a semantic node is a header that divides content into sections. /// /// For example, headers can be used to divide a list of alphabetically /// sorted words into the sections A, B, C, etc. as can be found in many /// address book applications. /// {@endtemplate} static const SemanticsFlag isHeader = SemanticsFlag._(_kIsHeaderIndex, 'isHeader'); /// {@template dart.ui.semantics.isObscured} /// Whether the value of the semantics node is obscured. /// /// This is usually used for text fields to indicate that its content /// is a password or contains other sensitive information. /// {@endtemplate} static const SemanticsFlag isObscured = SemanticsFlag._(_kIsObscuredIndex, 'isObscured'); /// {@template dart.ui.semantics.isMultiline} /// Whether the value of the semantics node is coming from a multi-line text /// field. /// /// This is used for text fields to distinguish single-line text fields from /// multi-line ones. /// {@endtemplate} static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex, 'isMultiline'); /// {@template dart.ui.semantics.scopesRoute} /// Whether the semantics node is the root of a subtree for which a route name /// should be announced. /// /// When a node with this flag is removed from the semantics tree, the /// framework will select the last in depth-first, paint order node with this /// flag. When a node with this flag is added to the semantics tree, it is /// selected automatically, unless there were multiple nodes with this flag /// added. In this case, the last added node in depth-first, paint order /// will be selected. /// /// From this selected node, the framework will search in depth-first, paint /// order for the first node with a [namesRoute] flag and a non-null, /// non-empty label. The [namesRoute] and [scopesRoute] flags may be on the /// same node. The label of the found node will be announced as an edge /// transition. If no non-empty, non-null label is found then: /// /// * VoiceOver will make a chime announcement. /// * TalkBack will make no announcement /// /// Semantic nodes annotated with this flag are generally not a11y focusable. /// /// This is used in widgets such as Routes, Drawers, and Dialogs to /// communicate significant changes in the visible screen. /// {@endtemplate} static const SemanticsFlag scopesRoute = SemanticsFlag._(_kScopesRouteIndex, 'scopesRoute'); /// {@template dart.ui.semantics.namesRoute} /// Whether the semantics node label is the name of a visually distinct /// route. /// /// This is used by certain widgets like Drawers and Dialogs, to indicate /// that the node's semantic label can be used to announce an edge triggered /// semantics update. /// /// Semantic nodes annotated with this flag will still receive a11y focus. /// /// Updating this label within the same active route subtree will not cause /// additional announcements. /// {@endtemplate} static const SemanticsFlag namesRoute = SemanticsFlag._(_kNamesRouteIndex, 'namesRoute'); /// {@template dart.ui.semantics.isHidden} /// Whether the semantics node is considered hidden. /// /// Hidden elements are currently not visible on screen. They may be covered /// by other elements or positioned outside of the visible area of a viewport. /// /// Hidden elements cannot gain accessibility focus though regular touch. The /// only way they can be focused is by moving the focus to them via linear /// navigation. /// /// Platforms are free to completely ignore hidden elements and new platforms /// are encouraged to do so. /// /// Instead of marking an element as hidden it should usually be excluded from /// the semantics tree altogether. Hidden elements are only included in the /// semantics tree to work around platform limitations and they are mainly /// used to implement accessibility scrolling on iOS. /// /// See also: /// /// * [RenderObject.describeSemanticsClip] /// {@endtemplate} static const SemanticsFlag isHidden = SemanticsFlag._(_kIsHiddenIndex, 'isHidden'); /// {@template dart.ui.semantics.isImage} /// Whether the semantics node represents an image. /// /// Both TalkBack and VoiceOver will inform the user the semantics node /// represents an image. /// {@endtemplate} static const SemanticsFlag isImage = SemanticsFlag._(_kIsImageIndex, 'isImage'); /// {@template dart.ui.semantics.isLiveRegion} /// Whether the semantics node is a live region. /// /// A live region indicates that updates to semantics node are important. /// Platforms may use this information to make polite announcements to the /// user to inform them of updates to this node. /// /// An example of a live region is a [SnackBar] widget. On Android and iOS, /// live region causes a polite announcement to be generated automatically, /// even if the widget does not have accessibility focus. This announcement /// may not be spoken if the OS accessibility services are already /// announcing something else, such as reading the label of a focused /// widget or providing a system announcement. /// {@endtemplate} static const SemanticsFlag isLiveRegion = SemanticsFlag._(_kIsLiveRegionIndex, 'isLiveRegion'); /// {@template dart.ui.semantics.hasToggledState} /// The semantics node has the quality of either being "on" or "off". /// /// This flag is mutually exclusive with [hasCheckedState]. /// /// For example, a switch has toggled state. /// /// See also: /// /// * [SemanticsFlag.isToggled], which controls whether the node is "on" or "off". /// {@endtemplate} static const SemanticsFlag hasToggledState = SemanticsFlag._( _kHasToggledStateIndex, 'hasToggledState', ); /// {@template dart.ui.semantics.isToggled} /// If true, the semantics node is "on". If false, the semantics node is /// "off". /// /// For example, if a switch is in the on position, [isToggled] is true. /// /// See also: /// /// * [SemanticsFlag.hasToggledState], which enables a toggled state. /// {@endtemplate} static const SemanticsFlag isToggled = SemanticsFlag._(_kIsToggledIndex, 'isToggled'); /// {@template dart.ui.semantics.hasImplicitScrolling} /// Whether the platform can scroll the semantics node when the user attempts /// to move focus to an offscreen child. /// /// For example, a [ListView] widget has implicit scrolling so that users can /// easily move the accessibility focus to the next set of children. A /// [PageView] widget does not have implicit scrolling, so that users don't /// navigate to the next page when reaching the end of the current one. /// {@endtemplate} static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._( _kHasImplicitScrollingIndex, 'hasImplicitScrolling', ); /// {@template dart.ui.semantics.hasExpandedState} /// The semantics node has the quality of either being "expanded" or "collapsed". /// /// For example, a [SubmenuButton] widget has expanded state. /// /// See also: /// /// * [SemanticsFlag.isExpanded], which controls whether the node is "expanded" or "collapsed". /// {@endtemplate} static const SemanticsFlag hasExpandedState = SemanticsFlag._( _kHasExpandedStateIndex, 'hasExpandedState', ); /// {@template dart.ui.semantics.isExpanded} /// Whether a semantics node is expanded. /// /// If true, the semantics node is "expanded". If false, the semantics node is /// "collapsed". /// /// For example, if a [SubmenuButton] shows its children, [isExpanded] is true. /// /// See also: /// /// * [SemanticsFlag.hasExpandedState], which enables an expanded/collapsed state. /// {@endtemplate} static const SemanticsFlag isExpanded = SemanticsFlag._(_kIsExpandedIndex, 'isExpanded'); /// {@template dart.ui.semantics.hasRequiredState} /// The semantics node has the quality of either being required or not. /// /// See also: /// /// * [SemanticsFlag.isRequired], which controls whether the node is required. /// {@endtemplate} static const SemanticsFlag hasRequiredState = SemanticsFlag._( _kHasRequiredStateIndex, 'hasRequiredState', ); /// {@template dart.ui.semantics.isRequired} /// Whether a semantics node is required. /// /// If true, user input is required on the semantics node before a form can /// be submitted. /// /// For example, a login form requires its email text field to be non-empty. /// /// See also: /// /// * [SemanticsFlag.hasRequiredState], which enables a required state state. /// {@endtemplate} static const SemanticsFlag isRequired = SemanticsFlag._(_kIsRequiredIndex, 'isRequired'); /// The possible semantics flags. /// /// The map's key is the [index] of the flag and the value is the flag itself. static const Map _kFlagById = { _kHasCheckedStateIndex: hasCheckedState, _kIsCheckedIndex: isChecked, _kHasSelectedStateIndex: hasSelectedState, _kIsSelectedIndex: isSelected, _kIsButtonIndex: isButton, _kIsTextFieldIndex: isTextField, _kIsFocusedIndex: isFocused, _kHasEnabledStateIndex: hasEnabledState, _kIsEnabledIndex: isEnabled, _kIsInMutuallyExclusiveGroupIndex: isInMutuallyExclusiveGroup, _kIsHeaderIndex: isHeader, _kIsObscuredIndex: isObscured, _kScopesRouteIndex: scopesRoute, _kNamesRouteIndex: namesRoute, _kIsHiddenIndex: isHidden, _kIsImageIndex: isImage, _kIsLiveRegionIndex: isLiveRegion, _kHasToggledStateIndex: hasToggledState, _kIsToggledIndex: isToggled, _kHasImplicitScrollingIndex: hasImplicitScrolling, _kIsMultilineIndex: isMultiline, _kIsReadOnlyIndex: isReadOnly, _kIsFocusableIndex: isFocusable, _kIsLinkIndex: isLink, _kIsSliderIndex: isSlider, _kIsKeyboardKeyIndex: isKeyboardKey, _kIsCheckStateMixedIndex: isCheckStateMixed, _kHasExpandedStateIndex: hasExpandedState, _kIsExpandedIndex: isExpanded, _kHasRequiredStateIndex: hasRequiredState, _kIsRequiredIndex: isRequired, }; // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs static List get values => _kFlagById.values.toList(growable: false); // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs static SemanticsFlag? fromIndex(int index) => _kFlagById[index]; @override String toString() => 'SemanticsFlag.$name'; } /// Checked state of a semantics node. enum CheckedState { /// The semantics node does not have a check state. none(0), /// The semantics node is checked. isTrue(1), /// The semantics node is not checked. isFalse(2), /// The semantics node represents a tristate checkbox in a mixed state. mixed(3); /// The Constructor of the flag. const CheckedState(this.value); /// The value of the flag. final int value; /// If two semantics nodes both have check state, they have conflict and can't be merged. bool hasConflict(CheckedState other) => this != CheckedState.none && other != CheckedState.none; /// Semanitcs nodes will only be merged when they are not in conflict. CheckedState merge(CheckedState other) { if (this == CheckedState.mixed || other == CheckedState.mixed) { return CheckedState.mixed; } if (this == CheckedState.isTrue || other == CheckedState.isTrue) { return CheckedState.isTrue; } if (this == CheckedState.isFalse || other == CheckedState.isFalse) { return CheckedState.isFalse; } return CheckedState.none; } } /// Tristate flags for a semantics not enum Tristate { /// The property is not applicable to this semantics node. none(0), /// The property is applicable and its state is "true" or "on". isTrue(1), /// The property is applicable and its state is "false" or "off". isFalse(2); /// The Constructor of the flag. const Tristate(this.value); /// The value of the flag. final int value; /// If two semantics nodes both have this property, they have conflict and can't be merged. bool hasConflict(Tristate other) => this != Tristate.none && other != Tristate.none; /// Semanitcs nodes will only be merged when they are not in conflict. Tristate merge(Tristate other) { if (this == Tristate.isTrue || other == Tristate.isTrue) { return Tristate.isTrue; } if (this == Tristate.isFalse || other == Tristate.isFalse) { return Tristate.isFalse; } return Tristate.none; } /// Convert a Tristate flag to bool or null. bool? toBoolOrNull() { switch (this) { case Tristate.none: return null; case Tristate.isTrue: return true; case Tristate.isFalse: return false; } } } /// Describes how a semantic node should behave during hit testing. /// /// This enum allows the framework to communicate pointer event handling /// behavior to the platform's accessibility layer. Different platforms /// may implement this behavior differently based on their accessibility /// infrastructure. /// /// See also: /// * [SemanticsUpdateBuilder.updateNode], which accepts this enum. enum SemanticsHitTestBehavior { /// Defer to the platform's default hit test behavior inference. /// /// When set to defer, the platform will infer the appropriate behavior /// based on the semantic node's properties such as interactive behaviors, /// route scoping, etc. /// /// On the web, the default inferred behavior is `transparent` for /// non-interactive semantic nodes, allowing pointer events to pass through. /// /// This is the default value and provides backward compatibility. defer, /// The semantic element is opaque to hit testing, consuming any pointer /// events within its bounds and preventing them from reaching elements /// behind it in Z-order (siblings and ancestors). /// /// Children of this node can still receive pointer events normally. /// Only elements that are visually behind this node (lower in the stacking /// order) will be blocked from receiving events. /// /// This is typically used for modal surfaces like dialogs, bottom sheets, /// and drawers that should block interaction with content behind them while /// still allowing interaction with their own content. /// /// Platform implementations: /// * On the web, this results in `pointer-events: all` CSS property. opaque, /// The semantic element is transparent to hit testing. /// /// Transparent nodes do not receive hit test events and allow events to pass /// through to elements behind them. /// /// Note: This differs from the framework's `HitTestBehavior.translucent`, /// which receives events while also allowing pass-through. Web's binary /// `pointer-events` property (all or none) cannot support true translucent /// behavior. /// /// Platform implementations: /// * On the web, this results in `pointer-events: none` CSS property. transparent, } /// Represents a collection of boolean flags that convey semantic information /// about a widget's accessibility state and properties. /// /// For example, These flags can indicate if an element is /// checkable, currently checked, selectable, or functions as a button. class SemanticsFlags extends NativeFieldWrapperClass1 { /// Creates a set of semantics flags that describe various states of a widget. /// All flags default to `false` unless specified. SemanticsFlags({ this.isChecked = CheckedState.none, this.isSelected = Tristate.none, this.isEnabled = Tristate.none, this.isToggled = Tristate.none, this.isExpanded = Tristate.none, this.isRequired = Tristate.none, this.isFocused = Tristate.none, this.isButton = false, this.isTextField = false, this.isInMutuallyExclusiveGroup = false, this.isHeader = false, this.isObscured = false, this.scopesRoute = false, this.namesRoute = false, this.isHidden = false, this.isImage = false, this.isLiveRegion = false, this.hasImplicitScrolling = false, this.isMultiline = false, this.isReadOnly = false, this.isLink = false, this.isSlider = false, this.isKeyboardKey = false, this.isAccessibilityFocusBlocked = false, }) { _initSemanticsFlags( this, isChecked.value, isSelected.value, isEnabled.value, isToggled.value, isExpanded.value, isRequired.value, isFocused.value, isButton, isTextField, isInMutuallyExclusiveGroup, isHeader, isObscured, scopesRoute, namesRoute, isHidden, isImage, isLiveRegion, hasImplicitScrolling, isMultiline, isReadOnly, isLink, isSlider, isKeyboardKey, isAccessibilityFocusBlocked, ); } @Native< Void Function( Handle, Int, Int, Int, Int, Int, Int, Int, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, ) >(symbol: 'NativeSemanticsFlags::initSemanticsFlags') external static void _initSemanticsFlags( SemanticsFlags instance, int isChecked, int isSelected, int isEnabled, int isToggled, int isExpanded, int isRequired, int isFocused, bool isButton, bool isTextField, bool isInMutuallyExclusiveGroup, bool isHeader, bool isObscured, bool scopesRoute, bool namesRoute, bool isHidden, bool isImage, bool isLiveRegion, bool hasImplicitScrolling, bool isMultiline, bool isReadOnly, bool isLink, bool isSlider, bool isKeyboardKey, bool isAccessibilityFocusBlocked, ); /// The set of semantics flags with every flag set to false. static SemanticsFlags none = SemanticsFlags(); /// {@macro dart.ui.semantics.hasCheckedState} final CheckedState isChecked; /// {@macro dart.ui.semantics.isSelected} final Tristate isSelected; /// {@macro dart.ui.semantics.isEnabled} final Tristate isEnabled; /// {@macro dart.ui.semantics.isToggled} final Tristate isToggled; /// {@macro dart.ui.semantics.isExpanded} final Tristate isExpanded; /// {@macro dart.ui.semantics.isRequired} final Tristate isRequired; /// {@macro dart.ui.semantics.isFocused} final Tristate isFocused; /// whether this node's accessibility focus is blocked. /// /// If `true`, this node is not accessibility focusable. /// If `false`, the a11y focusability is determined based on /// the node's role and other properties, such as whether it is a button. /// /// This is for accessibility focus, which is the focus used by screen readers /// like TalkBack and VoiceOver. It is different from input focus, which is /// usually held by the element that currently responds to keyboard inputs. final bool isAccessibilityFocusBlocked; /// {@macro dart.ui.semantics.isButton} final bool isButton; /// {@macro dart.ui.semantics.isTextField} final bool isTextField; /// {@macro dart.ui.semantics.isInMutuallyExclusiveGroup} final bool isInMutuallyExclusiveGroup; /// {@macro dart.ui.semantics.isHeader} final bool isHeader; /// {@macro dart.ui.semantics.isObscured} final bool isObscured; /// {@macro dart.ui.semantics.scopesRoute} final bool scopesRoute; /// {@macro dart.ui.semantics.namesRoute} final bool namesRoute; /// {@macro dart.ui.semantics.isHidden} final bool isHidden; /// {@macro dart.ui.semantics.isImage} final bool isImage; /// {@macro dart.ui.semantics.isLiveRegion} final bool isLiveRegion; /// {@macro dart.ui.semantics.hasImplicitScrolling} final bool hasImplicitScrolling; /// {@macro dart.ui.semantics.isMultiline} final bool isMultiline; /// {@macro dart.ui.semantics.isReadOnly} final bool isReadOnly; /// {@macro dart.ui.semantics.isLink} final bool isLink; /// {@macro dart.ui.semantics.isSlider} final bool isSlider; /// {@macro dart.ui.semantics.isKeyboardKey} final bool isKeyboardKey; /// Combines two sets of flags, such that if a flag it set to true in any of the two sets, the resulting set contains that flag set to true. SemanticsFlags merge(SemanticsFlags other) { return SemanticsFlags( isChecked: isChecked.merge(other.isChecked), isSelected: isSelected.merge(other.isSelected), isEnabled: isEnabled.merge(other.isEnabled), isToggled: isToggled.merge(other.isToggled), isExpanded: isExpanded.merge(other.isExpanded), isRequired: isRequired.merge(other.isRequired), isFocused: isFocused.merge(other.isFocused), isButton: isButton || other.isButton, isTextField: isTextField || other.isTextField, isInMutuallyExclusiveGroup: isInMutuallyExclusiveGroup || other.isInMutuallyExclusiveGroup, isHeader: isHeader || other.isHeader, isObscured: isObscured || other.isObscured, scopesRoute: scopesRoute || other.scopesRoute, namesRoute: namesRoute || other.namesRoute, isHidden: isHidden || other.isHidden, isImage: isImage || other.isImage, isLiveRegion: isLiveRegion || other.isLiveRegion, hasImplicitScrolling: hasImplicitScrolling || other.hasImplicitScrolling, isMultiline: isMultiline || other.isMultiline, isReadOnly: isReadOnly || other.isReadOnly, isLink: isLink || other.isLink, isSlider: isSlider || other.isSlider, isKeyboardKey: isKeyboardKey || other.isKeyboardKey, isAccessibilityFocusBlocked: isAccessibilityFocusBlocked || other.isAccessibilityFocusBlocked, ); } /// Copy the semantics flags, with some of them optionally replaced. SemanticsFlags copyWith({ CheckedState? isChecked, Tristate? isSelected, Tristate? isEnabled, Tristate? isToggled, Tristate? isExpanded, Tristate? isRequired, Tristate? isFocused, bool? isButton, bool? isTextField, bool? isInMutuallyExclusiveGroup, bool? isHeader, bool? isObscured, bool? scopesRoute, bool? namesRoute, bool? isHidden, bool? isImage, bool? isLiveRegion, bool? hasImplicitScrolling, bool? isMultiline, bool? isReadOnly, bool? isLink, bool? isSlider, bool? isKeyboardKey, bool? isAccessibilityFocusBlocked, }) { return SemanticsFlags( isChecked: isChecked ?? this.isChecked, isSelected: isSelected ?? this.isSelected, isButton: isButton ?? this.isButton, isTextField: isTextField ?? this.isTextField, isFocused: isFocused ?? this.isFocused, isEnabled: isEnabled ?? this.isEnabled, isInMutuallyExclusiveGroup: isInMutuallyExclusiveGroup ?? this.isInMutuallyExclusiveGroup, isHeader: isHeader ?? this.isHeader, isObscured: isObscured ?? this.isObscured, scopesRoute: scopesRoute ?? this.scopesRoute, namesRoute: namesRoute ?? this.namesRoute, isHidden: isHidden ?? this.isHidden, isImage: isImage ?? this.isImage, isLiveRegion: isLiveRegion ?? this.isLiveRegion, isToggled: isToggled ?? this.isToggled, hasImplicitScrolling: hasImplicitScrolling ?? this.hasImplicitScrolling, isMultiline: isMultiline ?? this.isMultiline, isReadOnly: isReadOnly ?? this.isReadOnly, isLink: isLink ?? this.isLink, isSlider: isSlider ?? this.isSlider, isKeyboardKey: isKeyboardKey ?? this.isKeyboardKey, isExpanded: isExpanded ?? this.isExpanded, isRequired: isRequired ?? this.isRequired, isAccessibilityFocusBlocked: isAccessibilityFocusBlocked ?? this.isAccessibilityFocusBlocked, ); } @override bool operator ==(Object other) => identical(this, other) || other is SemanticsFlags && runtimeType == other.runtimeType && isChecked == other.isChecked && isSelected == other.isSelected && isEnabled == other.isEnabled && isToggled == other.isToggled && isExpanded == other.isExpanded && isRequired == other.isRequired && isFocused == other.isFocused && isButton == other.isButton && isTextField == other.isTextField && isInMutuallyExclusiveGroup == other.isInMutuallyExclusiveGroup && isHeader == other.isHeader && isObscured == other.isObscured && scopesRoute == other.scopesRoute && namesRoute == other.namesRoute && isHidden == other.isHidden && isImage == other.isImage && isLiveRegion == other.isLiveRegion && hasImplicitScrolling == other.hasImplicitScrolling && isMultiline == other.isMultiline && isReadOnly == other.isReadOnly && isLink == other.isLink && isSlider == other.isSlider && isKeyboardKey == other.isKeyboardKey && isAccessibilityFocusBlocked == other.isAccessibilityFocusBlocked; @override int get hashCode => Object.hashAll([ isChecked, isSelected, isEnabled, isToggled, isExpanded, isRequired, isFocused, isButton, isTextField, isInMutuallyExclusiveGroup, isHeader, isObscured, scopesRoute, namesRoute, isHidden, isImage, isLiveRegion, hasImplicitScrolling, isMultiline, isReadOnly, isLink, isSlider, isKeyboardKey, isAccessibilityFocusBlocked, ]); /// Convert flags to a list of string. List toStrings() { return [ if (isChecked != CheckedState.none) 'hasCheckedState', if (isChecked == CheckedState.isTrue) 'isChecked', if (isSelected == Tristate.isTrue) 'isSelected', if (isButton) 'isButton', if (isTextField) 'isTextField', if (isFocused == Tristate.isTrue) 'isFocused', if (isEnabled != Tristate.none) 'hasEnabledState', if (isEnabled == Tristate.isTrue) 'isEnabled', if (isInMutuallyExclusiveGroup) 'isInMutuallyExclusiveGroup', if (isHeader) 'isHeader', if (isObscured) 'isObscured', if (scopesRoute) 'scopesRoute', if (namesRoute) 'namesRoute', if (isHidden) 'isHidden', if (isImage) 'isImage', if (isLiveRegion) 'isLiveRegion', if (isToggled != Tristate.none) 'hasToggledState', if (isToggled == Tristate.isTrue) 'isToggled', if (hasImplicitScrolling) 'hasImplicitScrolling', if (isMultiline) 'isMultiline', if (isReadOnly) 'isReadOnly', if (isFocused != Tristate.none) 'isFocusable', if (isAccessibilityFocusBlocked) 'isAccessibilityFocusBlocked', if (isLink) 'isLink', if (isSlider) 'isSlider', if (isKeyboardKey) 'isKeyboardKey', if (isChecked == CheckedState.mixed) 'isCheckStateMixed', if (isExpanded != Tristate.none) 'hasExpandedState', if (isExpanded == Tristate.isTrue) 'isExpanded', if (isSelected != Tristate.none) 'hasSelectedState', if (isRequired != Tristate.none) 'hasRequiredState', if (isRequired == Tristate.isTrue) 'isRequired', ]; } @Deprecated( 'Use hasConflictingFlags instead.' 'This feature was deprecated after v3.39.0-0.0.pre', ) /// Checks if any of the boolean semantic flags are set to true /// in both this instance and the [other] instance. bool hasRepeatedFlags(SemanticsFlags other) { return isChecked.hasConflict(other.isChecked) || isSelected.hasConflict(other.isSelected) || isEnabled.hasConflict(other.isEnabled) || isToggled.hasConflict(other.isToggled) || isEnabled.hasConflict(other.isEnabled) || isExpanded.hasConflict(other.isExpanded) || isRequired.hasConflict(other.isRequired) || isFocused.hasConflict(other.isFocused) || (isButton && other.isButton) || (isTextField && other.isTextField) || (isInMutuallyExclusiveGroup && other.isInMutuallyExclusiveGroup) || (isHeader && other.isHeader) || (isObscured && other.isObscured) || (scopesRoute && other.scopesRoute) || (namesRoute && other.namesRoute) || (isHidden && other.isHidden) || (isImage && other.isImage) || (isLiveRegion && other.isLiveRegion) || (hasImplicitScrolling && other.hasImplicitScrolling) || (isMultiline && other.isMultiline) || (isReadOnly && other.isReadOnly) || (isLink && other.isLink) || (isSlider && other.isSlider) || (isKeyboardKey && other.isKeyboardKey); } /// Checks if any flags are conflicted in this instance and the [other] instance. bool hasConflictingFlags(SemanticsFlags other) { return isChecked.hasConflict(other.isChecked) || isSelected.hasConflict(other.isSelected) || isEnabled.hasConflict(other.isEnabled) || isToggled.hasConflict(other.isToggled) || isEnabled.hasConflict(other.isEnabled) || isExpanded.hasConflict(other.isExpanded) || isRequired.hasConflict(other.isRequired) || isFocused.hasConflict(other.isFocused) || (isButton && other.isButton) || (isTextField && other.isTextField) || (isInMutuallyExclusiveGroup && other.isInMutuallyExclusiveGroup) || (isHeader && other.isHeader) || (isObscured && other.isObscured) || (scopesRoute && other.scopesRoute) || (namesRoute && other.namesRoute) || (isHidden && other.isHidden) || (isImage && other.isImage) || (isLiveRegion && other.isLiveRegion) || (hasImplicitScrolling && other.hasImplicitScrolling) || (isMultiline && other.isMultiline) || (isReadOnly && other.isReadOnly) || (isLink && other.isLink) || (isSlider && other.isSlider) || (isKeyboardKey && other.isKeyboardKey) || (isAccessibilityFocusBlocked != other.isAccessibilityFocusBlocked); } } /// The validation result of a form field. /// /// The type, shape, and correctness of the value is specific to the kind of /// form field used. For example, a phone number text field may check that the /// value is a properly formatted phone number, and/or that the phone number has /// the right area code. A group of radio buttons may validate that the user /// selected at least one radio option. enum SemanticsValidationResult { /// The node has no validation information attached to it. /// /// This is the default value. Most semantics nodes do not contain validation /// information. Typically, only nodes that are part of an input form - text /// fields, checkboxes, radio buttons, dropdowns - are validated and attach /// validation results to their corresponding semantics nodes. none, /// The entered value is valid, and no error should be displayed to the user. valid, /// The entered value is invalid, and an error message should be communicated /// to the user. invalid, } // When adding a new StringAttribute, the classes in these files must be // updated as well. // * engine/src/flutter/lib/web_ui/lib/semantics.dart // * engine/src/flutter/lib/ui/semantics/string_attribute.h // * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java // * engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_api_test.dart // * engine/src/flutter/testing/dart/semantics_test.dart /// An abstract interface for string attributes that affects how assistive /// technologies, e.g. VoiceOver or TalkBack, treat the text. /// /// See also: /// /// * [AttributedString], where the string attributes are used. /// * [SpellOutStringAttribute], which causes the assistive technologies to /// spell out the string character by character when announcing the string. /// * [LocaleStringAttribute], which causes the assistive technologies to /// treat the string in the specific language. abstract base class StringAttribute extends NativeFieldWrapperClass1 { StringAttribute._({required this.range}); /// The range of the text to which this attribute applies. final TextRange range; /// Creates a new attribute with all properties copied except for range, which /// is updated to the specified value. /// /// For example, the [LocaleStringAttribute] specifies a [Locale] for its /// range of characters. Copying it will result in a new /// [LocaleStringAttribute] that has the same locale but an updated /// [TextRange]. StringAttribute copy({required TextRange range}); } /// A string attribute that causes the assistive technologies, e.g. VoiceOver, /// to spell out the string character by character. /// /// See also: /// /// * [AttributedString], where the string attributes are used. /// * [LocaleStringAttribute], which causes the assistive technologies to /// treat the string in the specific language. base class SpellOutStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// spell out when the assistive technologies announce the string. SpellOutStringAttribute({required TextRange range}) : super._(range: range) { _initSpellOutStringAttribute(this, range.start, range.end); } @Native( symbol: 'NativeStringAttribute::initSpellOutStringAttribute', ) external static void _initSpellOutStringAttribute( SpellOutStringAttribute instance, int start, int end, ); @override StringAttribute copy({required TextRange range}) { return SpellOutStringAttribute(range: range); } @override String toString() { return 'SpellOutStringAttribute($range)'; } } /// A string attribute that causes the assistive technologies, e.g. VoiceOver, /// to treat string as a certain language. /// /// See also: /// /// * [AttributedString], where the string attributes are used. /// * [SpellOutStringAttribute], which causes the assistive technologies to /// spell out the string character by character when announcing the string. base class LocaleStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// treated as the language specified by the [locale] when the assistive /// technologies announce the string. LocaleStringAttribute({required TextRange range, required this.locale}) : super._(range: range) { _initLocaleStringAttribute(this, range.start, range.end, locale.toLanguageTag()); } /// The language of this attribute. final Locale locale; @Native( symbol: 'NativeStringAttribute::initLocaleStringAttribute', ) external static void _initLocaleStringAttribute( LocaleStringAttribute instance, int start, int end, String locale, ); @override StringAttribute copy({required TextRange range}) { return LocaleStringAttribute(range: range, locale: locale); } @override String toString() { return 'LocaleStringAttribute($range, ${locale.toLanguageTag()})'; } } /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to /// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the /// user. abstract class SemanticsUpdateBuilder { /// Creates an empty [SemanticsUpdateBuilder] object. factory SemanticsUpdateBuilder() = _NativeSemanticsUpdateBuilder; /// Update the information associated with the node with the given `id`. /// /// The semantics nodes form a tree, with the root of the tree always having /// an id of zero. The `childrenInTraversalOrder` and `childrenInHitTestOrder` /// are the ids of the nodes that are immediate children of this node. The /// former enumerates children in traversal order, and the latter enumerates /// the same children in the hit test order. The two lists must have the same /// length and contain the same ids. They may only differ in the order the /// ids are listed in. For more information about different child orders, see /// [DebugSemanticsDumpOrder]. /// /// The system retains the nodes that are currently reachable from the root. /// A given update need not contain information for nodes that do not change /// in the update. If a node is not reachable from the root after an update, /// the node will be discarded from the tree. /// /// The `flags` are a bit field of [SemanticsFlag]s that apply to this node. /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this /// node, the [PlatformDispatcher.onSemanticsActionEvent] will be called with /// a [SemanticsActionEvent] specifying the action to be performed. Because /// the semantics tree is maintained asynchronously, the /// [PlatformDispatcher.onSemanticsActionEvent] callback might be called with /// an action that is no longer possible. /// /// The `identifier` is a string that describes the node for UI automation /// tools that work by querying the accessibility hierarchy, such as Android /// UI Automator, iOS XCUITest, or Appium. It's not exposed to users. /// /// The `label` is a string that describes this node. The `value` property /// describes the current value of the node as a string. The `increasedValue` /// string will become the `value` string after a [SemanticsAction.increase] /// action is performed. The `decreasedValue` string will become the `value` /// string after a [SemanticsAction.decrease] action is performed. The `hint` /// string describes what result an action performed on this node has. The /// reading direction of all these strings is given by `textDirection`. /// /// The `labelAttributes`, `valueAttributes`, `hintAttributes`, /// `increasedValueAttributes`, and `decreasedValueAttributes` are the lists of /// [StringAttribute] carried by the `label`, `value`, `hint`, `increasedValue`, /// and `decreasedValue` respectively. Their contents must not be changed during /// the semantics update. /// /// The `tooltip` is a string that describe additional information when user /// hover or long press on the backing widget of this semantics node. /// /// The fields `textSelectionBase` and `textSelectionExtent` describe the /// currently selected text within `value`. A value of -1 indicates no /// current text selection base or extent. /// /// The field `maxValueLength` is used to indicate that an editable text /// field has a limit on the number of characters entered. If it is -1 there /// is no limit on the number of characters entered. The field /// `currentValueLength` indicates how much of that limit has already been /// used up. When `maxValueLength` is >= 0, `currentValueLength` must also be /// >= 0, otherwise it should be specified to be -1. /// /// The field `platformViewId` references the platform view, whose semantics /// nodes will be added as children to this node. If a platform view is /// specified, `childrenInHitTestOrder` and `childrenInTraversalOrder` must /// be empty. A value of -1 indicates that this node is not associated with a /// platform view. /// /// For scrollable nodes `scrollPosition` describes the current scroll /// position in logical pixel. `scrollExtentMax` and `scrollExtentMin` /// describe the maximum and minimum in-range values that `scrollPosition` can /// be. Both or either may be infinity to indicate unbound scrolling. The /// value for `scrollPosition` can (temporarily) be outside this range, for /// example during an overscroll. `scrollChildren` is the count of the /// total number of child nodes that contribute semantics and `scrollIndex` /// is the index of the first visible child node that contributes semantics. /// /// The `traversalParent` specifies the ID of the semantics node that serves as /// the logical parent of this node for accessibility traversal. This /// parameter is only used by the web engine to establish parent-child /// relationships between nodes that are not directly connected in paint order. /// To ensure correct accessibility traversal, `traversalParent` should be set /// to the logical traversal parent node ID. This parameter is web-specific /// because other platforms can complete grafting when generating the /// semantics tree in traversal order. After grafting, the traversal order and /// hit-test order will be different, which is acceptable for other platforms. /// However, the web engine assumes these two orders are exactly the same, so /// grafting cannot be performed ahead of time on web. Instead, the traversal /// order is updated in the web engine by setting the `aria-owns` attribute /// through this parameter. A value of -1 indicates no special traversal /// parent. This parameter has no effect on other platforms. /// /// The `rect` is the region occupied by this node in its own coordinate /// system. /// /// The `transform` is a matrix that maps this node's coordinate system into /// its parent's coordinate system. /// /// The `headingLevel` describes that this node is a heading and the hierarchy /// level this node represents as a heading. A value of 0 indicates that this /// node is not a heading. A value of 1 or greater indicates that this node is /// a heading at the specified level. The valid value range is from 1 to 6, /// inclusive. This attribute is only used for Web platform, and it will have /// no effect on other platforms. /// /// The `linkUrl` describes the URI that this node links to. If the node is /// not a link, this should be an empty string. /// /// The `role` describes the role of this node. Defaults to /// [SemanticsRole.none] if not set. /// /// The `locale` describes the language of the content in this node. i.e. /// label, value, and hint. /// /// If `validationResult` is not null, indicates the result of validating a /// form field. If null, indicates that the node is not being validated, or /// that the result is unknown. Form fields that validate user input but do /// not use this argument should use other ways to communicate validation /// errors to the user, such as embedding validation error text in the label. /// /// The `hitTestBehavior` describes how this node should behave during hit /// testing. When set to [SemanticsHitTestBehavior.defer] (the default), the /// platform will infer appropriate behavior based on other semantic properties /// of the node itself (not inherited from parent). Different platforms may /// implement this differently. /// /// For example, modal surfaces like dialogs can set this to /// [SemanticsHitTestBehavior.opaque] to block pointer events from reaching /// content behind them, while non-interactive decorative elements can set it /// to [SemanticsHitTestBehavior.transparent] to allow pointer events to pass /// through. /// /// See also: /// /// * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/heading_role /// * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-level /// * [SemanticsValidationResult], that describes possible values for the /// `validationResult` argument. /// * [SemanticsHitTestBehavior], which describes how hit testing behaves. void updateNode({ required int id, required SemanticsFlags flags, required int actions, required int maxValueLength, required int currentValueLength, required int textSelectionBase, required int textSelectionExtent, required int platformViewId, required int scrollChildren, required int scrollIndex, required int traversalParent, required double scrollPosition, required double scrollExtentMax, required double scrollExtentMin, required Rect rect, required String identifier, required String label, required List labelAttributes, required String value, required List valueAttributes, required String increasedValue, required List increasedValueAttributes, required String decreasedValue, required List decreasedValueAttributes, required String hint, required List hintAttributes, required String tooltip, required TextDirection? textDirection, required Float64List transform, required Float64List hitTestTransform, required Int32List childrenInTraversalOrder, required Int32List childrenInHitTestOrder, required Int32List additionalActions, int headingLevel = 0, String linkUrl = '', SemanticsRole role = SemanticsRole.none, required List? controlsNodes, SemanticsValidationResult validationResult = SemanticsValidationResult.none, SemanticsHitTestBehavior hitTestBehavior = SemanticsHitTestBehavior.defer, required SemanticsInputType inputType, required Locale? locale, required String minValue, required String maxValue, }); /// Update the custom semantics action associated with the given `id`. /// /// The name of the action exposed to the user is the `label`. For overridden /// standard actions this value is ignored. /// /// The `hint` should describe what happens when an action occurs, not the /// manner in which a tap is accomplished. For example, use "delete" instead /// of "double tap to delete". /// /// The text direction of the `hint` and `label` is the same as the global /// window. /// /// For overridden standard actions, `overrideId` corresponds with a /// [SemanticsAction.index] value. For custom actions this argument should not be /// provided. void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}); /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// /// The returned object can be passed to [PlatformDispatcher.updateSemantics] /// to actually update the semantics retained by the system. /// /// This object is unusable after calling build. SemanticsUpdate build(); } base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implements SemanticsUpdateBuilder { _NativeSemanticsUpdateBuilder() { _constructor(); } @Native(symbol: 'SemanticsUpdateBuilder::Create') external void _constructor(); @override void updateNode({ required int id, required SemanticsFlags flags, required int actions, required int maxValueLength, required int currentValueLength, required int textSelectionBase, required int textSelectionExtent, required int platformViewId, required int scrollChildren, required int scrollIndex, required int traversalParent, required double scrollPosition, required double scrollExtentMax, required double scrollExtentMin, required Rect rect, required String identifier, required String label, required List labelAttributes, required String value, required List valueAttributes, required String increasedValue, required List increasedValueAttributes, required String decreasedValue, required List decreasedValueAttributes, required String hint, required List hintAttributes, required String tooltip, required TextDirection? textDirection, required Float64List transform, required Float64List hitTestTransform, required Int32List childrenInTraversalOrder, required Int32List childrenInHitTestOrder, required Int32List additionalActions, int headingLevel = 0, String linkUrl = '', SemanticsRole role = SemanticsRole.none, required List? controlsNodes, SemanticsValidationResult validationResult = SemanticsValidationResult.none, SemanticsHitTestBehavior hitTestBehavior = SemanticsHitTestBehavior.defer, required SemanticsInputType inputType, required Locale? locale, required String minValue, required String maxValue, }) { assert(_matrix4IsValid(transform)); assert( headingLevel >= 0 && headingLevel <= 6, 'Heading level must be between 1 and 6, or 0 to indicate that this node is not a heading.', ); _updateNode( id, flags, actions, maxValueLength, currentValueLength, textSelectionBase, textSelectionExtent, platformViewId, scrollChildren, scrollIndex, traversalParent, scrollPosition, scrollExtentMax, scrollExtentMin, rect.left, rect.top, rect.right, rect.bottom, identifier, label, labelAttributes, value, valueAttributes, increasedValue, increasedValueAttributes, decreasedValue, decreasedValueAttributes, hint, hintAttributes, tooltip, textDirection != null ? textDirection.index + 1 : 0, transform, hitTestTransform, childrenInTraversalOrder, childrenInHitTestOrder, additionalActions, headingLevel, linkUrl, role.index, controlsNodes, validationResult.index, hitTestBehavior.index, inputType.index, locale?.toLanguageTag() ?? '', minValue, maxValue, ); } @Native< Void Function( Pointer, Int32, Handle, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Int32, Handle, Handle, Handle, Handle, Handle, Int32, Handle, Int32, Handle, Int32, Int32, Int32, Handle, Handle, Handle, ) >(symbol: 'SemanticsUpdateBuilder::updateNode') external void _updateNode( int id, SemanticsFlags flags, int actions, int maxValueLength, int currentValueLength, int textSelectionBase, int textSelectionExtent, int platformViewId, int scrollChildren, int scrollIndex, int traversalParent, double scrollPosition, double scrollExtentMax, double scrollExtentMin, double left, double top, double right, double bottom, String? identifier, String label, List labelAttributes, String value, List valueAttributes, String increasedValue, List increasedValueAttributes, String decreasedValue, List decreasedValueAttributes, String hint, List hintAttributes, String tooltip, int textDirection, Float64List transform, Float64List hitTestTransform, Int32List childrenInTraversalOrder, Int32List childrenInHitTestOrder, Int32List additionalActions, int headingLevel, String linkUrl, int role, List? controlsNodes, int validationResultIndex, int hitTestBehaviorIndex, int inputType, String locale, String minValue, String maxValue, ); @override void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) { _updateCustomAction(id, label ?? '', hint ?? '', overrideId); } @Native, Int32, Handle, Handle, Int32)>( symbol: 'SemanticsUpdateBuilder::updateCustomAction', ) external void _updateCustomAction(int id, String label, String hint, int overrideId); @override SemanticsUpdate build() { final semanticsUpdate = _NativeSemanticsUpdate._(); _build(semanticsUpdate); return semanticsUpdate; } @Native, Handle)>(symbol: 'SemanticsUpdateBuilder::build') external void _build(_NativeSemanticsUpdate outSemanticsUpdate); @override String toString() => 'SemanticsUpdateBuilder'; } /// An opaque object representing a batch of semantics updates. /// /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree /// using the [PlatformDispatcher.updateSemantics] method. abstract class SemanticsUpdate { /// Releases the resources used by this semantics update. /// /// After calling this function, the semantics update is cannot be used /// further. /// /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). void dispose(); } base class _NativeSemanticsUpdate extends NativeFieldWrapperClass1 implements SemanticsUpdate { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. _NativeSemanticsUpdate._(); @override @Native)>(symbol: 'SemanticsUpdate::dispose') external void dispose(); @override String toString() => 'SemanticsUpdate'; } ˆã-+4,.KP/O JKO+]PI`OIcJGZNG`MJNLN1CIHZIHZ<Q5A0EQ5Q)$1EQ5R*%3DFW1QLK>L2A3N?K2Q>N>HJ*QQN4N&!<H@LJ*QQNN&!IJ+A3NNMFKW+EQ5L$,EQ5M% 5JPJFQNNOBKQQPKOQGMOMKE9QQLOQCQE8JHHQ2BT3BZ&MP!#%#+'GI'AA'=?m$Zm$G 07O#D3 "  F? R?;@D8291Q7$ K M7<P5O# L?PBL, &N G2B0O  K5 OK/85K =Q,€„IFPg)POE`=EfQP!b 26&&". 7 . B?NI^0)3H4./-0.4.?-010.-25/:101,.3760660GKN6SPSH4eIP^QQ9P4S>Jb@.BPOJY69DE4FB5UQ.PPA/,QQM\-5RQV09O_-5V2;e/.2\+8MPFP0BN_.BNY4FLO2@.KNKY?@KHK'"-NJK!V/;H=\0O O_0QIPNNLOHNJA4-PF=_/J LOMN \-7OQPMPQNM8-V,6JS13LM3NNNEH1b4H>/X@.K KJY9P+PINJE!5T@dA/,QRW\5JSA/,NOR\$QJ-!/#%!-!C#%#'-!7%#%)1/#/#m$Vm$C .'6 % )J #"\cJ+E!G"I#'= B D#^WJ#?A/DIHIE CJMEOE KJ5GN4MO9 I :P'MKN JN9;H8Q5(%$$%%$-' . "#  8,%&?11 ,++,,+:;?LQNN**-<)*,-,*).6#-,(*/€�/3633663,5b,252,)8P52&,;eG& '.1+4..a+141+(7.O41%+:11d $ !.*,**,,*(.L(,.,(&0@.,$(2L 0   !)=97 &595D $&$ (958&$5F *@;7;;7'9B6054222442(.L(,.,(&0@.,$(0T354222442(.L(,.,(&0@.,$(2L+MOQM(!=PONAP P I7=YR:K>AMOK2G,=Q)L;F4O4AK2=JEP@0B5&  52 .O+AMO;JK(dV&8@3$  5@ G6AQ (8DKOQMOPMP!OOO0OQQPN;P,MOHMPOOPMJBRSSOHLI-KOAMP7OJNPIIQLNINORHIRQNHQQPPNK?KO&QPQNP$N2>'LNNNLPMORN"?NPP\_M&N#!%$&!!"$%% 44$=$=3+$+1/*-*QO+IP.MO!MGT`PP?3J($K  #!%$&!!"$%% 44$=$=3+$+1/*-*QO+( .b     ; %       2 ++44*"(&!! aBG:X 8YC 1@HM;!<JK%`JJ L *dart:ui/semantics.dart À^Àh³À^eÀfdÀ^{À_ À_À_4À_D—ìÀdÙÀ_—,org-dartlang-sdk:///flutter/lib/ui/text.dartÀ=$// Copyright 2013 The Flutter Authors. 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.ui; /// A [TextStyle.height] value that indicates the text span should take /// the height defined by the font, which may not be exactly the height of /// [TextStyle.fontSize]. // To change the sentinel value, search for "kTextHeightNone" in the source code. const double kTextHeightNone = 0.0; /// Whether to use the italic type variation of glyphs in the font. /// /// Some modern fonts allow this to be selected in a more fine-grained manner. /// See [FontVariation.italic] for details. /// /// Italic type is distinct from slanted glyphs. To control the slant of a /// glyph, consider the [FontVariation.slant] font feature. enum FontStyle { /// Use the upright ("Roman") glyphs. normal, /// Use glyphs that have a more pronounced angle and typically a cursive style /// ("italic type"). italic, } /// The thickness of the glyphs used to draw the text. /// /// Values must be in the range 1..1000. /// /// Fonts are typically weighted on a 9-point scale, which, for historical /// reasons, uses the names 100 to 900. In Flutter, these are named `w100` to /// `w900` and have the following conventional meanings: /// /// * [w100]: Thin, the thinnest font weight. /// /// * [w200]: Extra light. /// /// * [w300]: Light. /// /// * [w400]: Normal. The constant [FontWeight.normal] is an alias for this value. /// /// * [w500]: Medium. /// /// * [w600]: Semi-bold. /// /// * [w700]: Bold. The constant [FontWeight.bold] is an alias for this value. /// /// * [w800]: Extra-bold. /// /// * [w900]: Black, the thickest font weight. /// /// For example, the font named "Roboto Medium" is typically exposed as a font /// with the name "Roboto" and the weight [FontWeight.w500]. /// /// Some modern fonts allow the weight to be adjusted in arbitrary increments. /// When using these fonts, applications can specify [FontWeight] instances /// constructed using values other than the predefined values. For these fonts, /// [FontWeight] will set the value of the `wght` axis (producing the same /// results as explicitly setting that attribute using [FontVariation.weight]). class FontWeight { /// Creates a [FontWeight] object, which can be added to a [TextStyle] to /// select the thickness of a font's glyphs. const FontWeight(this.value) : assert(value >= 1, 'Font weight must be between 1 and 1000'), assert(value <= 1000, 'Font weight must be between 1 and 1000'); /// The encoded integer value of this font weight. @Deprecated('Use value, which is more precise.') int get index => (value ~/ 100 - 1).clamp(0, 8); /// The thickness value of this font weight. final int value; /// Thin, the least thick. static const FontWeight w100 = FontWeight(100); /// Extra-light. static const FontWeight w200 = FontWeight(200); /// Light. static const FontWeight w300 = FontWeight(300); /// Normal / regular / plain. static const FontWeight w400 = FontWeight(400); /// Medium. static const FontWeight w500 = FontWeight(500); /// Semi-bold. static const FontWeight w600 = FontWeight(600); /// Bold. static const FontWeight w700 = FontWeight(700); /// Extra-bold. static const FontWeight w800 = FontWeight(800); /// Black, the most thick. static const FontWeight w900 = FontWeight(900); /// The default font weight. static const FontWeight normal = w400; /// A commonly used font weight that is heavier than normal. static const FontWeight bold = w700; /// A list of all the font weights. static const List values = [ w100, w200, w300, w400, w500, w600, w700, w800, w900, ]; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is FontWeight && other.value == value; } @override int get hashCode => value; /// Linearly interpolates between two font weights. /// /// If both `a` and `b` are null, then this method will return null. Otherwise, /// any null values for `a` or `b` are interpreted as equivalent to [normal] /// (also known as [w400]). /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). The result /// is clamped to the range [w100]–[w900]. /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static FontWeight? lerp(FontWeight? a, FontWeight? b, double t) { if (a == null && b == null) { return null; } return FontWeight( _lerpInt((a ?? normal).value, (b ?? normal).value, t).round().clamp(100, 900), ); } @override String toString() { if (value % 100 != 0) { return 'FontWeight($value)'; } return const { 0: 'FontWeight.w100', 1: 'FontWeight.w200', 2: 'FontWeight.w300', 3: 'FontWeight.w400', 4: 'FontWeight.w500', 5: 'FontWeight.w600', 6: 'FontWeight.w700', 7: 'FontWeight.w800', 8: 'FontWeight.w900', }[index]!; } } /// A feature tag and value that affect the selection of glyphs in a font. /// /// Different fonts support different features. Consider using a tool /// such as to examine your fonts to /// determine what features are available. /// /// {@tool sample} /// This example shows usage of several OpenType font features, /// including Small Caps (selected manually using the "smcp" code), /// old-style figures, fractional ligatures, and stylistic sets. /// /// ** See code in examples/api/lib/ui/text/font_feature.0.dart ** /// {@end-tool} /// /// Some fonts also support continuous font variations; see the [FontVariation] /// class. /// /// See also: /// /// * , /// Wikipedia's description of these typographic features. /// /// * , /// Microsoft's registry of these features. class FontFeature { /// Creates a [FontFeature] object, which can be added to a [TextStyle] to /// change how the engine selects glyphs when rendering text. /// /// `feature` is the four-character tag that identifies the feature. /// These tags are specified by font formats such as OpenType. /// /// `value` is the value that the feature will be set to. The behavior /// of the value depends on the specific feature. Many features are /// flags whose value can be 1 (when enabled) or 0 (when disabled). /// /// See const FontFeature(this.feature, [this.value = 1]) : assert(feature.length == 4, 'Feature tag must be exactly four characters long.'), assert(value >= 0, 'Feature value must be zero or a positive integer.'); /// Create a [FontFeature] object that enables the feature with the given tag. const FontFeature.enable(String feature) : this(feature, 1); /// Create a [FontFeature] object that disables the feature with the given tag. const FontFeature.disable(String feature) : this(feature, 0); // Features below should be alphabetic by feature tag. This makes it // easier to determine when a feature is missing so that we avoid // adding duplicates. // // The full list is extremely long, and many of the features are // language-specific, or indeed force-enabled for particular locales // by HarfBuzz, so we don't even attempt to be comprehensive here. // Features listed below are those we deemed "interesting enough" to // have their own constructor, mostly on the basis of whether we // could find a font where the feature had a useful effect that // could be demonstrated. // Start of feature tag list. // ------------------------------------------------------------------------ /// Access alternative glyphs. (`aalt`) /// /// This feature selects the given glyph variant for glyphs in the span. /// /// {@tool sample} /// The Raleway font supports several alternate glyphs. The code /// below shows how specific glyphs can be selected. With `aalt` set /// to zero, the default, the normal glyphs are used. With a /// non-zero value, Raleway substitutes small caps for lower case /// letters. With value 2, the lowercase "a" changes to a stemless /// "a", whereas the lowercase "t" changes to a vertical bar instead /// of having a curve. By targeting specific letters in the text /// (using [widgets.Text.rich]), the desired rendering for each glyph can be /// achieved. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_aalt.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_alternative.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.alternative(this.value) : feature = 'aalt'; /// Use alternative ligatures to represent fractions. (`afrc`) /// /// When this feature is enabled (and the font supports it), /// sequences of digits separated by U+002F SOLIDUS character (/) or /// U+2044 FRACTION SLASH (â�„) are replaced by ligatures that /// represent the corresponding fraction. These ligatures may differ /// from those used by the [FontFeature.fractions] feature. /// /// This feature overrides all other features. /// /// {@tool sample} /// The Ubuntu Mono font supports the `afrc` feature. It causes digits /// before slashes to become superscripted and digits after slashes to become /// subscripted. This contrasts to the effect seen with [FontFeature.fractions]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_afrc.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_alternative_fractions.0.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.fractions], which has a similar (but different) effect. /// * const FontFeature.alternativeFractions() : feature = 'afrc', value = 1; /// Enable contextual alternates. (`calt`) /// /// With this feature enabled, specific glyphs may be replaced by /// alternatives based on nearby text. /// /// {@tool sample} /// The Barriecito font supports the `calt` feature. It causes some /// letters in close proximity to other instances of themselves to /// use different glyphs, to give the appearance of more variation /// in the glyphs, rather than having each letter always use a /// particular glyph. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_calt.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_contextual_alternates.0.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.randomize], which is more a rarely supported but more /// powerful way to get a similar effect. /// * const FontFeature.contextualAlternates() : feature = 'calt', value = 1; /// Enable case-sensitive forms. (`case`) /// /// Some glyphs, for example parentheses or operators, are typically /// designed to fit nicely with mixed case, or even predominantly /// lowercase, text. When these glyphs are placed near strings of /// capital letters, they appear a little off-center. /// /// This feature, when supported by the font, causes these glyphs to /// be shifted slightly, or otherwise adjusted, so as to form a more /// aesthetically pleasing combination with capital letters. /// /// {@tool sample} /// The Piazzolla font supports the `case` feature. It causes /// parentheses, brackets, braces, guillemets, slashes, bullets, and /// some other glyphs (not shown below) to be shifted up slightly so /// that capital letters appear centered in comparison. When the /// feature is disabled, those glyphs are optimized for use with /// lowercase letters, and so capital letters appear to ride higher /// relative to the punctuation marks. /// /// The difference is very subtle. It may be most obvious when /// examining the square brackets compared to the capital A. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_case.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_case_sensitive_forms.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.caseSensitiveForms() : feature = 'case', value = 1; /// Select a character variant. (`cv01` through `cv99`) /// /// Fonts may have up to 99 character variant sets, numbered 1 /// through 99, each of which can be independently enabled or /// disabled. /// /// Related character variants are typically grouped into stylistic /// sets, controlled by the [FontFeature.stylisticSet] feature /// (`ssXX`). /// /// {@tool sample} /// The Source Code Pro font supports the `cvXX` feature for several /// characters. In the example below, variants 1 (`cv01`), 2 /// (`cv02`), and 4 (`cv04`) are selected. Variant 1 changes the /// rendering of the "a" character, variant 2 changes the lowercase /// "g" character, and variant 4 changes the lowercase "i" and "l" /// characters. There are also variants (not shown here) that /// control the rendering of various greek characters such as beta /// and theta. /// /// Notably, this can be contrasted with the stylistic sets, where /// the set which affects the "a" character also affects beta, and /// the set which affects the "g" character also affects theta and /// delta. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_cvXX.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_character_variant.0.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.stylisticSet], which allows for groups of characters /// variants to be selected at once, as opposed to individual character variants. /// * factory FontFeature.characterVariant(int value) { assert(value >= 1); assert(value <= 99); return FontFeature('cv${value.toString().padLeft(2, "0")}'); } /// Display digits as denominators. (`dnom`) /// /// This is typically used automatically by the font rendering /// system as part of the implementation of `frac` for the denominator /// part of fractions (see [FontFeature.fractions]). /// /// {@tool sample} /// The Piazzolla font supports the `dnom` feature. It causes /// the digits to be rendered smaller and near the bottom of the EM box. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_dnom.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_denominator.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.denominator() : feature = 'dnom', value = 1; /// Use ligatures to represent fractions. (`afrc`) /// /// When this feature is enabled (and the font supports it), /// sequences of digits separated by U+002F SOLIDUS character (/) or /// U+2044 FRACTION SLASH (â�„) are replaced by ligatures that /// represent the corresponding fraction. /// /// This feature may imply the [FontFeature.numerators] and /// [FontFeature.denominator] features. /// /// {@tool sample} /// The Ubuntu Mono font supports the `frac` feature. It causes /// digits around slashes to be turned into dedicated fraction /// glyphs. This contrasts to the effect seen with /// [FontFeature.alternativeFractions]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_frac.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_fractions.0.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.alternativeFractions], which has a similar (but different) effect. /// * const FontFeature.fractions() : feature = 'frac', value = 1; /// Use historical forms. (`hist`) /// /// Some fonts have alternatives for letters whose forms have changed /// through the ages. In the Latin alphabet, this is common for /// example with the long-form "s" or the Fraktur "k". This feature enables /// those alternative glyphs. /// /// This does not enable legacy ligatures, only single-character alternatives. /// To enable historical ligatures, use [FontFeature.historicalLigatures]. /// /// This feature may override other glyph-substitution features. /// /// {@tool sample} /// The Cardo font supports the `hist` feature specifically for the /// letter "s": it changes occurrences of that letter for the glyph /// used by U+017F LATIN SMALL LETTER LONG S. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_historical.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_historical_forms.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.historicalForms() : feature = 'hist', value = 1; /// Use historical ligatures. (`hlig`) /// /// Some fonts support ligatures that have fallen out of favor today, /// but were historically in common use. This feature enables those /// ligatures. /// /// For example, the "long s" glyph was historically typeset with /// characters such as "t" and "h" as a single ligature. /// /// This does not enable the legacy forms, only ligatures. See /// [FontFeature.historicalForms] to enable single characters to be /// replaced with their historical alternatives. Combining both is /// usually desired since the ligatures typically apply specifically /// to characters that have historical forms as well. For example, /// the historical forms feature might replace the "s" character /// with the "long s" (Å¿) character, while the historical ligatures /// feature might specifically apply to cases where "long s" is /// followed by other characters such as "t". In such cases, without /// the historical forms being enabled, the ligatures would only /// apply when the "long s" is used explicitly. /// /// This feature may override other glyph-substitution features. /// /// {@tool sample} /// The Cardo font supports the `hlig` feature. It has legacy /// ligatures for "VI" and "NT", and various ligatures involving the /// "long s". In the example below, both historical forms (`hist 1`) /// and historical ligatures (`hlig 1`) are enabled, so, for /// instance, "fish" becomes "fiÅ¿h" which is then rendered using a /// ligature for the last two characters. /// /// Similarly, the word "business" is turned into "buÅ¿ineſſ" by /// `hist`, and the `Å¿i` and `ſſ` pairs are ligated by `hlig`. /// Observe in particular the position of the dot of the "i" in /// "business" in the various combinations of these features. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_historical.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_historical_ligatures.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.historicalLigatures() : feature = 'hlig', value = 1; /// Use lining figures. (`lnum`) /// /// Some fonts have digits that, like lowercase latin letters, have /// both descenders and ascenders. In some situations, especially in /// conjunction with capital letters, this leads to an aesthetically /// questionable irregularity. Lining figures, on the other hand, /// have a uniform height, and align with the baseline and the /// height of capital letters. Conceptually, they can be thought of /// as "capital digits". /// /// This feature may conflict with [FontFeature.oldstyleFigures]. /// /// {@tool sample} /// The Sorts Mill Goudy font supports the `lnum` feature. It causes /// digits to fit more seamlessly with capital letters. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_lnum.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_lining_figures.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.liningFigures() : feature = 'lnum', value = 1; /// Use locale-specific glyphs. (`locl`) /// /// Some characters, most notably those in the Unicode Han /// Unification blocks, vary in presentation based on the locale in /// use. For example, the ideograph for "grass" (U+8349, è�‰) has a /// broken top line in Traditional Chinese, but a solid top line in /// Simplified Chinese, Japanese, Korean, and Vietnamese. This kind /// of variation also exists with other alphabets, for example /// Cyrillic characters as used in the Bulgarian and Serbian /// alphabets vary from their Russian counterparts. /// /// A particular font may default to the forms for the locale for /// which it was constructed, but still support alternative forms /// for other locales. When this feature is enabled, the locale (as /// specified using [painting.TextStyle.locale], for instance) is /// used to determine which glyphs to use when locale-specific /// alternatives exist. Disabling this feature causes the font /// rendering to ignore locale information and only use the default /// glyphs. /// /// This feature is enabled by default. Using /// `FontFeature.localeAware(enable: false)` disables the /// locale-awareness. (So does not specifying the locale in the /// first place, of course.) /// /// {@tool sample} /// The Noto Sans CJK font supports the `locl` feature for CJK characters. /// In this example, the `localeAware` feature is not explicitly used, as it is /// enabled by default. This example instead shows how to set the locale, /// thus demonstrating how Noto Sans adapts the glyph shapes to the locale. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_locl.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_locale_aware.0.dart ** /// {@end-tool} /// /// See also: /// /// * /// * /// * const FontFeature.localeAware({bool enable = true}) : feature = 'locl', value = enable ? 1 : 0; /// Display alternative glyphs for numerals (alternate annotation forms). (`nalt`) /// /// Replaces glyphs used in numbering lists (e.g. 1, 2, 3...; or a, b, c...) with notational /// variants that might be more typographically interesting. /// /// Fonts sometimes support multiple alternatives, and the argument /// selects the set to use (a positive integer, or 0 to disable the /// feature). The default set if none is specified is 1. /// /// {@tool sample} /// The Gothic A1 font supports several notational variant sets via /// the `nalt` feature. /// /// Set 1 changes the spacing of the glyphs. Set 2 parenthesizes the /// latin letters and reduces the numerals to subscripts. Set 3 /// circles the glyphs. Set 4 parenthesizes the digits. Set 5 uses /// reverse-video circles for the digits. Set 7 superscripts the /// digits. /// /// The code below shows how to select set 3. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_nalt.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_notational_forms.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.notationalForms([this.value = 1]) : feature = 'nalt', assert(value >= 0); /// Display digits as numerators. (`numr`) /// /// This is typically used automatically by the font rendering /// system as part of the implementation of `frac` for the numerator /// part of fractions (see [FontFeature.fractions]). /// /// {@tool sample} /// The Piazzolla font supports the `numr` feature. It causes /// the digits to be rendered smaller and near the top of the EM box. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_numr.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_numerators.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.numerators() : feature = 'numr', value = 1; /// Use old style figures. (`onum`) /// /// Some fonts have variants of the figures (e.g. the digit 9) that, /// when this feature is enabled, render with descenders under the /// baseline instead of being entirely above the baseline. If the /// default digits are lining figures, this allows the selection of /// digits that fit better with mixed case (uppercase and lowercase) /// text. /// /// This overrides [FontFeature.slashedZero] and may conflict with /// [FontFeature.liningFigures]. /// /// {@tool sample} /// The Piazzolla font supports the `onum` feature. It causes /// digits to extend below the baseline. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_onum.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_oldstyle_figures.0.dart ** /// {@end-tool} /// /// See also: /// /// * /// * const FontFeature.oldstyleFigures() : feature = 'onum', value = 1; /// Use ordinal forms for alphabetic glyphs. (`ordn`) /// /// Some fonts have variants of the alphabetic glyphs intended for /// use after numbers when expressing ordinals, as in "1st", "2nd", /// "3rd". This feature enables those alternative glyphs. /// /// This may override other features that substitute glyphs. /// /// {@tool sample} /// The Piazzolla font supports the `ordn` feature. It causes /// alphabetic glyphs to become smaller and superscripted. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ordn.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_ordinal_forms.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.ordinalForms() : feature = 'ordn', value = 1; /// Use proportional (varying width) figures. (`pnum`) /// /// For fonts that have both proportional and tabular (monospace) figures, /// this enables the proportional figures. /// /// This is mutually exclusive with [FontFeature.tabularFigures]. /// /// The default behavior varies from font to font. /// /// {@tool sample} /// The Kufam font supports the `pnum` feature. It causes the digits /// to become proportionally-sized, rather than all being the same /// width. In this font this is especially noticeable with the digit /// "1": normally, the 1 has very noticeable serifs in this /// sans-serif font, but with the proportionally figures enabled, /// the digit becomes much narrower. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_pnum.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_proportional_figures.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.proportionalFigures() : feature = 'pnum', value = 1; /// Randomize the alternate forms used in text. (`rand`) /// /// For example, this can be used with suitably-prepared handwriting fonts to /// vary the forms used for each character, so that, for instance, the word /// "cross-section" would be rendered with two different "c"s, two different "o"s, /// and three different "s"s. /// /// Contextual alternates ([FontFeature.contextualAlternates]) /// provide a similar effect in some fonts, without using /// randomness. /// /// See also: /// /// * const FontFeature.randomize() : feature = 'rand', value = 1; /// Enable stylistic alternates. (`salt`) /// /// Some fonts have alternative forms that are not tied to a /// particular purpose (such as being historical forms, or /// contextually relevant alternatives, or ligatures, etc). This /// font feature enables these purely stylistic alternatives. /// /// This may override other features that substitute glyphs. /// /// {@tool sample} /// The Source Code Pro font supports the `salt` feature. It causes /// some glyphs to be rendered differently, for example the "a" and /// "g" glyphs change from their typographically common /// double-storey forms to simpler single-storey forms, the dollar /// sign's line changes from discontinuous to continuous (and is /// angled), and the "0" rendering changes from a center dot to a /// slash. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_salt.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_stylistic_alternates.0.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.contextualAlternates], which is enables alternates specific to certain contexts. /// * const FontFeature.stylisticAlternates() : feature = 'salt', value = 1; /// Use scientific inferiors. (`sinf`) /// /// Some fonts have variants of the figures (e.g. the digit 2) that, /// when this feature is enabled, render in a manner more /// appropriate for subscripted digits ("inferiors") used in /// scientific contexts, e.g. the subscripts in chemical formulae. /// /// This may override other features that substitute glyphs. /// /// {@tool sample} /// The Piazzolla font supports the `sinf` feature. It causes /// digits to be smaller and subscripted. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_sinf.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_scientific_inferiors.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.scientificInferiors() : feature = 'sinf', value = 1; /// Select a stylistic set. (`ss01` through `ss20`) /// /// Fonts may have up to 20 stylistic sets, numbered 1 through 20, /// each of which can be independently enabled or disabled. /// /// For more fine-grained control, in some fonts individual /// character variants can also be controlled by the /// [FontFeature.characterVariant] feature (`cvXX`). /// /// {@tool sample} /// The Source Code Pro font supports the `ssXX` feature for several /// sets. In the example below, stylistic sets 2 (`ss02`), 3 /// (`ss03`), and 4 (`ss04`) are selected. Stylistic set 2 changes /// the rendering of the "a" character and the beta character, /// stylistic set 3 changes the lowercase "g", theta, and delta /// characters, and stylistic set 4 changes the lowercase "i" and /// "l" characters. /// /// This font also supports character variants (see /// [FontFeature.characterVariant]). /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ssXX_1.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_stylistic_set.0.dart ** /// {@end-tool} /// /// {@tool sample} /// The Piazzolla font supports the `ssXX` feature for more /// elaborate stylistic effects. Set 1 turns some Latin characters /// into Roman numerals, set 2 enables some ASCII characters to be /// used to create pretty arrows, and so forth. /// /// _These_ stylistic sets do _not_ correspond to character variants. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ssXX_2.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_stylistic_set.1.dart ** /// {@end-tool} /// /// See also: /// /// * [FontFeature.characterVariant], which allows for individual character /// variants to be selected, as opposed to entire sets. /// * factory FontFeature.stylisticSet(int value) { assert(value >= 1); assert(value <= 20); return FontFeature('ss${value.toString().padLeft(2, "0")}'); } /// Enable subscripts. (`subs`) /// /// This feature causes some fonts to change some glyphs to their subscripted form. /// /// It typically does not affect all glyphs, and so is not appropriate for generally causing /// all text to be subscripted. /// /// This may override other features that substitute glyphs. /// /// {@tool sample} /// The Piazzolla font supports the `subs` feature. It causes /// digits to be smaller and subscripted. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_subs.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_subscripts.0.dart ** /// {@end-tool} /// /// See also: /// /// * /// * [FontFeature.scientificInferiors], which is similar but intended specifically for /// subscripts used in scientific contexts. /// * [FontFeature.superscripts], which is similar but for subscripting. const FontFeature.subscripts() : feature = 'subs', value = 1; /// Enable superscripts. (`sups`) /// /// This feature causes some fonts to change some glyphs to their /// superscripted form. This may be more than just changing their /// position. For example, digits might change to lining figures /// (see [FontFeature.liningFigures]) in addition to being raised /// and shrunk. /// /// It typically does not affect all glyphs, and so is not /// appropriate for generally causing all text to be superscripted. /// /// This may override other features that substitute glyphs. /// /// {@tool sample} /// The Sorts Mill Goudy font supports the `sups` feature. It causes /// digits to be smaller, superscripted, and changes them to lining /// figures (so they are all the same height). /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_sups.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_superscripts.0.dart ** /// {@end-tool} /// /// See also: /// /// * /// * [FontFeature.subscripts], which is similar but for subscripting. const FontFeature.superscripts() : feature = 'sups', value = 1; /// Enable swash glyphs. (`swsh`) /// /// Some fonts have beautiful flourishes on some characters. These /// come in many forms, such as exaggerated serifs, long tails, long /// entry strokes, or other forms of decorative extensions to the /// base character. /// /// This feature enables the rendering of these flourishes. Some /// fonts have many swashes per character; the argument, if /// specified, selects which swash to use (0 disables them /// altogether). /// /// Some fonts have an absurd number of alternative swashes. For /// example, Adobe's Poetica famously has 63 different ampersand /// forms available through this feature! /// /// {@tool sample} /// The BioRhyme Expanded font supports the `swsh` feature specifically /// for the capital "Q" and "R" glyphs and the ampersand. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_swsh.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_swash.0.dart ** /// {@end-tool} /// /// See also: /// /// * /// * const FontFeature.swash([this.value = 1]) : feature = 'swsh', assert(value >= 0); /// Use tabular (monospace) figures. (`tnum`) /// /// For fonts that have both proportional (varying width) and tabular figures, /// this enables the tabular figures. Tabular figures are monospaced (all the /// same width), so that they align in tables of figures. /// /// This is mutually exclusive with [FontFeature.proportionalFigures]. /// /// The default behavior varies from font to font. /// /// {@tool sample} /// The Piazzolla font supports the `tnum` feature. It causes the /// digits to become uniformly-sized, rather than having variable /// widths. In this font this is especially noticeable with the /// digit "1"; with tabular figures enabled, the "1" digit is more /// widely spaced. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_tnum.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_tabular_figures.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.tabularFigures() : feature = 'tnum', value = 1; /// Use the slashed zero. (`zero`) /// /// Some fonts contain both a circular zero and a zero with a slash. This /// enables the use of the latter form. /// /// This is overridden by [FontFeature.oldstyleFigures]. /// /// {@tool sample} /// The Source Code Pro font supports the `zero` feature. It causes the /// zero digit to be drawn with a slash rather than the default rendering, /// which in this case has a dot through the zero rather than a slash. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_zero.png) /// /// ** See code in examples/api/lib/ui/text/font_feature.font_feature_slashed_zero.0.dart ** /// {@end-tool} /// /// See also: /// /// * const FontFeature.slashedZero() : feature = 'zero', value = 1; // ------------------------------------------------------------------------ // End of feature tags list. /// The tag that identifies the effect of this feature. Must consist of 4 /// ASCII characters (typically lowercase letters). /// /// These features are defined in a registry maintained by Microsoft: /// final String feature; /// The value assigned to this feature. /// /// Must be a positive integer. Many features are Boolean values that accept /// values of either 0 (feature is disabled) or 1 (feature is enabled). Other /// features have a bound range of values (which may be documented in these /// API docs for features that have dedicated constructors, and are generally /// documented in the official registry). In some cases the precise supported /// range depends on the font. /// /// See also: /// /// * final int value; static const int _kEncodedSize = 8; void _encode(ByteData byteData) { assert(feature.codeUnits.every((int c) => c >= 0x20 && c <= 0x7F)); for (var i = 0; i < 4; i++) { byteData.setUint8(i, feature.codeUnitAt(i)); } byteData.setInt32(4, value, _kFakeHostEndian); } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is FontFeature && other.feature == feature && other.value == value; } @override int get hashCode => Object.hash(feature, value); @override String toString() => "FontFeature('$feature', $value)"; } /// An axis tag and value that can be used to customize variable fonts. /// /// Some fonts are variable fonts that can generate a range of different /// font faces by altering the values of the font's design axes. /// /// For example: /// /// ```dart /// const TextStyle(fontVariations: [ui.FontVariation('slnt', -5.0)]) /// ``` /// /// Font variations are distinct from font features, as exposed by the /// [FontFeature] class. Where features can be enabled or disabled in a discrete /// manner, font variations provide a continuous axis of control. /// /// See also: /// /// * , /// which lists registered axis tags. /// /// * , /// an overview of the font variations technology. class FontVariation { /// Creates a [FontVariation] object, which can be added to a [TextStyle] to /// change the variable attributes of a font. /// /// `axis` is the four-character tag that identifies the design axis. /// OpenType lists the [currently registered axis /// tags](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). /// /// `value` is the value that the axis will be set to. The behavior /// depends on how the font implements the axis. const FontVariation(this.axis, this.value) : assert(axis.length == 4, 'Axis tag must be exactly four characters long.'), assert( value >= -32768.0 && value < 32768.0, 'Value must be representable as a signed 16.16 fixed-point number, i.e. it must be in this range: -32768.0 ≤ value < 32768.0', ); // Constructors below should be alphabetic by axis tag. This makes it easier // to determine when an axis is missing so that we avoid adding duplicates. // Start of axis tag list. // ------------------------------------------------------------------------ /// Variable font style. (`ital`) /// /// Varies the style of glyphs in the font between normal and italic. /// /// Values must in the range 0.0 (meaning normal, or Roman, as in /// [FontStyle.normal]) to 1.0 (meaning fully italic, as in /// [FontStyle.italic]). /// /// This is distinct from [FontVariation.slant], which leans the characters /// without changing the font style. /// /// See also: /// /// * const FontVariation.italic(this.value) : assert(value >= 0.0), assert(value <= 1.0), axis = 'ital'; /// Optical size optimization. (`opzs`) /// /// Changes the rendering of the font to be optimized for the given text size. /// Normally, the optical size of the font will be derived from the font size. /// /// This feature could be used when the text represents a particular physical /// font size, for example text in the representation of a hardcopy magazine, /// which does not correspond to the actual font size being used to render the /// text. By setting the optical size explicitly, font variations that might /// be applied as the text is zoomed will be fixed at the size being /// represented by the text. /// /// This feature could also be used to smooth animations. If a font varies its /// rendering as the font size is adjusted, it may appear to "quiver" (or, one /// might even say, "flutter") if the font size is animated. By setting a /// fixed optical size, the rendering can be fixed to one particular style as /// the text size animates. /// /// Values must be greater than zero, and are interpreted as points. A point /// is 1/72 of an inch, or 1.333 logical pixels (96/72). /// /// See also: /// /// * const FontVariation.opticalSize(this.value) : assert(value > 0.0), axis = 'opsz'; /// Variable font width. (`slnt`) /// /// Varies the slant of glyphs in the font. /// /// Values must be greater than -90.0 and less than +90.0, and represents the /// angle in _counter-clockwise_ degrees relative to "normal", at 0.0. /// /// For example, to lean the glyphs forward by 45 degrees, one would use /// `FontVariation.slant(-45.0)`. /// /// This is distinct from [FontVariation.italic], in that slant leans the /// characters without changing the font style. /// /// See also: /// /// * const FontVariation.slant(this.value) : assert(value > -90.0), assert(value < 90.0), axis = 'slnt'; /// Variable font width. (`wdth`) /// /// Varies the width of glyphs in the font. /// /// Values must be greater than zero, with no upper limit. 100.0 represents /// the "normal" width. Smaller values are "condensed", greater values are /// "extended". /// /// See also: /// /// * const FontVariation.width(this.value) : assert(value >= 0.0), axis = 'wdth'; /// Variable font weight. (`wght`) /// /// Applications should avoid using this and should instead declare font /// weight by specifying a [FontWeight], which will implicitly set this /// attribute. However, if a value is provided for this attribute, then it /// will override the [FontWeight]. /// /// Varies the stroke thickness of the font. /// /// Values must be in the range 1..1000, and are to be interpreted in a manner /// consistent with the values of [FontWeight]. For instance, `400` is the /// "normal" weight, and `700` is "bold". /// /// See also: /// /// * const FontVariation.weight(this.value) : assert(value >= 1), assert(value <= 1000), axis = 'wght'; // ------------------------------------------------------------------------ // End of axis tags list. /// The tag that identifies the design axis. /// /// An axis tag must consist of 4 ASCII characters. final String axis; /// The value assigned to this design axis. /// /// The range of usable values depends on the specification of the axis. /// /// While this property is represented as a [double] in this API /// ([binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)), /// fonts use the fixed-point 16.16 format to represent the value of font /// variations. This means that the actual range is -32768.0 to approximately /// 32767.999985 and in principle the smallest increment between two values is /// approximately 0.000015 (1/65536). /// /// Unfortunately for technical reasons the value is first converted to the /// [binary32 floating point /// format](https://en.wikipedia.org/wiki/Single-precision_floating-point_format), /// which only has 24 bits of precision. This means that for values outside /// the range -256.0 to 256.0, the smallest increment is larger than what is /// technically supported by OpenType. At the extreme edge of the range, the /// smallest increment is only approximately ±0.002. final double value; static const int _kEncodedSize = 8; void _encode(ByteData byteData) { assert(axis.codeUnits.every((int c) => c >= 0x20 && c <= 0x7F)); for (var i = 0; i < 4; i++) { byteData.setUint8(i, axis.codeUnitAt(i)); } byteData.setFloat32(4, value, _kFakeHostEndian); } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is FontVariation && other.axis == axis && other.value == value; } @override int get hashCode => Object.hash(axis, value); /// Linearly interpolates between two font variations. /// /// If the two variations have different axis tags, the interpolation switches /// abruptly from one to the other at t=0.5. Otherwise, the value is /// interpolated (see [lerpDouble]. /// /// The value is not clamped to the valid values of the axis tag, but it is /// clamped to the valid range of font variations values in general (the range /// of signed 16.16 fixed point numbers). /// /// The `t` argument represents position on the timeline, with 0.0 meaning /// that the interpolation has not started, returning `a` (or something /// equivalent to `a`), 1.0 meaning that the interpolation has finished, /// returning `b` (or something equivalent to `b`), and values in between /// meaning that the interpolation is at the relevant point on the timeline /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and /// 1.0, so negative values and values greater than 1.0 are valid (and can /// easily be generated by curves such as [Curves.elasticInOut]). /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static FontVariation? lerp(FontVariation? a, FontVariation? b, double t) { if (a?.axis != b?.axis || (a == null && b == null)) { return t < 0.5 ? a : b; } return FontVariation( a!.axis, clampDouble(lerpDouble(a.value, b!.value, t)!, -32768.0, 32768.0 - 1.0 / 65536.0), ); } @override String toString() => "FontVariation('$axis', $value)"; } /// The measurements of a character (or a sequence of visually connected /// characters) within a paragraph. /// /// See also: /// /// * [Paragraph.getGlyphInfoAt], which finds the [GlyphInfo] associated with /// a code unit in the text. /// * [Paragraph.getClosestGlyphInfoForOffset], which finds the [GlyphInfo] of /// the glyph(s) onscreen that's closest to the given [Offset]. final class GlyphInfo { /// Creates a [GlyphInfo] with the specified values. GlyphInfo( this.graphemeClusterLayoutBounds, this.graphemeClusterCodeUnitRange, this.writingDirection, ); GlyphInfo._( double left, double top, double right, double bottom, int graphemeStart, int graphemeEnd, bool isLTR, ) : graphemeClusterLayoutBounds = Rect.fromLTRB(left, top, right, bottom), graphemeClusterCodeUnitRange = TextRange(start: graphemeStart, end: graphemeEnd), writingDirection = isLTR ? TextDirection.ltr : TextDirection.rtl; /// The layout bounding rect of the associated character, in the paragraph's /// coordinates. /// /// This is **not** a tight bounding box that encloses the character's outline. /// The vertical extent reported is derived from the font metrics (instead of /// glyph metrics), and the horizontal extent is the horizontal advance of the /// character. final Rect graphemeClusterLayoutBounds; /// The UTF-16 range of the associated character in the text. final TextRange graphemeClusterCodeUnitRange; /// The writing direction within the [GlyphInfo]. final TextDirection writingDirection; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } return other is GlyphInfo && graphemeClusterLayoutBounds == other.graphemeClusterLayoutBounds && graphemeClusterCodeUnitRange == other.graphemeClusterCodeUnitRange && writingDirection == other.writingDirection; } @override int get hashCode => Object.hash(graphemeClusterLayoutBounds, graphemeClusterCodeUnitRange, writingDirection); @override String toString() => 'Glyph($graphemeClusterLayoutBounds, textRange: $graphemeClusterCodeUnitRange, direction: $writingDirection)'; } /// Whether and how to align text horizontally. // The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign. enum TextAlign { /// Align the text on the left edge of the container. left, /// Align the text on the right edge of the container. right, /// Align the text in the center of the container. center, /// Stretch lines of text that end with a soft line break to fill the width of /// the container. /// /// Lines that end with hard line breaks are aligned towards the [start] edge. justify, /// Align the text on the leading edge of the container. /// /// For left-to-right text ([TextDirection.ltr]), this is the left edge. /// /// For right-to-left text ([TextDirection.rtl]), this is the right edge. start, /// Align the text on the trailing edge of the container. /// /// For left-to-right text ([TextDirection.ltr]), this is the right edge. /// /// For right-to-left text ([TextDirection.rtl]), this is the left edge. end, } /// A horizontal line used for aligning text. enum TextBaseline { /// The horizontal line used to align the bottom of glyphs for alphabetic characters. /// /// This baseline is often used for alphabetical scripts like Latin, Greek, /// Cyrillic, etc. /// /// Characters with descenders (like 'p', 'g', or 'y') extend below this line. alphabetic, /// The horizontal line used to align ideographic characters. /// /// This baseline is often used for scripts with uniform square heights, /// like Chinese, Japanese, Korean, etc. ideographic, } /// A linear decoration to draw near the text. final class TextDecoration { const TextDecoration._(this._mask); /// Creates a decoration that paints the union of all the given decorations. factory TextDecoration.combine(List decorations) { var mask = 0; for (final decoration in decorations) { mask |= decoration._mask; } return TextDecoration._(mask); } final int _mask; /// The bit mask value that represents this decoration. int get maskValue => _mask; /// Whether this decoration will paint at least as much decoration as the given decoration. bool contains(TextDecoration other) { return (_mask | other._mask) == _mask; } /// Do not draw a decoration static const TextDecoration none = TextDecoration._(0x0); /// Draw a line underneath each line of text static const TextDecoration underline = TextDecoration._(0x1); /// Draw a line above each line of text static const TextDecoration overline = TextDecoration._(0x2); /// Draw a line through each line of text static const TextDecoration lineThrough = TextDecoration._(0x4); @override bool operator ==(Object other) { return other is TextDecoration && other._mask == _mask; } @override int get hashCode => _mask.hashCode; @override String toString() { if (_mask == 0) { return 'TextDecoration.none'; } final values = []; if (_mask & underline._mask != 0) { values.add('underline'); } if (_mask & overline._mask != 0) { values.add('overline'); } if (_mask & lineThrough._mask != 0) { values.add('lineThrough'); } if (values.length == 1) { return 'TextDecoration.${values[0]}'; } return 'TextDecoration.combine([${values.join(", ")}])'; } } /// The style in which to draw a text decoration enum TextDecorationStyle { /// Draw a solid line solid, /// Draw two lines double, /// Draw a dotted line dotted, /// Draw a dashed line dashed, /// Draw a sinusoidal line wavy, } /// {@macro dart.ui.textLeadingDistribution} enum TextLeadingDistribution { /// Distributes the [leading](https://en.wikipedia.org/wiki/Leading) /// of the text proportionally above and below the text, to the font's /// ascent/descent ratio. /// /// {@template dart.ui.leading} /// The leading of a text run is defined as /// `TextStyle.height * TextStyle.fontSize - TextStyle.fontSize`. When /// [TextStyle.height] is not set, the text run uses the leading specified by /// the font instead. /// {@endtemplate} proportional, /// Distributes the ["leading"](https://en.wikipedia.org/wiki/Leading) /// of the text evenly above and below the text (i.e. evenly above the /// font's ascender and below the descender). /// /// {@macro dart.ui.leading} /// /// The leading can become negative when [TextStyle.height] is smaller than /// 1.0. /// /// This is the default strategy used by CSS, known as /// ["half-leading"](https://www.w3.org/TR/css-inline-3/#half-leading). even, } /// {@template dart.ui.textHeightBehavior} /// Defines how to apply [TextStyle.height] over and under text. /// /// [TextHeightBehavior.applyHeightToFirstAscent] and /// [TextHeightBehavior.applyHeightToLastDescent] represent whether the /// [TextStyle.height] modifier will be applied to the corresponding metric. By /// default both properties are true, and [TextStyle.height] is applied as /// normal. When set to false, the font's default ascent will be used. /// /// [TextHeightBehavior.leadingDistribution] determines how the /// leading is distributed over and under text. This /// property applies before [TextHeightBehavior.applyHeightToFirstAscent] and /// [TextHeightBehavior.applyHeightToLastDescent]. /// /// {@endtemplate} class TextHeightBehavior { /// Creates a new TextHeightBehavior object. /// /// * applyHeightToFirstAscent: When true, the [TextStyle.height] modifier /// will be applied to the ascent of the first line. When false, the font's /// default ascent will be used. /// * applyHeightToLastDescent: When true, the [TextStyle.height] modifier /// will be applied to the descent of the last line. When false, the font's /// default descent will be used. /// * leadingDistribution: How the leading is distributed over and under /// text. /// /// All properties default to true (height modifications applied as normal). const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, this.leadingDistribution = TextLeadingDistribution.proportional, }); /// Creates a new TextHeightBehavior object from an encoded form. /// /// See [_encode] for the creation of the encoded form. const TextHeightBehavior._fromEncoded(int encoded, this.leadingDistribution) : applyHeightToFirstAscent = (encoded & 0x1) == 0, applyHeightToLastDescent = (encoded & 0x2) == 0; /// Whether to apply the [TextStyle.height] modifier to the ascent of the first /// line in the paragraph. /// /// When true, the [TextStyle.height] modifier will be applied to the ascent /// of the first line. When false, the font's default ascent will be used and /// the [TextStyle.height] will have no effect on the ascent of the first line. /// /// This property only has effect if a non-null [TextStyle.height] is specified. /// /// Defaults to true (height modifications applied as normal). final bool applyHeightToFirstAscent; /// Whether to apply the [TextStyle.height] modifier to the descent of the last /// line in the paragraph. /// /// When true, the [TextStyle.height] modifier will be applied to the descent /// of the last line. When false, the font's default descent will be used and /// the [TextStyle.height] will have no effect on the descent of the last line. /// /// This property only has effect if a non-null [TextStyle.height] is specified. /// /// Defaults to true (height modifications applied as normal). final bool applyHeightToLastDescent; /// {@template dart.ui.textLeadingDistribution} /// How the ["leading"](https://en.wikipedia.org/wiki/Leading) is distributed /// over and under the text. /// /// Does not affect layout when [TextStyle.height] is not specified. The /// leading can become negative, for example, when [TextLeadingDistribution.even] /// is used with a [TextStyle.height] much smaller than 1.0. /// {@endtemplate} /// /// Defaults to [TextLeadingDistribution.proportional], final TextLeadingDistribution leadingDistribution; /// Returns an encoded int representation of this object (excluding /// [leadingDistribution]). int _encode() { return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is TextHeightBehavior && other.applyHeightToFirstAscent == applyHeightToFirstAscent && other.applyHeightToLastDescent == applyHeightToLastDescent && other.leadingDistribution == leadingDistribution; } @override int get hashCode { return Object.hash( applyHeightToFirstAscent, applyHeightToLastDescent, leadingDistribution.index, ); } @override String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' 'applyHeightToLastDescent: $applyHeightToLastDescent, ' 'leadingDistribution: $leadingDistribution' ')'; } } /// Determines if lists [a] and [b] are deep equivalent. /// /// Returns true if the lists are both null, or if they are both non-null, have /// the same length, and contain the same elements in the same order. Returns /// false otherwise. bool _listEquals(List? a, List? b) { if (a == null) { return b == null; } if (b == null || a.length != b.length) { return false; } for (var index = 0; index < a.length; index += 1) { if (a[index] != b[index]) { return false; } } return true; } // This encoding must match the C++ version of ParagraphBuilder::pushStyle. // // The encoded array buffer has 8 elements. // // - Element 0: A bit field where the ith bit indicates whether the ith element // has a non-null value. Bits 8 to 12 indicate whether |fontFamily|, // |fontSize|, |letterSpacing|, |wordSpacing|, and |height| are non-null, // respectively. Bit 0 indicates the [TextLeadingDistribution] of the text // style. // // - Element 1: The |color| in ARGB with 8 bits per channel. // // - Element 2: A bit field indicating which text decorations are present in // the |textDecoration| list. The ith bit is set if there's a TextDecoration // with enum index i in the list. // // - Element 3: The |decorationColor| in ARGB with 8 bits per channel. // // - Element 4: The bit field of the |decorationStyle|. // // - Element 5: The index of the |fontWeight|. // // - Element 6: The enum index of the |fontStyle|. // // - Element 7: The enum index of the |textBaseline|. // Int32List _encodeTextStyle( Color? color, TextDecoration? decoration, Color? decorationColor, TextDecorationStyle? decorationStyle, double? decorationThickness, FontWeight? fontWeight, FontStyle? fontStyle, TextBaseline? textBaseline, String? fontFamily, List? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, Locale? locale, Paint? background, Paint? foreground, List? shadows, List? fontFeatures, List? fontVariations, ) { final result = Int32List(9); // The 0th bit of result[0] is reserved for leadingDistribution. if (color != null) { result[0] |= 1 << 1; result[1] = color.value; } if (decoration != null) { result[0] |= 1 << 2; result[2] = decoration._mask; } if (decorationColor != null) { result[0] |= 1 << 3; result[3] = decorationColor.value; } if (decorationStyle != null) { result[0] |= 1 << 4; result[4] = decorationStyle.index; } if (fontWeight != null) { result[0] |= 1 << 5; result[5] = fontWeight.value; } if (fontStyle != null) { result[0] |= 1 << 6; result[6] = fontStyle.index; } if (textBaseline != null) { result[0] |= 1 << 7; result[7] = textBaseline.index; } if (decorationThickness != null) { result[0] |= 1 << 8; } if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { result[0] |= 1 << 9; // Passed separately to native. } if (fontSize != null) { result[0] |= 1 << 10; // Passed separately to native. } if (letterSpacing != null) { result[0] |= 1 << 11; // Passed separately to native. } if (wordSpacing != null) { result[0] |= 1 << 12; // Passed separately to native. } if (height != null) { result[0] |= 1 << 13; // Passed separately to native. } if (locale != null) { result[0] |= 1 << 14; // Passed separately to native. } if (background != null) { result[0] |= 1 << 15; // Passed separately to native. } if (foreground != null) { result[0] |= 1 << 16; // Passed separately to native. } if (shadows != null) { result[0] |= 1 << 17; // Passed separately to native. } if (fontFeatures != null) { result[0] |= 1 << 18; // Passed separately to native. } if (fontVariations != null) { result[0] |= 1 << 19; // Passed separately to native. } return result; } /// An opaque object that determines the size, position, and rendering of text. /// /// See also: /// /// * [TextStyle](https://api.flutter.dev/flutter/painting/TextStyle-class.html), the class in the [painting] library. /// class TextStyle { /// Creates a new TextStyle object. /// /// * `color`: The color to use when painting the text. If this is specified, `foreground` must be null. /// * `decoration`: The decorations to paint near the text (e.g., an underline). /// * `decorationColor`: The color in which to paint the text decorations. /// * `decorationStyle`: The style in which to paint the text decorations (e.g., dashed). /// * `decorationThickness`: The thickness of the decoration as a multiplier on the thickness specified by the font. /// * `fontWeight`: The typeface thickness to use when painting the text (e.g., bold). /// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., italics). /// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). If a `fontFamilyFallback` is /// provided and `fontFamily` is not, then the first font family in `fontFamilyFallback` will take the position of /// the preferred font family. When a higher priority font cannot be found or does not contain a glyph, a lower /// priority font will be used. /// * `fontFamilyFallback`: An ordered list of the names of the fonts to fallback on when a glyph cannot /// be found in a higher priority font. When the `fontFamily` is null, the first font family in this list /// is used as the preferred font. Internally, the 'fontFamily` is concatenated to the front of this list. /// When no font family is provided through 'fontFamilyFallback' (null or empty) or `fontFamily`, then the /// platform default font will be used. /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting the text. /// * `letterSpacing`: The amount of space (in logical pixels) to add between each letter. /// * `wordSpacing`: The amount of space (in logical pixels) to add at each sequence of white-space (i.e. between each word). /// * `textBaseline`: The common baseline that should be aligned between this text span and its parent text span, or, for the root text spans, with the line box. /// * `height`: The height of this text span, as a multiplier of the font size. Setting the `height` to `kTextHeightNone` will allow the line height /// to take the height as defined by the font, which may not be exactly the height of the fontSize. /// * `leadingDistribution`: When `height` is set to a non-null that is not `kTextHeightNone`, how the extra vertical space should be distributed over and under the text. /// Defaults to the paragraph's [TextHeightBehavior] if left unspecified. /// * `locale`: The locale used to select region-specific glyphs. /// * `background`: The paint drawn as a background for the text. /// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null. /// * `fontFeatures`: The font features that should be applied to the text. /// * `fontVariations`: The font variations that should be applied to the text. TextStyle({ Color? color, TextDecoration? decoration, Color? decorationColor, TextDecorationStyle? decorationStyle, double? decorationThickness, FontWeight? fontWeight, FontStyle? fontStyle, TextBaseline? textBaseline, String? fontFamily, List? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, List? shadows, List? fontFeatures, List? fontVariations, }) : assert( color == null || foreground == null, 'Cannot provide both a color and a foreground\n' 'The color argument is just a shorthand for "foreground: Paint()..color = color".', ), _encoded = _encodeTextStyle( color, decoration, decorationColor, decorationStyle, decorationThickness, fontWeight, fontStyle, textBaseline, fontFamily, fontFamilyFallback, fontSize, letterSpacing, wordSpacing, height, locale, background, foreground, shadows, fontFeatures, fontVariations, ), _leadingDistribution = leadingDistribution, _fontFamily = fontFamily ?? '', _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, _letterSpacing = letterSpacing, _wordSpacing = wordSpacing, _height = height, _decorationThickness = decorationThickness, _locale = locale, _background = background, _foreground = foreground, _shadows = shadows, _fontFeatures = fontFeatures, _fontVariations = fontVariations; final Int32List _encoded; final String _fontFamily; final List? _fontFamilyFallback; final double? _fontSize; final double? _letterSpacing; final double? _wordSpacing; final double? _height; final double? _decorationThickness; final Locale? _locale; final Paint? _background; final Paint? _foreground; final List? _shadows; final List? _fontFeatures; final List? _fontVariations; final TextLeadingDistribution? _leadingDistribution; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } return other is TextStyle && other._leadingDistribution == _leadingDistribution && other._fontFamily == _fontFamily && other._fontSize == _fontSize && other._letterSpacing == _letterSpacing && other._wordSpacing == _wordSpacing && other._height == _height && other._decorationThickness == _decorationThickness && other._locale == _locale && other._background == _background && other._foreground == _foreground && _listEquals(other._encoded, _encoded) && _listEquals(other._shadows, _shadows) && _listEquals(other._fontFamilyFallback, _fontFamilyFallback) && _listEquals(other._fontFeatures, _fontFeatures) && _listEquals(other._fontVariations, _fontVariations); } @override int get hashCode { final List? shadows = _shadows; final List? fontFeatures = _fontFeatures; final List? fontVariations = _fontVariations; return Object.hash( Object.hashAll(_encoded), _leadingDistribution, _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, shadows == null ? null : Object.hashAll(shadows), _decorationThickness, fontFeatures == null ? null : Object.hashAll(fontFeatures), fontVariations == null ? null : Object.hashAll(fontVariations), ); } @override String toString() { final List? fontFamilyFallback = _fontFamilyFallback; final heightText = _encoded[0] & 0x02000 == 0x02000 ? (_height == kTextHeightNone ? 'kTextHeightNone' : '${_height}x') : 'unspecified'; return 'TextStyle(' 'color: ${_encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' 'decoration: ${_encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' 'decorationColor: ${_encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' 'decorationStyle: ${_encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' 'fontWeight: ${_encoded[0] & 0x00020 == 0x00020 ? FontWeight(_encoded[5]) : "unspecified"}, ' 'fontStyle: ${_encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' 'textBaseline: ${_encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' 'fontFamily: ${_encoded[0] & 0x00200 == 0x00200 && _fontFamily != '' ? _fontFamily : "unspecified"}, ' 'fontFamilyFallback: ${_encoded[0] & 0x00200 == 0x00200 && fontFamilyFallback != null && fontFamilyFallback.isNotEmpty ? fontFamilyFallback : "unspecified"}, ' 'fontSize: ${_encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' 'letterSpacing: ${_encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' 'wordSpacing: ${_encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' 'height: $heightText, ' 'leadingDistribution: ${_leadingDistribution ?? "unspecified"}, ' 'locale: ${_encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' 'background: ${_encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' 'foreground: ${_encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' 'shadows: ${_encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' 'fontFeatures: ${_encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}, ' 'fontVariations: ${_encoded[0] & 0x80000 == 0x80000 ? _fontVariations : "unspecified"}' ')'; } } // This encoding must match the C++ version ParagraphBuilder::build. // // The encoded array buffer has 6 elements. // // - Element 0: A bit mask indicating which fields are non-null. // Bit 0 is unused. Bits 1-n are set if the corresponding index in the // encoded array is non-null. The remaining bits represent fields that // are passed separately from the array. // // - Element 1: The enum index of the |textAlign|. // // - Element 2: The enum index of the |textDirection|. // // - Element 3: The index of the |fontWeight|. // // - Element 4: The enum index of the |fontStyle|. // // - Element 5: The value of |maxLines|. // // - Element 6: The encoded value of |textHeightBehavior|, except its leading // distribution. Int32List _encodeParagraphStyle( TextAlign? textAlign, TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, TextHeightBehavior? textHeightBehavior, FontWeight? fontWeight, FontStyle? fontStyle, StrutStyle? strutStyle, String? ellipsis, Locale? locale, ) { final result = Int32List(7); // also update paragraph_builder.cc if (textAlign != null) { result[0] |= 1 << 1; result[1] = textAlign.index; } if (textDirection != null) { result[0] |= 1 << 2; result[2] = textDirection.index; } if (fontWeight != null) { result[0] |= 1 << 3; result[3] = fontWeight.value; } if (fontStyle != null) { result[0] |= 1 << 4; result[4] = fontStyle.index; } if (maxLines != null) { result[0] |= 1 << 5; result[5] = maxLines; } if (textHeightBehavior != null) { result[0] |= 1 << 6; result[6] = textHeightBehavior._encode(); } if (fontFamily != null) { result[0] |= 1 << 7; // Passed separately to native. } if (fontSize != null) { result[0] |= 1 << 8; // Passed separately to native. } // Paragraph styles are unique in a paragraph, there is no inheriting so // height == null and height == kTextHeightNone are semantically equivalent. if (height != null && height != kTextHeightNone) { result[0] |= 1 << 9; // Passed separately to native. } if (strutStyle != null) { result[0] |= 1 << 10; // Passed separately to native. } if (ellipsis != null) { result[0] |= 1 << 11; // Passed separately to native. } if (locale != null) { result[0] |= 1 << 12; // Passed separately to native. } return result; } /// An opaque object that determines the configuration used by /// [ParagraphBuilder] to position lines within a [Paragraph] of text. class ParagraphStyle { /// Creates a new ParagraphStyle object. /// /// * `textAlign`: The alignment of the text within the lines of the /// paragraph. If the last line is ellipsized (see `ellipsis` below), the /// alignment is applied to that line after it has been truncated but before /// the ellipsis has been added. /// See: https://github.com/flutter/flutter/issues/9819 /// /// * `textDirection`: The directionality of the text, left-to-right (e.g. /// Norwegian) or right-to-left (e.g. Hebrew). This controls the overall /// directionality of the paragraph, as well as the meaning of /// [TextAlign.start] and [TextAlign.end] in the `textAlign` field. /// /// * `maxLines`: The maximum number of lines painted. Lines beyond this /// number are silently dropped. For example, if `maxLines` is 1, then only /// one line is rendered. If `maxLines` is null, but `ellipsis` is not null, /// then lines after the first one that overflows the width constraints are /// dropped. The width constraints are those set in the /// [ParagraphConstraints] object passed to the [Paragraph.layout] method. /// /// * `fontFamily`: The name of the font family to apply when painting the text, /// in the absence of a `textStyle` being attached to the span. /// /// * `fontSize`: The fallback size of glyphs (in logical pixels) to /// use when painting the text. This is used when there is no [TextStyle]. /// /// * `height`: The fallback height of the spans as a multiplier of the font /// size. The fallback height is used when no height is provided through /// [TextStyle.height]. Omitting `height` here (or setting it to /// [kTextHeightNone]) and in [TextStyle] will allow the line height to take /// the height as defined by the font, which may not be exactly the height of /// the `fontSize`. /// /// * `textHeightBehavior`: Specifies how the `height` multiplier is /// applied to ascent of the first line and the descent of the last line. /// /// * `leadingDistribution`: Specifies how the extra vertical space added by /// the `height` multiplier should be distributed over and under the text. /// Defaults to [TextLeadingDistribution.proportional]. /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). /// /// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., /// italics). /// /// * `strutStyle`: The properties of the strut. Strut defines a set of minimum /// vertical line height related metrics and can be used to obtain more /// advanced line spacing behavior. /// /// * `ellipsis`: String used to ellipsize overflowing text. If `maxLines` is /// not null, then the `ellipsis`, if any, is applied to the last rendered /// line, if that line overflows the width constraints. If `maxLines` is /// null, then the `ellipsis` is applied to the first line that overflows /// the width constraints, and subsequent lines are dropped. The width /// constraints are those set in the [ParagraphConstraints] object passed to /// the [Paragraph.layout] method. The empty string and the null value are /// considered equivalent and turn off this behavior. /// /// * `locale`: The locale used to select region-specific glyphs. ParagraphStyle({ TextAlign? textAlign, TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, TextHeightBehavior? textHeightBehavior, FontWeight? fontWeight, FontStyle? fontStyle, StrutStyle? strutStyle, String? ellipsis, Locale? locale, }) : _encoded = _encodeParagraphStyle( textAlign, textDirection, maxLines, fontFamily, fontSize, height, textHeightBehavior, fontWeight, fontStyle, strutStyle, ellipsis, locale, ), _fontFamily = fontFamily, _fontSize = fontSize, _height = height, _strutStyle = strutStyle, _ellipsis = ellipsis, _locale = locale, _leadingDistribution = textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; final Int32List _encoded; final String? _fontFamily; final double? _fontSize; final double? _height; final StrutStyle? _strutStyle; final String? _ellipsis; final Locale? _locale; final TextLeadingDistribution _leadingDistribution; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is ParagraphStyle && other._fontFamily == _fontFamily && other._fontSize == _fontSize && other._height == _height && other._strutStyle == _strutStyle && other._ellipsis == _ellipsis && other._locale == _locale && other._leadingDistribution == _leadingDistribution && _listEquals(other._encoded, _encoded); } @override int get hashCode => Object.hash( Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution, ); @override String toString() { return 'ParagraphStyle(' 'textAlign: ${_encoded[0] & 0x002 == 0x002 ? TextAlign.values[_encoded[1]] : "unspecified"}, ' 'textDirection: ${_encoded[0] & 0x004 == 0x004 ? TextDirection.values[_encoded[2]] : "unspecified"}, ' 'fontWeight: ${_encoded[0] & 0x008 == 0x008 ? FontWeight(_encoded[3]) : "unspecified"}, ' 'fontStyle: ${_encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]] : "unspecified"}, ' 'maxLines: ${_encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' 'textHeightBehavior: ${_encoded[0] & 0x040 == 0x040 ? TextHeightBehavior._fromEncoded(_encoded[6], _leadingDistribution).toString() : "unspecified"}, ' 'fontFamily: ${_encoded[0] & 0x080 == 0x080 ? _fontFamily : "unspecified"}, ' 'fontSize: ${_encoded[0] & 0x100 == 0x100 ? _fontSize : "unspecified"}, ' 'height: ${_encoded[0] & 0x200 == 0x200 ? "${_height}x" : "unspecified"}, ' 'strutStyle: ${_encoded[0] & 0x400 == 0x400 ? _strutStyle : "unspecified"}, ' 'ellipsis: ${_encoded[0] & 0x800 == 0x800 ? '"$_ellipsis"' : "unspecified"}, ' 'locale: ${_encoded[0] & 0x1000 == 0x1000 ? _locale : "unspecified"}' ')'; } } // Serialize strut properties into ByteData. This encoding errs towards // compactness. The first 8 bits is a bitmask that records which properties are // null. The rest of the values are encoded in the same order encountered in the // bitmask. The final returned value truncates any unused bytes at the end. For // ease of decoding, all 8 bit integers are stored before any 32 bit integers. // // We serialize this more thoroughly than ParagraphStyle because it is // much more likely that the strut is empty/null and we wish to add // minimal overhead for non-strut cases. ByteData _encodeStrut( String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, bool? forceStrutHeight, ) { // Strut styles are unique in a paragraph, there is no inheriting so // height == null and height == kTextHeightNone are semantically equivalent. final bool hasHeightOverride = height != null && height != kTextHeightNone; if (fontFamily == null && fontSize == null && !hasHeightOverride && leadingDistribution == null && leading == null && fontWeight == null && fontStyle == null && forceStrutHeight == null) { return ByteData(0); } final data = ByteData(24); // Max size is 24 bytes var bitmask = 0; var byteCount = 4; if (fontWeight != null) { bitmask |= 1 << 0; data.setInt32(byteCount, fontWeight.value, _kFakeHostEndian); byteCount += 4; } if (fontStyle != null) { bitmask |= 1 << 1; data.setInt32(byteCount, fontStyle.index, _kFakeHostEndian); byteCount += 4; } if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { bitmask |= 1 << 2; // passed separately to native } // The 3rd bit (0-indexed) is reserved for leadingDistribution. if (fontSize != null) { bitmask |= 1 << 4; data.setFloat32(byteCount, fontSize, _kFakeHostEndian); byteCount += 4; } if (hasHeightOverride) { bitmask |= 1 << 5; data.setFloat32(byteCount, height, _kFakeHostEndian); byteCount += 4; } if (leading != null) { bitmask |= 1 << 6; data.setFloat32(byteCount, leading, _kFakeHostEndian); byteCount += 4; } if (forceStrutHeight ?? false) { bitmask |= 1 << 7; } data.setInt32(0, bitmask, _kFakeHostEndian); assert(byteCount <= 24); assert(bitmask >> 32 == 0, 'strut bitmask overflow: $bitmask'); return ByteData.view(data.buffer, 0, byteCount); } /// See also: /// /// * [StrutStyle](https://api.flutter.dev/flutter/painting/StrutStyle-class.html), the class in the [painting] library. /// class StrutStyle { /// Creates a new StrutStyle object. /// /// * `fontFamily`: The name of the font to use when painting the text (e.g., /// Roboto). /// /// * `fontFamilyFallback`: An ordered list of font family names that will be /// searched for when the font in `fontFamily` cannot be found. /// /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting /// the text. /// /// * `height`: The minimum height of the line boxes, as a multiplier of the /// font size. The lines of the paragraph will be at least /// `(height + leading) * fontSize` tall when `fontSize` is not null. Omitting /// `height` (or setting it to [kTextHeightNone]) will allow the minimum line /// height to take the height as defined by the font, which may not be exactly /// the height of the `fontSize`. When `fontSize` is null, there is no minimum /// line height. Tall glyphs due to baseline alignment or large /// [TextStyle.fontSize] may cause the actual line height after layout to be /// taller than specified here. The `fontSize` must be provided for /// this property to take effect. /// /// * `leading`: The minimum amount of leading between lines as a multiple of /// the font size. `fontSize` must be provided for this property to take /// effect. The leading added by this property is distributed evenly over /// and under the text, regardless of `leadingDistribution`. /// /// * `leadingDistribution`: how the extra vertical space added by the /// `height` multiplier should be distributed over and under the text, /// independent of `leading` (which is always distributed evenly over and /// under text). Defaults to the paragraph's [TextHeightBehavior]'s leading /// distribution. /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). /// /// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., /// italics). /// /// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly /// `(height + leading) * fontSize` tall from baseline to baseline. /// [TextStyle] is no longer able to influence the line height, and any tall /// glyphs may overlap with lines above. If a `fontFamily` is specified, the /// total ascent of the first line will be the min of the `Ascent + half-leading` /// of the `fontFamily` and `(height + leading) * fontSize`. Otherwise, it /// will be determined by the Ascent + half-leading of the first text. StrutStyle({ String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, bool? forceStrutHeight, }) : _encoded = _encodeStrut( fontFamily, fontFamilyFallback, fontSize, height, leadingDistribution, leading, fontWeight, fontStyle, forceStrutHeight, ), _leadingDistribution = leadingDistribution, _fontFamily = fontFamily, _fontFamilyFallback = fontFamilyFallback; final ByteData _encoded; // Most of the data for strut is encoded. final String? _fontFamily; final List? _fontFamilyFallback; final TextLeadingDistribution? _leadingDistribution; bool get _enabled => _encoded.lengthInBytes > 0; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is StrutStyle && other._fontFamily == _fontFamily && other._leadingDistribution == _leadingDistribution && _listEquals(other._fontFamilyFallback, _fontFamilyFallback) && _listEquals(other._encoded.buffer.asInt8List(), _encoded.buffer.asInt8List()); } @override int get hashCode => Object.hash(Object.hashAll(_encoded.buffer.asInt8List()), _fontFamily, _leadingDistribution); } /// A direction in which text flows. /// /// Some languages are written from the left to the right (for example, English, /// Tamil, or Chinese), while others are written from the right to the left (for /// example Aramaic, Hebrew, or Urdu). Some are also written in a mixture, for /// example Arabic is mostly written right-to-left, with numerals written /// left-to-right. /// /// The text direction must be provided to APIs that render text or lay out /// boxes horizontally, so that they can determine which direction to start in: /// either right-to-left, [TextDirection.rtl]; or left-to-right, /// [TextDirection.ltr]. /// /// ## Design discussion /// /// Flutter is designed to address the needs of applications written in any of /// the world's currently-used languages, whether they use a right-to-left or /// left-to-right writing direction. Flutter does not support other writing /// modes, such as vertical text or boustrophedon text, as these are rarely used /// in computer programs. /// /// It is common when developing user interface frameworks to pick a default /// text direction — typically left-to-right, the direction most familiar to the /// engineers working on the framework — because this simplifies the development /// of applications on the platform. Unfortunately, this frequently results in /// the platform having unexpected left-to-right biases or assumptions, as /// engineers will typically miss places where they need to support /// right-to-left text. This then results in bugs that only manifest in /// right-to-left environments. /// /// In an effort to minimize the extent to which Flutter experiences this /// category of issues, the lowest levels of the Flutter framework do not have a /// default text reading direction. Any time a reading direction is necessary, /// for example when text is to be displayed, or when a /// writing-direction-dependent value is to be interpreted, the reading /// direction must be explicitly specified. Where possible, such as in `switch` /// statements, the right-to-left case is listed first, to avoid the impression /// that it is an afterthought. /// /// At the higher levels (specifically starting at the widgets library), an /// ambient [Directionality] is introduced, which provides a default. Thus, for /// instance, a [widgets.Text] widget in the scope of a [MaterialApp] widget /// does not need to be given an explicit writing direction. The /// [Directionality.of] static method can be used to obtain the ambient text /// direction for a particular [BuildContext]. /// /// ### Known left-to-right biases in Flutter /// /// Despite the design intent described above, certain left-to-right biases have /// nonetheless crept into Flutter's design. These include: /// /// * The [Canvas] origin is at the top left, and the x-axis increases in a /// left-to-right direction. /// /// * The default localization in the widgets and material libraries is /// American English, which is left-to-right. /// /// ### Visual properties vs directional properties /// /// Many classes in the Flutter framework are offered in two versions, a /// visually-oriented variant, and a text-direction-dependent variant. For /// example, [EdgeInsets] is described in terms of top, left, right, and bottom, /// while [EdgeInsetsDirectional] is described in terms of top, start, end, and /// bottom, where start and end correspond to right and left in right-to-left /// text and left and right in left-to-right text. /// /// There are distinct use cases for each of these variants. /// /// Text-direction-dependent variants are useful when developing user interfaces /// that should "flip" with the text direction. For example, a paragraph of text /// in English will typically be left-aligned and a quote will be indented from /// the left, while in Arabic it will be right-aligned and indented from the /// right. Both of these cases are described by the direction-dependent /// [TextAlign.start] and [EdgeInsetsDirectional.start]. /// /// In contrast, the visual variants are useful when the text direction is known /// and not affected by the reading direction. For example, an application /// giving driving directions might show a "turn left" arrow on the left and a /// "turn right" arrow on the right — and would do so whether the application /// was localized to French (left-to-right) or Hebrew (right-to-left). /// /// In practice, it is also expected that many developers will only be /// targeting one language, and in that case it may be simpler to think in /// visual terms. // The order of this enum must match the order of the values in TextDirection.h's TextDirection. enum TextDirection { /// The text flows from right to left (e.g. Arabic, Hebrew). rtl, /// The text flows from left to right (e.g., English, French). ltr, } /// A rectangle enclosing a run of text. /// /// This is similar to [Rect] but includes an inherent [TextDirection]. class TextBox { /// Creates an object that describes a box containing text. const TextBox.fromLTRBD(this.left, this.top, this.right, this.bottom, this.direction); /// The left edge of the text box, irrespective of direction. /// /// To get the leading edge (which may depend on the [direction]), consider [start]. final double left; /// The top edge of the text box. final double top; /// The right edge of the text box, irrespective of direction. /// /// To get the trailing edge (which may depend on the [direction]), consider [end]. final double right; /// The bottom edge of the text box. final double bottom; /// The direction in which text inside this box flows. final TextDirection direction; /// Returns a rect of the same size as this box. Rect toRect() => Rect.fromLTRB(left, top, right, bottom); /// The [left] edge of the box for left-to-right text; the [right] edge of the box for right-to-left text. /// /// See also: /// /// * [direction], which specifies the text direction. double get start { return (direction == TextDirection.ltr) ? left : right; } /// The [right] edge of the box for left-to-right text; the [left] edge of the box for right-to-left text. /// /// See also: /// /// * [direction], which specifies the text direction. double get end { return (direction == TextDirection.ltr) ? right : left; } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other.runtimeType != runtimeType) { return false; } return other is TextBox && other.left == left && other.top == top && other.right == right && other.bottom == bottom && other.direction == direction; } @override int get hashCode => Object.hash(left, top, right, bottom, direction); @override String toString() => 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)'; } /// A way to disambiguate a [TextPosition] when its offset could match two /// different locations in the rendered string. /// /// For example, at an offset where the rendered text wraps, there are two /// visual positions that the offset could represent: one prior to the line /// break (at the end of the first line) and one after the line break (at the /// start of the second line). A text affinity disambiguates between these two /// cases. /// /// This affects only line breaks caused by wrapping, not explicit newline /// characters. For newline characters, the position is fully specified by the /// offset alone, and there is no ambiguity. /// /// [TextAffinity] also affects bidirectional text at the interface between LTR /// and RTL text. Consider the following string, where the lowercase letters /// will be displayed as LTR and the uppercase letters RTL: "helloHELLO". When /// rendered, the string would appear visually as "helloOLLEH". An offset of 5 /// would be ambiguous without a corresponding [TextAffinity]. Looking at the /// string in code, the offset represents the position just after the "o" and /// just before the "H". When rendered, this offset could be either in the /// middle of the string to the right of the "o" or at the end of the string to /// the right of the "H". enum TextAffinity { /// The position has affinity for the upstream side of the text position, i.e. /// in the direction of the beginning of the string. /// /// In the example of an offset at the place where text is wrapping, upstream /// indicates the end of the first line. /// /// In the bidirectional text example "helloHELLO", an offset of 5 with /// [TextAffinity] upstream would appear in the middle of the rendered text, /// just to the right of the "o". See the definition of [TextAffinity] for the /// full example. upstream, /// The position has affinity for the downstream side of the text position, /// i.e. in the direction of the end of the string. /// /// In the example of an offset at the place where text is wrapping, /// downstream indicates the beginning of the second line. /// /// In the bidirectional text example "helloHELLO", an offset of 5 with /// [TextAffinity] downstream would appear at the end of the rendered text, /// just to the right of the "H". See the definition of [TextAffinity] for the /// full example. downstream, } /// A position in a string of text. /// /// A TextPosition can be used to describe a caret position in between /// characters. The [offset] points to the position between `offset - 1` and /// `offset` characters of the string, and the [affinity] is used to describe /// which character this position affiliates with. /// /// One use case is when rendered text is forced to wrap. In this case, the offset /// where the wrap occurs could visually appear either at the end of the first /// line or the beginning of the second line. The second way is with /// bidirectional text. An offset at the interface between two different text /// directions could have one of two locations in the rendered text. /// /// See the documentation for [TextAffinity] for more information on how /// TextAffinity disambiguates situations like these. class TextPosition { /// Creates an object representing a particular position in a string. /// /// The arguments must not be null (so the [offset] argument is required). const TextPosition({required this.offset, this.affinity = TextAffinity.downstream}); /// The index of the character that immediately follows the position in the /// string representation of the text. /// /// For example, given the string `'Hello'`, offset 0 represents the cursor /// being before the `H`, while offset 5 represents the cursor being just /// after the `o`. final int offset; /// Disambiguates cases where the position in the string given by [offset] /// could represent two different visual positions in the rendered text. For /// example, this can happen when text is forced to wrap, or when one string /// of text is rendered with multiple text directions. /// /// See the documentation for [TextAffinity] for more information on how /// TextAffinity disambiguates situations like these. final TextAffinity affinity; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is TextPosition && other.offset == offset && other.affinity == affinity; } @override int get hashCode => Object.hash(offset, affinity); @override String toString() { return 'TextPosition(offset: $offset, affinity: $affinity)'; } } /// A range of characters in a string of text. class TextRange { /// Creates a text range. /// /// The [start] and [end] arguments must not be null. Both the [start] and /// [end] must either be greater than or equal to zero or both exactly -1. /// /// The text included in the range includes the character at [start], but not /// the one at [end]. /// /// Instead of creating an empty text range, consider using the [empty] /// constant. const TextRange({required this.start, required this.end}) : assert(start >= -1), assert(end >= -1); /// A text range that starts and ends at offset. /// /// The [offset] argument must be non-null and greater than or equal to -1. const TextRange.collapsed(int offset) : assert(offset >= -1), start = offset, end = offset; /// A text range that contains nothing and is not in the text. static const TextRange empty = TextRange(start: -1, end: -1); /// The index of the first character in the range. /// /// If [start] and [end] are both -1, the text range is empty. final int start; /// The next index after the characters in this range. /// /// If [start] and [end] are both -1, the text range is empty. final int end; /// Whether this range represents a valid position in the text. bool get isValid => start >= 0 && end >= 0; /// Whether this range is empty (but still potentially placed inside the text). bool get isCollapsed => start == end; /// Whether the start of this range precedes the end. bool get isNormalized => end >= start; /// The text before this range. String textBefore(String text) { assert(isNormalized); return text.substring(0, start); } /// The text after this range. String textAfter(String text) { assert(isNormalized); return text.substring(end); } /// The text inside this range. String textInside(String text) { assert(isNormalized); return text.substring(start, end); } @override bool operator ==(Object other) { if (identical(this, other)) { return true; } return other is TextRange && other.start == start && other.end == end; } @override int get hashCode => Object.hash(start.hashCode, end.hashCode); @override String toString() => 'TextRange(start: $start, end: $end)'; } /// Layout constraints for [Paragraph] objects. /// /// Instances of this class are typically used with [Paragraph.layout]. /// /// The only constraint that can be specified is the [width]. See the discussion /// at [width] for more details. class ParagraphConstraints { /// Creates constraints for laying out a paragraph. /// /// The [width] argument must not be null. const ParagraphConstraints({required this.width}); /// The width the paragraph should use whey computing the positions of glyphs. /// /// If possible, the paragraph will select a soft line break prior to reaching /// this width. If no soft line break is available, the paragraph will select /// a hard line break prior to reaching this width. If that would force a line /// break without any characters having been placed (i.e. if the next /// character to be laid out does not fit within the given width constraint) /// then the next character is allowed to overflow the width constraint and a /// forced line break is placed after it (even if an explicit line break /// follows). /// /// The width influences how ellipses are applied. See the discussion at /// [ParagraphStyle.new] for more details. /// /// This width is also used to position glyphs according to the [TextAlign] /// alignment described in the [ParagraphStyle] used when building the /// [Paragraph] with a [ParagraphBuilder]. final double width; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is ParagraphConstraints && other.width == width; } @override int get hashCode => width.hashCode; @override String toString() => 'ParagraphConstraints(width: $width)'; } /// Defines various ways to vertically bound the boxes returned by /// [Paragraph.getBoxesForRange]. /// /// See [BoxWidthStyle] for a similar property to control width. enum BoxHeightStyle { /// Provide tight bounding boxes that fit heights per run. This style may result /// in uneven bounding boxes that do not nicely connect with adjacent boxes. tight, /// The height of the boxes will be the maximum height of all runs in the /// line. All boxes in the same line will be the same height. /// /// This does not guarantee that the boxes will cover the entire vertical height of the line /// when there is additional line spacing. /// /// See [BoxHeightStyle.includeLineSpacingTop], [BoxHeightStyle.includeLineSpacingMiddle], /// and [BoxHeightStyle.includeLineSpacingBottom] for styles that will cover /// the entire line. max, /// Extends the top and bottom edge of the bounds to fully cover any line /// spacing. /// /// The top and bottom of each box will cover half of the /// space above and half of the space below the line. /// /// {@template dart.ui.boxHeightStyle.includeLineSpacing} /// The top edge of each line should be the same as the bottom edge /// of the line above. There should be no gaps in vertical coverage given any /// amount of line spacing. Line spacing is not included above the first line /// and below the last line due to no additional space present there. /// {@endtemplate} includeLineSpacingMiddle, /// Extends the top edge of the bounds to fully cover any line spacing. /// /// The line spacing will be added to the top of the box. /// /// {@macro dart.ui.boxHeightStyle.includeLineSpacing} includeLineSpacingTop, /// Extends the bottom edge of the bounds to fully cover any line spacing. /// /// The line spacing will be added to the bottom of the box. /// /// {@macro dart.ui.boxHeightStyle.includeLineSpacing} includeLineSpacingBottom, /// Calculate box heights based on the metrics of this paragraph's [StrutStyle]. /// /// Boxes based on the strut will have consistent heights throughout the /// entire paragraph. The top edge of each line will align with the bottom /// edge of the previous line. It is possible for glyphs to extend outside /// these boxes. strut, } /// Defines various ways to horizontally bound the boxes returned by /// [Paragraph.getBoxesForRange]. /// /// See [BoxHeightStyle] for a similar property to control height. enum BoxWidthStyle { /// Provide tight bounding boxes that fit widths to the runs of each line /// independently. tight, /// Adds up to two additional boxes as needed at the beginning and/or end /// of each line so that the widths of the boxes in line are the same width /// as the widest line in the paragraph. /// /// The additional boxes on each line are only added when the relevant box /// at the relevant edge of that line does not span the maximum width of /// the paragraph. max, } /// Where to vertically align the placeholder relative to the surrounding text. /// /// Used by [ParagraphBuilder.addPlaceholder]. enum PlaceholderAlignment { /// Match the baseline of the placeholder with the baseline. /// /// The [TextBaseline] to use must be specified and non-null when using this /// alignment mode. baseline, /// Align the bottom edge of the placeholder with the baseline such that the /// placeholder sits on top of the baseline. /// /// The [TextBaseline] to use must be specified and non-null when using this /// alignment mode. aboveBaseline, /// Align the top edge of the placeholder with the baseline specified /// such that the placeholder hangs below the baseline. /// /// The [TextBaseline] to use must be specified and non-null when using this /// alignment mode. belowBaseline, /// Align the top edge of the placeholder with the top edge of the text. /// /// When the placeholder is very tall, the extra space will hang from /// the top and extend through the bottom of the line. top, /// Align the bottom edge of the placeholder with the bottom edge of the text. /// /// When the placeholder is very tall, the extra space will rise from the /// bottom and extend through the top of the line. bottom, /// Align the middle of the placeholder with the middle of the text. /// /// When the placeholder is very tall, the extra space will grow equally /// from the top and bottom of the line. middle, } /// [LineMetrics] stores the measurements and statistics of a single line in the /// paragraph. /// /// The measurements here are for the line as a whole, and represent the maximum /// extent of the line instead of per-run or per-glyph metrics. For more detailed /// metrics, see [TextBox] and [Paragraph.getBoxesForRange]. /// /// [LineMetrics] should be obtained directly from the [Paragraph.computeLineMetrics] /// method. class LineMetrics { /// Creates a [LineMetrics] object with only the specified values. LineMetrics({ required this.hardBreak, required this.ascent, required this.descent, required this.unscaledAscent, required this.height, required this.width, required this.left, required this.baseline, required this.lineNumber, }); LineMetrics._( this.hardBreak, this.ascent, this.descent, this.unscaledAscent, this.height, this.width, this.left, this.baseline, this.lineNumber, ); /// True if this line ends with an explicit line break (e.g. '\n') or is the end /// of the paragraph. False otherwise. final bool hardBreak; /// The rise from the [baseline] as calculated from the font and style for this line. /// /// This is the final computed ascent and can be impacted by the strut, height, scaling, /// as well as outlying runs that are very tall. /// /// The [ascent] is provided as a positive value, even though it is typically defined /// in fonts as negative. This is to ensure the signage of operations with these /// metrics directly reflects the intended signage of the value. For example, /// the y coordinate of the top edge of the line is `baseline - ascent`. final double ascent; /// The drop from the [baseline] as calculated from the font and style for this line. /// /// This is the final computed ascent and can be impacted by the strut, height, scaling, /// as well as outlying runs that are very tall. /// /// The y coordinate of the bottom edge of the line is `baseline + descent`. final double descent; /// The rise from the [baseline] as calculated from the font and style for this line /// ignoring the [TextStyle.height]. /// /// The [unscaledAscent] is provided as a positive value, even though it is typically /// defined in fonts as negative. This is to ensure the signage of operations with /// these metrics directly reflects the intended signage of the value. final double unscaledAscent; /// Total height of the line from the top edge to the bottom edge. /// /// This is equivalent to `round(ascent + descent)`. This value is provided /// separately due to rounding causing sub-pixel differences from the unrounded /// values. final double height; /// Width of the line from the left edge of the leftmost glyph to the right /// edge of the rightmost glyph. /// /// This is not the same as the width of the pargraph. /// /// See also: /// /// * [Paragraph.width], the max width passed in during layout. /// * [Paragraph.longestLine], the width of the longest line in the paragraph. final double width; /// The x coordinate of left edge of the line. /// /// The right edge can be obtained with `left + width`. final double left; /// The y coordinate of the baseline for this line from the top of the paragraph. /// /// The bottom edge of the paragraph up to and including this line may be obtained /// through `baseline + descent`. final double baseline; /// The number of this line in the overall paragraph, with the first line being /// index zero. /// /// For example, the first line is line 0, second line is line 1. final int lineNumber; @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is LineMetrics && other.hardBreak == hardBreak && other.ascent == ascent && other.descent == descent && other.unscaledAscent == unscaledAscent && other.height == height && other.width == width && other.left == left && other.baseline == baseline && other.lineNumber == lineNumber; } @override int get hashCode => Object.hash( hardBreak, ascent, descent, unscaledAscent, height, width, left, baseline, lineNumber, ); @override String toString() { return 'LineMetrics(hardBreak: $hardBreak, ' 'ascent: $ascent, ' 'descent: $descent, ' 'unscaledAscent: $unscaledAscent, ' 'height: $height, ' 'width: $width, ' 'left: $left, ' 'baseline: $baseline, ' 'lineNumber: $lineNumber)'; } } /// A paragraph of text. /// /// A paragraph retains the size and position of each glyph in the text and can /// be efficiently resized and painted. /// /// To create a [Paragraph] object, use a [ParagraphBuilder]. /// /// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph] /// method. abstract class Paragraph { /// The amount of horizontal space this paragraph occupies. /// /// Valid only after [layout] has been called. double get width; /// The amount of vertical space this paragraph occupies. /// /// Valid only after [layout] has been called. double get height; /// The distance from the left edge of the leftmost glyph to the right edge of /// the rightmost glyph in the paragraph. /// /// Valid only after [layout] has been called. double get longestLine; /// The minimum width that this paragraph could be without failing to paint /// its contents within itself. /// /// Valid only after [layout] has been called. double get minIntrinsicWidth; /// Returns the smallest width beyond which increasing the width never /// decreases the height. /// /// Valid only after [layout] has been called. double get maxIntrinsicWidth; /// The distance from the top of the paragraph to the alphabetic /// baseline of the first line, in logical pixels. double get alphabeticBaseline; /// The distance from the top of the paragraph to the ideographic /// baseline of the first line, in logical pixels. double get ideographicBaseline; /// True if there is more vertical content, but the text was truncated, either /// because we reached `maxLines` lines of text or because the `maxLines` was /// null, `ellipsis` was not null, and one of the lines exceeded the width /// constraint. /// /// See the discussion of the `maxLines` and `ellipsis` arguments at /// [ParagraphStyle.new]. bool get didExceedMaxLines; /// Computes the size and position of each glyph in the paragraph. /// /// The [ParagraphConstraints] control how wide the text is allowed to be. void layout(ParagraphConstraints constraints); /// Returns a list of text boxes that enclose the given text range. /// /// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization /// of how the boxes are bound vertically and horizontally. Both style /// parameters default to the tight option, which will provide close-fitting /// boxes and will not account for any line spacing. /// /// Coordinates of the TextBox are relative to the upper-left corner of the paragraph, /// where positive y values indicate down. /// /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. /// /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. List getBoxesForRange( int start, int end, { BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight, }); /// Returns a list of text boxes that enclose all placeholders in the paragraph. /// /// The order of the boxes are in the same order as passed in through /// [ParagraphBuilder.addPlaceholder]. /// /// Coordinates of the [TextBox] are relative to the upper-left corner of the paragraph, /// where positive y values indicate down. List getBoxesForPlaceholders(); /// Returns the text position closest to the given offset. /// /// This method always returns a [TextPosition] for any given [offset], even /// when the [offset] is not close to any text, or when the paragraph is empty. /// This is useful for determining the text to select when the user drags the /// text selection handle. /// /// See also: /// /// * [getClosestGlyphInfoForOffset], which returns more information about /// the closest character to an [Offset]. TextPosition getPositionForOffset(Offset offset); /// Returns the [GlyphInfo] of the glyph closest to the given `offset` in the /// paragraph coordinate system, or null if if the text is empty, or is /// entirely clipped or ellipsized away. /// /// This method first finds the line closest to `offset.dy`, and then returns /// the [GlyphInfo] of the closest glyph(s) within that line. GlyphInfo? getClosestGlyphInfoForOffset(Offset offset); /// Returns the [GlyphInfo] located at the given UTF-16 `codeUnitOffset` in /// the paragraph, or null if the given `codeUnitOffset` is out of the visible /// lines or is ellipsized. GlyphInfo? getGlyphInfoAt(int codeUnitOffset); /// Returns the [TextRange] of the word at the given [TextPosition]. /// /// Characters not part of a word, such as spaces, symbols, and punctuation, /// have word breaks on both sides. In such cases, this method will return /// (offset, offset+1). Word boundaries are defined more precisely in Unicode /// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries /// /// The [TextPosition] is treated as caret position, its [TextPosition.affinity] /// is used to determine which character this position points to. For example, /// the word boundary at `TextPosition(offset: 5, affinity: TextPosition.upstream)` /// of the `string = 'Hello word'` will return range (0, 5) because the position /// points to the character 'o' instead of the space. TextRange getWordBoundary(TextPosition position); /// Returns the [TextRange] of the line at the given [TextPosition]. /// /// The newline (if any) is returned as part of the range. /// /// Not valid until after layout. /// /// This can potentially be expensive, since it needs to compute the line /// metrics, so use it sparingly. TextRange getLineBoundary(TextPosition position); /// Returns the full list of [LineMetrics] that describe in detail the various /// metrics of each laid out line. /// /// Not valid until after layout. /// /// This can potentially return a large amount of data, so it is not recommended /// to repeatedly call this. Instead, cache the results. List computeLineMetrics(); /// Returns the [LineMetrics] for the line at `lineNumber`, or null if the /// given `lineNumber` is greater than or equal to [numberOfLines]. LineMetrics? getLineMetricsAt(int lineNumber); /// The total number of visible lines in the paragraph. /// /// Returns a non-negative number. If `maxLines` is non-null, the value of /// [numberOfLines] never exceeds `maxLines`. int get numberOfLines; /// Returns the line number of the line that contains the code unit that /// `codeUnitOffset` points to. /// /// This method returns null if the given `codeUnitOffset` is out of bounds, or /// is logically after the last visible codepoint. This includes the case where /// its codepoint belongs to a visible line, but the text layout library /// replaced it with an ellipsis. /// /// If the target code unit points to a control character that introduces /// mandatory line breaks (most notably the line feed character `LF`, typically /// represented in strings as the escape sequence "\n"), to conform to /// [the unicode rules](https://unicode.org/reports/tr14/#LB4), the control /// character itself is always considered to be at the end of "current" line /// rather than the beginning of the new line. int? getLineNumberAt(int codeUnitOffset); /// Release the resources used by this object. The object is no longer usable /// after this method is called. void dispose(); /// Whether this reference to the underlying picture is [dispose]d. /// /// This only returns a valid value if asserts are enabled, and must not be /// used otherwise. bool get debugDisposed; } base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragraph { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// /// To create a [Paragraph] object, use a [ParagraphBuilder]. _NativeParagraph._(); bool _needsLayout = true; @override @Native)>(symbol: 'Paragraph::width', isLeaf: true) external double get width; @override @Native)>(symbol: 'Paragraph::height', isLeaf: true) external double get height; @override @Native)>(symbol: 'Paragraph::longestLine', isLeaf: true) external double get longestLine; @override @Native)>(symbol: 'Paragraph::minIntrinsicWidth', isLeaf: true) external double get minIntrinsicWidth; @override @Native)>(symbol: 'Paragraph::maxIntrinsicWidth', isLeaf: true) external double get maxIntrinsicWidth; @override @Native)>(symbol: 'Paragraph::alphabeticBaseline', isLeaf: true) external double get alphabeticBaseline; @override @Native)>(symbol: 'Paragraph::ideographicBaseline', isLeaf: true) external double get ideographicBaseline; @override @Native)>(symbol: 'Paragraph::didExceedMaxLines', isLeaf: true) external bool get didExceedMaxLines; @override void layout(ParagraphConstraints constraints) { _layout(constraints.width); assert(() { _needsLayout = false; return true; }()); } @Native, Double)>(symbol: 'Paragraph::layout', isLeaf: true) external void _layout(double width); List _decodeTextBoxes(Float32List encoded) { final int count = encoded.length ~/ 5; final boxes = []; var position = 0; for (var index = 0; index < count; index += 1) { boxes.add( TextBox.fromLTRBD( encoded[position++], encoded[position++], encoded[position++], encoded[position++], TextDirection.values[encoded[position++].toInt()], ), ); } return boxes; } @override List getBoxesForRange( int start, int end, { BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight, }) { return _decodeTextBoxes( _getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index), ); } // See paragraph.cc for the layout of this return value. @Native, Uint32, Uint32, Uint32, Uint32)>( symbol: 'Paragraph::getRectsForRange', ) external Float32List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle); @override List getBoxesForPlaceholders() { return _decodeTextBoxes(_getBoxesForPlaceholders()); } @Native)>(symbol: 'Paragraph::getRectsForPlaceholders') external Float32List _getBoxesForPlaceholders(); @override TextPosition getPositionForOffset(Offset offset) { final List encoded = _getPositionForOffset(offset.dx, offset.dy); return TextPosition(offset: encoded[0], affinity: TextAffinity.values[encoded[1]]); } @Native, Double, Double)>(symbol: 'Paragraph::getPositionForOffset') external List _getPositionForOffset(double dx, double dy); @override GlyphInfo? getGlyphInfoAt(int codeUnitOffset) => _getGlyphInfoAt(codeUnitOffset, GlyphInfo._); @Native, Uint32, Handle)>(symbol: 'Paragraph::getGlyphInfoAt') external GlyphInfo? _getGlyphInfoAt(int codeUnitOffset, Function constructor); @override GlyphInfo? getClosestGlyphInfoForOffset(Offset offset) => _getClosestGlyphInfoForOffset(offset.dx, offset.dy, GlyphInfo._); @Native, Double, Double, Handle)>( symbol: 'Paragraph::getClosestGlyphInfo', ) external GlyphInfo? _getClosestGlyphInfoForOffset(double dx, double dy, Function constructor); @override TextRange getWordBoundary(TextPosition position) { final int characterPosition; switch (position.affinity) { case TextAffinity.upstream: characterPosition = position.offset - 1; case TextAffinity.downstream: characterPosition = position.offset; } final List boundary = _getWordBoundary(characterPosition); return TextRange(start: boundary[0], end: boundary[1]); } @Native, Uint32)>(symbol: 'Paragraph::getWordBoundary') external List _getWordBoundary(int offset); @override TextRange getLineBoundary(TextPosition position) { final List boundary = _getLineBoundary(position.offset); final line = TextRange(start: boundary[0], end: boundary[1]); final List nextBoundary = _getLineBoundary(position.offset + 1); final nextLine = TextRange(start: nextBoundary[0], end: nextBoundary[1]); // If there is no next line, because we're at the end of the field, return line. if (!nextLine.isValid) { return line; } // _getLineBoundary only considers the offset and assumes that the // TextAffinity is upstream. In the case that TextPosition is just after a // word wrap (downstream), we need to return the line for the next offset. if (position.affinity == TextAffinity.downstream && line != nextLine && position.offset == line.end && line.end == nextLine.start) { return TextRange(start: nextBoundary[0], end: nextBoundary[1]); } return line; } @Native, Uint32)>(symbol: 'Paragraph::getLineBoundary') external List _getLineBoundary(int offset); // Redirecting the paint function in this way solves some dependency problems // in the C++ code. If we straighten out the C++ dependencies, we can remove // this indirection. @Native, Pointer, Double, Double)>(symbol: 'Paragraph::paint') external void _paint(_NativeCanvas canvas, double x, double y); @override List computeLineMetrics() { final Float64List encoded = _computeLineMetrics(); final int count = encoded.length ~/ 9; var position = 0; final metrics = [ for (int index = 0; index < count; index += 1) LineMetrics( hardBreak: encoded[position++] != 0, ascent: encoded[position++], descent: encoded[position++], unscaledAscent: encoded[position++], height: encoded[position++], width: encoded[position++], left: encoded[position++], baseline: encoded[position++], lineNumber: encoded[position++].toInt(), ), ]; return metrics; } @Native)>(symbol: 'Paragraph::computeLineMetrics') external Float64List _computeLineMetrics(); @override LineMetrics? getLineMetricsAt(int lineNumber) => _getLineMetricsAt(lineNumber, LineMetrics._); @Native, Uint32, Handle)>(symbol: 'Paragraph::getLineMetricsAt') external LineMetrics? _getLineMetricsAt(int lineNumber, Function constructor); @override @Native)>(symbol: 'Paragraph::getNumberOfLines') external int get numberOfLines; @override int? getLineNumberAt(int codeUnitOffset) { final int lineNumber = _getLineNumber(codeUnitOffset); return lineNumber < 0 ? null : lineNumber; } @Native, Uint32)>(symbol: 'Paragraph::getLineNumberAt') external int _getLineNumber(int codeUnitOffset); @override void dispose() { assert(!_disposed); assert(() { _disposed = true; return true; }()); _dispose(); } /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @Native)>(symbol: 'Paragraph::dispose') external void _dispose(); bool _disposed = false; @override bool get debugDisposed { bool? disposed; assert(() { disposed = _disposed; return true; }()); return disposed ?? (throw StateError( '$runtimeType.debugDisposed is only available when asserts are enabled.', )); } @override String toString() { String? result; assert(() { if (_disposed && _needsLayout) { result = 'Paragraph(DISPOSED while dirty)'; } if (_disposed && !_needsLayout) { result = 'Paragraph(DISPOSED)'; } return true; }()); if (result != null) { return result!; } if (_needsLayout) { return 'Paragraph(dirty)'; } return 'Paragraph()'; } } /// Builds a [Paragraph] containing text with the given styling information. /// /// To set the paragraph's alignment, truncation, and ellipsizing behavior, pass /// an appropriately-configured [ParagraphStyle] object to the /// [ParagraphBuilder.new] constructor. /// /// Then, call combinations of [pushStyle], [addText], and [pop] to add styled /// text to the object. /// /// Finally, call [build] to obtain the constructed [Paragraph] object. After /// this point, the builder is no longer usable. /// /// After constructing a [Paragraph], call [Paragraph.layout] on it and then /// paint it with [Canvas.drawParagraph]. abstract class ParagraphBuilder { /// Creates a new [ParagraphBuilder] object, which is used to create a /// [Paragraph]. factory ParagraphBuilder(ParagraphStyle style) = _NativeParagraphBuilder; /// The number of placeholders currently in the paragraph. int get placeholderCount; /// The scales of the placeholders in the paragraph. List get placeholderScales; /// Applies the given style to the added text until [pop] is called. /// /// See [pop] for details. void pushStyle(TextStyle style); /// Ends the effect of the most recent call to [pushStyle]. /// /// Internally, the paragraph builder maintains a stack of text styles. Text /// added to the paragraph is affected by all the styles in the stack. Calling /// [pop] removes the topmost style in the stack, leaving the remaining styles /// in effect. void pop(); /// Adds the given text to the paragraph. /// /// The text will be styled according to the current stack of text styles. void addText(String text); /// Adds an inline placeholder space to the paragraph. /// /// The paragraph will contain a rectangular space with no text of the dimensions /// specified. /// /// The `width` and `height` parameters specify the size of the placeholder rectangle. /// /// The `alignment` parameter specifies how the placeholder rectangle will be vertically /// aligned with the surrounding text. When [PlaceholderAlignment.baseline], /// [PlaceholderAlignment.aboveBaseline], and [PlaceholderAlignment.belowBaseline] /// alignment modes are used, the baseline needs to be set with the `baseline`. /// When using [PlaceholderAlignment.baseline], `baselineOffset` indicates the distance /// of the baseline down from the top of the rectangle. The default `baselineOffset` /// is the `height`. /// /// Examples: /// /// * For a 30x50 placeholder with the bottom edge aligned with the bottom of the text, use: /// `addPlaceholder(30, 50, PlaceholderAlignment.bottom);` /// * For a 30x50 placeholder that is vertically centered around the text, use: /// `addPlaceholder(30, 50, PlaceholderAlignment.middle);`. /// * For a 30x50 placeholder that sits completely on top of the alphabetic baseline, use: /// `addPlaceholder(30, 50, PlaceholderAlignment.aboveBaseline, baseline: TextBaseline.alphabetic)`. /// * For a 30x50 placeholder with 40 pixels above and 10 pixels below the alphabetic baseline, use: /// `addPlaceholder(30, 50, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, baselineOffset: 40)`. /// /// Lines are permitted to break around each placeholder. /// /// Decorations will be drawn based on the font defined in the most recently /// pushed [TextStyle]. The decorations are drawn as if unicode text were present /// in the placeholder space, and will draw the same regardless of the height and /// alignment of the placeholder. To hide or manually adjust decorations to fit, /// a text style with the desired decoration behavior should be pushed before /// adding a placeholder. /// /// Any decorations drawn through a placeholder will exist on the same canvas/layer /// as the text. This means any content drawn on top of the space reserved by /// the placeholder will be drawn over the decoration, possibly obscuring the /// decoration. /// /// Placeholders are represented by a unicode 0xFFFC "object replacement character" /// in the text buffer. For each placeholder, one object replacement character is /// added on to the text buffer. /// /// The `scale` parameter will scale the `width` and `height` by the specified amount, /// and keep track of the scale. The scales of placeholders added can be accessed /// through [placeholderScales]. This is primarily used for accessibility scaling. void addPlaceholder( double width, double height, PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, TextBaseline? baseline, }); /// Applies the given paragraph style and returns a [Paragraph] containing the /// added text and associated styling. /// /// After calling this function, the paragraph builder object is invalid and /// cannot be used further. Paragraph build(); } base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements ParagraphBuilder { _NativeParagraphBuilder(ParagraphStyle style) : _defaultLeadingDistribution = style._leadingDistribution { List? strutFontFamilies; final StrutStyle? strutStyle = style._strutStyle; final ByteData? encodedStrutStyle; if (strutStyle != null && strutStyle._enabled) { final String? fontFamily = strutStyle._fontFamily; strutFontFamilies = [?fontFamily, ...?strutStyle._fontFamilyFallback]; assert(TextLeadingDistribution.values.length <= 2); final TextLeadingDistribution leadingDistribution = strutStyle._leadingDistribution ?? style._leadingDistribution; encodedStrutStyle = strutStyle._encoded; int bitmask = encodedStrutStyle.getInt32(0, _kFakeHostEndian); bitmask |= (leadingDistribution.index) << 3; encodedStrutStyle.setInt32(0, bitmask, _kFakeHostEndian); } else { encodedStrutStyle = null; } _constructor( style._encoded, encodedStrutStyle, style._fontFamily ?? '', strutFontFamilies, style._fontSize ?? 0, style._height ?? 0, style._ellipsis ?? '', _encodeLocale(style._locale), ); } @Native( symbol: 'ParagraphBuilder::Create', ) external void _constructor( Int32List encoded, ByteData? strutData, String fontFamily, List? strutFontFamily, double fontSize, double height, String ellipsis, String locale, ); @override int get placeholderCount => _placeholderCount; int _placeholderCount = 0; @override List get placeholderScales => _placeholderScales; final List _placeholderScales = []; final TextLeadingDistribution _defaultLeadingDistribution; @override void pushStyle(TextStyle style) { final fullFontFamilies = []; fullFontFamilies.add(style._fontFamily); final List? fontFamilyFallback = style._fontFamilyFallback; if (fontFamilyFallback != null) { fullFontFamilies.addAll(fontFamilyFallback); } final Int32List encoded = style._encoded; final TextLeadingDistribution finalLeadingDistribution = style._leadingDistribution ?? _defaultLeadingDistribution; // ensure the enum can be represented using 1 bit. assert(TextLeadingDistribution.values.length <= 2); // Use the leading distribution from the paragraph's style if it's not // explicitly set in `style`. encoded[0] |= finalLeadingDistribution.index << 0; ByteData? encodedFontFeatures; final List? fontFeatures = style._fontFeatures; if (fontFeatures != null) { encodedFontFeatures = ByteData(fontFeatures.length * FontFeature._kEncodedSize); var byteOffset = 0; for (final FontFeature feature in fontFeatures) { feature._encode( ByteData.view(encodedFontFeatures.buffer, byteOffset, FontFeature._kEncodedSize), ); byteOffset += FontFeature._kEncodedSize; } } ByteData? encodedFontVariations; final List? fontVariations = style._fontVariations; if (fontVariations != null) { encodedFontVariations = ByteData(fontVariations.length * FontVariation._kEncodedSize); var byteOffset = 0; for (final FontVariation variation in fontVariations) { variation._encode( ByteData.view(encodedFontVariations.buffer, byteOffset, FontVariation._kEncodedSize), ); byteOffset += FontVariation._kEncodedSize; } } _pushStyle( encoded, fullFontFamilies, style._fontSize ?? 0, style._letterSpacing ?? 0, style._wordSpacing ?? 0, style._height ?? 0, style._decorationThickness ?? 0, _encodeLocale(style._locale), style._background?._objects, style._background?._data, style._foreground?._objects, style._foreground?._data, Shadow._encodeShadows(style._shadows), encodedFontFeatures, encodedFontVariations, ); } @Native< Void Function( Pointer, Handle, Handle, Double, Double, Double, Double, Double, Handle, Handle, Handle, Handle, Handle, Handle, Handle, Handle, ) >(symbol: 'ParagraphBuilder::pushStyle') external void _pushStyle( Int32List encoded, List fontFamilies, double fontSize, double letterSpacing, double wordSpacing, double height, double decorationThickness, String locale, List? backgroundObjects, ByteData? backgroundData, List? foregroundObjects, ByteData? foregroundData, ByteData shadowsData, ByteData? fontFeaturesData, ByteData? fontVariationsData, ); static String _encodeLocale(Locale? locale) => locale?.toString() ?? ''; @override @Native)>(symbol: 'ParagraphBuilder::pop', isLeaf: true) external void pop(); @override void addText(String text) { final String? error = _addText(text); if (error != null) { throw ArgumentError(error); } } @Native, Handle)>(symbol: 'ParagraphBuilder::addText') external String? _addText(String text); @override void addPlaceholder( double width, double height, PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, TextBaseline? baseline, }) { // Require a baseline to be specified if using a baseline-based alignment. assert( !(alignment == PlaceholderAlignment.aboveBaseline || alignment == PlaceholderAlignment.belowBaseline || alignment == PlaceholderAlignment.baseline) || baseline != null, ); // Default the baselineOffset to height if null. This will place the placeholder // fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline]. baselineOffset = baselineOffset ?? height; _addPlaceholder( width * scale, height * scale, alignment.index, baselineOffset * scale, (baseline ?? TextBaseline.alphabetic).index, ); _placeholderCount++; _placeholderScales.add(scale); } @Native, Double, Double, Uint32, Double, Uint32)>( symbol: 'ParagraphBuilder::addPlaceholder', ) external void _addPlaceholder( double width, double height, int alignment, double baselineOffset, int baseline, ); @override Paragraph build() { final paragraph = _NativeParagraph._(); _build(paragraph); return paragraph; } @Native, Handle)>(symbol: 'ParagraphBuilder::build') external void _build(_NativeParagraph outParagraph); @override String toString() => 'ParagraphBuilder'; } /// Loads a font from a buffer and makes it available for rendering text. /// /// * `list`: A list of bytes containing the font file. /// * `fontFamily`: The family name used to identify the font in text styles. /// If this is not provided, then the family name will be extracted from the font file. Future loadFontFromList(Uint8List list, {String? fontFamily}) { return _futurize((_Callback callback) { _loadFontFromList(list, callback, fontFamily ?? ''); return null; }).then((_) => _sendFontChangeMessage()); } final ByteData _fontChangeMessage = utf8 .encode(json.encode({'type': 'fontsChange'})) .buffer .asByteData(); FutureOr _sendFontChangeMessage() async { const kSystemChannelName = 'flutter/system'; if (PlatformDispatcher.instance.onPlatformMessage != null) { _invoke3( PlatformDispatcher.instance.onPlatformMessage, PlatformDispatcher.instance._onPlatformMessageZone, kSystemChannelName, _fontChangeMessage, (ByteData? responseData) {}, ); } else { channelBuffers.push(kSystemChannelName, _fontChangeMessage, (ByteData? responseData) {}); } } @Native(symbol: 'FontCollection::LoadFontFromList') external void _loadFontFromList(Uint8List list, _Callback callback, String fontFamily); ŽíP/M@GAIFFR4XOQ?R@GDCGEGCB N*KCG?DEGCO^^V@A?G?G>1IPS^hNVJ-D)FEEA^hL/VJ,GDD8GG?@GGCCF)A?^gVH:A@FAG?CFE@EEEE ^dKW[4A/AI7@K^^VA5?G?,>*BA5*^\YV?%HBN QMCFF0dcVE)HFD;AFEGECFBGC2C@GG?E,BAB@dgVI#FGGDAFDG:^aVC+=FDFFA?6DDFDAAF0D'^gVI;PNU A<V?,?=C@?FF:ECD ^ggVI)G>77G?EABD6'``>EE2H``O=V0A"V_"?@,^]V[1L@$DDCD=F?GF1^_VJB$EGDC>=CC,J<^XVN'W)*QQPPQOGQQLPO;WT$.PIK$L2W($.NMWO%KJM&/QM,WeN/6.KCZLPQ(NUNOO7&$E"05 #,Q 09QG&NQ,MJKLNPMDO M:Y 9I$O PC7 &'MXHORPQ*@04( #"!LN4 ` u0e89 5 QQ ;KL <LK.XNQ@K+/&OE, #:^(+</A*@,C #< & $('*!,=1    -GI".IPII0N 9J+A6HPKG@5N3/NQ&NQ'LO**ED:O77ROPRSA'RPPRSA'2PKT?:5F^ #,*FF:  ! !@@4 9PN.+6 L,QHMN >NP%H9047($#'C"!'!'"!$%]$$$$$$$$$$ $Px&kSM\wY[vwt$knoo,Z]€€€¤€—h€­NDDcNR *! &2%).:] $ 3'1'#3!!%)+ & *.7 #"!>,(2.$>$,,69OHL ,;A 8BF B8K\lf{ulfkqo€¨Vfb JRZZT^` E,BJK,4804*O! *C!%"!$.$$KO5$$$$?G+GNQ%<MMCHKPQP4 #   gobgT€ VRTVWN HPQPOGD)$0GON%"5BA]#B<:;#/B3z'PPEPO?SRSSDQH&PMNAIKNPKQXHQQVOK&2  3!1E+73 #",",>O\ d%QQOJLPAONLQMQQOKDH JQO8HPP LPMAM/.Q<M I14IKQPN3=QQPMH9QKONGGKa?A)H>Y@W$AV'9!3<m:<m:< #", "& H €™K0KLNO KO-PMPPONLPQ7P+JOQ N6G=JNQ$GMN3SOEOEI6HMWN)NLMOO9K8 #,Z 5 A/MMPJ<3N^A@5A9AB.R(8)"#%!" "#' #"K A >0HQ!6-5QQPQHOPKK-NI- #,B & >C"ASO L@_-]OL<8O >1<1Q,1N"1 I1 C5!D5"QPMGEM1FNIO7Y-LU":7SH)[-+=ORPN/4PJ+P@:NQ1GOMPNSQVS84G=$L$4Q%$S;*MF1:M0K"RRK$LRINO1,P#FNTJ@ T U Z# `) `) a* b+ ^' 2  ['8+5=  ":7P;K+e ,9X3 5KXeB a_Q OQQ,M9TY[OURZW_=R>]ggv<OTTSPVPPVT#YTU&Q)Ob0A%6'59U::I/E3@ $b($ 1 <6= $)-H&3.=C78K"7#A W8\ 1%G"]>` 3!'$# # - +  && "K W *"W* &O ;A=UR/3#Q0! ,S7 +J8NYF09,)C 0/?B5:# ^[^dart:ui/text.dartÀ_éÀfdÀh³À`ÀdÙÀa?À`}À`‹À`¥À`¹À`ÇÀacÀa…Àa§ÀaµÀaË—ì.org-dartlang-sdk:///flutter/lib/ui/window.dartÀÉŒ// Copyright 2013 The Flutter Authors. 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.ui; /// A configurable display that a [FlutterView] renders on. /// /// Use [FlutterView.display] to get the current display for that view. class Display { const Display._({ required this.id, required this.devicePixelRatio, required this.size, required this.refreshRate, }); /// A unique identifier for this display. /// /// This identifier is unique among a list of displays the Flutter framework /// is aware of, and is not derived from any platform specific identifiers for /// displays. final int id; /// The device pixel ratio of this display. /// /// This value is the same as the value of [FlutterView.devicePixelRatio] for /// all view objects attached to this display. final double devicePixelRatio; /// The physical size of this display. final Size size; /// The refresh rate in FPS of this display. final double refreshRate; @override String toString() => 'Display(id: $id, size: $size, devicePixelRatio: $devicePixelRatio, refreshRate: $refreshRate)'; } /// A view into which a Flutter [Scene] is drawn. /// /// Each [FlutterView] has its own layer tree that is rendered /// whenever [render] is called on it with a [Scene]. /// /// References to [FlutterView] objects are obtained via the [PlatformDispatcher]. /// /// ## Insets and Padding /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/window_padding.mp4} /// /// In this illustration, the black areas represent system UI that the app /// cannot draw over. The red area represents view padding that the view may not /// be able to detect gestures in and may not want to draw in. The grey area /// represents the system keyboard, which can cover over the bottom view padding /// when visible. /// /// The [viewInsets] are the physical pixels which the operating /// system reserves for system UI, such as the keyboard, which would fully /// obscure any content drawn in that area. /// /// The [viewPadding] are the physical pixels on each side of the /// display that may be partially obscured by system UI or by physical /// intrusions into the display, such as an overscan region on a television or a /// "notch" on a phone. Unlike the insets, these areas may have portions that /// show the user view-painted pixels without being obscured, such as a /// notch at the top of a phone that covers only a subset of the area. Insets, /// on the other hand, either partially or fully obscure the window, such as an /// opaque keyboard or a partially translucent status bar, which cover an area /// without gaps. /// /// The [padding] property is computed from both /// [viewInsets] and [viewPadding]. It will allow a /// view inset to consume view padding where appropriate, such as when a phone's /// keyboard is covering the bottom view padding and so "absorbs" it. /// /// Clients that want to position elements relative to the view padding /// regardless of the view insets should use the [viewPadding] /// property, e.g. if you wish to draw a widget at the center of the screen with /// respect to the iPhone "safe area" regardless of whether the keyboard is /// showing. /// /// [padding] is useful for clients that want to know how much /// padding should be accounted for without concern for the current inset(s) /// state, e.g. determining whether a gesture should be considered for scrolling /// purposes. This value varies based on the current state of the insets. For /// example, a visible keyboard will consume all gestures in the bottom part of /// the [viewPadding] anyway, so there is no need to account for /// that in the [padding], which is always safe to use for such /// calculations. class FlutterView { FlutterView._(this.viewId, this.platformDispatcher, this._viewConfiguration); /// The opaque ID for this view. final int viewId; /// The platform dispatcher that this view is registered with, and gets its /// information from. final PlatformDispatcher platformDispatcher; /// The configuration of this view. _ViewConfiguration _viewConfiguration; /// The [Display] this view is drawn in. Display get display { assert(platformDispatcher._displays.containsKey(_viewConfiguration.displayId)); return platformDispatcher._displays[_viewConfiguration.displayId]!; } /// The number of device pixels for each logical pixel for the screen this /// view is displayed on. /// /// This number might not be a power of two. Indeed, it might not even be an /// integer. For example, the Nexus 6 has a device pixel ratio of 3.5. /// /// Device pixels are also referred to as physical pixels. Logical pixels are /// also referred to as device-independent or resolution-independent pixels. /// /// By definition, there are roughly 38 logical pixels per centimeter, or /// about 96 logical pixels per inch, of the physical display. The value /// returned by [devicePixelRatio] is ultimately obtained either from the /// hardware itself, the device drivers, or a hard-coded value stored in the /// operating system or firmware, and may be inaccurate, sometimes by a /// significant margin. /// /// The Flutter framework operates in logical pixels, so it is rarely /// necessary to directly deal with this property. /// /// When this changes, [PlatformDispatcher.onMetricsChanged] is called. When /// using the Flutter framework, using [MediaQuery.of] to obtain the device /// pixel ratio (via [MediaQueryData.devicePixelRatio]), instead of directly /// obtaining the [devicePixelRatio] from a [FlutterView], will automatically /// cause any widgets dependent on this value to rebuild when it changes, /// without having to listen to [PlatformDispatcher.onMetricsChanged]. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [Display.devicePixelRatio], which reports the DPR of the display. /// The value here is equal to the value exposed on [display]. double get devicePixelRatio => _viewConfiguration.devicePixelRatio; /// The sizing constraints in physical pixels for this view. /// /// The view can take on any [Size] that fulfills these constraints. These /// constraints are typically used by an UI framework as the input for its /// layout algorithm to determine an appropriate size for the view. To size /// the view, the selected size must be provided to the [render] method and it /// must satisfy the constraints. /// /// When this changes, [PlatformDispatcher.onMetricsChanged] is called. /// /// At startup, the constraints for the view may not be known before Dart code /// runs. If this value is observed early in the application lifecycle, it may /// report constraints with all dimensions set to zero. /// /// This value does not take into account any on-screen keyboards or other /// system UI. If the constraints are tight, the [padding] and [viewInsets] /// properties provide information about how much of each side of the view may /// be obscured by system UI. If the constraints are loose, this information /// is not known upfront. /// /// See also: /// /// * [physicalSize], which returns the current size of the view. ViewConstraints get physicalConstraints => _viewConfiguration.viewConstraints; /// The current dimensions of the rectangle as last reported by the platform /// into which scenes rendered in this view are drawn. /// /// If the view is configured with loose [physicalConstraints] this value /// may be outdated by a few frames as it only updates when the size chosen /// for a frame (as provided to the [render] method) is processed by the /// platform. Because of this, [physicalConstraints] should be used instead of /// this value as the root input to the layout algorithm of UI frameworks. /// /// When this changes, [PlatformDispatcher.onMetricsChanged] is called. When /// using the Flutter framework, using [MediaQuery.of] to obtain the size (via /// [MediaQueryData.size]), instead of directly obtaining the [physicalSize] /// from a [FlutterView], will automatically cause any widgets dependent on the /// size to rebuild when the size changes, without having to listen to /// [PlatformDispatcher.onMetricsChanged]. /// /// At startup, the size of the view may not be known before Dart code runs. /// If this value is observed early in the application lifecycle, it may /// report [Size.zero]. /// /// This value does not take into account any on-screen keyboards or other /// system UI. The [padding] and [viewInsets] properties provide information /// about how much of each side of the view may be obscured by system UI. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. Size get physicalSize => _viewConfiguration.size; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but over which the operating system will likely /// place system UI, such as the keyboard, that fully obscures any content. /// /// When this property changes, [PlatformDispatcher.onMetricsChanged] is /// called. When using the Flutter framework, using [MediaQuery.of] to obtain /// the insets (via [MediaQueryData.viewInsets]), instead of directly /// obtaining the [viewInsets] from a [FlutterView], will automatically cause /// any widgets dependent on the insets to rebuild when they change, without /// having to listen to [PlatformDispatcher.onMetricsChanged]. /// /// The relationship between this [viewInsets], /// [viewPadding], and [padding] are described in /// more detail in the documentation for [FlutterView]. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the view insets in material /// design applications. ViewPadding get viewInsets => _viewConfiguration.viewInsets; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but which may be partially obscured by system /// UI (such as the system notification area), or physical intrusions in /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// /// Unlike [padding], this value does not change relative to /// [viewInsets]. For example, on an iPhone X, it will not /// change in response to the soft keyboard being visible or hidden, whereas /// [padding] will. /// /// When this property changes, [PlatformDispatcher.onMetricsChanged] is /// called. When using the Flutter framework, using [MediaQuery.of] to obtain /// the padding (via [MediaQueryData.viewPadding]), instead of directly /// obtaining the [viewPadding] from a [FlutterView], will automatically cause /// any widgets dependent on the padding to rebuild when it changes, without /// having to listen to [PlatformDispatcher.onMetricsChanged]. /// /// The relationship between this [viewInsets], /// [viewPadding], and [padding] are described in /// more detail in the documentation for [FlutterView]. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the padding in material design /// applications. ViewPadding get viewPadding => _viewConfiguration.viewPadding; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but where the operating system will consume /// input gestures for the sake of system navigation. /// /// For example, an operating system might use the vertical edges of the /// screen, where swiping inwards from the edges takes users backward /// through the history of screens they previously visited. /// /// When this property changes, [PlatformDispatcher.onMetricsChanged] is called. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. ViewPadding get systemGestureInsets => _viewConfiguration.systemGestureInsets; /// The number of physical pixels on each side of the display rectangle into /// which the view can render, but which may be partially obscured by system /// UI (such as the system notification area), or physical intrusions in /// the display (e.g. overscan regions on television screens or phone sensor /// housings). /// /// This value is calculated by taking `max(0.0, FlutterView.viewPadding - /// FlutterView.viewInsets)`. This will treat a system IME that increases the /// bottom inset as consuming that much of the bottom padding. For example, on /// an iPhone X, [EdgeInsets.bottom] of [FlutterView.padding] is the same as /// [EdgeInsets.bottom] of [FlutterView.viewPadding] when the soft keyboard is /// not drawn (to account for the bottom soft button area), but will be `0.0` /// when the soft keyboard is visible. /// /// When this changes, [PlatformDispatcher.onMetricsChanged] is called. When /// using the Flutter framework, using [MediaQuery.of] to obtain the padding /// (via [MediaQueryData.padding]), instead of directly obtaining the /// [padding] from a [FlutterView], will automatically cause any widgets /// dependent on the padding to rebuild when it changes, without having to /// listen to [PlatformDispatcher.onMetricsChanged]. /// /// The relationship between this [viewInsets], [viewPadding], and [padding] /// are described in more detail in the documentation for [FlutterView]. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the padding in material design /// applications. ViewPadding get padding => _viewConfiguration.padding; /// Additional configuration for touch gestures performed on this view. /// /// For example, the touch slop defined in physical pixels may be provided /// by the gesture settings and should be preferred over the framework /// touch slop constant. GestureSettings get gestureSettings => _viewConfiguration.gestureSettings; /// {@template dart.ui.ViewConfiguration.displayFeatures} /// Areas of the display that are obstructed by hardware features. /// /// This list is populated only on Android. If the device has no display /// features, this list is empty. /// /// The coordinate space in which the [DisplayFeature.bounds] are defined spans /// across the screens currently in use. This means that the space between the screens /// is virtually part of the Flutter view space, with the [DisplayFeature.bounds] /// of the display feature as an obstructed area. The [DisplayFeature.type] can /// be used to determine if this display feature obstructs the screen or not. /// For example, [DisplayFeatureType.hinge] and [DisplayFeatureType.cutout] both /// obstruct the display, while [DisplayFeatureType.fold] is a crease in the display. /// /// Folding [DisplayFeature]s like the [DisplayFeatureType.hinge] and /// [DisplayFeatureType.fold] also have a [DisplayFeature.state] which can be /// used to determine the posture the device is in. /// {@endtemplate} /// /// When this changes, [PlatformDispatcher.onMetricsChanged] is called. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism to access this data. List get displayFeatures => _viewConfiguration.displayFeatures; /// The radii of the display corners in physical pixels. /// /// This is currently populated only on Android API 31+. On earlier Android /// versions, iOS, and other platforms, this value is `null`. DisplayCornerRadii? get displayCornerRadii => _viewConfiguration.displayCornerRadii; /// Updates the view's rendering on the GPU with the newly provided [Scene]. /// /// This function must be called within the scope of the /// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame] /// callbacks being invoked. /// /// If this function is called a second time during a single /// [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame] /// callback sequence or called outside the scope of those callbacks, the call /// will be ignored. /// /// To record graphical operations, first create a [PictureRecorder], then /// construct a [Canvas], passing that [PictureRecorder] to its constructor. /// After issuing all the graphical operations, call the /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain /// the final [Picture] that represents the issued graphical operations. /// /// Next, create a [SceneBuilder], and add the [Picture] to it using /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can /// then obtain a [Scene] object, which you can display to the user via this /// [render] function. /// /// If the view is configured with loose [physicalConstraints] (i.e. /// [ViewConstraints.isTight] returns false) a `size` satisfying those /// constraints must be provided. This method does not check that the provided /// `size` actually meets the constraints (this should be done in a higher /// level), but an illegal `size` may result in undefined rendering behavior. /// If no `size` is provided, [physicalSize] is used instead. /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. void render(Scene scene, {Size? size}) { _render( viewId, scene as _NativeScene, size?.width ?? physicalSize.width, size?.height ?? physicalSize.height, ); } @Native, Double, Double)>( symbol: 'PlatformConfigurationNativeApi::Render', ) external static void _render(int viewId, _NativeScene scene, double width, double height); /// Change the retained semantics data about this [FlutterView]. /// /// [PlatformDispatcher.setSemanticsTreeEnabled] must be called with true /// before sending update through this method. /// /// This function disposes the given update, which means the semantics update /// cannot be used further. void updateSemantics(SemanticsUpdate update) => _updateSemantics(viewId, update as _NativeSemanticsUpdate); @Native)>( symbol: 'PlatformConfigurationNativeApi::UpdateSemantics', ) external static void _updateSemantics(int viewId, _NativeSemanticsUpdate update); @override String toString() => 'FlutterView(id: $viewId)'; } /// Deprecated. Will be removed in a future version of Flutter. /// /// This class is deprecated to prepare for Flutter's upcoming support for /// multiple views and eventually multiple windows. /// /// This class has been split into two classes: [FlutterView] and /// [PlatformDispatcher]. A [FlutterView] gives an application access to /// view-specific functionality while the [PlatformDispatcher] contains /// platform-specific functionality that applies to all views. /// /// This class backs the global [window] singleton, which is also deprecated. /// See the docs on [window] for migration options. /// /// See also: /// /// * [FlutterView], which gives an application access to view-specific /// functionality. /// * [PlatformDispatcher], which gives an application access to /// platform-specific functionality. @Deprecated( 'Use FlutterView or PlatformDispatcher instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' 'This feature was deprecated after v3.7.0-32.0.pre.', ) class SingletonFlutterWindow extends FlutterView { @Deprecated( 'Use FlutterView or PlatformDispatcher instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' 'This feature was deprecated after v3.7.0-32.0.pre.', ) SingletonFlutterWindow._() : super._( // TODO(dkwingsmt): This crashes if the implicit view is disabled. We need // to resolve this by the time embedders are allowed to disable the implicit // view. // https://github.com/flutter/flutter/issues/131651 _implicitViewId!, PlatformDispatcher.instance, const _ViewConfiguration(), ); // Gets its configuration from the FlutterView with the same ID if it exists. @override _ViewConfiguration get _viewConfiguration => platformDispatcher._views[viewId]?._viewConfiguration ?? super._viewConfiguration; /// A callback that is invoked whenever the [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], or /// [systemGestureInsets] values change. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// When using the Flutter framework, the [MediaQuery] widget exposes much of /// these metrics. Using [MediaQuery.of] to obtain them allows the framework /// to automatically rebuild widgets that depend on them, without having to /// manage the [onMetricsChanged] callback. /// /// See [PlatformDispatcher.onMetricsChanged] for more information. VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; set onMetricsChanged(VoidCallback? callback) { platformDispatcher.onMetricsChanged = callback; } /// The system-reported default locale of the device. /// /// {@template dart.ui.window.accessorForwardWarning} /// Accessing this value returns the value contained in the /// [PlatformDispatcher] singleton, so instead of getting it from here, you /// should consider getting it from /// `WidgetsBinding.instance.platformDispatcher` instead (or, when /// `WidgetsBinding` isn't available, from [PlatformDispatcher.instance]). The /// reason this value forwards to the [PlatformDispatcher] is to provide /// convenience for applications that only use a single main window. /// {@endtemplate} /// /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// /// This is the first locale selected by the user and is the user's primary /// locale (the locale the device UI is displayed in) /// /// This is equivalent to `locales.first` and will provide an empty non-null /// locale if the [locales] list has not been set or is empty. Locale get locale => platformDispatcher.locale; /// The full system-reported supported locales of the device. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// /// The list is ordered in order of priority, with lower-indexed locales being /// preferred over higher-indexed ones. The first element is the primary [locale]. /// /// The [onLocaleChanged] callback is called whenever this value changes. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. List get locales => platformDispatcher.locales; /// Performs the platform-native locale resolution. /// /// Each platform may return different results. /// /// If the platform fails to resolve a locale, then this will return null. /// /// This method returns synchronously and is a direct call to /// platform specific APIs without invoking method channels. Locale? computePlatformResolvedLocale(List supportedLocales) { return platformDispatcher.computePlatformResolvedLocale(supportedLocales); } /// A callback that is invoked whenever [locale] changes value. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; set onLocaleChanged(VoidCallback? callback) { platformDispatcher.onLocaleChanged = callback; } /// The lifecycle state immediately after dart isolate initialization. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This property will not be updated as the lifecycle changes. /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. String get initialLifecycleState => platformDispatcher.initialLifecycleState; /// The system-reported text scale. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This establishes the text scaling factor to use when rendering text, /// according to the user's platform preferences. /// /// The [onTextScaleFactorChanged] callback is called whenever this value /// changes. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. double get textScaleFactor => platformDispatcher.textScaleFactor; /// Whether the spell check service is supported on the current platform. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This option is used by [EditableTextState] to define its /// [SpellCheckConfiguration] when spell check is enabled, but no spell check /// service is specified. bool get nativeSpellCheckServiceDefined => platformDispatcher.nativeSpellCheckServiceDefined; /// Whether showing system context menu is supported on the current platform. /// /// This option is used by [AdaptiveTextSelectionToolbar] to decide whether /// to show system context menu, or to fallback to the default Flutter context /// menu. bool get supportsShowingSystemContextMenu => platformDispatcher.supportsShowingSystemContextMenu; /// Whether briefly displaying the characters as you type in obscured text /// fields is enabled in system settings. /// /// See also: /// /// * [EditableText.obscureText], which when set to true hides the text in /// the text field. bool get brieflyShowPassword => platformDispatcher.brieflyShowPassword; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This option is used by [showTimePicker]. bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; set onTextScaleFactorChanged(VoidCallback? callback) { platformDispatcher.onTextScaleFactorChanged = callback; } /// The setting indicating the current brightness mode of the host platform. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// If the platform has no preference, [platformBrightness] defaults to /// [Brightness.light]. Brightness get platformBrightness => platformDispatcher.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; set onPlatformBrightnessChanged(VoidCallback? callback) { platformDispatcher.onPlatformBrightnessChanged = callback; } /// The setting indicating the system font of the host platform. /// /// {@macro dart.ui.window.accessorForwardWarning} String? get systemFontFamily => platformDispatcher.systemFontFamily; /// A callback that is invoked whenever [systemFontFamily] changes value. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. VoidCallback? get onSystemFontFamilyChanged => platformDispatcher.onSystemFontFamilyChanged; set onSystemFontFamilyChanged(VoidCallback? callback) { platformDispatcher.onSystemFontFamilyChanged = callback; } /// A callback that is invoked to notify the window that it is an appropriate /// time to provide a scene using the [SceneBuilder] API and the [render] /// method. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// When possible, this is driven by the hardware VSync signal. This is only /// called if [scheduleFrame] has been called since the last time this /// callback was invoked. /// /// The [onDrawFrame] callback is invoked immediately after [onBeginFrame], /// after draining any microtasks (e.g. completions of any [Future]s) queued /// by the [onBeginFrame] handler. /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; set onBeginFrame(FrameCallback? callback) { platformDispatcher.onBeginFrame = callback; } /// A callback that is invoked for each frame after [onBeginFrame] has /// completed and after the microtask queue has been drained. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This can be used to implement a second phase of frame rendering that /// happens after any deferred work queued by the [onBeginFrame] phase. /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; set onDrawFrame(VoidCallback? callback) { platformDispatcher.onDrawFrame = callback; } /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use /// [PlatformDispatcher.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. /// /// This can be used to see if the window has missed frames (through /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high /// latencies (through [FrameTiming.totalSpan]). /// /// Unlike [Timeline], the timing information here is available in the release /// mode (additional to the profile and the debug mode). Hence this can be /// used to monitor the application's performance in the wild. /// /// {@macro dart.ui.TimingsCallback.list} /// /// If this is null, no additional work will be done. If this is not null, /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; set onReportTimings(TimingsCallback? callback) { platformDispatcher.onReportTimings = callback; } /// A callback that is invoked when pointer data is available. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// /// See also: /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback) { platformDispatcher.onPointerDataPacket = callback; } /// A callback that is invoked when key data is available. /// /// The framework invokes this callback in the same zone in which the /// callback was set. KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; set onKeyData(KeyDataCallback? callback) { platformDispatcher.onKeyData = callback; } /// The route or path that the embedder requested when the application was /// launched. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// This will be the string "`/`" if no particular route was requested. /// /// ## Android /// /// On Android, the initial route can be set on the [initialRoute](/javadoc/io/flutter/embedding/android/FlutterActivity.NewEngineIntentBuilder.html#initialRoute-java.lang.String-) /// method of the [FlutterActivity](/javadoc/io/flutter/embedding/android/FlutterActivity.html)'s /// intent builder. /// /// On a standalone engine, see https://docs.flutter.dev/development/add-to-app/android/add-flutter-screen#initial-route-with-a-cached-engine. /// /// ## iOS /// /// On iOS, the initial route can be set with the /// [`FlutterViewController.setInitialRoute`](/ios-embedder/interface_flutter_view_controller.html#a7f269c2da73312f856d42611cc12a33f) /// initializer. /// /// On a standalone engine, see https://docs.flutter.dev/development/add-to-app/ios/add-flutter-screen#route. /// /// See also: /// /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. String get defaultRouteName => platformDispatcher.defaultRouteName; /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and /// [onDrawFrame] callbacks be invoked. /// /// {@template dart.ui.window.functionForwardWarning} /// Calling this function forwards the call to the same function on the /// [PlatformDispatcher] singleton, so instead of calling it here, you should /// consider calling it on `WidgetsBinding.instance.platformDispatcher` instead (or, when /// `WidgetsBinding` isn't available, on [PlatformDispatcher.instance]). The /// reason this function forwards to the [PlatformDispatcher] is to provide /// convenience for applications that only use a single main window. /// {@endtemplate} /// /// See also: /// /// * [SchedulerBinding], the Flutter framework class which manages the /// scheduling of frames. void scheduleFrame() => platformDispatcher.scheduleFrame(); /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. bool get semanticsEnabled => platformDispatcher.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; set onSemanticsEnabledChanged(VoidCallback? callback) { platformDispatcher.onSemanticsEnabledChanged = callback; } /// The [FrameData] object for the current frame. FrameData get frameData => platformDispatcher.frameData; /// A callback that is invoked when the window updates the [FrameData]. VoidCallback? get onFrameDataChanged => platformDispatcher.onFrameDataChanged; set onFrameDataChanged(VoidCallback? callback) { platformDispatcher.onFrameDataChanged = callback; } /// Additional accessibility features that may be enabled by the platform. AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The framework invokes this callback in the same zone in which the /// callback was set. VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback) { platformDispatcher.onAccessibilityFeaturesChanged = callback; } /// Sends a message to a platform-specific plugin. /// /// {@macro dart.ui.window.functionForwardWarning} /// /// The `name` parameter determines which plugin receives the message. The /// `data` parameter contains the message payload and is typically UTF-8 /// encoded JSON but can be arbitrary data. If the plugin replies to the /// message, `callback` will be called with the response. /// /// The framework invokes [callback] in the same zone in which this method /// was called. void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { platformDispatcher.sendPlatformMessage(name, data, callback); } /// Deprecated. Migrate to [ChannelBuffers.setListener] instead. /// /// Called whenever this window receives a message from a platform-specific /// plugin. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// The `name` parameter determines which plugin sent the message. The `data` /// parameter is the payload and is typically UTF-8 encoded JSON but can be /// arbitrary data. /// /// Message handlers must call the function given in the `callback` parameter. /// If the handler does not need to respond, the handler should pass null to /// the callback. /// /// The framework invokes this callback in the same zone in which the /// callback was set. @Deprecated( 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; @Deprecated( 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) set onPlatformMessage(PlatformMessageCallback? callback) { platformDispatcher.onPlatformMessage = callback; } /// Set the debug name associated with this platform dispatcher's root /// isolate. /// /// {@macro dart.ui.window.accessorForwardWarning} /// /// Normally debug names are automatically generated from the Dart port, entry /// point, and source file. For example: `main.dart$main-1234`. /// /// This can be combined with flutter tools `--isolate-filter` flag to debug /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. /// Note that this does not rename any child isolates of the root. void setIsolateDebugName(String name) => PlatformDispatcher.instance.setIsolateDebugName(name); } /// Additional accessibility features that may be enabled by the platform. /// /// It is not possible to enable these settings from Flutter, instead they are /// used by the platform to indicate that additional accessibility features are /// enabled. // // When changes are made to this class, the equivalent APIs in each of the // embedders *must* be updated. class AccessibilityFeatures { const AccessibilityFeatures._(this._index); static const int _kAccessibleNavigationIndex = 1 << 0; static const int _kInvertColorsIndex = 1 << 1; static const int _kDisableAnimationsIndex = 1 << 2; static const int _kBoldTextIndex = 1 << 3; static const int _kReduceMotionIndex = 1 << 4; static const int _kHighContrastIndex = 1 << 5; static const int _kOnOffSwitchLabelsIndex = 1 << 6; static const int _kNoAnnounceIndex = 1 << 7; static const int _kNoAutoPlayAnimatedImagesIndex = 1 << 8; static const int _kNoAutoPlayVideosIndex = 1 << 9; static const int _kDeterministicCursorIndex = 1 << 10; // A bitfield which represents each enabled feature. final int _index; /// Whether there is a running accessibility service which is changing the /// interaction model of the device. /// /// For example, TalkBack on Android and VoiceOver on iOS enable this flag. bool get accessibleNavigation => _kAccessibleNavigationIndex & _index != 0; /// The platform is inverting the colors of the application. bool get invertColors => _kInvertColorsIndex & _index != 0; /// The platform is requesting that animations be disabled or simplified. bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; /// The platform is requesting that text be rendered at a bold font weight. /// /// Only supported on iOS and Android API 31+. bool get boldText => _kBoldTextIndex & _index != 0; /// The platform is requesting that certain animations be simplified and /// parallax effects removed. /// /// Only supported on iOS. bool get reduceMotion => _kReduceMotionIndex & _index != 0; /// The platform is requesting that UI be rendered with darker colors. /// /// Only supported on iOS. bool get highContrast => _kHighContrastIndex & _index != 0; /// The platform is requesting to show on/off labels inside switches. /// /// Only supported on iOS. bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0; /// Whether the platform supports accessibility announcement API, /// i.e. [SemanticsService.sendAnnouncement]. /// /// Some platforms do not support or discourage the use of /// announcement. Using [SemanticsService.sendAnnouncement] on those platform /// may be ignored. Consider using other way to convey message to the /// user. For example, Android discourages the uses of direct message /// announcement, and rather encourages using other semantic /// properties such as [SemanticsProperties.liveRegion] to convey /// message to the user. /// /// Returns `false` on platforms where announcements are deprecated or /// unsupported by the underlying platform. /// /// Returns `true` on platforms where such announcements are /// generally supported without discouragement. (iOS, web etc) // This index check is inverted (== 0 vs != 0); far more platforms support // "announce" than discourage it. bool get supportsAnnounce => _kNoAnnounceIndex & _index == 0; /// Whether the platform allows auto-playing animated images. /// /// Only supported on iOS. /// /// Always returns `true` on other platforms. // This index check is inverted (== 0 vs != 0) since most of the platforms // don't have an option to disable animated images auto play. bool get autoPlayAnimatedImages => _kNoAutoPlayAnimatedImagesIndex & _index == 0; /// Whether the platform allows auto-playing videos. /// /// Only supported on iOS. /// /// Always returns `true` on other platforms. // This index check is inverted (== 0 vs != 0) since most of the platforms // don't have an option to disable videos auto play. bool get autoPlayVideos => _kNoAutoPlayVideosIndex & _index == 0; /// The platform is requesting to show deterministic (non-blinking) cursor in /// editable text fields. /// /// Only supported on iOS. bool get deterministicCursor => _kDeterministicCursorIndex & _index != 0; @override String toString() { final features = []; if (accessibleNavigation) { features.add('accessibleNavigation'); } if (invertColors) { features.add('invertColors'); } if (disableAnimations) { features.add('disableAnimations'); } if (boldText) { features.add('boldText'); } if (reduceMotion) { features.add('reduceMotion'); } if (highContrast) { features.add('highContrast'); } if (onOffSwitchLabels) { features.add('onOffSwitchLabels'); } if (supportsAnnounce) { features.add('supportsAnnounce'); } if (autoPlayAnimatedImages) { features.add('autoPlayAnimatedImages'); } if (autoPlayVideos) { features.add('autoPlayVideos'); } if (deterministicCursor) { features.add('deterministicCursor'); } return 'AccessibilityFeatures$features'; } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is AccessibilityFeatures && other._index == _index; } @override int get hashCode => _index.hashCode; } /// Describes the contrast of a theme or color palette. enum Brightness { /// The color is dark and will require a light text color to achieve readable /// contrast. /// /// For example, the color might be dark grey, requiring white text. dark, /// The color is light and will require a dark text color to achieve readable /// contrast. /// /// For example, the color might be bright white, requiring black text. light, } /// Deprecated. Will be removed in a future version of Flutter. /// /// This global property is deprecated to prepare for Flutter's upcoming support /// for multiple views and multiple windows. /// /// It represents the main view for applications where there is only one /// view, such as applications designed for single-display mobile devices. /// If the embedder supports multiple views, it points to the first view /// created which is assumed to be the main view. It throws if no view has /// been created yet or if the first view has been removed again. /// /// The following options exists to migrate code that relies on accessing /// this deprecated property: /// /// If a [BuildContext] is available, consider looking up the current /// [FlutterView] associated with that context via [View.of]. It gives access /// to the same functionality as this deprecated property. However, the /// platform-specific functionality has moved to the [PlatformDispatcher], /// which may be accessed from the view returned by [View.of] via /// [FlutterView.platformDispatcher]. Using [View.of] with a [BuildContext] is /// the preferred option to migrate away from this deprecated [window] /// property. /// /// If no context is available to look up a [FlutterView], the /// [PlatformDispatcher] can be used directly for platform-specific /// functionality. It also maintains a list of all available [FlutterView]s in /// [PlatformDispatcher.views] to access view-specific functionality without a /// context. If possible, consider accessing the [PlatformDispatcher] via the /// binding (e.g. `WidgetsBinding.instance.platformDispatcher`) instead of the /// static singleton [PlatformDispatcher.instance]. See /// [PlatformDispatcher.instance] for more information about why this is /// preferred. /// /// See also: /// /// * [FlutterView], which gives an application access to view-specific /// functionality. /// * [PlatformDispatcher], which gives an application access to /// platform-specific functionality. /// * [PlatformDispatcher.views], for a list of all available views. @Deprecated( 'Look up the current FlutterView from the context via View.of(context) or consult the PlatformDispatcher directly instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' 'This feature was deprecated after v3.7.0-32.0.pre.', ) final SingletonFlutterWindow window = SingletonFlutterWindow._(); /// Additional data available on each flutter frame. /// /// See also: /// /// * [Window.frameData] and [PlatformDispatcher.frameData], which expose the /// frame data for the current frame. /// * [PlatformDispatcher.onFrameDataChanged], which notifies listeners when /// a window's frame data has changed. class FrameData { const FrameData._({this.frameNumber = -1}); /// The number of the current frame. /// /// This number monotonically increases, but doesn't necessarily /// start at a particular value. /// /// If not provided, defaults to -1. final int frameNumber; } /// Platform specific configuration for gesture behavior, such as touch slop. /// /// These settings are provided via [FlutterView.gestureSettings] to each /// view, and should be favored for configuring gesture behavior over the /// framework constants. /// /// A `null` field indicates that the platform or view does not have a preference /// and the fallback constants should be used instead. class GestureSettings { /// Create a new [GestureSettings] value. /// /// Consider using [GestureSettings.copyWith] on an existing settings object /// to ensure that newly added fields are correctly set. const GestureSettings({this.physicalTouchSlop, this.physicalDoubleTapSlop}); /// The number of physical pixels a pointer is allowed to drift before it is /// considered an intentional movement. /// /// If `null`, the framework's default touch slop configuration should be used /// instead. final double? physicalTouchSlop; /// The number of physical pixels that the first and second tap of a double tap /// can drift apart to still be recognized as a double tap. /// /// If `null`, the framework's default double tap slop configuration should be used /// instead. final double? physicalDoubleTapSlop; /// Create a new [GestureSettings] object from an existing value, overwriting /// all of the provided fields. GestureSettings copyWith({double? physicalTouchSlop, double? physicalDoubleTapSlop}) { return GestureSettings( physicalTouchSlop: physicalTouchSlop ?? this.physicalTouchSlop, physicalDoubleTapSlop: physicalDoubleTapSlop ?? this.physicalDoubleTapSlop, ); } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is GestureSettings && other.physicalTouchSlop == physicalTouchSlop && other.physicalDoubleTapSlop == physicalDoubleTapSlop; } @override int get hashCode => Object.hash(physicalTouchSlop, physicalDoubleTapSlop); @override String toString() => 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; } „ºSK*N&EQKGK?N8OA2@5K?QULK*:62M@?IOB5HK0K03I5BL0P&5K4LK*DL5?P`PNQ dM,NJP5/NK5HK0]9<O5JNN5HK0cK/5MDO5H_:=4;JQ36M_T5H69?B55MKK<MeBCN5PNQOH6;Z6;=5I5QBOUEbKOP K .916-116/=597M'NN?>LHN16K >I>HHE0=PHH?DI.?AM$@@0M@T70M7DPL ! ,$) $$)(".&+- #,E '8PGPJ @Q-IKIKBJFNHKBOG?DOONO8IHA'E B8B5O)N*.'C#'NJJR7,O;OO*Q#R>V'P"YFR #,'8> M odart:ui/window.dartÀc+ÀfdÀh³—ìÀdÙÀ[m3org-dartlang-sdk:///flutter/lib/ui/annotations.dart…k// Copyright 2013 The Flutter Authors. 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.ui; /// Annotation to keep [Object.toString] overrides as-is instead of removing /// them for size optimization purposes. /// /// For certain uris (currently `dart:ui` and `package:flutter`) the Dart /// compiler will remove [Object.toString] overrides from classes in /// profile/release mode to reduce code size. /// /// Individual classes can opt out of this behavior via the following /// annotations: /// /// * `@pragma('flutter:keep-to-string')` /// * `@pragma('flutter:keep-to-string-in-subtypes')` /// /// See https://github.com/dart-lang/sdk/blob/main/runtime/docs/pragmas.md /// /// For example, in the following class the `toString` method will remain as /// `return _buffer.toString();`, even if the `--delete-tostring-package-uri` /// option would otherwise apply and replace it with `return super.toString()`. /// (By convention, `dart:ui` is usually imported `as ui`, hence the prefix.) /// /// ```dart /// class MyStringBuffer { /// final StringBuffer _buffer = StringBuffer(); /// /// // ... /// /// @ui.keepToString /// @override /// String toString() { /// return _buffer.toString(); /// } /// } /// ``` const pragma keepToString = pragma('flutter:keep-to-string'); *dart:ui/annotations.dartÀeÀh³,org-dartlang-sdk:///flutter/lib/ui/lerp.dart…š// Copyright 2013 The Flutter Authors. 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.ui; /// Linearly interpolate between two numbers, `a` and `b`, by an extrapolation /// factor `t`. /// /// When `a` and `b` are equal or both NaN, `a` is returned. Otherwise, /// `a`, `b`, and `t` are required to be finite or null, and the result of `a + /// (b - a) * t` is returned, where nulls are defaulted to 0.0. double? lerpDouble(num? a, num? b, double t) { if (a == b || (a?.isNaN ?? false) && (b?.isNaN ?? false)) { return a?.toDouble(); } a ??= 0.0; b ??= 0.0; assert(a.isFinite, 'Cannot interpolate between finite and non-finite values'); assert(b.isFinite, 'Cannot interpolate between finite and non-finite values'); assert(t.isFinite, 't must be finite when interpolating between values'); return a * (1.0 - t) + b * t; } /// Linearly interpolate between two doubles. /// /// Same as [lerpDouble] but specialized for non-null `double` type. double _lerpDouble(double a, double b, double t) { // This doesn't match _lerpInt to preserve specific behaviors when dealing // with infinity and nan. return a * (1.0 - t) + b * t; } /// Linearly interpolate between two integers. /// /// Same as [lerpDouble] but specialized for non-null `int` type. double _lerpInt(int a, int b, double t) { return a + (b - a) * t; } ) QQL .E3M /B*dart:ui/lerp.dart,org-dartlang-sdk:///flutter/lib/ui/math.dart‚˜// Copyright 2013 The Flutter Authors. 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.ui; /// Same as [num.clamp] but optimized for a non-null [double]. /// /// This is faster because it avoids polymorphism, boxing, and special cases for /// floating point numbers. // // See also: //dev/benchmarks/microbenchmarks/lib/foundation/clamp.dart double clampDouble(double x, double min, double max) { assert(min <= max && !max.isNaN && !min.isNaN); if (x < min) { return min; } if (x > max) { return max; } if (x.isNaN) { return max; } return x; }  Function(ProcessSignal signal)? _signalWatch; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) StreamSubscription? _signalSubscription; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) bool _serveDevtools = true; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) bool _enableServicePortFallback = false; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) bool _waitForDdsToAdvertiseService = false; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) bool _printDtd = false; File? _residentCompilerInfoFile = null; @pragma('vm:entry-point', !bool.fromEnvironment('dart.vm.product')) void _populateResidentCompilerInfoFile( /// If either `--resident-compiler-info-file` or `--resident-server-info-file` /// was supplied on the command line, the CLI argument should be forwarded as /// the argument to this parameter. If neither option was supplied, the /// argument to this parameter should be [null]. String? residentCompilerInfoFilePathArgumentFromCli, ) { _residentCompilerInfoFile = getResidentCompilerInfoFileConsideringArgsImpl( residentCompilerInfoFilePathArgumentFromCli, ); } File? _getResidentCompilerInfoFile() => _residentCompilerInfoFile; // HTTP server. late final Server server; Future cleanupCallback() async { // Cancel the sigquit subscription. await _signalSubscription?.cancel(); _signalSubscription = null; try { await server.shutdown(true); } catch (e, st) { print('Error in vm-service shutdown: $e\n$st\n'); } // Call out to embedder's shutdown callback. _shutdown(); } Future ddsConnectedCallback() async { final serviceAddress = server!.serverAddress.toString(); _notifyServerState(serviceAddress); onServerAddressChange(serviceAddress); if (_waitForDdsToAdvertiseService) { await server.outputConnectionInformation(); } } Future ddsDisconnectedCallback() async { final serviceAddress = server.serverAddress.toString(); _notifyServerState(serviceAddress); onServerAddressChange(serviceAddress); } Future createTempDirCallback(String base) async { final temp = await Directory.systemTemp.createTemp(base); // Underneath the temporary directory, create a directory with the // same name as the DevFS name [base]. final fsUri = temp.uri.resolveUri(Uri.directory(base)); await Directory.fromUri(fsUri).create(); return fsUri; } Future deleteDirCallback(Uri path) async => await Directory.fromUri(path).delete(recursive: true); class PendingWrite { PendingWrite(this.uri, this.bytes); final completer = Completer(); final Uri uri; final List bytes; Future write() async { final file = File.fromUri(uri); final parent_directory = file.parent; await parent_directory.create(recursive: true); if (await file.exists()) { await file.delete(); } await file.writeAsBytes(bytes); completer.complete(); WriteLimiter._writeCompleted(); } } class WriteLimiter { static final pendingWrites = []; // non-rooted Android devices have a very low limit for the number of // open files. Artificially cap ourselves to 16. static const kMaxOpenWrites = 16; static int openWrites = 0; static Future scheduleWrite(Uri path, List bytes) async { // Create a new pending write. final pw = PendingWrite(path, bytes); pendingWrites.add(pw); _maybeWriteFiles(); return pw.completer.future; } static void _maybeWriteFiles() { while (openWrites < kMaxOpenWrites) { if (pendingWrites.length > 0) { final pw = pendingWrites.removeLast(); pw.write(); openWrites++; } else { break; } } } static void _writeCompleted() { openWrites--; assert(openWrites >= 0); _maybeWriteFiles(); } } Future writeFileCallback(Uri path, List bytes) async => WriteLimiter.scheduleWrite(path, bytes); Future writeStreamFileCallback(Uri path, Stream> bytes) async { final file = File.fromUri(path); final parent_directory = file.parent; await parent_directory.create(recursive: true); if (await file.exists()) { await file.delete(); } final sink = await file.openWrite(); await sink.addStream(bytes); await sink.close(); } Future> readFileCallback(Uri path) async => await File.fromUri(path).readAsBytes(); Future>> listFilesCallback(Uri dirPath) async { final dir = Directory.fromUri(dirPath); final dirPathStr = dirPath.path; final stream = dir.list(recursive: true); final result = >[]; await for (var fileEntity in stream) { final filePath = Uri.file(fileEntity.path).path; final stat = await fileEntity.stat(); if (stat.type == FileSystemEntityType.file && filePath.startsWith(dirPathStr)) { final map = {}; map['name'] = '/' + filePath.substring(dirPathStr.length); map['size'] = stat.size; map['modified'] = stat.modified.millisecondsSinceEpoch; result.add(map); } } return result; } Uri? serverInformationCallback() => server.serverAddress; Future _toggleWebServer() async { // Toggle HTTP server. if (server.running) { await server.shutdown(true); await VMService().clearState(); } else { await server.startup(); } } Future webServerControlCallback(bool enable, bool? silenceOutput) async { if (silenceOutput != null) { silentVMService = silenceOutput; } if (server.running != enable) { await _toggleWebServer(); } return server.serverAddress; } void webServerAcceptNewWebSocketConnections(bool enable) { server.acceptNewWebSocketConnections = enable; } void _registerSignalHandler() { if (VMService().isExiting) { // If the VM started shutting down we don't want to register this signal // handler, otherwise we'll cause the VM to hang after killing the service // isolate. return; } final signalWatch = _signalWatch; if (signalWatch == null) { // Cannot register for signals. return; } if (_isWindows || _isFuchsia) { // Cannot register for signals on Windows or Fuchsia. return; } _signalSubscription = signalWatch( ProcessSignal.sigquit, ).listen((_) => _toggleWebServer()); } @pragma( 'vm:entry-point', !bool.fromEnvironment('dart.vm.product') ? 'get' : false, ) void main() { // Set embedder hooks. VMServiceEmbedderHooks.cleanup = cleanupCallback; VMServiceEmbedderHooks.createTempDir = createTempDirCallback; VMServiceEmbedderHooks.ddsConnected = ddsConnectedCallback; VMServiceEmbedderHooks.ddsDisconnected = ddsDisconnectedCallback; VMServiceEmbedderHooks.deleteDir = deleteDirCallback; VMServiceEmbedderHooks.writeFile = writeFileCallback; VMServiceEmbedderHooks.writeStreamFile = writeStreamFileCallback; VMServiceEmbedderHooks.readFile = readFileCallback; VMServiceEmbedderHooks.listFiles = listFilesCallback; VMServiceEmbedderHooks.serverInformation = serverInformationCallback; VMServiceEmbedderHooks.webServerControl = webServerControlCallback; VMServiceEmbedderHooks.acceptNewWebSocketConnections = webServerAcceptNewWebSocketConnections; VMServiceEmbedderHooks.getResidentCompilerInfoFile = _getResidentCompilerInfoFile; server = Server( // Always instantiate the vmservice object so that the exit message // can be delivered and waiting loaders can be cancelled. VMService(), _ip, _port, _originCheckDisabled, _authCodesDisabled, _serviceInfoFilename, _enableServicePortFallback, ); if (_autoStart) { _toggleWebServer(); } _registerSignalHandler(); } @pragma("vm:external-name", "VMServiceIO_Shutdown") external void _shutdown(); �7NN<%(DD4DD&D0D!*D#:DDDDDD8DD)D,D(D(QPJ37N1C'&'!6/,;&)'0/:&)7:(!$ P%";1 MO $$ ": %' <4@>D88D68HF9.7$H>  4dart:vmservice_ioÀeÀh³[org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/vmservice_server.dartÀW onWebSocketMessage(message)); socket.done.then((_) => close()); } Future disconnect() => socket.close(); void onWebSocketMessage(message) { if (message is String) { dynamic jsonObj; try { jsonObj = json.decode(message); } catch (e) { socket.close(parseErrorCode, 'Message parse error: $e'); return; } if (jsonObj is! Map) { socket.close(notMapErrorCode, 'Message must be a JSON map.'); return; } final Map map = jsonObj; final rpc = Message.fromJsonRpc(this, map); switch (rpc.type) { case MessageType.Request: onRequest(rpc); break; case MessageType.Notification: onNotification(rpc); break; case MessageType.Response: onResponse(rpc); break; } } else { socket.close(binaryMessageErrorCode, 'Message must be a string.'); } } void post(Response? result) { if (result == null) { // The result of a notification event. Do nothing. return; } try { switch (result.kind) { case ResponsePayloadKind.String: case ResponsePayloadKind.Binary: socket.add(result.payload); break; case ResponsePayloadKind.Utf8String: socket.addUtf8Text(result.payload as List); break; } } on StateError catch (_) { // VM has shutdown, do nothing. return; } } Map toJson() => { ...super.toJson(), 'type': 'WebSocketClient', 'socket': '$socket', }; } class HttpRequestClient extends Client { static final jsonContentType = ContentType( 'application', 'json', charset: 'utf-8', ); final HttpRequest request; HttpRequestClient(this.request, VMService service) : super(service, sendEvents: false); Future disconnect() async { await request.response.close(); close(); } void post(Response? result) { if (result == null) { // The result of a notification event. Nothing to do other than close the // connection. close(); return; } HttpResponse response = request.response; // We closed the connection for bad origins earlier. response.headers.add('Access-Control-Allow-Origin', '*'); response.headers.contentType = jsonContentType; switch (result.kind) { case ResponsePayloadKind.String: response.write(result.payload); break; case ResponsePayloadKind.Utf8String: response.add(result.payload); break; case ResponsePayloadKind.Binary: throw 'Can not handle binary responses'; } response.close(); close(); } Map toJson() { final map = super.toJson(); map['type'] = 'HttpRequestClient'; map['request'] = '$request'; return map; } } /// Responsible for launching a DevTools instance when the service is started /// via SIGQUIT. class _DebuggingSession { Future start( Uri serverAddress, String host, String port, bool disableServiceAuthCodes, bool enableDevTools, ) async { // This code is part of the SDK and it is ok to have a reference to the // internals of the Dart SDK in terms of location of the snapshot etc. // It is more efficient doing it this way instead of invoking the Dart CLI // with the 'development-service' command which would then dispatch to the // Dart AOT runtime. final dartDir = File(Platform.executable).parent.path; final suffix = Platform.isWindows ? '.exe' : ''; final dartAotRuntime = 'dartaotruntime${suffix}'; final dart = 'dart${suffix}'; var executable = [dartDir, dartAotRuntime].join(Platform.pathSeparator); var script = [ dartDir, 'snapshots', 'dds_aot.dart.snapshot', ].join(Platform.pathSeparator); if (FileSystemEntity.typeSync(script) == FileSystemEntityType.notFound) { script = [dartDir, 'dds_aot.dart.snapshot'].join(Platform.pathSeparator); if (FileSystemEntity.typeSync(script) == FileSystemEntityType.notFound) { // We could be running on IA32 architecture so check if the JIT // snapshot is available. executable = [dartDir, dart].join(Platform.pathSeparator); script = [dartDir, 'dds.dart.snapshot'].join(Platform.pathSeparator); if (FileSystemEntity.typeSync(script) == FileSystemEntityType.notFound) { script = 'development-service'; } } } // If the directory of dart is '.' it's likely that dart is on the user's // PATH. If so, './dart' might not exist and we should be using 'dart' // instead. if (dartDir == '.' && (FileSystemEntity.typeSync(executable)) == FileSystemEntityType.notFound) { executable = dart; } var process = await Process.start(executable, [ script, '--vm-service-uri=$serverAddress', '--bind-address=$host', '--bind-port=$port', if (disableServiceAuthCodes) '--disable-service-auth-codes', if (enableDevTools) '--serve-devtools', if (_enableServicePortFallback) '--enable-service-port-fallback', ], mode: ProcessStartMode.detachedWithStdio); if (process == null) { stderr.writeln('Could not start the VM service: Process.start failed\n'); return false; } _process = process; // DDS will close stderr once it's finished launching. final launchResultStderr = await _process.stderr .transform(utf8.decoder) .join(); void printError(String details) => stderr.writeln('Could not start the VM service: $details'); try { final result = json.decode(launchResultStderr) as Map; if (result case {'state': 'started'}) { if (result case {'devToolsUri': String devToolsUri}) { // NOTE: update pkg/dartdev/lib/src/commands/run.dart if this message // is changed to ensure consistency. const devToolsMessagePrefix = 'The Dart DevTools debugger and profiler is available at:'; serverPrint('$devToolsMessagePrefix $devToolsUri'); } if (result case {'dtd': {'uri': String dtdUri}} when _printDtd) { serverPrint('The Dart Tooling Daemon (DTD) is available at: $dtdUri'); } } else { printError(result['error'] ?? result); return false; } } catch (_) { // Malformed JSON was likely encountered, so output the entirety of // stderr in the error message. printError("Couldn't parse JSON: ${launchResultStderr}"); return false; } return true; } void shutdown() => _process.kill(); late Process _process; } class Server { static const WEBSOCKET_PATH = '/ws'; static const ROOT_REDIRECT_PATH = '/index.html'; final VMService _service; final String _ip; final bool _originCheckDisabled; final bool _authCodesDisabled; final bool _enableServicePortFallback; final String? _serviceInfoFilename; HttpServer? _httpServer; bool get running => _running; bool _running = false; bool acceptNewWebSocketConnections = true; int _port = -1; // Ensures only one server is started even if many requests to launch // the server come in concurrently. Completer? _startingCompleter; _DebuggingSession? _ddsInstance; /// Returns the server address including the auth token. Uri? get serverAddress { // If DDS is connected it should be treated as the "true" VM service and be // advertised as such. if (_service.ddsUri != null) { return _service.ddsUri; } final server = _httpServer; if (server != null) { final ip = server.address.address; final port = server.port; final path = !_authCodesDisabled ? '$serviceAuthToken/' : '/'; return Uri(scheme: 'http', host: ip, port: port, path: path); } return null; } // On Fuchsia, authentication codes are disabled by default. To enable, the authentication token // would have to be written into the hub alongside the port number. Server( this._service, this._ip, this._port, this._originCheckDisabled, bool authCodesDisabled, this._serviceInfoFilename, this._enableServicePortFallback, ) : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia); Future startup() async { if (running) { // Already running. return; } { final startingCompleter = _startingCompleter; if (startingCompleter != null) { if (!startingCompleter.isCompleted) { await startingCompleter.future; } return; } } final startingCompleter = Completer(); _startingCompleter = startingCompleter; // Startup HTTP server. Future startServer() async { try { var address; var addresses = await InternetAddress.lookup(_ip); // Prefer IPv4 addresses. for (int i = 0; i < addresses.length; i++) { address = addresses[i]; if (address.type == InternetAddressType.IPv4) break; } _httpServer = await HttpServer.bind(address, _port); } catch (e, st) { if (_port != 0 && _enableServicePortFallback) { serverPrint( 'Failed to bind Dart VM service HTTP server to port $_port. ' 'Falling back to automatic port selection', ); _port = 0; return await startServer(); } else { serverPrint( 'Could not start Dart VM service HTTP server:\n' '$e\n$st', ); _notifyServerState(''); onServerAddressChange(null); return false; } } return true; } if (!(await startServer())) { startingCompleter.complete(true); return; } if (_service.isExiting) { serverPrint( 'Dart VM service HTTP server exiting before listening as ' 'vm service has received exit request\n', ); startingCompleter.complete(true); await shutdown(true); return; } final server = _httpServer!; server.listen(_requestHandler, cancelOnError: true); if (_waitForDdsToAdvertiseService) { _ddsInstance = _DebuggingSession(); await _ddsInstance!.start( serverAddress!, _ddsIP, _ddsPort.toString(), _authCodesDisabled, _serveDevtools, ); } else { await outputConnectionInformation(); } // Server is up and running. _running = true; _notifyServerState(serverAddress.toString()); onServerAddressChange('$serverAddress'); startingCompleter.complete(true); } Future shutdown(bool forced) async { // If start is pending, wait for it to complete. if (_startingCompleter != null) { if (!_startingCompleter!.isCompleted) { await _startingCompleter!.future; } } final server = _httpServer; if (server == null) { // Not started. return; } if (Platform.isFuchsia) { _cleanupFuchsiaState(server.port); } final address = serverAddress!; try { // Shutdown HTTP server and subscription. await server.close(force: forced); if (!_service.isExiting) { // Only print this message if the service has been toggled off, not // when the VM is exiting. serverPrint('Dart VM service no longer listening on $address'); } } catch (e, st) { serverPrint('Could not shutdown Dart VM service HTTP server:\n$e\n$st\n'); } finally { _ddsInstance?.shutdown(); _ddsInstance = null; _httpServer = null; _startingCompleter = null; _running = false; _notifyServerState(''); onServerAddressChange(null); } } Future outputConnectionInformation() async { serverPrint('The Dart VM service is listening on $serverAddress'); if (Platform.isFuchsia) { _writeFuchsiaState(_httpServer!.port); } final serviceInfoFilenameLocal = _serviceInfoFilename; if (serviceInfoFilenameLocal != null && serviceInfoFilenameLocal.isNotEmpty) { await _dumpServiceInfoToFile(serviceInfoFilenameLocal); } } bool _isAllowedOrigin(String origin) { Uri uri; try { uri = Uri.parse(origin); } catch (_) { return false; } // Explicitly add localhost and 127.0.0.1 on any port (necessary for // adb port forwarding). if ((uri.host == 'localhost') || (uri.host == '::1') || (uri.host == '127.0.0.1')) { return true; } final server = _httpServer!; if ((uri.port == server.port) && ((uri.host == server.address.address) || (uri.host == server.address.host))) { return true; } return false; } bool _originCheck(HttpRequest request) { if (_originCheckDisabled) { // Always allow. return true; } // First check the web-socket specific origin. List? origins = request.headers['Sec-WebSocket-Origin']; if (origins == null) { // Fall back to the general Origin field. origins = request.headers['Origin']; } if (origins == null) { // No origin sent. This is a non-browser client or a same-origin request. return true; } for (final origin in origins) { if (_isAllowedOrigin(origin)) { return true; } } return false; } /// Checks the [requestUri] for the service auth token and returns the path /// as a String. If the service auth token check fails, returns null. /// Returns a Uri if a redirect is required. dynamic _checkAuthTokenAndGetPath(Uri requestUri) { if (_authCodesDisabled) { return requestUri.path == '/' ? ROOT_REDIRECT_PATH : requestUri.path; } final List requestPathSegments = requestUri.pathSegments; if (requestPathSegments.isEmpty) { // Malformed. return null; } // Check that we were given the auth token. final authToken = requestPathSegments[0]; if (authToken != serviceAuthToken) { // Malformed. return null; } // Missing a trailing '/'. We'll need to redirect to serve // ROOT_REDIRECT_PATH correctly, otherwise the response is misinterpreted. if (requestPathSegments.length == 1) { // requestPathSegments is unmodifiable. Copy it. final pathSegments = List.from(requestPathSegments); // Adding an empty string to the path segments results in the path having // a trailing '/'. pathSegments.add(''); return requestUri.replace(pathSegments: pathSegments); } // Construct the actual request path by chopping off the auth token. return (requestPathSegments[1] == '') ? ROOT_REDIRECT_PATH : '/${requestPathSegments.sublist(1).join('/')}'; } Future _processDevFSRequest(HttpRequest request) async { String? fsName; String? fsPath; Uri? fsUri; try { // Extract the fs name and fs path from the request headers. fsName = request.headers['dev_fs_name']![0]; // Prefer Uri encoding first, then fallback to path encoding. if (request.headers['dev_fs_uri_b64'] case [String base64Uri]) { fsUri = Uri.parse(utf8.decode(base64.decode(base64Uri))); } else if (request.headers['dev_fs_path_b64'] case [String base64Uri]) { fsPath = utf8.decode(base64.decode(base64Uri)); } else if (request.headers['dev_fs_path'] case [String path]) { fsPath = path; } } catch (_) { /* ignore */ } try { final result = await _service.devfs.handlePutStream( fsName, fsPath, fsUri, request.cast>().transform(gzip.decoder), ); request.response.headers.contentType = HttpRequestClient.jsonContentType; request.response.write(result); } catch (e, st) { request.response.statusCode = HttpStatus.internalServerError; request.response.write(e); } finally { request.response.close(); } } void _handleWebSocketRequest(HttpRequest request) { final subprotocols = request.headers['sec-websocket-protocol']; if (acceptNewWebSocketConnections) { WebSocketTransformer.upgrade( request, protocolSelector: subprotocols == null ? null : (_) => 'implicit-redirect', compression: CompressionOptions.compressionOff, ).then((WebSocket webSocket) { WebSocketClient(webSocket, _service); }); } else { // Attempt to redirect client to the DDS instance. request.response.redirect(_service.ddsUri!); } } Future _redirectToDevTools(HttpRequest request) async { final ddsUri = _service.ddsUri; if (ddsUri == null) { request.response.headers.contentType = ContentType.text; request.response.write( 'This VM does not have a registered Dart ' 'Development Service (DDS) instance and is not currently serving ' 'Dart DevTools.', ); request.response.close(); return; } // We build this path manually rather than manipulating ddsUri directly // as the resulting path requires an unencoded '#'. The Uri class will // always encode '#' as '%23' in paths to avoid conflicts with fragments, // which will result in the redirect failing. final path = StringBuffer(); // Add authentication code to the path. if (ddsUri.pathSegments.length > 1) { path.writeAll([ ddsUri.pathSegments .sublist(0, ddsUri.pathSegments.length - 1) .join('/'), '/', ]); } final queryComponent = Uri.encodeQueryComponent( ddsUri.replace(scheme: 'ws', path: '${path}ws').toString(), ); path.writeAll(['devtools/', '?uri=$queryComponent']); final redirectUri = Uri.parse('http://${ddsUri.host}:${ddsUri.port}/$path'); request.response.redirect(redirectUri); return; } Future _requestHandler(HttpRequest request) async { if (!_originCheck(request)) { // This is a cross origin attempt to connect request.response.statusCode = HttpStatus.forbidden; request.response.write('forbidden origin'); request.response.close(); return; } if (request.method == 'PUT') { // PUT requests are forwarded to DevFS for processing. await _processDevFSRequest(request); return; } if (request.method != 'GET') { // Not a GET request. Do nothing. request.response.statusCode = HttpStatus.methodNotAllowed; request.response.write('method not allowed'); request.response.close(); return; } final result = _checkAuthTokenAndGetPath(request.uri); if (result == null) { // Either no authentication code was provided when one was expected or an // incorrect authentication code was provided. request.response.statusCode = HttpStatus.forbidden; request.response.write('missing or invalid authentication code'); request.response.close(); return; } else if (result is Uri) { // The URI contains the valid auth token but is missing a trailing '/'. // Redirect to the same URI with the trailing '/' to correctly serve // index.html. request.response.redirect(result); return; } final String path = result; if (path == WEBSOCKET_PATH) { _handleWebSocketRequest(request); return; } // Don't redirect HTTP VM service requests, just requests for DevTools // assets. if (path == ROOT_REDIRECT_PATH) { await _redirectToDevTools(request); return; } // HTTP based service request. final client = HttpRequestClient(request, _service); final message = Message.fromUri( client, Uri(path: path, queryParameters: request.uri.queryParameters), ); client.onRequest(message); // exception free, no need to try catch } Future _dumpServiceInfoToFile(String serviceInfoFilenameLocal) async { final serviceInfo = {'uri': serverAddress.toString()}; const kFileScheme = 'file://'; // There's lots of URI parsing weirdness as Uri.parse doesn't do the right // thing with Windows drive letters. Only use Uri.parse with known file // URIs, and use Uri.file otherwise to properly handle drive letters in // paths. final uri = serviceInfoFilenameLocal.startsWith(kFileScheme) ? Uri.parse(serviceInfoFilenameLocal) : Uri.file(serviceInfoFilenameLocal); final file = File.fromUri(uri); return file.writeAsString(json.encode(serviceInfo)); } void _writeFuchsiaState(int port) { // Create a file with the port number. final tmp = Directory.systemTemp.path; final path = '$tmp/dart.services/${port}'; serverPrint('Creating $path'); File(path).createSync(recursive: true); } void _cleanupFuchsiaState(int port) { // Remove the file with the port number. final tmp = Directory.systemTemp.path; final path = '$tmp/dart.services/$port'; serverPrint('Deleting $path'); File(path).deleteSync(); } } @pragma("vm:external-name", "VMServiceIO_NotifyServerState") external void _notifyServerState(String uri); ‚½NN<KEB.*  '*2+'E=&/% (A.F02"'# I 9 ))&-; &%). 5)$$  P.9>4'(+&'1 " '!N" LKOO;56"M$NPPH"CN1-* NK3-4)C.H2P;5!'D N.?P/(J> JQ /J&@&'3#!)& -H&'#;P# ) EDcF %F!4'.* 1,' ;"5"? =8J8 &= "' "(C2 (!9)*! +!2-&-5&.* )$ 0)!L#HQ !#5G-;,/>) I%%!%12+ 3E0+P$&NH/6LF'0.)?O+7CP=I*:A C3DGBO8F ;; P&D! 6D)$/*8%. 93@$?3K LKN2!,*8 5B:Q, <"3:2 #=+#(A4 ;P5:H  NK) "(K&*#9%EGOL#OLLA..$9&++/#,(-+-#=.'dart:vmservice_io/vmservice_server.dartÀeÀh³borg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/bin/resident_compiler_utils.dartŠP// Copyright (c) 2024, 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:vmservice_io'; /// If [maybeUri] is a file uri, this function returns the result of converting /// [maybeUri] to a file path. Otherwise, it returns [maybeUri] back unmodified. String maybeUriToFilename(String maybeUri) { try { return Uri.parse(maybeUri).toFilePath(); } catch (_) { return maybeUri; } } /// Joins [a] and [b] with a backslash if [Platform.isWindows] is true, /// otherwise joins [a] and [b] with a slash. String joinPathComponents(String a, String b) => !Platform.isWindows ? '$a/$b' : '$a\\$b'; /// The user's home directory for the current platform, or [null] if it can't be /// found. Directory? get homeDir { final envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME'; final envValue = Platform.environment[envKey]; if (envValue == null) { return null; } final dir = Directory(envValue); return dir.existsSync() ? dir : null; } /// The directory used to store the default resident compiler info file, which /// is the `.dart` subdirectory of the user's home directory. This function will /// return [null] the directory is inaccessible. Directory? getDartStorageDirectory() { var dir = homeDir; if (dir == null) { return null; } else { return Directory(joinPathComponents(dir.path, '.dart')); } } /// The default resident frontend compiler information file. /// /// Resident frontend compiler info files contain the contents: /// `address:$address port:$port`. File? get defaultResidentServerInfoFile { var dartConfigDir = getDartStorageDirectory(); if (dartConfigDir == null) return null; return File( joinPathComponents(dartConfigDir.path, 'dartdev_compilation_server_info'), ); } // Used in `pkg/dartdev/lib/src/resident_frontend_utils.dart` and in // `sdk/lib/_internal/vm/bin/vmservice_io.dart`. File? getResidentCompilerInfoFileConsideringArgsImpl( /// If either `--resident-compiler-info-file` or `--resident-server-info-file` /// was supplied on the command line, the CLI argument should be forwarded as /// the argument to this parameter. If neither option was supplied, the /// argument to this parameter should be [null]. String? residentCompilerInfoFilePathArgumentFromCli, ) { if (residentCompilerInfoFilePathArgumentFromCli != null) { return File( maybeUriToFilename(residentCompilerInfoFilePathArgumentFromCli), ); } return defaultResidentServerInfoFile!; } NNN<PQ--H.1.Q M1#(OQ1' ==@#*1*OE16QPJ37=G).dart:vmservice_io/resident_compiler_utils.dartCorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/core.dart›Ô// 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. /// Built-in types, collections, /// and other core functionality for every Dart program. /// /// This library is automatically imported. /// /// Some classes in this library, /// such as [String] and [num], /// support Dart's built-in data types. /// Other classes, such as [List] and [Map], provide data structures /// for managing collections of objects. /// And still other classes represent commonly used types of data /// such as URIs, dates and times, and errors. /// /// ## Numbers and booleans /// /// [int] and [double] provide support for Dart's built-in numerical data types: /// integers and double-precision floating point numbers, respectively. /// An object of type [bool] is either true or false. /// Variables of these types can be constructed from literals: /// ```dart /// int meaningOfLife = 42; /// double valueOfPi = 3.141592; /// bool visible = true; /// ``` /// ## Strings and regular expressions /// /// A [String] is immutable and represents a sequence of characters. /// ```dart /// String shakespeareQuote = "All the world's a stage, ..."; /// ``` /// [StringBuffer] provides a way to construct strings efficiently. /// ```dart /// var moreShakespeare = StringBuffer(); /// moreShakespeare.write('And all the men and women '); /// moreShakespeare.write('merely players; ...'); /// ``` /// The [String] and [StringBuffer] classes implement string splitting, /// concatenation, and other string manipulation features. /// ```dart /// bool isPalindrome(String text) => text == text.split('').reversed.join(); /// ``` /// [RegExp] implements Dart regular expressions, /// which provide a grammar for matching patterns within text. /// For example, here's a regular expression that matches /// a substring containing one or more digits: /// ```dart /// var numbers = RegExp(r'\d+'); /// ``` /// Dart regular expressions have the same syntax and semantics as /// JavaScript regular expressions. See /// /// for the specification of JavaScript regular expressions. /// /// ## Collections /// /// The `dart:core` library provides basic collections, /// such as [List], [Map], and [Set]. /// /// A [List] is an ordered collection of objects, with a length. /// Lists are sometimes called arrays. /// Use a [List] when you need to access objects by index. /// ```dart /// var superheroes = ['Batman', 'Superman', 'Harry Potter']; /// ``` /// A [Set] is an unordered collection of unique objects. /// You cannot get an item efficiently by index (position). /// Adding an element which is already in the set, has no effect. /// ```dart /// var villains = {'Joker'}; /// print(villains.length); // 1 /// villains.addAll(['Joker', 'Lex Luthor', 'Voldemort']); /// print(villains.length); // 3 /// ``` /// A [Map] is an unordered collection of key-value pairs, /// where each key can only occur once. /// Maps are sometimes called associative arrays because /// maps associate a key to some value for easy retrieval. /// Use a [Map] when you need to access objects /// by a unique identifier. /// ```dart /// var sidekicks = {'Batman': 'Robin', /// 'Superman': 'Lois Lane', /// 'Harry Potter': 'Ron and Hermione'}; /// ``` /// In addition to these classes, /// `dart:core` contains [Iterable], /// an interface that defines functionality /// common in collections of objects. /// Examples include the ability /// to run a function on each element in the collection, /// to apply a test to each element, /// to retrieve an object, and to determine the number of elements. /// /// [Iterable] is implemented by [List] and [Set], /// and used by [Map] for its keys and values. /// /// For other kinds of collections, check out the /// `dart:collection` library. /// /// ## Date and time /// /// Use [DateTime] to represent a point in time /// and [Duration] to represent a span of time. /// /// You can create [DateTime] objects with constructors /// or by parsing a correctly formatted string. /// ```dart /// var now = DateTime.now(); /// var berlinWallFell = DateTime(1989, 11, 9); /// var moonLanding = DateTime.parse("1969-07-20"); /// ``` /// Create a [Duration] object by specifying the individual time units. /// ```dart /// var timeRemaining = const Duration(hours: 56, minutes: 14); /// ``` /// In addition to [DateTime] and [Duration], /// `dart:core` contains the [Stopwatch] class for measuring elapsed time. /// /// ## Uri /// /// A [Uri] object represents a uniform resource identifier, /// which identifies a resource, for example on the web. /// ```dart /// var dartlang = Uri.parse('http://dartlang.org/'); /// ``` /// ## Errors /// /// The [Error] class represents the occurrence of an error /// during runtime. /// Subclasses of this class represent specific kinds of errors. /// /// ## Other documentation /// /// For more information about how to use the built-in types, refer to /// [Built-in Types](https://dart.dev/language/built-in-types). /// /// Also, check out the /// [introduction to `dart:core`](https://dart.dev/libraries/dart-core) /// for more coverage of types in this library. /// /// The [Dart Language Specification](https://dart.dev/resources/language/spec) /// provides technical details. /// /// {@category Core} library dart.core; import "dart:collection"; import "dart:_internal" hide Symbol, LinkedList, LinkedListEntry; import "dart:_internal" as internal show Symbol; import "dart:_internal" show isToStringVisiting, toStringVisiting; import "dart:convert" show ascii, base64, Base64Codec, Encoding, latin1, StringConversionSink, utf8; import "dart:math" show Random; // Used by List.shuffle. import "dart:typed_data" show Uint8List; export "dart:async" show Future, FutureExtensions, Stream; @Since("3.0") export "dart:async" show FutureIterable, FutureRecord2, FutureRecord3, FutureRecord4, FutureRecord5, FutureRecord6, FutureRecord7, FutureRecord8, FutureRecord9, ParallelWaitError; export "dart:collection" show NullableIterableExtensions, IterableExtensions; part "annotations.dart"; part "bigint.dart"; part "bool.dart"; part "comparable.dart"; part "date_time.dart"; part "double.dart"; part "duration.dart"; part "enum.dart"; part "errors.dart"; part "exceptions.dart"; part "function.dart"; part "identical.dart"; part "int.dart"; part "invocation.dart"; part "iterable.dart"; part "iterator.dart"; part "list.dart"; part "map.dart"; part "null.dart"; part "num.dart"; part "object.dart"; part "pattern.dart"; part "print.dart"; part "record.dart"; part "regexp.dart"; part "set.dart"; part "sink.dart"; part "stacktrace.dart"; part "stopwatch.dart"; part "string.dart"; part "string_buffer.dart"; part "string_sink.dart"; part "symbol.dart"; part "type.dart"; part "uri.dart"; part "weak.dart"; €ÝNN<!9," (E)B/QH6? "'E >D *92H; N2?:/ "C(<=8&A'; >: = int Function(T a, T b); /// Interface used by types that have an intrinsic ordering. /// /// The [compareTo] operation defines a total ordering of objects, /// which can be used for ordering and sorting. /// /// The [Comparable] interface should be used for the natural ordering of a type. /// If a type can be ordered in more than one way, /// and none of them is the obvious natural ordering, /// then it might be better not to use the [Comparable] interface, /// and to provide separate [Comparator]s instead. /// /// It is recommended that the order of a [Comparable] agrees /// with its operator [operator ==] equality (`a.compareTo(b) == 0` iff `a == b`), /// but this is not a requirement. /// For example, [double] and [DateTime] have `compareTo` methods /// that do not agree with operator [operator ==]. /// For doubles the [compareTo] method is more precise than the equality, /// and for [DateTime] it is less precise. /// /// Examples: /// ```dart /// (0.0).compareTo(-0.0); // => 1 /// 0.0 == -0.0; // => true /// var now = DateTime.now(); /// var utcNow = now.toUtc(); /// now == utcNow; // => false /// now.compareTo(utcNow); // => 0 /// ``` /// The [Comparable] interface does not imply the existence /// of the comparison operators `<`, `<=`, `>` and `>=`. /// These should only be defined /// if the ordering is a less-than/greater-than ordering, /// that is, an ordering where you would naturally /// use the words "less than" about the order of two elements. /// /// If the equality operator and [compareTo] disagree, /// the comparison operators should follow the equality operator, /// and will likely also disagree with [compareTo]. /// Otherwise they should match the [compareTo] method, /// so that `a < b` iff `a.compareTo(b) < 0`. /// /// The [double] class defines comparison operators /// that are compatible with equality. /// The operators differ from [double.compareTo] on -0.0 and NaN. /// /// The [DateTime] class has no comparison operators, instead it has the more /// precisely named [DateTime.isBefore] and [DateTime.isAfter], which both /// agree with [DateTime.compareTo]. abstract interface class Comparable { /// Compares this object to another object. /// /// Returns a value like a [Comparator] when comparing `this` to [other]. /// That is, it returns a negative integer if `this` is ordered before [other], /// a positive integer if `this` is ordered after [other], /// and zero if `this` and [other] are ordered together. /// /// The [other] argument must be a value that is comparable to this object. int compareTo(T other); /// A [Comparator] that compares one comparable to another. /// /// It returns the result of `a.compareTo(b)`. /// The call may fail at run-time /// if `a` is not comparable to the type of `b`. /// /// This utility function is used as the default comparator /// for ordering collections, for example in the [List] sort function. static int compare(Comparable a, Comparable b) => a.compareTo(b); } [NN<4GFLJ5'50=C0R36C3>S#B3J+ %()%<9!:3?7B48.4'BNK%).LR=;N>1$3>IDdart:core/comparable.dartTorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/uri_patch.dartŠk// Copyright (c) 2013, 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 "core_patch.dart"; typedef Uri _UriBaseClosure(); Uri _unsupportedUriBase() { throw UnsupportedError("'Uri.base' is not supported"); } // _uriBaseClosure can be overwritten by the embedder to supply a different // value for Uri.base. @pragma("vm:entry-point") _UriBaseClosure _uriBaseClosure = _unsupportedUriBase; @patch class Uri { @patch static Uri get base => _uriBaseClosure(); } /// VM implementation of Uri. @patch class _Uri { static final bool _isWindowsCached = _isWindowsPlatform; @pragma("vm:external-name", "Uri_isWindowsPlatform") external static bool get _isWindowsPlatform; @patch static bool get _isWindows => _isWindowsCached; @patch static String _uriEncode( int canonicalMask, String text, Encoding encoding, bool spaceToPlus, ) { // First check if the text will be changed by encoding. int i = 0; if (identical(encoding, utf8) || identical(encoding, latin1) || identical(encoding, ascii)) { // Encoding is compatible with the original string. // Find first character that needs encoding. for (; i < text.length; i++) { var char = text.codeUnitAt(i); if (char >= 128 || _charTables.codeUnitAt(char) & canonicalMask == 0) { break; } } } if (i == text.length) return text; // Encode the string into bytes then generate an ASCII only string // by percent encoding selected bytes. StringBuffer result = StringBuffer(); for (int j = 0; j < i; j++) { result.writeCharCode(text.codeUnitAt(j)); } // TODO(lrn): Is there a way to only encode from index i and forwards. var bytes = encoding.encode(text); for (; i < bytes.length; i++) { int byte = bytes[i]; if (byte < 128 && ((_charTables.codeUnitAt(byte) & canonicalMask) != 0)) { result.writeCharCode(byte); } else if (spaceToPlus && byte == _SPACE) { result.writeCharCode(_PLUS); } else { const String hexDigits = '0123456789ABCDEF'; result ..writeCharCode(_PERCENT) ..writeCharCode(hexDigits.codeUnitAt(byte >> 4)) ..writeCharCode(hexDigits.codeUnitAt(byte & 0x0f)); } } return result.toString(); } @patch static String _makeQueryFromParameters( Map*/> queryParameters, ) { return _makeQueryFromParametersDefault(queryParameters); } } \NN<9L7 , ;7/ 2 <%'&:3%'P 'G+*"0K'$Q$2%5$;> *G=dart:core-patch/uri_patch.dartÀeÀh³Jorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/annotations.dart¹// Copyright (c) 2013, 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"; /// The annotation `@Deprecated('migration')` marks a feature as deprecated. /// /// The annotation [deprecated] is a shorthand for deprecating until /// an unspecified "next release" without migration instructions. /// /// A feature can be any part of an API, from a full library to a single /// parameter. /// /// The intent of the `@Deprecated` annotation is to inform authors /// who are currently using the feature, /// that they will soon need to stop using that feature in their code, /// even if the feature is currently still working correctly. /// /// Deprecation is an early warning that the deprecated feature /// is scheduled to be removed at a later time, /// a time possibly specified in [message]. /// A deprecated feature should no longer be used, /// code using it will break at some point in the future. /// If existing code is using the feature, /// that code should be rewritten to no longer use the deprecated feature. /// /// A deprecated feature should document how the same effect can be achieved in /// [message], so the programmer knows how to rewrite the code. /// /// The `@Deprecated` annotation applies to libraries, top-level declarations /// (variables, getters, setters, functions, classes, mixins, /// extension and typedefs), /// class-level declarations (variables, getters, setters, methods, operators or /// constructors, whether static or not), named optional parameters and /// trailing optional positional parameters. /// /// Deprecation applies transitively to parts of a deprecated feature: /// /// - If a library is deprecated, so is every member of it. /// - If a class is deprecated, so is every member of it. /// - If a variable is deprecated, so are its implicit getter and setter. /// /// If a feature is deprecated in a superclass, it is *not* automatically /// deprecated in a subclass as well. It is reasonable to remove a member /// from a superclass and retain it in a subclass, so it needs to be possible /// to deprecate the member only in the superclass. /// /// A tool that processes Dart source code may report when: /// /// - the code imports a deprecated library. /// - the code exports a deprecated library, or any deprecated member of /// a non-deprecated library. /// - the code refers statically to a deprecated declaration. /// - the code uses a member of an object with a statically known /// type, where the member is deprecated on the interface of the static type. /// - the code calls a method with an argument where the /// corresponding optional parameter is deprecated on the object's static type. /// /// If the deprecated use is inside a library, class or method which is itself /// deprecated, the tool should not bother the user about it. /// A deprecated feature is expected to use other deprecated features. class Deprecated { /// Message provided to the user when they use the deprecated feature. /// /// The message should explain how to migrate away from the feature if an /// alternative is available, and when the deprecated feature is expected to be /// removed. final String? message; final _DeprecationKind _kind; /// Creates a deprecation annotation which specifies the migration path and /// expiration of the annotated feature. /// /// The [message] is displayed as part of the warning. The message should be /// aimed at the programmer using the annotated feature, and should recommend /// an alternative (if available), and say when this feature is expected to /// be removed if that is sooner or later than the next major version. const Deprecated(this.message) : _kind = _DeprecationKind.use; /// Creates an annotation which deprecates implementing a class or mixin. /// /// The annotation can be used on `class` or `mixin` declarations whose /// interface can currently be implemented, so they are not marked `final`, /// `sealed`, or `base`, but where the ability to implement will be removed /// in a later release. /// /// Any existing class, mixin or enum declaration which `implements` the /// interface will cause a warning that such use is deprecated. Does not /// affect classes which extend or mix in the annotated class or mixin (see /// [Deprecated.extend], [Deprecated.mixin], and [Deprecated.subclass]). /// /// The annotation is not inherited by subclasses. If a public subclass will /// also become unimplementable, which it will if the annotated declaration /// becomes `final` or `base`, but not if it becomes `sealed`, then the /// subclass should deprecate implementation as well. /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who owns the implementing class, and /// should recommend an alternative (if available), and say when this /// functionality is expected to be removed if that is sooner or later than /// the next major version. const Deprecated.implement([this.message]) : _kind = _DeprecationKind.implement; /// Creates an annotation which deprecates extending a class. /// /// The annotation can be used on `class` declarations which can currently be /// extended, so they are not marked `final`, `sealed`, or `interface`, but /// where the ability to extend will be removed in a later release. /// /// Any existing class declaration which `extends` the class will cause a /// warning that such use is deprecated. Does not affect classes which /// implement or mix in the annotated class (see [Deprecated.implement], /// [Deprecated.mixin], and [Deprecated.subclass]). /// /// The annotation is not inherited by subclasses. If a public subclass will /// also become unextendable, which it will if the annotated declaration /// becomes `final` or `interface`, but not if it becomes `sealed`, then the /// subclass should deprecate extendability as well. /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who owns the extending class, and /// should recommend an alternative (if available), and say when this /// functionality is expected to be removed if that is sooner or later than /// the next major version. const Deprecated.extend([this.message]) : _kind = _DeprecationKind.extend; /// Creates an annotation which deprecates subclassing (implementing or /// extending) a class. /// /// The annotation can be used on `class` and `mixin` declarations which can /// currently be subclassed, so they are not marked `final`, or `sealed`, but /// where the ability to subclass will be removed in a later release. /// /// Any existing class declaration which `extends` or `implements` the /// annotated class or mixin will cause a warning that such use is /// deprecated. Does not affect classes which mix in the annotated class (see /// [Deprecated.extend], [Deprecated.implement] and [Deprecated.mixin]). /// /// The annotation is not inherited by subclasses. If a public subclass will /// also become unsubclassable, which it will if the annotated declaration /// becomes `final`, but not if it becomes `sealed`, then the subclass should /// deprecate subclassability as well. /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who owns the subclassing class, and /// should recommend an alternative (if available), and say when this /// functionality is expected to be removed if that is sooner or later than /// the next major version. const Deprecated.subclass([this.message]) : _kind = _DeprecationKind.subclass; /// Creates an annotation which deprecates instantiating a class. /// /// The annotation can be used on `class` declarations which can currently be /// instantiated, so they are not marked `abstract` or `sealed`, but where /// the ability to instantiate will be removed in a later release. /// /// Any existing code which instantiates the annotated class will cause a /// warning that such use is deprecated. /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who owns the instantiating code, and /// should recommend an alternative (if available), and say when this /// functionality is expected to be removed if that is sooner or later than /// the next major version. const Deprecated.instantiate([this.message]) : _kind = _DeprecationKind.instantiate; /// Creates an annotation which deprecates mixing in a class. /// /// The annotation can be used on `class` declarations which can currently be /// mixed in, so they are marked `mixin`, but where the ability to mix in /// will be removed in a later release. /// /// Any existing class declaration which mixes in the annotated class will /// cause a warning that such use is deprecated. Does not affect classes /// which extend or implement the annotated class (see [Deprecated.extend], /// [Deprecated.implement] and [Deprecated.subclass]). /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who owns the class which mixes in the /// annotated class, and should recommend an alternative (if available), and /// say when this functionality is expected to be removed if that is sooner /// or later than the next major version. const Deprecated.mixin([this.message]) : _kind = _DeprecationKind.mixin; /// Creates an annotation which deprecates omitting an argument for the /// annotated parameter. /// /// The annotation can be used on optional parameters of methods, /// constructors, or top-level functions, indicating the parameter will be /// required in a later release. /// /// Any call to a function which does not pass a value for the annotated /// parameter will cause a warning that such omission is deprecated. /// /// The annotation is not inherited in method overrides. /// /// The [message], if given, is displayed as part of the warning. The message /// should be aimed at the programmer who is calling the function with the /// annotated parameter, and should recommend an alternative (if available), /// and say when this functionality is expected to be removed if that is /// sooner or later than the next major version. const Deprecated.optional([this.message]) : _kind = _DeprecationKind.optional; String toString() => "Deprecated feature: $message"; } /// Marks a feature as [Deprecated] until the next release. const Deprecated deprecated = Deprecated("next release"); /// The various kinds of deprecations with which a feature can be annotated. /// /// Each deprecation kind is paired directly with a [Deprecated] constructor. /// [_DeprecationKind.use] is used by the unnamed [Deprecated.new] constructor /// and the [deprecated] constant. /// /// This enum can be private because the information is only intended for /// static tooling, such as the analyzer. Values may be added. enum _DeprecationKind { use, implement, extend, subclass, instantiate, mixin, optional, } class _Override { const _Override(); } /// Annotation on instance members which override an interface member. /// /// Annotations have no effect on the meaning of a Dart program. /// This annotation is recognized by the Dart analyzer, and it allows the /// analyzer to provide hints or warnings for some potential problems of an /// otherwise valid program. /// As such, the meaning of this annotation is defined by the Dart analyzer. /// /// The `@override` annotation expresses the intent /// that a declaration *should* override an interface method, /// something which is not visible from the declaration itself. /// This extra information allows the analyzer to provide a warning /// when that intent is not satisfied, /// where a member is intended to override a superclass member or /// implement an interface member, but fails to do so. /// Such a situation can arise if a member name is mistyped, /// or if the superclass renames the member. /// /// The `@override` annotation applies to instance methods, instance getters, /// instance setters and instance variables (fields). /// When applied to an instance variable, /// it means that the variable's implicit getter and setter (if any) /// are marked as overriding. It has no effect on the variable itself. /// /// Further [lints](https://dart.dev/lints) /// can be used to enable more warnings based on `@override` annotations. const Object override = _Override(); /// A hint to tools. /// /// Tools that work with Dart programs may accept hints to guide their behavior /// as `pragma` annotations on declarations. /// Each tool decides which hints it accepts, what they mean, and whether and /// how they apply to sub-parts of the annotated entity. /// /// Tools that recognize pragma hints should pick a pragma prefix to identify /// the tool. They should recognize any hint with a [name] starting with their /// prefix followed by `:` as if it was intended for that tool. A hint with a /// prefix for another tool should be ignored (unless compatibility with that /// other tool is a goal). /// /// A tool may recognize unprefixed names as well, if they would recognize that /// name with their own prefix in front. /// /// If the hint can be parameterized, /// an extra [options] object can be added as well. /// /// For example: /// /// ```dart template:top /// @pragma('Tool:pragma-name', [param1, param2, ...]) /// class Foo { } /// /// @pragma('OtherTool:other-pragma') /// void foo() { } /// ``` /// /// Here class `Foo` is annotated with a Tool specific pragma 'pragma-name' and /// function `foo` is annotated with a pragma 'other-pragma' /// specific to OtherTool. @pragma('vm:entry-point') final class pragma { /// The name of the hint. /// /// A string that is recognized by one or more tools, or such a string prefixed /// by a tool identifier and a colon, which is only recognized by that /// particular tool. final String name; /// Optional extra data parameterizing the hint. final Object? options; /// Creates a hint named [name] with optional [options]. const factory pragma(String name, [Object? options]) = pragma._; @pragma('dyn-module:language-impl:callable') const pragma._(this.name, [this.options]); } �;NN<MEBID)G>@0,3:+KP@N>QH-G=;KJJN4<-I >BP9RO>GILR N+OPNIALJNNKKNKONJ8PMHN-*@PNFLIK6OKO7PJHNMJOPHIEPKOMP)PLHNQDPMEL+PMHN/,@PL*MKN9PNON,KJDM#KG;PMOK3Q7<:MNO#J?  GAJLM4>@D'B7=-N6*EG,J%P-N9NONNP)&47&P=RI3;C/-dart:core/annotations.dartÀdúÀh³Àd÷ÀfdÀdÙÀeEorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/bigint.dartÀP×// Copyright (c) 2017, 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"; /// An arbitrarily large integer value. /// /// Big integers are signed and can have an arbitrary number of /// significant digits, only limited by memory. /// /// To create a big integer from the provided number, use [BigInt.from]. /// ```dart /// var bigInteger = BigInt.from(-1); // -1 /// bigInteger = BigInt.from(0.9999); // 0 /// bigInteger = BigInt.from(-10.99); // -10 /// bigInteger = BigInt.from(0x7FFFFFFFFFFFFFFF); // 9223372036854775807 /// bigInteger = BigInt.from(1e+30); // 1000000000000000019884624838656 /// ``` /// To parse a large integer value from a string, use [parse] or [tryParse]. /// ```dart /// var value = BigInt.parse('0x1ffffffffffffffff'); // 36893488147419103231 /// value = BigInt.parse('12345678901234567890'); // 12345678901234567890 /// ``` /// To check whether a big integer can be represented as an [int] without losing /// precision, use [isValidInt]. /// ```dart continued /// print(bigNumber.isValidInt); // false /// ``` /// To convert a big integer into an [int], use [toInt]. /// To convert a big integer into an [double], use [toDouble]. /// ```dart /// var bigValue = BigInt.from(10).pow(3); /// print(bigValue.isValidInt); // true /// print(bigValue.toInt()); // 1000 /// print(bigValue.toDouble()); // 1000.0 /// ``` /// **See also:** /// * [int]: An integer number. /// * [double]: A double-precision floating point number. /// * [num]: The super class for [int] and [double]. /// * [Built-in number types](https://dart.dev/language/built-in-types#numbers) /// * [Number representation](https://dart.dev/resources/language/number-representation) abstract final class BigInt implements Comparable { /// A big integer with the numerical value 0. external static BigInt get zero; /// A big integer with the numerical value 1. external static BigInt get one; /// A big integer with the numerical value 2. external static BigInt get two; /// Parses [source] as a, possibly signed, integer literal and returns its /// value. /// /// The [source] must be a non-empty sequence of base-[radix] digits, /// optionally prefixed with a minus or plus sign ('-' or '+'). /// /// The [radix] must be in the range 2..36. The digits used are /// first the decimal digits 0..9, and then the letters 'a'..'z' with /// values 10 through 35. Also accepts upper-case letters with the same /// values as the lower-case ones. /// /// If no [radix] is given then it defaults to 10. In this case, the [source] /// digits may also start with `0x`, in which case the number is interpreted /// as a hexadecimal literal, which effectively means that the `0x` is ignored /// and the radix is instead set to 16. /// /// For any int `n` and radix `r`, it is guaranteed that /// `n == int.parse(n.toRadixString(r), radix: r)`. /// /// Throws a [FormatException] if the [source] is not a valid integer literal, /// optionally prefixed by a sign. /// Examples: /// ```dart /// print(BigInt.parse('-12345678901234567890')); // -12345678901234567890 /// print(BigInt.parse('0xFF')); // 255 /// print(BigInt.parse('0xffffffffffffffff')); // 18446744073709551615 /// /// // From binary (base 2) value. /// print(BigInt.parse('1100', radix: 2)); // 12 /// print(BigInt.parse('00011111', radix: 2)); // 31 /// print(BigInt.parse('011111100101', radix: 2)); // 2021 /// // From octal (base 8) value. /// print(BigInt.parse('14', radix: 8)); // 12 /// print(BigInt.parse('37', radix: 8)); // 31 /// print(BigInt.parse('3745', radix: 8)); // 2021 /// // From hexadecimal (base 16) value. /// print(BigInt.parse('c', radix: 16)); // 12 /// print(BigInt.parse('1f', radix: 16)); // 31 /// print(BigInt.parse('7e5', radix: 16)); // 2021 /// // From base 35 value. /// print(BigInt.parse('y1', radix: 35)); // 1191 == 34 * 35 + 1 /// print(BigInt.parse('z1', radix: 35)); // Throws. /// // From base 36 value. /// print(BigInt.parse('y1', radix: 36)); // 1225 == 34 * 36 + 1 /// print(BigInt.parse('z1', radix: 36)); // 1261 == 35 * 36 + 1 /// ``` external static BigInt parse(String source, {int? radix}); /// Parses [source] as a, possibly signed, integer literal and returns its /// value. /// /// As [parse] except that this method returns `null` if the input is not /// valid /// /// Examples: /// ```dart /// print(BigInt.tryParse('-12345678901234567890')); // -12345678901234567890 /// print(BigInt.tryParse('0xFF')); // 255 /// print(BigInt.tryParse('0xffffffffffffffff')); // 18446744073709551615 /// /// // From binary (base 2) value. /// print(BigInt.tryParse('1100', radix: 2)); // 12 /// print(BigInt.tryParse('00011111', radix: 2)); // 31 /// print(BigInt.tryParse('011111100101', radix: 2)); // 2021 /// // From octal (base 8) value. /// print(BigInt.tryParse('14', radix: 8)); // 12 /// print(BigInt.tryParse('37', radix: 8)); // 31 /// print(BigInt.tryParse('3745', radix: 8)); // 2021 /// // From hexadecimal (base 16) value. /// print(BigInt.tryParse('c', radix: 16)); // 12 /// print(BigInt.tryParse('1f', radix: 16)); // 31 /// print(BigInt.tryParse('7e5', radix: 16)); // 2021 /// // From base 35 value. /// print(BigInt.tryParse('y1', radix: 35)); // 1191 == 34 * 35 + 1 /// print(BigInt.tryParse('z1', radix: 35)); // null /// // From base 36 value. /// print(BigInt.tryParse('y1', radix: 36)); // 1225 == 34 * 36 + 1 /// print(BigInt.tryParse('z1', radix: 36)); // 1261 == 35 * 36 + 1 /// ``` external static BigInt? tryParse(String source, {int? radix}); /// Creates a big integer from the provided [value] number. /// /// Examples: /// ```dart /// var bigInteger = BigInt.from(1); // 1 /// bigInteger = BigInt.from(0.9999); // 0 /// bigInteger = BigInt.from(-10.99); // -10 /// ``` external factory BigInt.from(num value); /// Returns the absolute value of this integer. /// /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. BigInt abs(); /// Return the negative value of this integer. /// /// The result of negating an integer always has the opposite sign, except /// for zero, which is its own negation. BigInt operator -(); /// Adds [other] to this big integer. /// /// The result is again a big integer. BigInt operator +(BigInt other); /// Subtracts [other] from this big integer. /// /// The result is again a big integer. BigInt operator -(BigInt other); /// Multiplies [other] by this big integer. /// /// The result is again a big integer. BigInt operator *(BigInt other); /// Double division operator. /// /// Matching the similar operator on [int], /// this operation first performs [toDouble] on both this big integer /// and [other], then does [double.operator/] on those values and /// returns the result. /// /// **Note:** The initial [toDouble] conversion may lose precision. /// /// Example: /// ```dart /// print(BigInt.from(1) / BigInt.from(2)); // 0.5 /// print(BigInt.from(1.99999) / BigInt.from(2)); // 0.5 /// ``` double operator /(BigInt other); /// Truncating integer division operator. /// /// Performs a truncating integer division, where the remainder is discarded. /// /// The remainder can be computed using the [remainder] method. /// /// Examples: /// ```dart /// var seven = BigInt.from(7); /// var three = BigInt.from(3); /// seven ~/ three; // => 2 /// (-seven) ~/ three; // => -2 /// seven ~/ -three; // => -2 /// seven.remainder(three); // => 1 /// (-seven).remainder(three); // => -1 /// seven.remainder(-three); // => 1 /// ``` BigInt operator ~/(BigInt other); /// Euclidean modulo operator. /// /// Returns the remainder of the Euclidean division. The Euclidean division of /// two integers `a` and `b` yields two integers `q` and `r` such that /// `a == b * q + r` and `0 <= r < b.abs()`. /// /// The sign of the returned value `r` is always positive. /// /// See [remainder] for the remainder of the truncating division. /// /// Example: /// ```dart /// print(BigInt.from(5) % BigInt.from(3)); // 2 /// print(BigInt.from(-5) % BigInt.from(3)); // 1 /// print(BigInt.from(5) % BigInt.from(-3)); // 2 /// print(BigInt.from(-5) % BigInt.from(-3)); // 1 /// ``` BigInt operator %(BigInt other); /// Returns the remainder of the truncating division of `this` by [other]. /// /// The result `r` of this operation satisfies: /// `this == (this ~/ other) * other + r`. /// As a consequence the remainder `r` has the same sign as the divider `this`. /// /// Example: /// ```dart /// print(BigInt.from(5).remainder(BigInt.from(3))); // 2 /// print(BigInt.from(-5).remainder(BigInt.from(3))); // -2 /// print(BigInt.from(5).remainder(BigInt.from(-3))); // 2 /// print(BigInt.from(-5).remainder(BigInt.from(-3))); // -2 /// ``` BigInt remainder(BigInt other); /// Shift the bits of this integer to the left by [shiftAmount]. /// /// Shifting to the left makes the number larger, effectively multiplying /// the number by `pow(2, shiftIndex)`. /// /// There is no limit on the size of the result. It may be relevant to /// limit intermediate values by using the "and" operator with a suitable /// mask. /// /// It is an error if [shiftAmount] is negative. BigInt operator <<(int shiftAmount); /// Shift the bits of this integer to the right by [shiftAmount]. /// /// Shifting to the right makes the number smaller and drops the least /// significant bits, effectively doing an integer division by ///`pow(2, shiftIndex)`. /// /// It is an error if [shiftAmount] is negative. BigInt operator >>(int shiftAmount); /// Bit-wise and operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with only the bits set that are set in /// both `this` and [other] /// /// Of both operands are negative, the result is negative, otherwise /// the result is non-negative. BigInt operator &(BigInt other); /// Bit-wise or operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with the bits set that are set in either /// of `this` and [other] /// /// If both operands are non-negative, the result is non-negative, /// otherwise the result is negative. BigInt operator |(BigInt other); /// Bit-wise exclusive-or operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with the bits set that are set in one, /// but not both, of `this` and [other] /// /// If the operands have the same sign, the result is non-negative, /// otherwise the result is negative. BigInt operator ^(BigInt other); /// The bit-wise negate operator. /// /// Treating `this` as a sufficiently large two's component integer, /// the result is a number with the opposite bits set. /// /// This maps any integer `x` to `-x - 1`. BigInt operator ~(); /// Whether this big integer is numerically smaller than [other]. bool operator <(BigInt other); /// Whether [other] is numerically greater than this big integer. bool operator <=(BigInt other); /// Whether this big integer is numerically greater than [other]. bool operator >(BigInt other); /// Whether [other] is numerically smaller than this big integer. bool operator >=(BigInt other); /// Compares this to `other`. /// /// Returns a negative number if `this` is less than `other`, zero if they are /// equal, and a positive number if `this` is greater than `other`. /// /// Example: /// ```dart /// print(BigInt.from(1).compareTo(BigInt.from(2))); // => -1 /// print(BigInt.from(2).compareTo(BigInt.from(1))); // => 1 /// print(BigInt.from(1).compareTo(BigInt.from(1))); // => 0 /// ``` int compareTo(BigInt other); /// Returns the minimum number of bits required to store this big integer. /// /// The number of bits excludes the sign bit, which gives the natural length /// for non-negative (unsigned) values. Negative values are complemented to /// return the bit position of the first bit that differs from the sign bit. /// /// To find the number of bits needed to store the value as a signed value, /// add one, i.e. use `x.bitLength + 1`. /// /// ```dart /// x.bitLength == (-x-1).bitLength; /// /// BigInt.from(3).bitLength == 2; // 00000011 /// BigInt.from(2).bitLength == 2; // 00000010 /// BigInt.from(1).bitLength == 1; // 00000001 /// BigInt.from(0).bitLength == 0; // 00000000 /// BigInt.from(-1).bitLength == 0; // 11111111 /// BigInt.from(-2).bitLength == 1; // 11111110 /// BigInt.from(-3).bitLength == 2; // 11111101 /// BigInt.from(-4).bitLength == 2; // 11111100 /// ``` int get bitLength; /// Returns the sign of this big integer. /// /// Returns 0 for zero, -1 for values less than zero and /// +1 for values greater than zero. int get sign; /// Whether this big integer is even. bool get isEven; /// Whether this big integer is odd. bool get isOdd; /// Whether this number is negative. bool get isNegative; /// Returns `this` to the power of [exponent]. /// /// Returns [one] if the [exponent] equals 0. /// /// The [exponent] must otherwise be positive. /// /// The result is always equal to the mathematical result of this to the power /// [exponent], only limited by the available memory. /// /// Example: /// ```dart /// var value = BigInt.from(1000); /// print(value.pow(0)); // 1 /// print(value.pow(1)); // 1000 /// print(value.pow(2)); // 1000000 /// print(value.pow(3)); // 1000000000 /// print(value.pow(4)); // 1000000000000 /// print(value.pow(5)); // 1000000000000000 /// print(value.pow(6)); // 1000000000000000000 /// print(value.pow(7)); // 1000000000000000000000 /// print(value.pow(8)); // 1000000000000000000000000 /// ``` BigInt pow(int exponent); /// Returns this integer to the power of [exponent] modulo [modulus]. /// /// The [exponent] must be non-negative and [modulus] must be /// positive. BigInt modPow(BigInt exponent, BigInt modulus); /// Returns the modular multiplicative inverse of this big integer /// modulo [modulus]. /// /// The [modulus] must be positive. /// /// It is an error if no modular inverse exists. // Returns 1/this % modulus, with modulus > 0. BigInt modInverse(BigInt modulus); /// Returns the greatest common divisor of this big integer and [other]. /// /// If either number is non-zero, the result is the numerically greatest /// integer dividing both `this` and `other`. /// /// The greatest common divisor is independent of the order, /// so `x.gcd(y)` is always the same as `y.gcd(x)`. /// /// For any integer `x`, `x.gcd(x)` is `x.abs()`. /// /// If both `this` and `other` is zero, the result is also zero. /// /// Example: /// ```dart /// print(BigInt.from(4).gcd(BigInt.from(2))); // 2 /// print(BigInt.from(8).gcd(BigInt.from(4))); // 4 /// print(BigInt.from(10).gcd(BigInt.from(12))); // 2 /// print(BigInt.from(10).gcd(BigInt.from(10))); // 10 /// print(BigInt.from(-2).gcd(BigInt.from(-3))); // 1 /// ``` BigInt gcd(BigInt other); /// Returns the least significant [width] bits of this big integer as a /// non-negative number (i.e. unsigned representation). The returned value has /// zeros in all bit positions higher than [width]. /// /// ```dart /// BigInt.from(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 /// ``` /// /// This operation can be used to simulate arithmetic from low level languages. /// For example, to increment an 8 bit quantity: /// /// ```dart /// q = (q + 1).toUnsigned(8); /// ``` /// /// `q` will count from `0` up to `255` and then wrap around to `0`. /// /// If the input fits in [width] bits without truncation, the result is the /// same as the input. The minimum width needed to avoid truncation of `x` is /// given by `x.bitLength`, i.e. /// /// ```dart /// x == x.toUnsigned(x.bitLength); /// ``` BigInt toUnsigned(int width); /// Returns the least significant [width] bits of this integer, extending the /// highest retained bit to the sign. This is the same as truncating the value /// to fit in [width] bits using an signed 2-s complement representation. The /// returned value has the same bit value in all positions higher than [width]. /// /// ```dart /// var big15 = BigInt.from(15); /// var big16 = BigInt.from(16); /// var big239 = BigInt.from(239); /// // V--sign bit-V /// big16.toSigned(5) == -big16; // 00010000 -> 11110000 /// big239.toSigned(5) == big15; // 11101111 -> 00001111 /// // ^ ^ /// ``` /// /// This operation can be used to simulate arithmetic from low level languages. /// For example, to increment an 8 bit signed quantity: /// /// ```dart /// q = (q + 1).toSigned(8); /// ``` /// /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to /// `127`. /// /// If the input value fits in [width] bits without truncation, the result is /// the same as the input. The minimum width needed to avoid truncation of `x` /// is `x.bitLength + 1`, i.e. /// /// ```dart /// x == x.toSigned(x.bitLength + 1); /// ``` BigInt toSigned(int width); /// Whether this big integer can be represented as an `int` without losing /// precision. /// /// **Warning:** this function may give a different result on /// dart2js, dev compiler, and the VM, due to the differences in /// integer precision. /// /// Example: /// ```dart /// var bigNumber = BigInt.parse('100000000000000000000000'); /// print(bigNumber.isValidInt); // false /// /// var value = BigInt.parse('0xFF'); // 255 /// print(value.isValidInt); // true /// ``` bool get isValidInt; /// Returns this [BigInt] as an [int]. /// /// If the number does not fit, clamps to the max (or min) /// integer. /// /// **Warning:** the clamping behaves differently between the web and /// native platforms due to the differences in integer precision. /// /// Example: /// ```dart /// var bigNumber = BigInt.parse('100000000000000000000000'); /// print(bigNumber.isValidInt); // false /// print(bigNumber.toInt()); // 9223372036854775807 /// ``` int toInt(); /// Returns this [BigInt] as a [double]. /// /// If the number is not representable as a [double], an /// approximation is returned. For numerically large integers, the /// approximation may be infinite. /// /// Example: /// ```dart /// var bigNumber = BigInt.parse('100000000000000000000000'); /// print(bigNumber.toDouble()); // 1e+23 /// ``` double toDouble(); /// Returns a String-representation of this integer. /// /// The returned string is parsable by [parse]. /// For any `BigInt` `i`, it is guaranteed that /// `i == BigInt.parse(i.toString())`. /// /// Example: /// ```dart /// var bigNumber = BigInt.parse('100000000000000000000000'); /// print(bigNumber.toString()); // "100000000000000000000000" /// ``` String toString(); /// Converts this [BigInt] to a string representation in the given [radix]. /// /// In the string representation, lower-case letters are used for digits above /// '9', with 'a' being 10 an 'z' being 35. /// /// The [radix] argument must be an integer in the range 2 to 36. /// /// Example: /// ```dart /// // Binary (base 2). /// print(BigInt.from(12).toRadixString(2)); // 1100 /// print(BigInt.from(31).toRadixString(2)); // 11111 /// print(BigInt.from(2021).toRadixString(2)); // 11111100101 /// print(BigInt.from(-12).toRadixString(2)); // -1100 /// // Octal (base 8). /// print(BigInt.from(12).toRadixString(8)); // 14 /// print(BigInt.from(31).toRadixString(8)); // 37 /// print(BigInt.from(2021).toRadixString(8)); // 3745 /// // Hexadecimal (base 16). /// print(BigInt.from(12).toRadixString(16)); // c /// print(BigInt.from(31).toRadixString(16)); // 1f /// print(BigInt.from(2021).toRadixString(16)); // 7e5 /// // Base 36. /// print(BigInt.from(35 * 36 + 1).toRadixString(36)); // z1 /// ``` String toRadixString(int radix); } ‚:NN<(@0I ,+-IHM MJQ!*9? +(%* :5PY<0#0"0"M HBBHJ%POQ*;6Q%M*I%37=$115+125C7CC =M L P-L%6:@$448+458F7FF A>,-/ +2G1M+()#/)#.)# .HDF5; #,PB""!"")*) $!QI/=D3445 #M2-R<>=? "CL*IL 3'DIA3'MNG"#MPE(#&MN*F(#$G9-D!D"D!D" QF@?? MONON+'33333333 ,;'(''101Q8% #&),/258 H@2E&31%KK0?64C66898 JQ6F R3! GNP#& PQPR##%:>>: R: O PQ!( M@C@,/' )=HD@,7 +;E%@, 722)@A NQ.D78@9559 569? #dart:core/bigint.dart^org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm_shared/lib/bigint_patch.dartÀ‘º// Copyright (c) 2017, 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. import "dart:_internal" show patch, unsafeCast; import "dart:typed_data" show Endian, Uint8List, Uint32List; /* * Copyright (c) 2003-2005 Tom Wu * Copyright (c) 2012 Adam Singer (adam@solvr.io) * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * In addition, the following condition applies: * * All redistributions must retain an intact copy of this copyright notice * and disclaimer. */ @patch class BigInt implements Comparable { @patch static BigInt get zero => _BigIntImpl.zero; @patch static BigInt get one => _BigIntImpl.one; @patch static BigInt get two => _BigIntImpl.two; @patch static BigInt parse(String source, {int? radix}) => _BigIntImpl.parse(source, radix: radix); @patch static BigInt? tryParse(String source, {int? radix}) => _BigIntImpl._tryParse(source, radix: radix); @patch factory BigInt.from(num value) => _BigIntImpl.from(value); } int _max(int a, int b) => a > b ? a : b; int _min(int a, int b) => a < b ? a : b; /// Allocate a new digits list of even length. Uint32List _newDigits(int length) => Uint32List(length + (length & 1)); class _DivRemResult { Uint32List? get dividendDigits => _dividendDigits; int? get dividendUsed => _dividendUsed; Uint32List? get divisorDigits => _divisorDigits; int? get divisorUsed => _divisorUsed; Uint32List get quoRemDigits => _quoRemDigits; int get quoRemUsed => _quoRemUsed; int get remUsed => _remUsed; int get rem_nsh => _rem_nsh; void update( Uint32List dividendDigits, int dividendUsed, Uint32List divisorDigits, int divisorUsed, Uint32List quoRemDigits, int quoRemUsed, int remUsed, int rem_nsh, ) { _dividendDigits = dividendDigits; _dividendUsed = dividendUsed; _divisorDigits = divisorDigits; _divisorUsed = divisorUsed; _quoRemDigits = quoRemDigits; _quoRemUsed = quoRemUsed; _remUsed = remUsed; _rem_nsh = rem_nsh; } Uint32List? _dividendDigits; int? _dividendUsed; Uint32List? _divisorDigits; int? _divisorUsed; late Uint32List _quoRemDigits; late int _quoRemUsed; late int _remUsed; late int _rem_nsh; } /** * An implementation for the arbitrarily large integer. * * The integer number is represented by a sign, an array of 32-bit unsigned * integers in little endian format, and a number of used digits in that array. */ @pragma("vm:entry-point") final class _BigIntImpl implements BigInt { // Bits per digit. static const int _digitBits = 32; static const int _digitBase = 1 << _digitBits; static const int _digitMask = (1 << _digitBits) - 1; // Bits per half digit. static const int _halfDigitBits = _digitBits >> 1; static const int _halfDigitMask = (1 << _halfDigitBits) - 1; @pragma('vm:shared') static final _BigIntImpl zero = _BigIntImpl._fromInt(0); @pragma('vm:shared') static final _BigIntImpl one = _BigIntImpl._fromInt(1); @pragma('vm:shared') static final _BigIntImpl two = _BigIntImpl._fromInt(2); @pragma('vm:shared') static final _BigIntImpl _minusOne = -one; @pragma('vm:shared') static final _BigIntImpl _oneDigitMask = _BigIntImpl._fromInt(_digitMask); @pragma('vm:shared') static final _BigIntImpl _twoDigitMask = (one << (2 * _digitBits)) - one; @pragma('vm:shared') static final _BigIntImpl _oneBillion = _BigIntImpl._fromInt(1000000000); static const int _minInt = -0x8000000000000000; static const int _maxInt = 0x7fffffffffffffff; /// Certain methods of _BigIntImpl class are intrinsified by the VM /// depending on the runtime flags. They return number of processed /// digits (2) which is different from non-intrinsic implementation (1). /// This flag is used to confuse constant propagation at compile time and /// avoid propagating return value to the callers. It should not be /// evaluated to a constant. /// Note that [_isIntrinsified] is still false if intrinsification occurs, /// so it should be used only inside methods which are replaced by /// intrinsification. static final bool _isIntrinsified = bool.fromEnvironment( 'dart.vm.not.a.compile.time.constant', ); // Result cache for last _divRem call. external static _DivRemResult get _cachedDivRemResult; /// Whether this bigint is negative. final bool _isNegative; /// The unsigned digits of this bigint. /// /// The least significant digit is in slot 0. /// The list may have more digits than needed. That is, `_digits.length` may /// be strictly greater than `_used`. /// Also, `_digits.length` must always be even, because intrinsics on 64-bit /// platforms may process a digit pair as a 64-bit value. /// /// Uint32List normally prevent pragma('vm:deeply-immutable')-check, but /// this class is explicitly allow-listed in deeply_immutable.dart. final Uint32List _digits; /// The number of used entries in [_digits]. /// /// To avoid reallocating [Uint32List]s, lists that are too big are not /// replaced, but `_used` reflects the smaller number of digits actually used. /// /// Note that functions shortening an existing list of digits to a smaller /// `_used` number of digits must ensure that the highermost pair of digits /// is correct when read as a 64-bit value by intrinsics. Therefore, if the /// smaller '_used' number is odd, the high digit of that pair must be /// explicitly cleared, i.e. _digits[_used] = 0, which cannot result in an /// out of bounds access, since the length of the list is guaranteed to be /// even. final int _used; /** * Parses [source] as a, possibly signed, integer literal and returns its * value. * * The [source] must be a non-empty sequence of base-[radix] digits, * optionally prefixed with a minus or plus sign ('-' or '+'). * * The [radix] must be in the range 2..36. The digits used are * first the decimal digits 0..9, and then the letters 'a'..'z' with * values 10 through 35. Also accepts upper-case letters with the same * values as the lower-case ones. * * If no [radix] is given then it defaults to 10. In this case, the [source] * digits may also start with `0x`, in which case the number is interpreted * as a hexadecimal literal, which effectively means that the `0x` is ignored * and the radix is instead set to 16. * * For any int `n` and radix `r`, it is guaranteed that * `n == int.parse(n.toRadixString(r), radix: r)`. * * Throws a [FormatException] if the [source] is not a valid integer literal, * optionally prefixed by a sign. */ static _BigIntImpl parse(String source, {int? radix}) { var result = _tryParse(source, radix: radix); if (result == null) { throw FormatException("Could not parse BigInt", source); } return result; } /// Parses a decimal bigint literal. /// /// The [source] must not contain leading or trailing whitespace. static _BigIntImpl _parseDecimal(String source, bool isNegative) { const _0 = 48; int part = 0; _BigIntImpl result = zero; // Read in the source 9 digits at a time. // The first part may have a few leading virtual '0's to make the remaining // parts all have exactly 9 digits. int digitInPartCount = 9 - unsafeCast(source.length.remainder(9)); if (digitInPartCount == 9) digitInPartCount = 0; for (int i = 0; i < source.length; i++) { part = part * 10 + source.codeUnitAt(i) - _0; if (++digitInPartCount == 9) { result = result * _oneBillion + _BigIntImpl._fromInt(part); part = 0; digitInPartCount = 0; } } if (isNegative) return -result; return result; } /// Returns the value of a given source digit. /// /// Source digits between "0" and "9" (inclusive) return their decimal value. /// /// Source digits between "a" and "z", or "A" and "Z" (inclusive) return /// 10 + their position in the ASCII alphabet. /// /// The incoming [codeUnit] must be an ASCII code-unit. static int _codeUnitToRadixValue(int codeUnit) { // We know that the characters must be ASCII as otherwise the // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus // guaranteed to be a safe operation, since it preserves digits // and lower-cases ASCII letters. const int _0 = 48; const int _9 = 57; const int _a = 97; if (_0 <= codeUnit && codeUnit <= _9) return codeUnit - _0; codeUnit |= 0x20; var result = codeUnit - _a + 10; return result; } /// Parses the given [source] string, starting at [startPos], as a hex /// literal. /// /// If [isNegative] is true, negates the result before returning it. /// /// The [source] (substring) must be a valid hex literal. static _BigIntImpl? _parseHex(String source, int startPos, bool isNegative) { int hexCharsPerDigit = _digitBits ~/ 4; int sourceLength = source.length - startPos; int used = (sourceLength + hexCharsPerDigit - 1) ~/ hexCharsPerDigit; var digits = _newDigits(used); int lastDigitLength = sourceLength - (used - 1) * hexCharsPerDigit; int digitIndex = used - 1; int i = startPos; int digit = 0; for (int j = 0; j < lastDigitLength; j++) { var value = _codeUnitToRadixValue(source.codeUnitAt(i++)); if (value >= 16) return null; digit = digit * 16 + value; } digits[digitIndex--] = digit; while (i < source.length) { digit = 0; for (int j = 0; j < hexCharsPerDigit; j++) { var value = _codeUnitToRadixValue(source.codeUnitAt(i++)); if (value >= 16) return null; digit = digit * 16 + value; } digits[digitIndex--] = digit; } if (used == 1 && digits[0] == 0) return zero; return _BigIntImpl._(isNegative, used, digits); } /// Parses the given [source] as a [radix] literal. /// /// The [source] will be checked for invalid characters. If it is invalid, /// this function returns `null`. static _BigIntImpl? _parseRadix(String source, int radix, bool isNegative) { var result = zero; var base = _BigIntImpl._fromInt(radix); for (int i = 0; i < source.length; i++) { var value = _codeUnitToRadixValue(source.codeUnitAt(i)); if (value >= radix) return null; result = result * base + _BigIntImpl._fromInt(value); } if (isNegative) return -result; return result; } /// Tries to parse the given [source] as a [radix] literal. /// /// Returns the parsed big integer, or `null` if it failed. /// /// If the [radix] is `null` accepts decimal literals or `0x` hex literals. static _BigIntImpl? _tryParse(String source, {int? radix}) { if (source == "") return null; final match = _parseRE.firstMatch(source); int signIndex = 1; int hexIndex = 3; int decimalIndex = 4; int nonDecimalHexIndex = 5; if (match == null) return null; final bool isNegative = match[signIndex] == "-"; final String? decimalMatch = match[decimalIndex]; final String? hexMatch = match[hexIndex]; final String? nonDecimalMatch = match[nonDecimalHexIndex]; if (radix == null) { if (decimalMatch != null) { // Cannot fail because we know that the digits are all decimal. return _parseDecimal(decimalMatch, isNegative); } if (hexMatch != null) { // Cannot fail because we know that the digits are all hex. return _parseHex(hexMatch, 2, isNegative); } return null; } if (radix < 2 || radix > 36) { throw RangeError.range(radix, 2, 36, 'radix'); } if (radix == 10 && decimalMatch != null) { return _parseDecimal(decimalMatch, isNegative); } if (radix == 16) { final match = decimalMatch ?? nonDecimalMatch; if (match != null) { return _parseHex(match, 0, isNegative); } } // The RegExp guarantees that one of the 3 matches is non-null. final nonNullMatch = (decimalMatch ?? nonDecimalMatch ?? hexMatch)!; return _parseRadix(nonNullMatch, radix, isNegative); } external static RegExp get _parseRE; /// Finds the amount significant digits in the provided [digits] array. static int _normalize(int used, Uint32List digits) { while (used > 0 && digits[used - 1] == 0) used--; return used; } /// Factory returning an instance initialized with the given field values. /// If the [digits] array contains leading 0s, the [used] value is adjusted /// accordingly. The [digits] array is not modified. _BigIntImpl._(bool isNegative, int used, Uint32List digits) : this._normalized(isNegative, _normalize(used, digits), digits); _BigIntImpl._normalized(bool isNegative, this._used, this._digits) : _isNegative = _used == 0 ? false : isNegative { assert(_digits.length.isEven); assert(_used.isEven || _digits[_used] == 0); // Leading zero for 64-bit. } /// Whether this big integer is zero. bool get _isZero => _used == 0; /// Allocates an array of the given [length] and copies the [digits] in the /// range [from] to [to-1], starting at index 0, followed by leading zero /// digits. static Uint32List _cloneDigits( Uint32List digits, int from, int to, int length, ) { var resultDigits = _newDigits(length); var n = to - from; for (var i = 0; i < n; i++) { resultDigits[i] = digits[from + i]; } return resultDigits; } /// Allocates a big integer from the provided [value] number. factory _BigIntImpl.from(num value) { if (value == 0) return zero; if (value == 1) return one; if (value == 2) return two; if (value.abs() < 0x100000000) { return _BigIntImpl._fromInt(value.toInt()); } if (value is double) { return _BigIntImpl._fromDouble(value); } return _BigIntImpl._fromInt(value as int); } factory _BigIntImpl._fromInt(int value) { bool isNegative = value < 0; assert(_digitBits == 32); var digits = _newDigits(2); if (isNegative) { // Handle the min 64-bit value differently, since its negation is not // positive. if (value == _minInt) { digits[1] = 0x80000000; return _BigIntImpl._(true, 2, digits); } value = -value; } if (value < _digitBase) { digits[0] = value; return _BigIntImpl._(isNegative, 1, digits); } digits[0] = value & _digitMask; digits[1] = value >> _digitBits; return _BigIntImpl._(isNegative, 2, digits); } /// An 8-byte Uint8List we can reuse for [_fromDouble] to avoid generating /// garbage. static final Uint8List _bitsForFromDouble = Uint8List(8); factory _BigIntImpl._fromDouble(double value) { const int exponentBias = 1075; if (value.isNaN || value.isInfinite) { throw ArgumentError("Value must be finite: $value"); } bool isNegative = value < 0; if (isNegative) value = -value; value = value.floorToDouble(); if (value == 0) return zero; var bits = _bitsForFromDouble; for (int i = 0; i < 8; i++) { bits[i] = 0; } bits.buffer.asByteData().setFloat64(0, value, Endian.little); // The exponent is in bits 53..63. var biasedExponent = (bits[7] << 4) + (bits[6] >> 4); var exponent = biasedExponent - exponentBias; assert(_digitBits == 32); // The significant bits are in 0 .. 52. var unshiftedDigits = _newDigits(2); unshiftedDigits[0] = (bits[3] << 24) + (bits[2] << 16) + (bits[1] << 8) + bits[0]; // Don't forget to add the hidden bit. unshiftedDigits[1] = ((0x10 | (bits[6] & 0xF)) << 16) + (bits[5] << 8) + bits[4]; var unshiftedBig = _BigIntImpl._normalized(false, 2, unshiftedDigits); _BigIntImpl absResult = unshiftedBig; if (exponent < 0) { absResult = unshiftedBig >> -exponent; } else if (exponent > 0) { absResult = unshiftedBig << exponent; } if (isNegative) return -absResult; return absResult; } /** * Return the negative value of this integer. * * The result of negating an integer always has the opposite sign, except * for zero, which is its own negation. */ _BigIntImpl operator -() { if (_used == 0) return this; return _BigIntImpl._(!_isNegative, _used, _digits); } /** * Returns the absolute value of this integer. * * For any integer `x`, the result is the same as `x < 0 ? -x : x`. */ _BigIntImpl abs() => _isNegative ? -this : this; /// Returns this << n*_digitBits. _BigIntImpl _dlShift(int n) { final used = _used; if (used == 0) { return zero; } final resultUsed = used + n; final digits = _digits; final resultDigits = _newDigits(resultUsed); for (int i = used - 1; i >= 0; i--) { resultDigits[i + n] = digits[i]; } return _BigIntImpl._(_isNegative, resultUsed, resultDigits); } /// Same as [_dlShift] but works on the decomposed big integers. /// /// Returns `resultUsed`. /// /// `resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1] << n*_digitBits`. static int _dlShiftDigits( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { if (xUsed == 0) { return 0; } if (n == 0 && identical(resultDigits, xDigits)) { return xUsed; } final resultUsed = xUsed + n; assert(resultDigits.length >= resultUsed + (resultUsed & 1)); for (int i = xUsed - 1; i >= 0; i--) { resultDigits[i + n] = xDigits[i]; } for (int i = n - 1; i >= 0; i--) { resultDigits[i] = 0; } if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; } return resultUsed; } /// Returns `this >> n*_digitBits`. _BigIntImpl _drShift(int n) { final used = _used; if (used == 0) { return zero; } final resultUsed = used - n; if (resultUsed <= 0) { return _isNegative ? _minusOne : zero; } final digits = _digits; final resultDigits = _newDigits(resultUsed); for (var i = n; i < used; i++) { resultDigits[i - n] = digits[i]; } final result = _BigIntImpl._(_isNegative, resultUsed, resultDigits); if (_isNegative) { // Round down if any bit was shifted out. for (var i = 0; i < n; i++) { if (digits[i] != 0) { return result - one; } } } return result; } /// Same as [_drShift] but works on the decomposed big integers. /// /// Returns `resultUsed`. /// /// `resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1] >> n*_digitBits`. static int _drShiftDigits( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { final resultUsed = xUsed - n; if (resultUsed <= 0) { return 0; } assert(resultDigits.length >= resultUsed + (resultUsed & 1)); for (var i = n; i < xUsed; i++) { resultDigits[i - n] = xDigits[i]; } if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; } return resultUsed; } /// Shifts the digits of [xDigits] into the right place in [resultDigits]. /// /// `resultDigits[ds..xUsed+ds] = xDigits[0..xUsed-1] << (n % _digitBits)` /// where `ds = n ~/ _digitBits` /// /// Does *not* clear digits below ds. /// /// Note: This function may be intrinsified. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:never-inline") static void _lsh( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { assert(xUsed > 0); final digitShift = n ~/ _digitBits; final bitShift = n % _digitBits; final carryBitShift = _digitBits - bitShift; final bitMask = (1 << carryBitShift) - 1; var carry = 0; for (int i = xUsed - 1; i >= 0; i--) { final digit = xDigits[i]; resultDigits[i + digitShift + 1] = (digit >> carryBitShift) | carry; carry = (digit & bitMask) << bitShift; } resultDigits[digitShift] = carry; } /** * Shift the bits of this integer to the left by [shiftAmount]. * * Shifting to the left makes the number larger, effectively multiplying * the number by `pow(2, shiftIndex)`. * * There is no limit on the size of the result. It may be relevant to * limit intermediate values by using the "and" operator with a suitable * mask. * * It is an error if [shiftAmount] is negative. */ _BigIntImpl operator <<(int shiftAmount) { if (shiftAmount < 0) { throw ArgumentError("shift-amount must be positive $shiftAmount"); } if (_isZero) return this; final digitShift = shiftAmount ~/ _digitBits; final bitShift = shiftAmount % _digitBits; if (bitShift == 0) { return _dlShift(digitShift); } // Need one extra digit to hold bits shifted by bitShift. var resultUsed = _used + digitShift + 1; // The 64-bit intrinsic requires one extra pair to work with. var resultDigits = _newDigits(resultUsed + 1); _lsh(_digits, _used, shiftAmount, resultDigits); return _BigIntImpl._(_isNegative, resultUsed, resultDigits); } /// resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1] << n. /// Returns resultUsed. static int _lShiftDigits( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { final digitsShift = n ~/ _digitBits; final bitShift = n % _digitBits; if (bitShift == 0) { return _dlShiftDigits(xDigits, xUsed, digitsShift, resultDigits); } // Need one extra digit to hold bits shifted by bitShift. var resultUsed = xUsed + digitsShift + 1; // The 64-bit intrinsic requires one extra pair to work with. assert(resultDigits.length >= resultUsed + 2 - (resultUsed & 1)); _lsh(xDigits, xUsed, n, resultDigits); var i = digitsShift; while (--i >= 0) { resultDigits[i] = 0; } if (resultDigits[resultUsed - 1] == 0) { resultUsed--; // Clamp result. } else if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; } return resultUsed; } /// resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1] >> n. /// /// Note: This function may be intrinsified. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:never-inline") static void _rsh( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { assert(xUsed > 0); final digitsShift = n ~/ _digitBits; final bitShift = n % _digitBits; final carryBitShift = _digitBits - bitShift; final bitMask = (1 << bitShift) - 1; var carry = xDigits[digitsShift] >> bitShift; final last = xUsed - digitsShift - 1; for (var i = 0; i < last; i++) { final digit = xDigits[i + digitsShift + 1]; resultDigits[i] = ((digit & bitMask) << carryBitShift) | carry; carry = digit >> bitShift; } resultDigits[last] = carry; } /** * Shift the bits of this integer to the right by [shiftAmount]. * * Shifting to the right makes the number smaller and drops the least * significant bits, effectively doing an integer division by *`pow(2, shiftIndex)`. * * It is an error if [shiftAmount] is negative. */ _BigIntImpl operator >>(int shiftAmount) { if (shiftAmount < 0) { throw ArgumentError("shift-amount must be positive $shiftAmount"); } if (_isZero) return this; final digitShift = shiftAmount ~/ _digitBits; final bitShift = shiftAmount % _digitBits; if (bitShift == 0) { return _drShift(digitShift); } final used = _used; final resultUsed = used - digitShift; if (resultUsed <= 0) { return _isNegative ? _minusOne : zero; } final digits = _digits; // The 64-bit intrinsic requires one extra pair to work with. final resultDigits = _newDigits(resultUsed + 1); _rsh(digits, used, shiftAmount, resultDigits); final result = _BigIntImpl._(_isNegative, resultUsed, resultDigits); if (_isNegative) { // Round down if any bit was shifted out. if ((digits[digitShift] & ((1 << bitShift) - 1)) != 0) { return result - one; } for (var i = 0; i < digitShift; i++) { if (digits[i] != 0) { return result - one; } } } return result; } /// resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1] >> n. /// Returns resultUsed. static int _rShiftDigits( Uint32List xDigits, int xUsed, int n, Uint32List resultDigits, ) { final digitShift = n ~/ _digitBits; final bitShift = n % _digitBits; if (bitShift == 0) { return _drShiftDigits(xDigits, xUsed, digitShift, resultDigits); } var resultUsed = xUsed - digitShift; if (resultUsed <= 0) { return 0; } // The 64-bit intrinsic requires one extra pair to work with. assert(resultDigits.length >= resultUsed + 1 + (resultUsed + 1 & 1)); _rsh(xDigits, xUsed, n, resultDigits); if (resultDigits[resultUsed - 1] == 0) { resultUsed--; // Clamp result. } else if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; } return resultUsed; } /// Compares this to [other] taking the absolute value of both operands. /// /// Returns 0 if abs(this) == abs(other); a positive number if /// abs(this) > abs(other); and a negative number if abs(this) < abs(other). int _absCompare(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); return _compareDigits(_digits, _used, other._digits, other._used); } /** * Compares this to `other`. * * Returns a negative number if `this` is less than `other`, zero if they are * equal, and a positive number if `this` is greater than `other`. */ int compareTo(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isNegative == other._isNegative) { var result = _absCompare(other); // Use 0 - result to avoid negative zero in JavaScript. return _isNegative ? 0 - result : result; } return _isNegative ? -1 : 1; } /// Compares `digits[0..used-1]` with `otherDigits[0..otherUsed-1]`. /// /// Returns 0 if equal; a positive number if larger; /// and a negative number if smaller. static int _compareDigits( Uint32List digits, int used, Uint32List otherDigits, int otherUsed, ) { var result = used - otherUsed; if (result == 0) { for (int i = used - 1; i >= 0; i--) { result = digits[i] - otherDigits[i]; if (result != 0) return result; } } return result; } /// resultDigits[0..used] = digits[0..used-1] + otherDigits[0..otherUsed-1]. /// used >= otherUsed > 0. /// /// Note: This function may be intrinsified. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:never-inline") static void _absAdd( Uint32List digits, int used, Uint32List otherDigits, int otherUsed, Uint32List resultDigits, ) { assert(used >= otherUsed && otherUsed > 0); var carry = 0; for (var i = 0; i < otherUsed; i++) { carry += digits[i] + otherDigits[i]; resultDigits[i] = carry & _digitMask; carry >>= _digitBits; } for (var i = otherUsed; i < used; i++) { carry += digits[i]; resultDigits[i] = carry & _digitMask; carry >>= _digitBits; } resultDigits[used] = carry; } /// resultDigits[0..used-1] = digits[0..used-1] - otherDigits[0..otherUsed-1]. /// used >= otherUsed > 0. /// /// Note: This function may be intrinsified. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:never-inline") static void _absSub( Uint32List digits, int used, Uint32List otherDigits, int otherUsed, Uint32List resultDigits, ) { assert(used >= otherUsed && otherUsed > 0); var carry = 0; for (var i = 0; i < otherUsed; i++) { carry += digits[i] - otherDigits[i]; resultDigits[i] = carry & _digitMask; carry >>= _digitBits; } for (var i = otherUsed; i < used; i++) { carry += digits[i]; resultDigits[i] = carry & _digitMask; carry >>= _digitBits; } } /// Returns `abs(this) + abs(other)` with sign set according to [isNegative]. _BigIntImpl _absAddSetSign(_BigIntImpl other, bool isNegative) { var used = _used; var otherUsed = other._used; if (used < otherUsed) { return other._absAddSetSign(this, isNegative); } if (used == 0) { assert(!isNegative); return zero; } if (otherUsed == 0) { return _isNegative == isNegative ? this : -this; } var resultUsed = used + 1; var resultDigits = _newDigits(resultUsed); _absAdd(_digits, used, other._digits, otherUsed, resultDigits); return _BigIntImpl._(isNegative, resultUsed, resultDigits); } /// Returns `abs(this) - abs(other)` with sign set according to [isNegative]. /// /// Requirement: `abs(this) >= abs(other)`. _BigIntImpl _absSubSetSign(_BigIntImpl other, bool isNegative) { assert(_absCompare(other) >= 0); var used = _used; if (used == 0) { assert(!isNegative); return zero; } var otherUsed = other._used; if (otherUsed == 0) { return _isNegative == isNegative ? this : -this; } var resultDigits = _newDigits(used); _absSub(_digits, used, other._digits, otherUsed, resultDigits); return _BigIntImpl._(isNegative, used, resultDigits); } /// Returns `abs(this) & abs(other)` with sign set according to [isNegative]. _BigIntImpl _absAndSetSign(_BigIntImpl other, bool isNegative) { var resultUsed = _min(_used, other._used); var digits = _digits; var otherDigits = other._digits; var resultDigits = _newDigits(resultUsed); for (var i = 0; i < resultUsed; i++) { resultDigits[i] = digits[i] & otherDigits[i]; } return _BigIntImpl._(isNegative, resultUsed, resultDigits); } /// Returns `abs(this) &~ abs(other)` with sign set according to [isNegative]. _BigIntImpl _absAndNotSetSign(_BigIntImpl other, bool isNegative) { var resultUsed = _used; var digits = _digits; var otherDigits = other._digits; var resultDigits = _newDigits(resultUsed); var m = _min(resultUsed, other._used); for (var i = 0; i < m; i++) { resultDigits[i] = digits[i] & ~otherDigits[i]; } for (var i = m; i < resultUsed; i++) { resultDigits[i] = digits[i]; } return _BigIntImpl._(isNegative, resultUsed, resultDigits); } /// Returns `abs(this) | abs(other)` with sign set according to [isNegative]. _BigIntImpl _absOrSetSign(_BigIntImpl other, bool isNegative) { var used = _used; var otherUsed = other._used; var resultUsed = _max(used, otherUsed); var digits = _digits; var otherDigits = other._digits; var resultDigits = _newDigits(resultUsed); var l, m; if (used < otherUsed) { l = other; m = used; } else { l = this; m = otherUsed; } for (var i = 0; i < m; i++) { resultDigits[i] = digits[i] | otherDigits[i]; } var lDigits = l._digits; for (var i = m; i < resultUsed; i++) { resultDigits[i] = lDigits[i]; } return _BigIntImpl._(isNegative, resultUsed, resultDigits); } /// Returns `abs(this) ^ abs(other)` with sign set according to [isNegative]. _BigIntImpl _absXorSetSign(_BigIntImpl other, bool isNegative) { var used = _used; var otherUsed = other._used; var resultUsed = _max(used, otherUsed); var digits = _digits; var otherDigits = other._digits; var resultDigits = _newDigits(resultUsed); var l, m; if (used < otherUsed) { l = other; m = used; } else { l = this; m = otherUsed; } for (var i = 0; i < m; i++) { resultDigits[i] = digits[i] ^ otherDigits[i]; } var lDigits = l._digits; for (var i = m; i < resultUsed; i++) { resultDigits[i] = lDigits[i]; } return _BigIntImpl._(isNegative, resultUsed, resultDigits); } /** * Bit-wise and operator. * * Treating both `this` and [other] as sufficiently large two's component * integers, the result is a number with only the bits set that are set in * both `this` and [other] * * Of both operands are negative, the result is negative, otherwise * the result is non-negative. */ _BigIntImpl operator &(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero || other._isZero) return zero; if (_isNegative == other._isNegative) { if (_isNegative) { // (-this) & (-other) == ~(this-1) & ~(other-1) // == ~((this-1) | (other-1)) // == -(((this-1) | (other-1)) + 1) _BigIntImpl this1 = _absSubSetSign(one, true); _BigIntImpl other1 = other._absSubSetSign(one, true); // Result cannot be zero if this and other are negative. return this1._absOrSetSign(other1, true)._absAddSetSign(one, true); } return _absAndSetSign(other, false); } // _isNegative != other._isNegative var p, n; if (_isNegative) { p = other; n = this; } else { // & is symmetric. p = this; n = other; } // p & (-n) == p & ~(n-1) == p &~ (n-1) var n1 = n._absSubSetSign(one, false); return p._absAndNotSetSign(n1, false); } /** * Bit-wise or operator. * * Treating both `this` and [other] as sufficiently large two's component * integers, the result is a number with the bits set that are set in either * of `this` and [other] * * If both operands are non-negative, the result is non-negative, * otherwise the result us negative. */ _BigIntImpl operator |(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero) return other; if (other._isZero) return this; if (_isNegative == other._isNegative) { if (_isNegative) { // (-this) | (-other) == ~(this-1) | ~(other-1) // == ~((this-1) & (other-1)) // == -(((this-1) & (other-1)) + 1) var this1 = _absSubSetSign(one, true); var other1 = other._absSubSetSign(one, true); // Result cannot be zero if this and a are negative. return this1._absAndSetSign(other1, true)._absAddSetSign(one, true); } return _absOrSetSign(other, false); } // _neg != a._neg var p, n; if (_isNegative) { p = other; n = this; } else { // | is symmetric. p = this; n = other; } // p | (-n) == p | ~(n-1) == ~((n-1) &~ p) == -(~((n-1) &~ p) + 1) var n1 = n._absSubSetSign(one, true); // Result cannot be zero if only one of this or a is negative. return n1._absAndNotSetSign(p, true)._absAddSetSign(one, true); } /** * Bit-wise exclusive-or operator. * * Treating both `this` and [other] as sufficiently large two's component * integers, the result is a number with the bits set that are set in one, * but not both, of `this` and [other] * * If the operands have the same sign, the result is non-negative, * otherwise the result is negative. */ _BigIntImpl operator ^(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero) return other; if (other._isZero) return this; if (_isNegative == other._isNegative) { if (_isNegative) { // (-this) ^ (-other) == ~(this-1) ^ ~(other-1) == (this-1) ^ (other-1) var this1 = _absSubSetSign(one, true); var other1 = other._absSubSetSign(one, true); return this1._absXorSetSign(other1, false); } return _absXorSetSign(other, false); } // _isNegative != a._isNegative var p, n; if (_isNegative) { p = other; n = this; } else { // ^ is symmetric. p = this; n = other; } // p ^ (-n) == p ^ ~(n-1) == ~(p ^ (n-1)) == -((p ^ (n-1)) + 1) var n1 = n._absSubSetSign(one, true); // Result cannot be zero if only one of this or a is negative. return p._absXorSetSign(n1, true)._absAddSetSign(one, true); } /** * The bit-wise negate operator. * * Treating `this` as a sufficiently large two's component integer, * the result is a number with the opposite bits set. * * This maps any integer `x` to `-x - 1`. */ _BigIntImpl operator ~() { if (_isZero) return _minusOne; if (_isNegative) { // ~(-this) == ~(~(this-1)) == this-1 return _absSubSetSign(one, false); } // ~this == -this-1 == -(this+1) // Result cannot be zero if this is positive. return _absAddSetSign(one, true); } /// Addition operator. _BigIntImpl operator +(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero) return other; if (other._isZero) return this; var isNegative = _isNegative; if (isNegative == other._isNegative) { // this + other == this + other // (-this) + (-other) == -(this + other) return _absAddSetSign(other, isNegative); } // this + (-other) == this - other == -(this - other) // (-this) + other == other - this == -(this - other) if (_absCompare(other) >= 0) { return _absSubSetSign(other, isNegative); } return other._absSubSetSign(this, !isNegative); } /// Subtraction operator. _BigIntImpl operator -(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero) return -other; if (other._isZero) return this; var isNegative = _isNegative; if (isNegative != other._isNegative) { // this - (-other) == this + other // (-this) - other == -(this + other) return _absAddSetSign(other, isNegative); } // this - other == this - a == -(this - other) // (-this) - (-other) == other - this == -(this - other) if (_absCompare(other) >= 0) { return _absSubSetSign(other, isNegative); } return other._absSubSetSign(this, !isNegative); } /// Multiplies `xDigits[xIndex]` with `multiplicandDigits` and adds the result /// to `accumulatorDigits`. /// /// The `multiplicandDigits` in the range `i` to `i`+`n`-1 are the /// multiplicand digits. /// /// The `accumulatorDigits` in the range `j` to `j`+`n`-1 are the accumulator /// digits. /// /// Concretely: /// `accumulatorDigits[j..j+n] += xDigits[xIndex] * m_digits[i..i+n-1]`. /// Returns 1. /// /// Note: This function may be intrinsified. Intrinsics on 64-bit platforms /// process digit pairs at even indices and returns 2. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:never-inline") static int _mulAdd( Uint32List xDigits, int xIndex, Uint32List multiplicandDigits, int i, Uint32List accumulatorDigits, int j, int n, ) { int x = xDigits[xIndex]; if (x == 0) { // No-op if x is 0. return _isIntrinsified ? 2 : 1; } int carry = 0; int xl = x & _halfDigitMask; int xh = x >> _halfDigitBits; while (--n >= 0) { int ml = multiplicandDigits[i] & _halfDigitMask; int mh = multiplicandDigits[i++] >> _halfDigitBits; int ph = xh * ml + mh * xl; int pl = xl * ml + ((ph & _halfDigitMask) << _halfDigitBits) + accumulatorDigits[j] + carry; carry = (pl >> _digitBits) + (ph >> _halfDigitBits) + xh * mh; accumulatorDigits[j++] = pl & _digitMask; } while (carry != 0) { int l = accumulatorDigits[j] + carry; carry = l >> _digitBits; accumulatorDigits[j++] = l & _digitMask; } return _isIntrinsified ? 2 : 1; } /// Multiplies `xDigits[i]` with `xDigits` and adds the result to /// `accumulatorDigits`. /// /// The `xDigits` in the range `i` to `used`-1 are the multiplicand digits. /// /// The `accumulatorDigits` in the range 2*`i` to `i`+`used`-1 are the /// accumulator digits. /// /// Concretely: /// `accumulatorDigits[2*i..i+used-1] += xDigits[i]*xDigits[i] + /// 2*xDigits[i]*xDigits[i+1..used-1]`. /// Returns 1. /// /// Note: This function may be intrinsified. Intrinsics on 64-bit platforms /// process digit pairs at even indices and returns 2. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:never-inline") static int _sqrAdd( Uint32List xDigits, int i, Uint32List accumulatorDigits, int used, ) { int x = xDigits[i]; if (x == 0) return _isIntrinsified ? 2 : 1; int j = 2 * i; int carry = 0; int xl = x & _halfDigitMask; int xh = x >> _halfDigitBits; int ph = 2 * xh * xl; int pl = xl * xl + ((ph & _halfDigitMask) << _halfDigitBits) + accumulatorDigits[j]; carry = (pl >> _digitBits) + (ph >> _halfDigitBits) + xh * xh; accumulatorDigits[j] = pl & _digitMask; x <<= 1; xl = x & _halfDigitMask; xh = x >> _halfDigitBits; int n = used - i - 1; int k = i + 1; j++; while (--n >= 0) { int l = xDigits[k] & _halfDigitMask; int h = xDigits[k++] >> _halfDigitBits; int ph = xh * l + h * xl; int pl = xl * l + ((ph & _halfDigitMask) << _halfDigitBits) + accumulatorDigits[j] + carry; carry = (pl >> _digitBits) + (ph >> _halfDigitBits) + xh * h; accumulatorDigits[j++] = pl & _digitMask; } carry += accumulatorDigits[i + used]; if (carry >= _digitBase) { accumulatorDigits[i + used] = carry - _digitBase; accumulatorDigits[i + used + 1] = 1; } else { accumulatorDigits[i + used] = carry; } return _isIntrinsified ? 2 : 1; } /// Multiplication operator. _BigIntImpl operator *(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); var used = _used; var otherUsed = other._used; if (used == 0 || otherUsed == 0) { return zero; } var resultUsed = used + otherUsed; var digits = _digits; var otherDigits = other._digits; var resultDigits = _newDigits(resultUsed); var i = 0; while (i < otherUsed) { i += _mulAdd(otherDigits, i, digits, 0, resultDigits, i, used); } return _BigIntImpl._( _isNegative != other._isNegative, resultUsed, resultDigits, ); } // resultDigits[0..resultUsed-1] = // xDigits[0..xUsed-1]*otherDigits[0..otherUsed-1]. // Returns resultUsed = xUsed + otherUsed. static int _mulDigits( Uint32List xDigits, int xUsed, Uint32List otherDigits, int otherUsed, Uint32List resultDigits, ) { var resultUsed = xUsed + otherUsed; var i = resultUsed + (resultUsed & 1); assert(resultDigits.length >= i); while (--i >= 0) { resultDigits[i] = 0; } i = 0; while (i < otherUsed) { i += _mulAdd(otherDigits, i, xDigits, 0, resultDigits, i, xUsed); } return resultUsed; } // resultDigits[0..resultUsed-1] = xDigits[0..xUsed-1]^2. // Returns resultUsed = 2*xUsed. static int _sqrDigits( Uint32List xDigits, int xUsed, Uint32List resultDigits, ) { var resultUsed = 2 * xUsed; assert(resultDigits.length >= resultUsed); // Since resultUsed is even, no need for a leading zero for // 64-bit processing. var i = resultUsed; while (--i >= 0) { resultDigits[i] = 0; } i = 0; while (i < xUsed - 1) { i += _sqrAdd(xDigits, i, resultDigits, xUsed); } // The last step is already done if digit pairs were processed above. if (i < xUsed) { _mulAdd(xDigits, i, xDigits, i, resultDigits, 2 * i, 1); } return resultUsed; } // Indices of the arguments of _estimateQuotientDigit. // For 64-bit processing by intrinsics on 64-bit platforms, the top digit pair // of the divisor is provided in the args array, and a 64-bit estimated // quotient is returned. However, on 32-bit platforms, the low 32-bit digit is // ignored and only one 32-bit digit is returned as the estimated quotient. static const int _divisorLowTopDigit = 0; // Low digit of top pair of divisor. static const int _divisorTopDigit = 1; // Top digit of divisor. static const int _quotientDigit = 2; // Estimated quotient. static const int _quotientHighDigit = 3; // High digit of estimated quotient. /// Estimate `args[_quotientDigit] = digits[i-1..i] ~/ args[_divisorTopDigit]` /// Returns 1. /// /// Note: This function may be intrinsified. Intrinsics on 64-bit platforms /// process a digit pair (i always odd): /// Estimate `args[_quotientDigit.._quotientHighDigit] = digits[i-3..i] ~/ /// args[_divisorLowTopDigit.._divisorTopDigit]`. /// Returns 2. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:never-inline") static int _estimateQuotientDigit(Uint32List args, Uint32List digits, int i) { // Verify that digit pairs are accessible for 64-bit processing. assert(digits.length >= 4); if (digits[i] == args[_divisorTopDigit]) { args[_quotientDigit] = _digitMask; } else { // Chop off one bit, since a Mint cannot hold 2 digits. var quotientDigit = ((digits[i] << (_digitBits - 1)) | (digits[i - 1] >> 1)) ~/ (args[_divisorTopDigit] >> 1); if (quotientDigit > _digitMask) { args[_quotientDigit] = _digitMask; } else { args[_quotientDigit] = quotientDigit; } } return _isIntrinsified ? 2 : 1; } /// Returns `trunc(this / other)`, with `other != 0`. _BigIntImpl _div(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); assert(other._used > 0); if (_used < other._used) { return zero; } _divRem(other); // Return quotient, i.e. // _lastQuoRem_digits[_lastRem_used.._lastQuoRem_used-1] with proper sign. var lastQuo_used = _cachedDivRemResult.quoRemUsed - _cachedDivRemResult.remUsed; var quo_digits = _cloneDigits( _cachedDivRemResult.quoRemDigits, _cachedDivRemResult.remUsed, _cachedDivRemResult.quoRemUsed, lastQuo_used, ); var quo = _BigIntImpl._(false, lastQuo_used, quo_digits); if ((_isNegative != other._isNegative) && (quo._used > 0)) { quo = -quo; } return quo; } /// Returns `this - other * trunc(this / other)`, with `other != 0`. _BigIntImpl _rem(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); assert(other._used > 0); if (_used < other._used) { return this; } _divRem(other); // Return remainder, i.e. // denormalized _lastQuoRem_digits[0.._lastRem_used-1] with proper sign. var remDigits = _cloneDigits( _cachedDivRemResult.quoRemDigits, 0, _cachedDivRemResult.remUsed, _cachedDivRemResult.remUsed, ); var rem = _BigIntImpl._(false, _cachedDivRemResult.remUsed, remDigits); if (_cachedDivRemResult.rem_nsh > 0) { rem = rem >> _cachedDivRemResult.rem_nsh; // Denormalize remainder. } if (_isNegative && (rem._used > 0)) { rem = -rem; } return rem; } /// Computes this ~/ other and this.remainder(other). /// /// Stores the result in [_cachedDivRemResult]'s [quoRemDigits], [quoRemUsed] /// and [remUsed]. The [quoRemDigits] contains the digits of *both*, the /// quotient and the remainder. /// /// Caches the input to avoid doing the work again when users write /// `a ~/ b` followed by a `a % b`. void _divRem(_BigIntImpl other) { // Check if result is already cached. if ((this._used == _cachedDivRemResult.dividendUsed) && (other._used == _cachedDivRemResult.divisorUsed) && identical(this._digits, _cachedDivRemResult.dividendDigits) && identical(other._digits, _cachedDivRemResult.divisorDigits)) { return; } assert(_used >= other._used); var nsh = _digitBits - other._digits[other._used - 1].bitLength; // For 64-bit processing, make sure other has an even number of digits. if (other._used.isOdd) { nsh += _digitBits; } // Concatenated positive quotient and normalized positive remainder. // The resultDigits can have at most one more digit than the dividend. Uint32List resultDigits; int resultUsed; // Normalized positive divisor (referred to as 'y'). // The normalized divisor has the most-significant bit of its most // significant digit set. // This makes estimating the quotient easier. Uint32List yDigits; int yUsed; if (nsh > 0) { // Extra digits for normalization, also used for possible _mulAdd carry. var numExtraDigits = (nsh + _digitBits - 1) ~/ _digitBits + 1; yDigits = _newDigits(other._used + numExtraDigits); yUsed = _lShiftDigits(other._digits, other._used, nsh, yDigits); resultDigits = _newDigits(_used + numExtraDigits); resultUsed = _lShiftDigits(_digits, _used, nsh, resultDigits); } else { yDigits = other._digits; yUsed = other._used; // Extra digit to hold possible _mulAdd carry. resultDigits = _cloneDigits(_digits, 0, _used, _used + 1); resultUsed = _used; } Uint32List args = _newDigits(4); args[_divisorLowTopDigit] = yDigits[yUsed - 2]; args[_divisorTopDigit] = yDigits[yUsed - 1]; // For 64-bit processing, make sure yUsed, i, and j are even. assert(yUsed.isEven); var i = resultUsed + (resultUsed & 1); var j = i - yUsed; // tmpDigits is a temporary array of i (even resultUsed) digits. var tmpDigits = _newDigits(i); var tmpUsed = _dlShiftDigits(yDigits, yUsed, j, tmpDigits); // Explicit first division step in case normalized dividend is larger or // equal to shifted normalized divisor. if (_compareDigits(resultDigits, resultUsed, tmpDigits, tmpUsed) >= 0) { assert(i == resultUsed); resultDigits[resultUsed++] = 1; // Quotient = 1. // Subtract divisor from remainder. _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); } else { // Account for possible carry in _mulAdd step. resultDigits[resultUsed++] = 0; } if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; // Leading zero for 64-bit processing. } // Negate y so we can later use _mulAdd instead of nonexistent _mulSub. var nyDigits = _newDigits(yUsed + 2); nyDigits[yUsed] = 1; _absSub(nyDigits, yUsed + 1, yDigits, yUsed, nyDigits); // nyDigits is read-only and has yUsed digits (possibly including several // leading zeros) plus a leading zero for 64-bit processing. // resultDigits is modified during iteration. // resultDigits[0..yUsed-1] is the current remainder. // resultDigits[yUsed..resultUsed-1] is the current quotient. --i; while (j > 0) { var d0 = _estimateQuotientDigit(args, resultDigits, i); j -= d0; var d1 = _mulAdd( args, _quotientDigit, nyDigits, 0, resultDigits, j, yUsed, ); // _estimateQuotientDigit and _mulAdd must agree on the number of digits // to process. assert(d0 == d1); if (d0 == 1) { if (resultDigits[i] < args[_quotientDigit]) { // Reusing the already existing tmpDigits array. var tmpUsed = _dlShiftDigits(nyDigits, yUsed, j, tmpDigits); _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); while (resultDigits[i] < --args[_quotientDigit]) { _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); } } } else { assert(d0 == 2); assert(resultDigits[i] <= args[_quotientHighDigit]); if (resultDigits[i] < args[_quotientHighDigit] || resultDigits[i - 1] < args[_quotientDigit]) { // Reusing the already existing tmpDigits array. var tmpUsed = _dlShiftDigits(nyDigits, yUsed, j, tmpDigits); _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); if (args[_quotientDigit] == 0) { --args[_quotientHighDigit]; } --args[_quotientDigit]; assert(resultDigits[i] <= args[_quotientHighDigit]); while (resultDigits[i] < args[_quotientHighDigit] || resultDigits[i - 1] < args[_quotientDigit]) { _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); if (args[_quotientDigit] == 0) { --args[_quotientHighDigit]; } --args[_quotientDigit]; assert(resultDigits[i] <= args[_quotientHighDigit]); } } } i -= d0; } // Cache result. _cachedDivRemResult.update( _digits, _used, other._digits, other._used, resultDigits, resultUsed, yUsed, nsh, ); } // Customized version of _rem() minimizing allocations for use in reduction. // Input: // xDigits[0..xUsed-1]: positive dividend. // yDigits[0..yUsed-1]: normalized positive divisor. // nyDigits[0..yUsed-1]: negated yDigits. // nsh: normalization shift amount. // args: top y digit(s) and place holder for estimated quotient digit(s). // tmpDigits: temp array of 2*yUsed digits. // resultDigits: result digits array large enough to temporarily hold // concatenated quotient and normalized remainder. // Output: // resultDigits[0..resultUsed-1]: positive remainder. // Returns resultUsed. static int _remDigits( Uint32List xDigits, int xUsed, Uint32List yDigits, int yUsed, Uint32List nyDigits, int nsh, Uint32List args, Uint32List tmpDigits, Uint32List resultDigits, ) { // Initialize resultDigits to normalized positive dividend. var resultUsed = _lShiftDigits(xDigits, xUsed, nsh, resultDigits); // For 64-bit processing, make sure yUsed, i, and j are even. assert(yUsed.isEven); var i = resultUsed + (resultUsed & 1); var j = i - yUsed; var tmpUsed = _dlShiftDigits(yDigits, yUsed, j, tmpDigits); // Explicit first division step in case normalized dividend is larger or // equal to shifted normalized divisor. if (_compareDigits(resultDigits, resultUsed, tmpDigits, tmpUsed) >= 0) { assert(i == resultUsed); resultDigits[resultUsed++] = 1; // Quotient = 1. // Subtract divisor from remainder. _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); } else { // Account for possible carry in _mulAdd step. resultDigits[resultUsed++] = 0; } if (resultUsed.isOdd) { resultDigits[resultUsed] = 0; // Leading zero for 64-bit processing. } // Negated yDigits passed in nyDigits allow the use of _mulAdd instead of // unimplemented _mulSub. // nyDigits is read-only and has yUsed digits (possibly including several // leading zeros) plus a leading zero for 64-bit processing. // resultDigits is modified during iteration. // resultDigits[0..yUsed-1] is the current remainder. // resultDigits[yUsed..resultUsed-1] is the current quotient. --i; while (j > 0) { var d0 = _estimateQuotientDigit(args, resultDigits, i); j -= d0; var d1 = _mulAdd( args, _quotientDigit, nyDigits, 0, resultDigits, j, yUsed, ); // _estimateQuotientDigit and _mulAdd must agree on the number of digits // to process. assert(d0 == d1); if (d0 == 1) { if (resultDigits[i] < args[_quotientDigit]) { var tmpUsed = _dlShiftDigits(nyDigits, yUsed, j, tmpDigits); _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); while (resultDigits[i] < --args[_quotientDigit]) { _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); } } } else { assert(d0 == 2); assert(resultDigits[i] <= args[_quotientHighDigit]); if ((resultDigits[i] < args[_quotientHighDigit]) || (resultDigits[i - 1] < args[_quotientDigit])) { var tmpUsed = _dlShiftDigits(nyDigits, yUsed, j, tmpDigits); _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); if (args[_quotientDigit] == 0) { --args[_quotientHighDigit]; } --args[_quotientDigit]; assert(resultDigits[i] <= args[_quotientHighDigit]); while ((resultDigits[i] < args[_quotientHighDigit]) || (resultDigits[i - 1] < args[_quotientDigit])) { _absSub(resultDigits, resultUsed, tmpDigits, tmpUsed, resultDigits); if (args[_quotientDigit] == 0) { --args[_quotientHighDigit]; } --args[_quotientDigit]; assert(resultDigits[i] <= args[_quotientHighDigit]); } } } i -= d0; } // Return remainder, i.e. denormalized resultDigits[0..yUsed-1]. resultUsed = yUsed; if (nsh > 0) { // Denormalize remainder. resultUsed = _rShiftDigits(resultDigits, resultUsed, nsh, resultDigits); } return resultUsed; } int get hashCode { // This is the [Jenkins hash function][1] but using masking to keep // values in SMI range. // // [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function int combine(int hash, int value) { hash = 0x1fffffff & (hash + value); hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); return hash ^ (hash >> 6); } int finish(int hash) { hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); hash = hash ^ (hash >> 11); return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); } if (_isZero) return 6707; // Just a random number. var hash = _isNegative ? 83585 : 429689; // Also random. for (int i = 0; i < _used; i++) { hash = combine(hash, _digits[i]); } return finish(hash); } /** * Test whether this value is numerically equal to `other`. * * If [other] is a [_BigIntImpl] returns whether the two operands have the * same value. * * Returns false if `other` is not a [_BigIntImpl]. */ bool operator ==(Object other) => other is _BigIntImpl && compareTo(other) == 0; /** * Returns the minimum number of bits required to store this big integer. * * The number of bits excludes the sign bit, which gives the natural length * for non-negative (unsigned) values. Negative values are complemented to * return the bit position of the first bit that differs from the sign bit. * * To find the number of bits needed to store the value as a signed value, * add one, i.e. use `x.bitLength + 1`. * * ``` * x.bitLength == (-x-1).bitLength * * new BigInt.from(3).bitLength == 2; // 00000011 * new BigInt.from(2).bitLength == 2; // 00000010 * new BigInt.from(1).bitLength == 1; // 00000001 * new BigInt.from(0).bitLength == 0; // 00000000 * new BigInt.from(-1).bitLength == 0; // 11111111 * new BigInt.from(-2).bitLength == 1; // 11111110 * new BigInt.from(-3).bitLength == 2; // 11111101 * new BigInt.from(-4).bitLength == 2; // 11111100 * ``` */ int get bitLength { if (_used == 0) return 0; final highBits = _digits[_used - 1]; assert(highBits != 0); int length = _digitBits * (_used - 1) + highBits.bitLength; if (!_isNegative) return length; // `this` is negative, i.e. `-x` for the magnitude `x`. We want to find the // bit length of `~this` or equivalently `-this-1`. // // -this-1 == -(-x)-1 == x-1 // // `x-1` will have the same bit length as `x` unless `x` is power of two // (e.g. 0x1000-1 = 0x0FFF). The magnitude is a power of two if the high // digit is a power of two and all the other digits are zero. if (highBits & (highBits - 1) != 0) return length; for (int i = _used - 2; i >= 0; i--) { if (_digits[i] != 0) return length; } return length - 1; } /** * Truncating division operator. * * Performs a truncating integer division, where the remainder is discarded. * * The remainder can be computed using the [remainder] method. * * Examples: * ``` * var seven = new BigInt.from(7); * var three = new BigInt.from(3); * seven ~/ three; // => 2 * (-seven) ~/ three; // => -2 * seven ~/ -three; // => -2 * seven.remainder(three); // => 1 * (-seven).remainder(three); // => -1 * seven.remainder(-three); // => 1 * ``` */ _BigIntImpl operator ~/(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (other._used == 0) { throw const IntegerDivisionByZeroException(); } return _div(other); } /** * Returns the remainder of the truncating division of `this` by [other]. * * The result `r` of this operation satisfies: * `this == (this ~/ other) * other + r`. * As a consequence the remainder `r` has the same sign as the divider `this`. */ _BigIntImpl remainder(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (other._used == 0) { throw const IntegerDivisionByZeroException(); } return _rem(other); } /// Division operator. double operator /(BigInt other) => this.toDouble() / other.toDouble(); /** Relational less than operator. */ bool operator <(BigInt other) => compareTo(other) < 0; /** Relational less than or equal operator. */ bool operator <=(BigInt other) => compareTo(other) <= 0; /** Relational greater than operator. */ bool operator >(BigInt other) => compareTo(other) > 0; /** Relational greater than or equal operator. */ bool operator >=(BigInt other) => compareTo(other) >= 0; /** * Euclidean modulo operator. * * Returns the remainder of the Euclidean division. The Euclidean division of * two integers `a` and `b` yields two integers `q` and `r` such that * `a == b * q + r` and `0 <= r < b.abs()`. * * The sign of the returned value `r` is always positive. * * See [remainder] for the remainder of the truncating division. */ _BigIntImpl operator %(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (other._used == 0) { throw const IntegerDivisionByZeroException(); } var result = _rem(other); if (result._isNegative) { if (other._isNegative) { result = result - other; } else { result = result + other; } } return result; } /** * Returns the sign of this big integer. * * Returns 0 for zero, -1 for values less than zero and * +1 for values greater than zero. */ int get sign { if (_used == 0) return 0; return _isNegative ? -1 : 1; } /// Whether this big integer is even. bool get isEven => _used == 0 || (_digits[0] & 1) == 0; /// Whether this big integer is odd. bool get isOdd => !isEven; /// Whether this number is negative. bool get isNegative => _isNegative; _BigIntImpl pow(int exponent) { if (exponent < 0) { throw ArgumentError("Exponent must not be negative: $exponent"); } if (exponent == 0) return one; // Exponentiation by squaring. var result = one; var base = this; while (exponent != 0) { if ((exponent & 1) == 1) { result *= base; } exponent >>= 1; // Skip unnecessary operation. if (exponent != 0) { base *= base; } } return result; } /** * Returns this integer to the power of [exponent] modulo [modulus]. * * The [exponent] must be non-negative and [modulus] must be * positive. */ _BigIntImpl modPow(BigInt bigExponent, BigInt bigModulus) { final exponent = _ensureSystemBigInt(bigExponent, 'bigExponent'); final modulus = _ensureSystemBigInt(bigModulus, 'bigModulus'); if (exponent._isNegative) { throw ArgumentError("exponent must be positive: $exponent"); } if (modulus <= zero) { throw ArgumentError("modulus must be strictly positive: $modulus"); } if (exponent._isZero) return one; final exponentBitlen = exponent.bitLength; if (exponentBitlen <= 0) return one; final bool cannotUseMontgomery = modulus.isEven || abs() >= modulus; if (cannotUseMontgomery || exponentBitlen < 64) { _BigIntReduction z = (cannotUseMontgomery || exponentBitlen < 8) ? _BigIntClassicReduction(modulus) : _BigIntMontgomeryReduction(modulus); var resultDigits = _newDigits(2 * z._normModulusUsed + 2); var result2Digits = _newDigits(2 * z._normModulusUsed + 2); var gDigits = _newDigits(z._normModulusUsed); var gUsed = z._convert(this, gDigits); // Initialize result with g. // Copy leading zero if any. for (int j = gUsed + (gUsed & 1) - 1; j >= 0; j--) { resultDigits[j] = gDigits[j]; } var resultUsed = gUsed; var result2Used; for (int i = exponentBitlen - 2; i >= 0; i--) { result2Used = z._sqr(resultDigits, resultUsed, result2Digits); if (exponent._digits[i ~/ _digitBits] & (1 << (i % _digitBits)) != 0) { resultUsed = z._mul( result2Digits, result2Used, gDigits, gUsed, resultDigits, ); } else { // Swap result and result2. var tmpDigits = resultDigits; var tmpUsed = resultUsed; resultDigits = result2Digits; resultUsed = result2Used; result2Digits = tmpDigits; result2Used = tmpUsed; } } return z._revert(resultDigits, resultUsed); } late int k; if (exponentBitlen < 18) k = 1; else if (exponentBitlen < 48) k = 3; else if (exponentBitlen < 144) k = 4; else if (exponentBitlen < 768) k = 5; else k = 6; _BigIntReduction z = _BigIntMontgomeryReduction(modulus); var n = 3; final int k1 = k - 1; final km = (1 << k) - 1; List gDigits = List.filled(km + 1, null); List gUsed = List.filled(km + 1, null); gDigits[1] = _newDigits(z._normModulusUsed); gUsed[1] = z._convert(this, gDigits[1]); if (k > 1) { var g2Digits = _newDigits(2 * z._normModulusUsed + 2); var g2Used = z._sqr(gDigits[1], gUsed[1], g2Digits); while (n <= km) { gDigits[n] = _newDigits(2 * z._normModulusUsed + 2); gUsed[n] = z._mul( g2Digits, g2Used, gDigits[n - 2], gUsed[n - 2], gDigits[n], ); n += 2; } } var w; var isOne = true; var resultDigits = one._digits; var resultUsed = one._used; var result2Digits = _newDigits(2 * z._normModulusUsed + 2); var result2Used; var exponentDigits = exponent._digits; var j = exponent._used - 1; int i = exponentDigits[j].bitLength - 1; while (j >= 0) { if (i >= k1) { w = (exponentDigits[j] >> (i - k1)) & km; } else { w = (exponentDigits[j] & ((1 << (i + 1)) - 1)) << (k1 - i); if (j > 0) { w |= exponentDigits[j - 1] >> (_digitBits + i - k1); } } n = k; while ((w & 1) == 0) { w >>= 1; --n; } if ((i -= n) < 0) { i += _digitBits; --j; } if (isOne) { // r == 1, don't bother squaring or multiplying it. resultDigits = _newDigits(2 * z._normModulusUsed + 2); resultUsed = gUsed[w]; var gwDigits = gDigits[w]; var ri = resultUsed + (resultUsed & 1); // Copy leading zero if any. while (--ri >= 0) { resultDigits[ri] = gwDigits[ri]; } isOne = false; } else { while (n > 1) { result2Used = z._sqr(resultDigits, resultUsed, result2Digits); resultUsed = z._sqr(result2Digits, result2Used, resultDigits); n -= 2; } if (n > 0) { result2Used = z._sqr(resultDigits, resultUsed, result2Digits); } else { var swapDigits = resultDigits; var swapUsed = resultUsed; resultDigits = result2Digits; resultUsed = result2Used; result2Digits = swapDigits; result2Used = swapUsed; } resultUsed = z._mul( result2Digits, result2Used, gDigits[w], gUsed[w], resultDigits, ); } while (j >= 0 && (exponentDigits[j] & (1 << i)) == 0) { result2Used = z._sqr(resultDigits, resultUsed, result2Digits); var swapDigits = resultDigits; var swapUsed = resultUsed; resultDigits = result2Digits; resultUsed = result2Used; result2Digits = swapDigits; result2Used = swapUsed; if (--i < 0) { i = _digitBits - 1; --j; } } } assert(!isOne); return z._revert(resultDigits, resultUsed); } // If inv is false, returns gcd(x, y). // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime"). static _BigIntImpl _binaryGcd(_BigIntImpl x, _BigIntImpl y, bool inv) { var xDigits = x._digits; var yDigits = y._digits; var xUsed = x._used; var yUsed = y._used; var maxUsed = _max(xUsed, yUsed); final maxLen = maxUsed + (maxUsed & 1); xDigits = _cloneDigits(xDigits, 0, xUsed, maxLen); yDigits = _cloneDigits(yDigits, 0, yUsed, maxLen); int shiftAmount = 0; if (inv) { if ((yUsed == 1) && (yDigits[0] == 1)) return one; if ((yUsed == 0) || (yDigits[0].isEven && xDigits[0].isEven)) { throw Exception("Not coprime"); } } else { if (x._isZero) { throw ArgumentError.value(0, "this", "must not be zero"); } if (y._isZero) { throw ArgumentError.value(0, "other", "must not be zero"); } if (((xUsed == 1) && (xDigits[0] == 1)) || ((yUsed == 1) && (yDigits[0] == 1))) return one; while (((xDigits[0] & 1) == 0) && ((yDigits[0] & 1) == 0)) { _rsh(xDigits, xUsed, 1, xDigits); _rsh(yDigits, yUsed, 1, yDigits); shiftAmount++; } if (shiftAmount >= _digitBits) { var digitShiftAmount = shiftAmount ~/ _digitBits; xUsed -= digitShiftAmount; yUsed -= digitShiftAmount; maxUsed -= digitShiftAmount; } if ((yDigits[0] & 1) == 1) { // Swap x and y. var tmpDigits = xDigits; var tmpUsed = xUsed; xDigits = yDigits; xUsed = yUsed; yDigits = tmpDigits; yUsed = tmpUsed; } } var uDigits = _cloneDigits(xDigits, 0, xUsed, maxLen); var vDigits = _cloneDigits(yDigits, 0, yUsed, maxLen + 2); // +2 for lsh. final bool ac = (xDigits[0] & 1) == 0; // Variables a, b, c, and d require one more digit. final abcdUsed = maxUsed + 1; final abcdLen = abcdUsed + (abcdUsed & 1) + 2; // +2 to satisfy _absAdd. bool aIsNegative = false; bool cIsNegative = false; late final Uint32List aDigits, cDigits; if (ac) { aDigits = _newDigits(abcdLen); aDigits[0] = 1; cDigits = _newDigits(abcdLen); } final Uint32List bDigits = _newDigits(abcdLen); final Uint32List dDigits = _newDigits(abcdLen); bool bIsNegative = false; bool dIsNegative = false; dDigits[0] = 1; while (true) { while ((uDigits[0] & 1) == 0) { _rsh(uDigits, maxUsed, 1, uDigits); if (ac) { if (((aDigits[0] & 1) == 1) || ((bDigits[0] & 1) == 1)) { // a += y if (aIsNegative) { if ((aDigits[maxUsed] != 0) || (_compareDigits(aDigits, maxUsed, yDigits, maxUsed)) > 0) { _absSub(aDigits, abcdUsed, yDigits, maxUsed, aDigits); } else { _absSub(yDigits, maxUsed, aDigits, maxUsed, aDigits); aIsNegative = false; } } else { _absAdd(aDigits, abcdUsed, yDigits, maxUsed, aDigits); } // b -= x if (bIsNegative) { _absAdd(bDigits, abcdUsed, xDigits, maxUsed, bDigits); } else if ((bDigits[maxUsed] != 0) || (_compareDigits(bDigits, maxUsed, xDigits, maxUsed) > 0)) { _absSub(bDigits, abcdUsed, xDigits, maxUsed, bDigits); } else { _absSub(xDigits, maxUsed, bDigits, maxUsed, bDigits); bIsNegative = true; } } _rsh(aDigits, abcdUsed, 1, aDigits); } else if ((bDigits[0] & 1) == 1) { // b -= x if (bIsNegative) { _absAdd(bDigits, abcdUsed, xDigits, maxUsed, bDigits); } else if ((bDigits[maxUsed] != 0) || (_compareDigits(bDigits, maxUsed, xDigits, maxUsed) > 0)) { _absSub(bDigits, abcdUsed, xDigits, maxUsed, bDigits); } else { _absSub(xDigits, maxUsed, bDigits, maxUsed, bDigits); bIsNegative = true; } } _rsh(bDigits, abcdUsed, 1, bDigits); } while ((vDigits[0] & 1) == 0) { _rsh(vDigits, maxUsed, 1, vDigits); if (ac) { if (((cDigits[0] & 1) == 1) || ((dDigits[0] & 1) == 1)) { // c += y if (cIsNegative) { if ((cDigits[maxUsed] != 0) || (_compareDigits(cDigits, maxUsed, yDigits, maxUsed) > 0)) { _absSub(cDigits, abcdUsed, yDigits, maxUsed, cDigits); } else { _absSub(yDigits, maxUsed, cDigits, maxUsed, cDigits); cIsNegative = false; } } else { _absAdd(cDigits, abcdUsed, yDigits, maxUsed, cDigits); } // d -= x if (dIsNegative) { _absAdd(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } else if ((dDigits[maxUsed] != 0) || (_compareDigits(dDigits, maxUsed, xDigits, maxUsed) > 0)) { _absSub(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } else { _absSub(xDigits, maxUsed, dDigits, maxUsed, dDigits); dIsNegative = true; } } _rsh(cDigits, abcdUsed, 1, cDigits); } else if ((dDigits[0] & 1) == 1) { // d -= x if (dIsNegative) { _absAdd(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } else if ((dDigits[maxUsed] != 0) || (_compareDigits(dDigits, maxUsed, xDigits, maxUsed) > 0)) { _absSub(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } else { _absSub(xDigits, maxUsed, dDigits, maxUsed, dDigits); dIsNegative = true; } } _rsh(dDigits, abcdUsed, 1, dDigits); } if (_compareDigits(uDigits, maxUsed, vDigits, maxUsed) >= 0) { // u -= v _absSub(uDigits, maxUsed, vDigits, maxUsed, uDigits); if (ac) { // a -= c if (aIsNegative == cIsNegative) { var a_cmp_c = _compareDigits(aDigits, abcdUsed, cDigits, abcdUsed); if (a_cmp_c > 0) { _absSub(aDigits, abcdUsed, cDigits, abcdUsed, aDigits); } else { _absSub(cDigits, abcdUsed, aDigits, abcdUsed, aDigits); aIsNegative = !aIsNegative && (a_cmp_c != 0); } } else { _absAdd(aDigits, abcdUsed, cDigits, abcdUsed, aDigits); } } // b -= d if (bIsNegative == dIsNegative) { var b_cmp_d = _compareDigits(bDigits, abcdUsed, dDigits, abcdUsed); if (b_cmp_d > 0) { _absSub(bDigits, abcdUsed, dDigits, abcdUsed, bDigits); } else { _absSub(dDigits, abcdUsed, bDigits, abcdUsed, bDigits); bIsNegative = !bIsNegative && (b_cmp_d != 0); } } else { _absAdd(bDigits, abcdUsed, dDigits, abcdUsed, bDigits); } } else { // v -= u _absSub(vDigits, maxUsed, uDigits, maxUsed, vDigits); if (ac) { // c -= a if (cIsNegative == aIsNegative) { var c_cmp_a = _compareDigits(cDigits, abcdUsed, aDigits, abcdUsed); if (c_cmp_a > 0) { _absSub(cDigits, abcdUsed, aDigits, abcdUsed, cDigits); } else { _absSub(aDigits, abcdUsed, cDigits, abcdUsed, cDigits); cIsNegative = !cIsNegative && (c_cmp_a != 0); } } else { _absAdd(cDigits, abcdUsed, aDigits, abcdUsed, cDigits); } } // d -= b if (dIsNegative == bIsNegative) { var d_cmp_b = _compareDigits(dDigits, abcdUsed, bDigits, abcdUsed); if (d_cmp_b > 0) { _absSub(dDigits, abcdUsed, bDigits, abcdUsed, dDigits); } else { _absSub(bDigits, abcdUsed, dDigits, abcdUsed, dDigits); dIsNegative = !dIsNegative && (d_cmp_b != 0); } } else { _absAdd(dDigits, abcdUsed, bDigits, abcdUsed, dDigits); } } // Exit loop if u == 0. var i = maxUsed; while ((i > 0) && (uDigits[i - 1] == 0)) --i; if (i == 0) break; } if (!inv) { if (shiftAmount > 0) { maxUsed = _lShiftDigits(vDigits, maxUsed, shiftAmount, vDigits); } return _BigIntImpl._(false, maxUsed, vDigits); } // No inverse if v != 1. var i = maxUsed - 1; while ((i > 0) && (vDigits[i] == 0)) --i; if ((i != 0) || (vDigits[0] != 1)) { throw Exception("Not coprime"); } if (dIsNegative) { while ((dDigits[maxUsed] != 0) || (_compareDigits(dDigits, maxUsed, xDigits, maxUsed) > 0)) { // d += x, d still negative _absSub(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } // d += x _absSub(xDigits, maxUsed, dDigits, maxUsed, dDigits); dIsNegative = false; } else { while ((dDigits[maxUsed] != 0) || (_compareDigits(dDigits, maxUsed, xDigits, maxUsed) >= 0)) { // d -= x _absSub(dDigits, abcdUsed, xDigits, maxUsed, dDigits); } } return _BigIntImpl._(false, maxUsed, dDigits); } /** * Returns the modular multiplicative inverse of this big integer * modulo [modulus]. * * The [modulus] must be positive. * * It is an error if no modular inverse exists. */ // Returns 1/this % modulus, with modulus > 0. _BigIntImpl modInverse(BigInt bigInt) { final modulus = _ensureSystemBigInt(bigInt, 'bigInt'); if (modulus <= zero) { throw ArgumentError("Modulus must be strictly positive: $modulus"); } if (modulus == one) return zero; var tmp = this; if (tmp._isNegative || (tmp._absCompare(modulus) >= 0)) { tmp %= modulus; } return _binaryGcd(modulus, tmp, true); } /** * Returns the greatest common divisor of this big integer and [other]. * * If either number is non-zero, the result is the numerically greatest * integer dividing both `this` and `other`. * * The greatest common divisor is independent of the order, * so `x.gcd(y)` is always the same as `y.gcd(x)`. * * For any integer `x`, `x.gcd(x)` is `x.abs()`. * * If both `this` and `other` is zero, the result is also zero. */ _BigIntImpl gcd(BigInt bigInt) { final other = _ensureSystemBigInt(bigInt, 'bigInt'); if (_isZero) return other.abs(); if (other._isZero) return this.abs(); return _binaryGcd(this, other, false); } /** * Returns the least significant [width] bits of this big integer as a * non-negative number (i.e. unsigned representation). The returned value has * zeros in all bit positions higher than [width]. * * ``` * new BigInt.from(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 * ``` * * This operation can be used to simulate arithmetic from low level languages. * For example, to increment an 8 bit quantity: * * ``` * q = (q + 1).toUnsigned(8); * ``` * * `q` will count from `0` up to `255` and then wrap around to `0`. * * If the input fits in [width] bits without truncation, the result is the * same as the input. The minimum width needed to avoid truncation of `x` is * given by `x.bitLength`, i.e. * * ``` * x == x.toUnsigned(x.bitLength); * ``` */ _BigIntImpl toUnsigned(int width) { return this & ((one << width) - one); } /** * Returns the least significant [width] bits of this integer, extending the * highest retained bit to the sign. This is the same as truncating the value * to fit in [width] bits using an signed 2-s complement representation. The * returned value has the same bit value in all positions higher than [width]. * * ``` * var big15 = new BigInt.from(15); * var big16 = new BigInt.from(16); * var big239 = new BigInt.from(239); * V--sign bit-V * big16.toSigned(5) == -big16 // 00010000 -> 11110000 * big239.toSigned(5) == big15 // 11101111 -> 00001111 * ^ ^ * ``` * * This operation can be used to simulate arithmetic from low level languages. * For example, to increment an 8 bit signed quantity: * * ``` * q = (q + 1).toSigned(8); * ``` * * `q` will count from `0` up to `127`, wrap to `-128` and count back up to * `127`. * * If the input value fits in [width] bits without truncation, the result is * the same as the input. The minimum width needed to avoid truncation of `x` * is `x.bitLength + 1`, i.e. * * ``` * x == x.toSigned(x.bitLength + 1); * ``` */ _BigIntImpl toSigned(int width) { // The value of binary number weights each bit by a power of two. The // twos-complement value weights the sign bit negatively. We compute the // value of the negative weighting by isolating the sign bit with the // correct power of two weighting and subtracting it from the value of the // lower bits. var signMask = one << (width - 1); return (this & (signMask - one)) - (this & signMask); } bool get isValidInt { assert(_digitBits == 32); return _used < 2 || (_used == 2 && (_digits[1] < 0x80000000 || (_isNegative && _digits[1] == 0x80000000 && _digits[0] == 0))); } int toInt() { assert(_digitBits == 32); if (_used == 0) return 0; if (_used == 1) return _isNegative ? -_digits[0] : _digits[0]; if (_used == 2 && _digits[1] < 0x80000000) { var result = (_digits[1] << _digitBits) | _digits[0]; return _isNegative ? -result : result; } return _isNegative ? _minInt : _maxInt; } /** * Returns this [_BigIntImpl] as a [double]. * * If the number is not representable as a [double], an * approximation is returned. For numerically large integers, the * approximation may be infinite. */ double toDouble() { const int exponentBias = 1075; // There are 11 bits for the exponent. // 2047 (all bits set to 1) is reserved for infinity and NaN. // When storing the exponent in the 11 bits, it is biased by exponentBias // to support negative exponents. const int maxDoubleExponent = 2046 - exponentBias; if (_isZero) return 0.0; // We fill the 53 bits little-endian. var resultBits = Uint8List(8); var length = _digitBits * (_used - 1) + _digits[_used - 1].bitLength; if (length > maxDoubleExponent + 53) { return _isNegative ? double.negativeInfinity : double.infinity; } // The most significant bit is for the sign. if (_isNegative) resultBits[7] = 0x80; // Write the exponent into bits 1..12: var biasedExponent = length - 53 + exponentBias; resultBits[6] = (biasedExponent & 0xF) << 4; resultBits[7] |= biasedExponent >> 4; int cachedBits = 0; int cachedBitsLength = 0; int digitIndex = _used - 1; int readBits(int n) { // Ensure that we have enough bits in [cachedBits]. while (cachedBitsLength < n) { int nextDigit; int nextDigitLength = _digitBits; // May get updated. if (digitIndex < 0) { nextDigit = 0; digitIndex--; } else { nextDigit = _digits[digitIndex]; if (digitIndex == _used - 1) nextDigitLength = nextDigit.bitLength; digitIndex--; } cachedBits = (cachedBits << nextDigitLength) + nextDigit; cachedBitsLength += nextDigitLength; } // Read the top [n] bits. var result = cachedBits >> (cachedBitsLength - n); // Remove the bits from the cache. cachedBits -= result << (cachedBitsLength - n); cachedBitsLength -= n; return result; } // The first leading 1 bit is implicit in the double-representation and can // be discarded. var leadingBits = readBits(5) & 0xF; resultBits[6] |= leadingBits; for (int i = 5; i >= 0; i--) { // Get the remaining 48 bits. resultBits[i] = readBits(8); } void roundUp() { // Simply consists of adding 1 to the whole 64 bit "number". // It will update the exponent, if necessary. // It might even round up to infinity (which is what we want). var carry = 1; for (int i = 0; i < 8; i++) { if (carry == 0) break; var sum = resultBits[i] + carry; resultBits[i] = sum & 0xFF; carry = sum >> 8; } } if (readBits(1) == 1) { if (resultBits[0].isOdd) { // Rounds to even all the time. roundUp(); } else { // Round up, if there is at least one other digit that is not 0. if (cachedBits != 0) { // There is already one in the cachedBits. roundUp(); } else { for (int i = digitIndex; i >= 0; i--) { if (_digits[i] != 0) { roundUp(); break; } } } } } return resultBits.buffer.asByteData().getFloat64(0, Endian.little); } /** * Returns a String-representation of this integer. * * The returned string is parsable by [parse]. * For any `_BigIntImpl` `i`, it is guaranteed that * `i == _BigIntImpl.parse(i.toString())`. */ String toString() { if (_used == 0) return "0"; if (_used == 1) { if (_isNegative) return (-_digits[0]).toString(); return _digits[0].toString(); } // Generate in chunks of 9 digits. // The chunks are in reversed order. var decimalDigitChunks = []; var rest = isNegative ? -this : this; while (rest._used > 1) { var digits9 = rest.remainder(_oneBillion).toString(); decimalDigitChunks.add(digits9); var zeros = 9 - digits9.length; if (zeros == 8) { decimalDigitChunks.add("00000000"); } else { if (zeros >= 4) { zeros -= 4; decimalDigitChunks.add("0000"); } if (zeros >= 2) { zeros -= 2; decimalDigitChunks.add("00"); } if (zeros >= 1) { decimalDigitChunks.add("0"); } } rest = rest ~/ _oneBillion; } decimalDigitChunks.add(rest._digits[0].toString()); if (_isNegative) decimalDigitChunks.add("-"); return decimalDigitChunks.reversed.join(); } int _toRadixCodeUnit(int digit) { const int _0 = 48; const int _a = 97; if (digit < 10) return _0 + digit; return _a + digit - 10; } /** * Converts this [BigInt] to a string representation in the given [radix]. * * In the string representation, lower-case letters are used for digits above * '9', with 'a' being 10 an 'z' being 35. * * The [radix] argument must be an integer in the range 2 to 36. */ String toRadixString(int radix) { if (radix < 2 || radix > 36) throw RangeError.range(radix, 2, 36); if (_used == 0) return "0"; if (_used == 1) { var digitString = _digits[0].toRadixString(radix); if (_isNegative) return "-" + digitString; return digitString; } if (radix == 16) return _toHexString(); var base = _BigIntImpl._fromInt(radix); var reversedDigitCodeUnits = []; var rest = this.abs(); while (!rest._isZero) { var digit = rest.remainder(base).toInt(); rest = rest ~/ base; reversedDigitCodeUnits.add(_toRadixCodeUnit(digit)); } var digitString = String.fromCharCodes(reversedDigitCodeUnits.reversed); if (_isNegative) return "-" + digitString; return digitString; } String _toHexString() { var chars = []; for (int i = 0; i < _used - 1; i++) { int chunk = _digits[i]; for (int j = 0; j < (_digitBits ~/ 4); j++) { chars.add(_toRadixCodeUnit(chunk & 0xF)); chunk >>= 4; } } var msbChunk = _digits[_used - 1]; while (msbChunk != 0) { chars.add(_toRadixCodeUnit(msbChunk & 0xF)); msbChunk >>= 4; } if (_isNegative) { const _dash = 45; chars.add(_dash); } return String.fromCharCodes(chars.reversed); } static _BigIntImpl _ensureSystemBigInt(BigInt bigInt, String parameterName) { if (bigInt is _BigIntImpl) return bigInt; throw ArgumentError.value( bigInt, parameterName, "Must be a platform BigInt", ); } } // Interface for modular reduction. abstract class _BigIntReduction { int get _normModulusUsed; // Return the number of digits used by resultDigits. int _convert(_BigIntImpl x, Uint32List resultDigits); int _mul( Uint32List xDigits, int xUsed, Uint32List yDigits, int yUsed, Uint32List resultDigits, ); int _sqr(Uint32List xDigits, int xUsed, Uint32List resultDigits); // Return x reverted to _BigIntImpl. _BigIntImpl _revert(Uint32List xDigits, int xUsed); } // Montgomery reduction on _BigIntImpl. class _BigIntMontgomeryReduction implements _BigIntReduction { final _BigIntImpl _modulus; final int _normModulusUsed; // Even if processing 64-bit (digit pairs). final Uint32List _modulusDigits; final Uint32List _args; final int _digitsPerStep; // Number of digits processed in one step. 1 or 2. static const int _xDigit = 0; // Index of digit of x. static const int _xHighDigit = 1; // Index of high digit of x (64-bit only). static const int _rhoDigit = 2; // Index of digit of rho. static const int _rhoHighDigit = 3; // Index of high digit of rho (64-bit). static const int _muDigit = 4; // Index of mu. static const int _muHighDigit = 5; // Index of high 32-bits of mu (64-bit). factory _BigIntMontgomeryReduction(_BigIntImpl modulus) { final Uint32List modulusDigits = modulus._digits; final Uint32List args = _newDigits(6); // Determine if we can process digit pairs by calling an intrinsic. final int digitsPerStep = _mulMod(args, args, 0); args[_xDigit] = modulusDigits[0]; int normModulusUsed = modulus._used; if (digitsPerStep == 1) { _invDigit(args); } else { assert(digitsPerStep == 2); normModulusUsed += modulus._used & 1; args[_xHighDigit] = modulusDigits[1]; _invDigitPair(args); } return _BigIntMontgomeryReduction._( modulus, normModulusUsed, modulusDigits, args, digitsPerStep, ); } _BigIntMontgomeryReduction._( this._modulus, this._normModulusUsed, this._modulusDigits, this._args, this._digitsPerStep, ); // Calculates -1/x % _digitBase, x is 32-bit digit. // xy == 1 (mod m) // xy = 1+km // xy(2-xy) = (1+km)(1-km) // x(y(2-xy)) = 1-k^2 m^2 // x(y(2-xy)) == 1 (mod m^2) // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 // Should reduce x and y(2-xy) by m^2 at each step to keep size bounded. // // Operation: // args[_rhoDigit] = 1/args[_xDigit] mod _digitBase. static void _invDigit(Uint32List args) { var x = args[_xDigit]; var y = x & 3; // y == 1/x mod 2^2 y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 y = (y * (2 - x * y % _BigIntImpl._digitBase)) % _BigIntImpl._digitBase; // y == 1/x mod _digitBase y = -y; // We really want the negative inverse. args[_rhoDigit] = y & _BigIntImpl._digitMask; assert(((x * y) & _BigIntImpl._digitMask) == _BigIntImpl._digitMask); } // Calculates -1/x % _digitBase^2, x is a pair of 32-bit digits. // Operation: // args[_rhoDigit.._rhoHighDigit] = // 1/args[_xDigit.._xHighDigit] mod _digitBase^2. static void _invDigitPair(Uint32List args) { var xl = args[_xDigit]; // Lower 32-bit digit of x. var y = xl & 3; // y == 1/x mod 2^2 y = (y * (2 - (xl & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 y = (y * (2 - (xl & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 y = (y * (2 - (((xl & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 y = (y * (2 - ((xl * y) & 0xffffffff))) & 0xffffffff; // y == 1/x mod 2^32 var x = (args[_xHighDigit] << _BigIntImpl._digitBits) | xl; y *= 2 - x * y; // Masking with 2^64-1 is implied by 64-bit arithmetic. // y == 1/x mod _digitBase^2 y = -y; // We really want the negative inverse. args[_rhoDigit] = y & _BigIntImpl._digitMask; args[_rhoHighDigit] = (y >> _BigIntImpl._digitBits) & _BigIntImpl._digitMask; assert(x * y == -1); } // Operation: // args[_muDigit] = args[_rhoDigit]*digits[i] mod _digitBase. // Returns 1. // Note: Intrinsics on 64-bit platforms process digit pairs at even indices: // args[_muDigit.._muHighDigit] = // args[_rhoDigit.._rhoHighDigit] * digits[i..i+1] mod _digitBase^2. // Returns 2. @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:never-inline") static int _mulMod(Uint32List args, Uint32List digits, int i) { var rhol = args[_rhoDigit] & _BigIntImpl._halfDigitMask; var rhoh = args[_rhoDigit] >> _BigIntImpl._halfDigitBits; var dh = digits[i] >> _BigIntImpl._halfDigitBits; var dl = digits[i] & _BigIntImpl._halfDigitMask; args[_muDigit] = (dl * rhol + (((dl * rhoh + dh * rhol) & _BigIntImpl._halfDigitMask) << _BigIntImpl._halfDigitBits)) & _BigIntImpl._digitMask; return _BigIntImpl._isIntrinsified ? 2 : 1; } // result = x*R mod _modulus. // Returns resultUsed. int _convert(_BigIntImpl x, Uint32List resultDigits) { // Montgomery reduction only works if abs(x) < _modulus. assert(x.abs() < _modulus); assert(_digitsPerStep == 1 || _normModulusUsed.isEven); var result = x.abs()._dlShift(_normModulusUsed)._rem(_modulus); if (x._isNegative && !result._isNegative && result._used > 0) { result = _modulus - result; } var used = result._used; var digits = result._digits; var i = used + (used & 1); while (--i >= 0) { resultDigits[i] = digits[i]; } return used; } _BigIntImpl _revert(Uint32List xDigits, int xUsed) { // Reserve enough digits for modulus squaring and accumulator carry. var resultDigits = _newDigits(2 * _normModulusUsed + 2); var i = xUsed + (xUsed & 1); while (--i >= 0) { resultDigits[i] = xDigits[i]; } var resultUsed = _reduce(resultDigits, xUsed); return _BigIntImpl._(false, resultUsed, resultDigits); } // x = x/R mod _modulus. // Returns xUsed. int _reduce(Uint32List xDigits, int xUsed) { while (xUsed < 2 * _normModulusUsed + 2) { // Pad x so _mulAdd has enough room later for a possible carry. xDigits[xUsed++] = 0; } var i = 0; while (i < _normModulusUsed) { var d = _mulMod(_args, xDigits, i); assert(d == _digitsPerStep); d = _BigIntImpl._mulAdd( _args, _muDigit, _modulusDigits, 0, xDigits, i, _normModulusUsed, ); assert(d == _digitsPerStep); i += d; } // Clamp x. while (xUsed > 0 && xDigits[xUsed - 1] == 0) { --xUsed; } xUsed = _BigIntImpl._drShiftDigits(xDigits, xUsed, i, xDigits); if (_BigIntImpl._compareDigits( xDigits, xUsed, _modulusDigits, _normModulusUsed, ) >= 0) { _BigIntImpl._absSub( xDigits, xUsed, _modulusDigits, _normModulusUsed, xDigits, ); } // Clamp x. while (xUsed > 0 && xDigits[xUsed - 1] == 0) { --xUsed; } return xUsed; } int _sqr(Uint32List xDigits, int xUsed, Uint32List resultDigits) { var resultUsed = _BigIntImpl._sqrDigits(xDigits, xUsed, resultDigits); return _reduce(resultDigits, resultUsed); } int _mul( Uint32List xDigits, int xUsed, Uint32List yDigits, int yUsed, Uint32List resultDigits, ) { var resultUsed = _BigIntImpl._mulDigits( xDigits, xUsed, yDigits, yUsed, resultDigits, ); return _reduce(resultDigits, resultUsed); } } // Modular reduction using "classic" algorithm. class _BigIntClassicReduction implements _BigIntReduction { final _BigIntImpl _modulus; // Modulus. int _normModulusUsed; _BigIntImpl _normModulus; // Normalized _modulus. Uint32List _normModulusDigits; Uint32List _negNormModulusDigits; // Negated _normModulus digits. int _modulusNsh; // Normalization shift amount. Uint32List _args; // Top _normModulus digit(s) and place holder for estimated // quotient digit(s). Uint32List _tmpDigits; // Temporary digits used during reduction. factory _BigIntClassicReduction(_BigIntImpl modulus) { // Preprocess arguments to _remDigits. int nsh = _BigIntImpl._digitBits - modulus._digits[modulus._used - 1].bitLength; // For 64-bit processing, make sure _negNormModulusDigits has an even number // of digits. if (modulus._used.isOdd) { nsh += _BigIntImpl._digitBits; } final _BigIntImpl normModulus = modulus << nsh; final int normModulusUsed = normModulus._used; final Uint32List normModulusDigits = normModulus._digits; assert(normModulusUsed.isEven); final Uint32List args = _newDigits(4); args[_BigIntImpl._divisorLowTopDigit] = normModulusDigits[normModulusUsed - 2]; args[_BigIntImpl._divisorTopDigit] = normModulusDigits[normModulusUsed - 1]; // Negate normModulus so we can use _mulAdd instead of // unimplemented _mulSub. final _BigIntImpl negNormModulus = _BigIntImpl.one._dlShift(normModulusUsed) - normModulus; late Uint32List negNormModulusDigits; if (negNormModulus._used < normModulusUsed) { negNormModulusDigits = _BigIntImpl._cloneDigits( negNormModulus._digits, 0, normModulusUsed, normModulusUsed, ); } else { negNormModulusDigits = negNormModulus._digits; } // negNormModulusDigits is read-only and has normModulusUsed digits (possibly // including several leading zeros) plus a leading zero for 64-bit // processing. final Uint32List tmpDigits = _newDigits(2 * normModulusUsed); return _BigIntClassicReduction._( modulus, normModulusUsed, normModulus, normModulusDigits, negNormModulusDigits, nsh, args, tmpDigits, ); } _BigIntClassicReduction._( this._modulus, this._normModulusUsed, this._normModulus, this._normModulusDigits, this._negNormModulusDigits, this._modulusNsh, this._args, this._tmpDigits, ); int _convert(_BigIntImpl x, Uint32List resultDigits) { var digits; var used; if (x._isNegative || x._absCompare(_modulus) >= 0) { var remainder = x._rem(_modulus); if (x._isNegative && remainder._used > 0) { assert(remainder._isNegative); remainder += _modulus; } assert(!remainder._isNegative); used = remainder._used; digits = remainder._digits; } else { used = x._used; digits = x._digits; } var i = used + (used & 1); // Copy leading zero if any. while (--i >= 0) { resultDigits[i] = digits[i]; } return used; } _BigIntImpl _revert(Uint32List xDigits, int xUsed) { return _BigIntImpl._(false, xUsed, xDigits); } int _reduce(Uint32List xDigits, int xUsed) { if (xUsed < _modulus._used) { return xUsed; } // The function _BigIntImpl._remDigits(...) is optimized for reduction and // equivalent to calling // 'convert(revert(xDigits, xUsed)._rem(_normModulus), xDigits);' return _BigIntImpl._remDigits( xDigits, xUsed, _normModulusDigits, _normModulusUsed, _negNormModulusDigits, _modulusNsh, _args, _tmpDigits, xDigits, ); } int _sqr(Uint32List xDigits, int xUsed, Uint32List resultDigits) { var resultUsed = _BigIntImpl._sqrDigits(xDigits, xUsed, resultDigits); return _reduce(resultDigits, resultUsed); } int _mul( Uint32List xDigits, int xUsed, Uint32List yDigits, int yUsed, Uint32List resultDigits, ) { var resultUsed = _BigIntImpl._mulDigits( xDigits, xUsed, yDigits, yUsed, resultDigits, ); return _reduce(resultDigits, resultUsed); } } ŒNN<0=#2ICGGFIBCFDDCLJJE1K- . , , 6/ :3 =))/H5*3(0%&"$ "!8LP,$175?;::-MLK21FFKLFME<+)9'*0O(O<KF/JQMNNIMM L GAAGI$ONP):5P$:2?'DE.P(L5.4%D$1PK1:3BKD&@%IG>N?#/ $56.?"H8D3#5/650DI9'J76MN7>FE6#M("NL" +"*@(! %2-/,! L /3$%1M<2#+;!$#!#"B':2,)F+EK*-,'0L*!81F3$ !1*'ACM 6"B+('$& !-1%'I0$ CM "B&($MM%(/, (%1.+ K-&BK)HK 2-I2/#>-B35A@ )%H>.BF+-%#$@/, )%1)2*%2F! CH@2-I2/#*-B53I0?- @ (%G)BJ+-%#$KAO#9GPE!9,'>0!G7(#,-(O/,0*+,-, Q/,0*+,-,PC!57/D@P.C%!7)D:PC/%/+4@QF%/+"5+#@PB!,%/ "4+$@PC!,%/ "4+$@LMF!*9/,89?7>AL+( ,++LOD'*9$,89?/6=M* G*CD%LM)E'*9$,P/64+$ D*CA#F8,#,)%2&*9$"+&/0::#04*9 $"+),03=#04QEPKN9,4# " &!"7:"6!E0,/$DNIC*N9,4 "0!" 4C,  +. 6!D0*8+ +$*9!''%/F(%:-(+& H<# /@ 5J?9QJQNQB>PQN+M4,4QE /) >F)(+.$8$9OF#(#&>AG$9M"( ##L+J*8PK"F&$*<   O6;GO=Q =::;GO+( "??   O6GO=Q =<Q-*$A E OH?'*@!?"@7=&(>M6$5LNNNM* %66666666 )@%P8%MMB7+*#OA %% !!()( +94L1,Q)94I(91;+94; PH.<C*94!!+:&!(:''&"G##!%G?>FC CJ&/)I6G-1AB4-##;&6GP &($($%! 2 " # # >.,1-=;=  $ @+ -2D?    G'#&"$  0)IIJ&,779F( BC1/C**':##%#!;N+8"M,%%44&,D-NGF%EE2LED" /,C0JCB -&,D-NGF%EE2LED" /,C0JCB -E>,PFF<D *NDD: B >,PFF<D *NDD: B 4I5.)&(F$?< (G?3D%21*;J%>+JJ/>63B#9%*+IQ5 I Q2 FMP" % &*OQPQ &&(8<<8 Q9  N OQ  ' $KNJO':(PC1<-,/:D$#+BN&7*#J+F1++51* :%>+N B- 9)6P)"#$#C4E$)$!(I52# H616- 8$'))*<'&,* ( ' "82/$'MP-C$G 91,,*0;M/*42'31P.#$"78 D'6(?J#O8O@=O@L!42@BO&K,4B=>65G/ 0 9= $+,0Q;'A*27  5RGB&  99(2'&" <#71/"OF#  EK. -  .!dart:core-patch/bigint_patch.dartÀeÀh³Àg@Corg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/bool.dart¤2// 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"; // Examples can assume: // class Logger { const Logger({String id}); } /// The reserved words `true` and `false` denote objects that are the only two /// instances of this class. /// /// It is a compile-time error for a class to attempt to extend or implement /// bool. @pragma("vm:entry-point") final class bool { /// Boolean value for [name] in the compilation configuration environment. /// /// The compilation configuration environment is provided by the /// surrounding tools which are compiling or running the Dart program. /// The environment is a mapping from a set of string keys to their associated /// string value. /// The string value, or lack of a value, associated with a [name] /// must be consistent across all calls to [String.fromEnvironment], /// [int.fromEnvironment], `bool.fromEnvironment` and [bool.hasEnvironment] /// in a single program. /// The string values can be directly accessed using [String.fromEnvironment]. /// /// This constructor parses the string value associated with [name] as /// a boolean, as if by [`bool.tryParse(value)`][bool.tryParse], /// meaning that it accepts only the strings `"true"` and `"false"`. /// /// If there is no value associated with [name] in the compilation /// configuration environment, or if the associated string value is not one /// of `"true"` or `"false"`, the value of the constructor invocation /// is the [defaultValue] boolean, which defaults to the boolean value /// `false`. /// /// The result is the same as that of: /// ```dart template:expression /// (const String.fromEnvironment(name) == "true") /// || ((const String.fromEnvironment(name) != "false") && defaultValue) /// ``` /// /// Example: /// ```dart /// const bool loggingEnabled = bool.fromEnvironment("logging"); /// ``` /// In order to check whether a value is there at all, use /// [bool.hasEnvironment]. Example: /// ```dart /// const bool? yesNoMaybe = bool.hasEnvironment("optionalFlag") /// ? bool.fromEnvironment("optionalFlag") /// : null; /// ``` /// /// To accept other strings than `"true"` or `"false"`, use the /// [String.fromEnvironment] constructor directly. Example: /// ```dart /// const isLoggingOn = (const String.fromEnvironment("logging") == "on"); /// ``` /// /// This constructor is only guaranteed to work when invoked as `const`. /// It may work as a non-constant invocation on some platforms which /// have access to compiler options at run-time, but most ahead-of-time /// compiled platforms will not have this information. external const factory bool.fromEnvironment( String name, { bool defaultValue = false, }); /// Whether [name] is declared in the compilation configuration environment. /// /// The compilation configuration environment is provided by the /// surrounding tools which are compiling or running the Dart program. /// The environment is a mapping from a set of string keys to their associated /// string value. /// The string value, or lack of a value, associated with a [name] /// must be consistent across all calls to [String.fromEnvironment], /// [int.fromEnvironment], `bool.fromEnvironment` and [bool.hasEnvironment] /// in a single program. /// /// This constructor evaluates to `true` if [name] has an associated value /// in the compilation configuration environment, and to `false` if not. /// If there is an associated value, then the value can be accessed using /// `const String.fromEnvironment(name)`. Otherwise, /// `String.fromEnvironment(name, defaultValue: someString)` /// is known to evaluate to the given `defaultValue`. /// /// The [String.fromEnvironment], [int.fromEnvironment] and /// [bool.fromEnvironment] constructors always produce a [String], [int], /// or [bool], as required for a constructor. /// In most cases, the absence of a configuration environment association /// for a [name] simply means that the code should fall back on a default /// behavior, and a default value of the same type typically represents that /// perfectly. /// /// In some cases, a value of different type, mostly `null`, may better /// represent the absence of a choice. In that case, this constructor can /// be used to first check whether there is a value, and only then use the /// other `fromEnvironment` constructors. /// Example: /// ```dart /// const int? indentOverride = bool.hasEnvironment("indent-override") /// ? int.fromEnvironment("indent-override") /// : null; /// void indentLines(List lines, int indentation) { /// int actualIndentation = indentOverride ?? indentation; /// // ... Do something to lines. /// } /// ``` /// This pattern allows a compilation configuration to provide an override /// value to the program, but also to not do so, and the program can tell the /// difference between an explicitly provided value and the absence of one. /// /// Another use case is to only do something extra when a needed value is /// available. Example: /// ```dart /// const Logger? logger = bool.hasEnvironment("logging-id") /// ? Logger(id: String.fromEnvironment("logging-id")) /// : null; /// ``` /// /// This constructor is only guaranteed to work when invoked as `const`. /// It may work as a non-constant invocation on some platforms which /// have access to compiler options at run-time, but most ahead-of-time /// compiled platforms will not have this information. external const factory bool.hasEnvironment(String name); /// Parses [source] as an, optionally case-insensitive, boolean literal. /// /// If [caseSensitive] is `true`, which is the default, /// the only accepted inputs are the strings `"true"` and `"false"`, /// which returns the results `true` and `false` respectively. /// /// If [caseSensitive] is `false`, any combination of upper and lower case /// ASCII letters in the words `"true"` and `"false"` are accepted, /// as if the input was first lower-cased. /// /// Throws a [FormatException] if the [source] string does not contain /// a valid boolean literal. /// /// Rather than throwing and immediately catching the [FormatException], /// instead use [tryParse] to handle a potential parsing error. /// /// Example: /// ```dart /// print(bool.parse('true')); // true /// print(bool.parse('false')); // false /// print(bool.parse('TRUE')); // throws FormatException /// print(bool.parse('TRUE', caseSensitive: false)); // true /// print(bool.parse('FALSE', caseSensitive: false)); // false /// print(bool.parse('NO')); // throws FormatException /// print(bool.parse('YES')); // throws FormatException /// print(bool.parse('0')); // throws FormatException /// print(bool.parse('1')); // throws FormatException /// ``` @Since("3.0") external static bool parse(String source, {bool caseSensitive = true}); /// Parses [source] as an, optionally case-insensitive, boolean literal. /// /// If [caseSensitive] is `true`, which is the default, /// the only accepted inputs are the strings `"true"` and `"false"`, /// which returns the results `true` and `false` respectively. /// /// If [caseSensitive] is `false`, any combination of upper and lower case /// ASCII letters in the words `"true"` and `"false"` are accepted, /// as if the input was first lower-cased. /// /// Returns `null` if the [source] string does not contain a valid /// boolean literal. /// /// If the input can be assumed to be valid, use [bool.parse] to avoid /// having to deal with a possible `null` result. /// /// Example: /// ```dart /// print(bool.tryParse('true')); // true /// print(bool.tryParse('false')); // false /// print(bool.tryParse('TRUE')); // null /// print(bool.tryParse('TRUE', caseSensitive: false)); // true /// print(bool.tryParse('FALSE', caseSensitive: false)); // false /// print(bool.tryParse('NO')); // null /// print(bool.tryParse('YES')); // null /// print(bool.tryParse('0')); // null /// print(bool.tryParse('1')); // null /// ``` @Since("3.0") external static bool? tryParse(String source, {bool caseSensitive = true}); external int get hashCode; /// The logical conjunction ("and") of this and [other]. /// /// Returns `true` if both this and [other] are `true`, and `false` otherwise. bool operator &(bool other) => other && this; /// The logical disjunction ("inclusive or") of this and [other]. /// /// Returns `true` if either this or [other] is `true`, and `false` otherwise. bool operator |(bool other) => other || this; /// The logical exclusive disjunction ("exclusive or") of this and [other]. /// /// Returns whether this and [other] are neither both `true` nor both `false`. bool operator ^(bool other) => !other == this; /// Returns either `"true"` for `true` and `"false"` for `false`. String toString() { return this ? "true" : "false"; } } €×NN</OM MCIQEGNQICGENHI)"5O C =&C1 B>M KGJ9/OCIQEGNMKL7?8>L0LLOJLM,I3>?& MPNL?= KGJ9;K:GAMF-IKB)+;?A9:88 JK:GAMF-EI4,.,BD*+)) N;Q0DQ0NQ1D$dart:core/bool.dartÀeÀh³œœ\org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm_shared/lib/bool_patch.dartˆ$// 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. import "dart:_internal" show patch; @patch @pragma('vm:deeply-immutable') @pragma("vm:entry-point") @pragma("wasm:entry-point") class bool { @patch @pragma("vm:external-name", "Bool_fromEnvironment") external const factory bool.fromEnvironment( String name, { bool defaultValue = false, }); @patch @pragma("vm:external-name", "Bool_hasEnvironment") external const factory bool.hasEnvironment(String name); @patch int get hashCode => this ? 1231 : 1237; int get _identityHashCode => this ? 1231 : 1237; @patch static bool parse(String source, {bool caseSensitive = true}) { if (caseSensitive) { return source == "true" || source != "false" && (throw FormatException("Invalid boolean", source)); } // Ignore case-sensitive when `caseSensitive` is false. return _compareIgnoreCase(source, "true") || !_compareIgnoreCase(source, "false") && (throw FormatException("Invalid boolean", source)); } @patch static bool? tryParse(String source, {bool caseSensitive = true}) { if (caseSensitive) { return source == "true" ? true : source == "false" ? false : null; } return _compareIgnoreCase(source, "true") ? true : _compareIgnoreCase(source, "false") ? false : null; } /// Compares a string against an ASCII lower-case letter-only string. /// /// Returns `true` if the [input] has the same length and same letters /// as [lowerCaseTarget], `false` if not. static bool _compareIgnoreCase(String input, String lowerCaseTarget) { if (input.length != lowerCaseTarget.length) return false; var delta = 0x20; for (var i = 0; i < input.length; i++) { delta |= input.codeUnitAt(i) ^ lowerCaseTarget.codeUnitAt(i); } return delta == 0x20; } } GNN<$ 6/ 5; *3 B!B<10@ F..HI,I>-Ddart:core-patch/bool_patch.dartÀeÀh³Horg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/date_time.dartÀ…½// Copyright (c) 2011, 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"; /// An instant in time, such as July 20, 1969, 8:18pm GMT. /// /// DateTimes can represent time values that are at a distance of at most /// 100,000,000 days from epoch (1970-01-01 UTC): -271821-04-20 to 275760-09-13. /// /// Create a `DateTime` object by using one of the constructors /// or by parsing a correctly formatted string, /// which complies with a subset of ISO 8601. /// **Note:** hours are specified between 0 and 23, /// as in a 24-hour clock. /// /// For example: /// ```dart /// final now = DateTime.now(); /// final berlinWallFell = DateTime.utc(1989, 11, 9); /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); // 8:18pm /// ``` /// /// A `DateTime` object is anchored either in the UTC time zone /// or in the local time zone of the current computer /// when the object is created. /// /// Once created, neither the value nor the time zone /// of a `DateTime` object may be changed. /// /// You can use properties to get /// the individual units of a `DateTime` object. /// ``` /// print(berlinWallFell.year); // 1989 /// print(berlinWallFell.month); // 11 /// print(berlinWallFell.day); // 9 /// print(moonLanding.hour); // 20 /// print(moonLanding.minute); // 18 /// ``` /// For convenience and readability, /// the `DateTime` class provides a constant for each `day` and `month` /// name - for example, [august] and [friday]. /// You can use these constants to improve code readability: /// ```dart /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); /// print(DateTime.november); // 11 /// assert(berlinWallFell.month == DateTime.november); /// assert(berlinWallFell.weekday == DateTime.thursday); /// ``` /// /// `Day` and `month` values begin at 1, and the week starts on `Monday`. /// That is, the constants [january] and [monday] are both 1. /// /// ## Working with UTC and local time /// /// A `DateTime` object is in the local time zone /// unless explicitly created in the UTC time zone. /// Use [isUtc] to determine whether a `DateTime` object is based in UTC. /// /// ```dart /// final dDay = DateTime.utc(1944, 6, 6); /// print(dDay.isUtc); // true /// /// final dDayLocal = DateTime(1944, 6, 6); /// print(dDayLocal.isUtc); // false /// ``` /// Use the methods [toLocal] and [toUtc] /// to get the equivalent date/time value specified in the other time zone. /// ``` /// final localDay = dDay.toLocal(); // e.g. 1944-06-06 02:00:00.000 /// print(localDay.isUtc); // false /// /// final utcFromLocal = localDay.toUtc(); // 1944-06-06 00:00:00.000Z /// print(utcFromLocal.isUtc); // true /// ``` /// Use [timeZoneName] to get an abbreviated name of the time zone /// for the `DateTime` object. /// ``` /// print(dDay.timeZoneName); // UTC /// print(localDay.timeZoneName); // e.g. EET /// ``` /// To find the difference /// between UTC and the time zone of a `DateTime` object /// call [timeZoneOffset]. /// ``` /// print(dDay.timeZoneOffset); // 0:00:00.000000 /// print(localDay.timeZoneOffset); // e.g. 2:00:00.000000 /// ``` /// /// ## Comparing DateTime objects /// /// The `DateTime` class contains methods for comparing `DateTime`s /// chronologically, such as [isAfter], [isBefore], and [isAtSameMomentAs]. /// ``` /// print(berlinWallFell.isAfter(moonLanding)); // true /// print(berlinWallFell.isBefore(moonLanding)); // false /// print(dDay.isAtSameMomentAs(localDay)); // true /// ``` /// /// ## Using DateTime with Duration /// /// Use the [add] and [subtract] methods with a [Duration] object /// to create a `DateTime` object based on another. /// For example, to find the point in time that is 36 hours after now, /// you can write: /// ```dart /// final now = DateTime.now(); /// final later = now.add(const Duration(hours: 36)); /// ``` /// /// To find out how much time is between two `DateTime` objects use /// [difference], which returns a [Duration] object: /// ``` /// final difference = berlinWallFell.difference(moonLanding); /// print(difference.inDays); // 7416 /// ``` /// /// The difference between two dates in different time zones /// is just the number of nanoseconds between the two points in time. /// It doesn't take calendar days into account. /// That means that the difference between two midnights in local time may be /// less than 24 hours times the number of days between them, /// if there is a daylight saving change in between. /// If the difference above is calculated using Australian local time, the /// difference is 7415 days and 23 hours, which is only 7415 whole days as /// reported by `inDays`. /// /// ## Other resources /// /// * See [Duration] to represent a span of time. /// * See [Stopwatch] to measure timespans. /// * The `DateTime` class does not provide internationalization. /// To internationalize your code, use /// the [intl](https://pub.dev/packages/intl) package. class DateTime implements Comparable { // Weekday constants that are returned by [weekday] method: static const int monday = 1; static const int tuesday = 2; static const int wednesday = 3; static const int thursday = 4; static const int friday = 5; static const int saturday = 6; static const int sunday = 7; static const int daysPerWeek = 7; // Month constants that are returned by the [month] getter. static const int january = 1; static const int february = 2; static const int march = 3; static const int april = 4; static const int may = 5; static const int june = 6; static const int july = 7; static const int august = 8; static const int september = 9; static const int october = 10; static const int november = 11; static const int december = 12; static const int monthsPerYear = 12; /// True if this [DateTime] is set to UTC time. /// /// ```dart /// final dDay = DateTime.utc(1944, 6, 6); /// print(dDay.isUtc); // true /// /// final local = DateTime(1944, 6, 6); /// print(local.isUtc); // false /// ``` final bool isUtc; /// Constructs a [DateTime] instance specified in the local time zone. /// /// For example, /// to create a `DateTime` object representing the 7th of September 2017, /// 5:30pm /// /// ```dart /// final dentistAppointment = DateTime(2017, 9, 7, 17, 30); /// ``` DateTime( int year, [ int month = 1, int day = 1, int hour = 0, int minute = 0, int second = 0, int millisecond = 0, int microsecond = 0, ]) : this._internal( year, month, day, hour, minute, second, millisecond, microsecond, false, ); /// Constructs a [DateTime] instance specified in the UTC time zone. /// /// ```dart /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); /// ``` /// /// When dealing with dates or historic events, preferably use UTC DateTimes, /// since they are unaffected by daylight-saving changes and are unaffected /// by the local timezone. DateTime.utc( int year, [ int month = 1, int day = 1, int hour = 0, int minute = 0, int second = 0, int millisecond = 0, int microsecond = 0, ]) : this._internal( year, month, day, hour, minute, second, millisecond, microsecond, true, ); /// Constructs a [DateTime] instance with current date and time in the /// local time zone. /// /// ```dart /// final now = DateTime.now(); /// ``` DateTime.now() : this._now(); /// Constructs a [DateTime] with the current UTC date and time. /// /// /// ```dart /// final mark = DateTime.timestamp(); /// ``` @Since("3.0") DateTime.timestamp() : this._nowUtc(); external DateTime._nowUtc(); /// Constructs a new [DateTime] instance based on [formattedString]. /// /// Throws a [FormatException] if the input string cannot be parsed. /// /// The function parses a subset of ISO 8601, /// which includes the subset accepted by RFC 3339. /// /// The accepted inputs are currently: /// /// * A date: A signed four-to-six digit year, two digit month and /// two digit day, optionally separated by `-` characters. /// Examples: "19700101", "-0004-12-24", "81030-04-01". /// * An optional time part, separated from the date by either `T` or a space. /// The time part is a two digit hour, /// then optionally a two digit minutes value, /// then optionally a two digit seconds value, and /// then optionally a '.' or ',' followed by at least a one digit /// second fraction. /// The minutes and seconds may be separated from the previous parts by a /// ':'. /// Examples: "12", "12:30:24.124", "12:30:24,124", "123010.50". /// * An optional time-zone offset part, /// possibly separated from the previous by a space. /// The time zone is either 'z' or 'Z', or it is a signed two digit hour /// part and an optional two digit minute part. The sign must be either /// "+" or "-", and cannot be omitted. /// The minutes may be separated from the hours by a ':'. /// Examples: "Z", "-10", "+01:30", "+1130". /// /// This includes the output of both [toString] and [toIso8601String], which /// will be parsed back into a `DateTime` object with the same time as the /// original. /// /// The result is always in either local time or UTC. /// If a time zone offset other than UTC is specified, /// the time is converted to the equivalent UTC time. /// /// Examples of accepted strings: /// /// * `"2012-02-27"` /// * `"2012-02-27 13:27:00"` /// * `"2012-02-27 13:27:00.123456789z"` /// * `"2012-02-27 13:27:00,123456789z"` /// * `"20120227 13:27:00"` /// * `"20120227T132700"` /// * `"20120227"` /// * `"+20120227"` /// * `"2012-02-27T14Z"` /// * `"2012-02-27T14+00:00"` /// * `"-123450101 00:00:00 Z"`: in the year -12345. /// * `"2002-02-27T14:00:00-0500"`: Same as `"2002-02-27T19:00:00Z"` /// /// This method accepts out-of-range component values and interprets /// them as overflows into the next larger component. /// For example, "2020-01-42" will be parsed as 2020-02-11, because /// the last valid date in that month is 2020-01-31, so 42 days is /// interpreted as 31 days of that month plus 11 days into the next month. /// /// To detect and reject invalid component values, use /// [DateFormat.parseStrict](https://pub.dev/documentation/intl/latest/intl/DateFormat/parseStrict.html) /// from the [intl](https://pub.dev/packages/intl) package. static DateTime parse(String formattedString) { var re = _parseFormat; Match? match = re.firstMatch(formattedString); if (match != null) { int parseIntOrZero(String? matched) { if (matched == null) return 0; return int.parse(matched); } // Parses fractional second digits of '.(\d+)' into the combined // microseconds. We only use the first 6 digits because of DateTime // precision of 999 milliseconds and 999 microseconds. int parseMilliAndMicroseconds(String? matched) { if (matched == null) return 0; int length = matched.length; assert(length >= 1); int result = 0; for (int i = 0; i < 6; i++) { result *= 10; if (i < matched.length) { result += matched.codeUnitAt(i) ^ 0x30; } } return result; } int years = int.parse(match[1]!); int month = int.parse(match[2]!); int day = int.parse(match[3]!); int hour = parseIntOrZero(match[4]); int minute = parseIntOrZero(match[5]); int second = parseIntOrZero(match[6]); int milliAndMicroseconds = parseMilliAndMicroseconds(match[7]); int millisecond = milliAndMicroseconds ~/ Duration.microsecondsPerMillisecond; int microsecond = milliAndMicroseconds.remainder(Duration.microsecondsPerMillisecond) as int; bool isUtc = false; if (match[8] != null) { // timezone part isUtc = true; String? tzSign = match[9]; if (tzSign != null) { // timezone other than 'Z' and 'z'. int sign = (tzSign == '-') ? -1 : 1; int hourDifference = int.parse(match[10]!); int minuteDifference = parseIntOrZero(match[11]); minuteDifference += 60 * hourDifference; minute -= sign * minuteDifference; } } DateTime? result = _finishParse( years, month, day, hour, minute, second, millisecond, microsecond, isUtc, ); if (result == null) { throw FormatException("Time out of range", formattedString); } return result; } else { throw FormatException("Invalid date format", formattedString); } } /// Constructs a new [DateTime] instance based on [formattedString]. /// /// Works like [parse] except that this function returns `null` /// where [parse] would throw a [FormatException]. static DateTime? tryParse(String formattedString) { // TODO: Optimize to avoid throwing. try { return parse(formattedString); } on FormatException { return null; } } static const int _maxMillisecondsSinceEpoch = 8640000000000000; static const int _maxMicrosecondsSinceEpoch = _maxMillisecondsSinceEpoch * Duration.microsecondsPerMillisecond; /// Constructs a new [DateTime] instance /// with the given [millisecondsSinceEpoch]. /// /// If [isUtc] is false then the date is in the local time zone. /// /// The constructed [DateTime] represents /// 1970-01-01T00:00:00Z + [millisecondsSinceEpoch] ms in the given /// time zone (local or UTC). /// ```dart /// final newYearsDay = /// DateTime.fromMillisecondsSinceEpoch(1641031200000, isUtc:true); /// print(newYearsDay); // 2022-01-01 10:00:00.000Z /// ``` external DateTime.fromMillisecondsSinceEpoch( int millisecondsSinceEpoch, { bool isUtc = false, }); /// Constructs a new [DateTime] instance /// with the given [microsecondsSinceEpoch]. /// /// If [isUtc] is false, then the date is in the local time zone. /// /// The constructed [DateTime] represents /// 1970-01-01T00:00:00Z + [microsecondsSinceEpoch] us in the given /// time zone (local or UTC). /// ```dart /// final newYearsEve = /// DateTime.fromMicrosecondsSinceEpoch(1640979000000000, isUtc:true); /// print(newYearsEve); // 2021-12-31 19:30:00.000Z /// ``` external DateTime.fromMicrosecondsSinceEpoch( int microsecondsSinceEpoch, { bool isUtc = false, }); /// Throws an error if the millisecondsSinceEpoch and microsecond components /// are out of range. /// /// Returns the millisecondsSinceEpoch component. static int _validate( int millisecondsSinceEpoch, int microsecond, bool isUtc, ) { if (microsecond < 0 || microsecond > 999) { throw RangeError.range(microsecond, 0, 999, "microsecond"); } if (millisecondsSinceEpoch < -_maxMillisecondsSinceEpoch || millisecondsSinceEpoch > _maxMillisecondsSinceEpoch) { throw RangeError.range( millisecondsSinceEpoch, -_maxMillisecondsSinceEpoch, _maxMillisecondsSinceEpoch, "millisecondsSinceEpoch", ); } if (millisecondsSinceEpoch == _maxMillisecondsSinceEpoch && microsecond != 0) { throw ArgumentError.value( microsecond, "microsecond", "Time including microseconds is outside valid range", ); } // For backwards compatibility with legacy mode. checkNotNullable(isUtc, "isUtc"); return millisecondsSinceEpoch; } /// Whether [other] is a [DateTime] at the same moment and in the /// same time zone (UTC or local). /// /// ```dart /// final dDayUtc = DateTime.utc(1944, 6, 6); /// final dDayLocal = dDayUtc.toLocal(); /// /// // These two dates are at the same moment, but are in different zones. /// assert(dDayUtc != dDayLocal); /// print(dDayUtc != dDayLocal); // true /// ``` /// /// See [isAtSameMomentAs] for a comparison that compares moments in time /// independently of their zones. external bool operator ==(Object other); external int get hashCode; /// Whether this [DateTime] occurs before [other]. /// /// The comparison is independent /// of whether the time is in UTC or in the local time zone. /// /// ```dart /// final now = DateTime.now(); /// final earlier = now.subtract(const Duration(seconds: 5)); /// print(earlier.isBefore(now)); // true /// print(!now.isBefore(now)); // true /// /// // This relation stays the same, even when changing timezones. /// print(earlier.isBefore(now.toUtc())); // true /// print(earlier.toUtc().isBefore(now)); // true /// /// print(!now.toUtc().isBefore(now)); // true /// print(!now.isBefore(now.toUtc())); // true /// ``` external bool isBefore(DateTime other); /// Whether this [DateTime] occurs after [other]. /// /// The comparison is independent /// of whether the time is in UTC or in the local time zone. /// /// ```dart /// final now = DateTime.now(); /// final later = now.add(const Duration(seconds: 5)); /// print(later.isAfter(now)); // true /// print(!now.isBefore(now)); // true /// /// // This relation stays the same, even when changing timezones. /// print(later.isAfter(now.toUtc())); // true /// print(later.toUtc().isAfter(now)); // true /// /// print(!now.toUtc().isAfter(now)); // true /// print(!now.isAfter(now.toUtc())); // true /// ``` external bool isAfter(DateTime other); /// Whether this [DateTime] occurs at the same moment as [other]. /// /// The comparison is independent of whether the time is in UTC or in the local /// time zone. /// /// ```dart /// final now = DateTime.now(); /// final later = now.add(const Duration(seconds: 5)); /// print(!later.isAtSameMomentAs(now)); // true /// print(now.isAtSameMomentAs(now)); // true /// /// // This relation stays the same, even when changing timezones. /// print(!later.isAtSameMomentAs(now.toUtc())); // true /// print(!later.toUtc().isAtSameMomentAs(now)); // true /// /// print(now.toUtc().isAtSameMomentAs(now)); // true /// print(now.isAtSameMomentAs(now.toUtc())); // true /// ``` external bool isAtSameMomentAs(DateTime other); /// Compares this DateTime object to [other], /// returning zero if the values are equal. /// /// A [compareTo] function returns: /// * a negative value if this DateTime [isBefore] [other]. /// * `0` if this DateTime [isAtSameMomentAs] [other], and /// * a positive value otherwise (when this DateTime [isAfter] [other]). /// /// ```dart /// final now = DateTime.now(); /// final future = now.add(const Duration(days: 2)); /// final past = now.subtract(const Duration(days: 2)); /// final newDate = now.toUtc(); /// /// print(now.compareTo(future)); // -1 /// print(now.compareTo(past)); // 1 /// print(now.compareTo(newDate)); // 0 /// ``` external int compareTo(DateTime other); /// Returns this DateTime value in the local time zone. /// /// Returns this [DateTime] if it is already in the local time zone. /// Otherwise this method is equivalent to: /// /// ```dart template:expression /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, /// isUtc: false) /// ``` DateTime toLocal() { if (isUtc) { return _withUtc(isUtc: false); } return this; } /// Returns this DateTime value in the UTC time zone. /// /// Returns this [DateTime] if it is already in UTC. /// Otherwise this method is equivalent to: /// /// ```dart template:expression /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, /// isUtc: true) /// ``` DateTime toUtc() { if (isUtc) return this; return _withUtc(isUtc: true); } external DateTime _withUtc({required bool isUtc}); static String _fourDigits(int n) { int absN = n.abs(); String sign = n < 0 ? "-" : ""; if (absN >= 1000) return "$n"; if (absN >= 100) return "${sign}0$absN"; if (absN >= 10) return "${sign}00$absN"; return "${sign}000$absN"; } static String _sixDigits(int n) { assert(n < -9999 || n > 9999); int absN = n.abs(); String sign = n < 0 ? "-" : "+"; if (absN >= 100000) return "$sign$absN"; return "${sign}0$absN"; } static String _threeDigits(int n) { if (n >= 100) return "${n}"; if (n >= 10) return "0${n}"; return "00${n}"; } static String _twoDigits(int n) { if (n >= 10) return "${n}"; return "0${n}"; } /// Returns a human-readable string for this instance. /// /// The returned string is constructed for the time zone of this instance. /// The `toString()` method provides a simply formatted string. /// It does not support internationalized strings. /// Use the [intl](https://pub.dev/packages/intl) package /// at the pub shared packages repo. /// /// The resulting string can be parsed back using [parse]. String toString() { String y = _fourDigits(year); String m = _twoDigits(month); String d = _twoDigits(day); String h = _twoDigits(hour); String min = _twoDigits(minute); String sec = _twoDigits(second); String ms = _threeDigits(millisecond); String us = microsecond == 0 ? "" : _threeDigits(microsecond); if (isUtc) { return "$y-$m-$d $h:$min:$sec.$ms${us}Z"; } else { return "$y-$m-$d $h:$min:$sec.$ms$us"; } } /// Returns an ISO-8601 full-precision extended format representation. /// /// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and /// `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, /// where: /// /// * `yyyy` is a, possibly negative, four digit representation of the year, /// if the year is in the range -9999 to 9999, /// otherwise it is a signed six digit representation of the year. /// * `MM` is the month in the range 01 to 12, /// * `dd` is the day of the month in the range 01 to 31, /// * `HH` are hours in the range 00 to 23, /// * `mm` are minutes in the range 00 to 59, /// * `ss` are seconds in the range 00 to 59 (no leap seconds), /// * `mmm` are milliseconds in the range 000 to 999, and /// * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals /// 0, then this part is omitted. /// /// The resulting string can be parsed back using [parse]. /// ```dart /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); /// final isoDate = moonLanding.toIso8601String(); /// print(isoDate); // 1969-07-20T20:18:04.000Z /// ``` String toIso8601String() { String y = (year >= -9999 && year <= 9999) ? _fourDigits(year) : _sixDigits(year); String m = _twoDigits(month); String d = _twoDigits(day); String h = _twoDigits(hour); String min = _twoDigits(minute); String sec = _twoDigits(second); String ms = _threeDigits(millisecond); String us = microsecond == 0 ? "" : _threeDigits(microsecond); if (isUtc) { return "$y-$m-${d}T$h:$min:$sec.$ms${us}Z"; } else { return "$y-$m-${d}T$h:$min:$sec.$ms$us"; } } /// Returns a new [DateTime] instance with [duration] added to this [DateTime]. /// /// ```dart /// final today = DateTime.now(); /// final fiftyDaysFromNow = today.add(const Duration(days: 50)); /// ``` /// /// Notice that the duration being added is actually 50 * 24 * 60 * 60 /// seconds. If the resulting `DateTime` has a different daylight saving offset /// than `this`, then the result won't have the same time-of-day as `this`, and /// may not even hit the calendar date 50 days later. /// /// Be careful when working with dates in local time. external DateTime add(Duration duration); /// Returns a new [DateTime] instance with [duration] subtracted from this /// [DateTime]. /// /// ```dart /// final today = DateTime.now(); /// final fiftyDaysAgo = today.subtract(const Duration(days: 50)); /// ``` /// /// Notice that the duration being subtracted is actually 50 * 24 * 60 * 60 /// seconds. If the resulting `DateTime` has a different daylight saving offset /// than `this`, then the result won't have the same time-of-day as `this`, and /// may not even hit the calendar date 50 days earlier. /// /// Be careful when working with dates in local time. external DateTime subtract(Duration duration); /// Returns a [Duration] with the difference when subtracting [other] from /// this [DateTime]. /// /// The returned [Duration] will be negative if [other] occurs after this /// [DateTime]. /// /// ```dart /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); /// final dDay = DateTime.utc(1944, DateTime.june, 6); /// /// final difference = berlinWallFell.difference(dDay); /// print(difference.inDays); // 16592 /// ``` /// /// The difference is measured in seconds and fractions of seconds. /// The difference above counts the number of fractional seconds between /// midnight at the beginning of those dates. /// If the dates above had been in local time, not UTC, then the difference /// between two midnights may not be a multiple of 24 hours due to daylight /// saving differences. /// /// For example, in Australia, similar code using local time instead of UTC: /// /// ```dart /// final berlinWallFell = DateTime(1989, DateTime.november, 9); /// final dDay = DateTime(1944, DateTime.june, 6); /// final difference = berlinWallFell.difference(dDay); /// print(difference.inDays); // 16591 /// assert(difference.inDays == 16592); /// ``` /// will fail because the difference is actually 16591 days and 23 hours, and /// [Duration.inDays] only returns the number of whole days. external Duration difference(DateTime other); external DateTime._internal( int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, bool isUtc, ); external DateTime._now(); /// Returns the [DateTime] corresponding to the given components, or `null` if /// the values are out of range. external static DateTime? _finishParse( int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, bool isUtc, ); /// The number of milliseconds since /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). /// /// This value is independent of the time zone. /// /// This value is at most /// 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. /// In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. external int get millisecondsSinceEpoch; /// The number of microseconds since /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). /// /// This value is independent of the time zone. /// /// This value is at most /// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. /// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. /// /// Note that this value does not always fit into 53 bits (the size of a IEEE /// double). On the web JavaScript platforms, there may be a rounding error /// for DateTime values sufficiently far from the epoch. The year range close /// to the epoch to avoid rounding is approximately 1685..2254. external int get microsecondsSinceEpoch; /// The time zone name. /// /// This value is provided by the operating system and may be an /// abbreviation or a full name. /// /// In the browser or on Unix-like systems commonly returns abbreviations, /// such as "CET" or "CEST". On Windows returns the full name, for example /// "Pacific Standard Time". external String get timeZoneName; /// The time zone offset, which /// is the difference between local time and UTC. /// /// The offset is positive for time zones east of UTC. /// /// Note, that JavaScript, Python and C return the difference between UTC and /// local time. Java, C# and Ruby return the difference between local time and /// UTC. /// /// For example, using local time in San Francisco, United States: /// ```dart /// final dateUS = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); /// print(dateUS); // 2021-11-01 13:18:04.000 /// print(dateUS.timeZoneName); // PDT ( Pacific Daylight Time ) /// print(dateUS.timeZoneOffset.inHours); // -7 /// print(dateUS.timeZoneOffset.inMinutes); // -420 /// ``` /// /// For example, using local time in Canberra, Australia: /// ```dart /// final dateAus = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); /// print(dateAus); // 2021-11-02 07:18:04.000 /// print(dateAus.timeZoneName); // AEDT ( Australian Eastern Daylight Time ) /// print(dateAus.timeZoneOffset.inHours); // 11 /// print(dateAus.timeZoneOffset.inMinutes); // 660 /// ``` external Duration get timeZoneOffset; /// The year. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.year); // 1969 /// ``` external int get year; /// The month `[1..12]`. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.month); // 7 /// assert(moonLanding.month == DateTime.july); /// ``` external int get month; /// The day of the month `[1..31]`. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.day); // 20 /// ``` external int get day; /// The hour of the day, expressed as in a 24-hour clock `[0..23]`. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.hour); // 20 /// ``` external int get hour; /// The minute `[0...59]`. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.minute); // 18 /// ``` external int get minute; /// The second `[0...59]`. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.second); // 4 /// ``` external int get second; /// The millisecond `[0...999]`. /// /// ```dart /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); /// print(date.millisecond); // 234 /// ``` external int get millisecond; /// The microsecond `[0...999]`. /// /// ```dart /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); /// print(date.microsecond); // 567 /// ``` external int get microsecond; /// The day of the week [monday]..[sunday]. /// /// In accordance with ISO 8601 /// a week starts with Monday, which has the value 1. /// /// ```dart /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); /// print(moonLanding.weekday); // 7 /// assert(moonLanding.weekday == DateTime.sunday); /// ``` external int get weekday; /* * date ::= yeardate time_opt timezone_opt * yeardate ::= year colon_opt month colon_opt day * year ::= sign_opt digit{4,6} * colon_opt ::= | ':' * sign ::= '+' | '-' * sign_opt ::= | sign * month ::= digit{2} * day ::= digit{2} * time_opt ::= | (' ' | 'T') hour minutes_opt * minutes_opt ::= | colon_opt digit{2} seconds_opt * seconds_opt ::= | colon_opt digit{2} millis_opt * micros_opt ::= | ('.' | ',') digit+ * timezone_opt ::= | space_opt timezone * space_opt ::= ' ' | * timezone ::= 'z' | 'Z' | sign digit{2} timezonemins_opt * timezonemins_opt ::= | colon_opt digit{2} */ static final RegExp _parseFormat = RegExp( r'^([+-]?\d{4,6})-?(\d\d)-?(\d\d)' // Day part. r'(?:[ T](\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d+))?)?)?' // Time part. r'( ?[zZ]| ?([-+])(\d\d)(?::?(\d\d))?)?)?$', ); // Timezone part. } /// Adds [copyWith] method to [DateTime] objects. @Since("2.19") extension DateTimeCopyWith on DateTime { /// Creates a new [DateTime] from this one by updating individual properties. /// /// The [copyWith] method creates a new [DateTime] object with values /// for the properties [DateTime.year], [DateTime.hour], etc, provided by /// similarly named arguments, or using the existing value of the property /// if no argument, or `null`, is provided. /// /// Example: /// ```dart /// final now = DateTime.now(); /// final sameTimeOnMoonLandingDay = /// now.copyWith(year: 1969, month: 07, day: 20); /// ``` /// /// Like for the [DateTime] and [DateTime.utc] constructors, /// which this operation uses to create the new value, /// property values are allowed to overflow or underflow the range /// of the property (like a [month] outside the 1 to 12 range), /// which can affect the more significant properties /// (for example, a month of 13 will result in the month of January /// of the next year.) /// /// Notice also that if the result is a local-time DateTime, /// seasonal time-zone adjustments (daylight saving) can cause some /// combinations of dates, hours and minutes to not exist, or to exist /// more than once. /// In the former case, a corresponding time in one of the two adjacent time /// zones is used instead. In the latter, one of the two options is chosen. DateTime copyWith({ int? year, int? month, int? day, int? hour, int? minute, int? second, int? millisecond, int? microsecond, bool? isUtc, }) { return ((isUtc ?? this.isUtc) ? DateTime.utc : DateTime.new)( year ?? this.year, month ?? this.month, day ?? this.day, hour ?? this.hour, minute ?? this.minute, second ?? this.second, millisecond ?? this.millisecond, microsecond ?? this.microsecond, ); } } ƒôNN<;JQ@0.4 6J@6 6+"1('$#%%H/= E$79J>'24J +,%*LE$G'C%.92;"DL8:4$B4G 6D5?&=F0N>5KK3-C(81> "!!$> !"!""'2-!*# IL ?  GA PN I" B) )GG06)E?1OM898$ ++ 7GG8FEM9k>23,'#GJ=7'%&$4 ((&+--FGN#./6<3- '  E EGB56) %B0H+/C,F J6 0"+/D,F M6 0"O4 0B@? %$" @!> 5&#D%0+M$+ L$+5$?"@,)E4411 *4$?"9))E1100 )DR"930E;;88 20.&?>L"7:#*'* *:G."B8 %87."B7 "5%$#--$#%-&!!$ 9MB5<'="" !%%+C0 -IDM O3G1<.0B=13!=7-4K12)PHLM."'8 ?9EB7F?FIONB''dart:core/date_time.dartœœÀh³\org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm_shared/lib/date_patch.dartÀQ¡// 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. import "dart:_internal" show patch, unsafeCast; // VM implementation of DateTime. @patch class DateTime { // Natives. // The natives have been moved up here to work around Issue 10401. @pragma("vm:external-name", "DateTime_currentTimeMicros") external static int _getCurrentMicros(); @pragma("vm:external-name", "DateTime_timeZoneName") external static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch); @pragma("vm:external-name", "DateTime_timeZoneOffsetInSeconds") external static int _timeZoneOffsetInSecondsForClampedSeconds( int secondsSinceEpoch, ); static const _MICROSECOND_INDEX = 0; static const _MILLISECOND_INDEX = 1; static const _SECOND_INDEX = 2; static const _MINUTE_INDEX = 3; static const _HOUR_INDEX = 4; static const _DAY_INDEX = 5; static const _WEEKDAY_INDEX = 6; static const _MONTH_INDEX = 7; static const _YEAR_INDEX = 8; /// The value of this DateTime, equal to [microsecondsSinceEpoch]. final int _value; List? __parts; /// Constructor for pre-validated components. DateTime._(this._value, {required this.isUtc}); /// Constructs a new [DateTime] instance with the given value. /// /// If [isUtc] is false, then the date is in the local time zone. DateTime._withValue(this._value, {required this.isUtc}) { _validate(millisecondsSinceEpoch, microsecond, isUtc); } @patch DateTime.fromMillisecondsSinceEpoch( int millisecondsSinceEpoch, { bool isUtc = false, }) : this._withValue( _validateMilliseconds(millisecondsSinceEpoch) * Duration.microsecondsPerMillisecond, isUtc: isUtc, ); @patch DateTime.fromMicrosecondsSinceEpoch( int microsecondsSinceEpoch, { bool isUtc = false, }) : this._withValue(microsecondsSinceEpoch, isUtc: isUtc); @patch DateTime._internal( int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, this.isUtc, ) : _value = _brokenDownDateToValue( year, month, day, hour, minute, second, millisecond, microsecond, isUtc, ) ?? (() => throw ArgumentError( '($year, $month, $day,' ' $hour, $minute, $second, $millisecond, $microsecond)', ))(); static int _validateMilliseconds(int millisecondsSinceEpoch) => RangeError.checkValueInInterval( millisecondsSinceEpoch, -_maxMillisecondsSinceEpoch, _maxMillisecondsSinceEpoch, "millisecondsSinceEpoch", ); @patch DateTime._now() : isUtc = false, _value = _getCurrentMicros(); @patch DateTime._nowUtc() : isUtc = true, _value = _getCurrentMicros(); @patch DateTime _withUtc({required bool isUtc}) { return DateTime._(_value, isUtc: isUtc); } @patch String get timeZoneName { if (isUtc) return "UTC"; return _timeZoneName(microsecondsSinceEpoch); } @patch Duration get timeZoneOffset { if (isUtc) return Duration(); int offsetInSeconds = _timeZoneOffsetInSeconds(microsecondsSinceEpoch); return Duration(seconds: offsetInSeconds); } @patch bool operator ==(Object other) => other is DateTime && _value == other.microsecondsSinceEpoch && isUtc == other.isUtc; @patch int get hashCode => (_value ^ (_value >> 30)) & 0x3FFFFFFF; @patch bool isBefore(DateTime other) => _value < other.microsecondsSinceEpoch; @patch bool isAfter(DateTime other) => _value > other.microsecondsSinceEpoch; @patch bool isAtSameMomentAs(DateTime other) => _value == other.microsecondsSinceEpoch; @patch int compareTo(DateTime other) => _value.compareTo(other.microsecondsSinceEpoch); /// The first list contains the days until each month in non-leap years. The /// second list contains the days in leap years. static const List> _DAYS_UNTIL_MONTH = [ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; static List _computeUpperPart(int localMicros) { const int DAYS_IN_4_YEARS = 4 * 365 + 1; const int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; const int DAYS_IN_400_YEARS = 4 * DAYS_IN_100_YEARS + 1; const int DAYS_1970_TO_2000 = 30 * 365 + 7; const int DAYS_OFFSET = 1000 * DAYS_IN_400_YEARS + 5 * DAYS_IN_400_YEARS - DAYS_1970_TO_2000; const int YEARS_OFFSET = 400000; int resultYear = 0; int resultMonth = 0; int resultDay = 0; // Always round down. final int daysSince1970 = _flooredDivision( localMicros, Duration.microsecondsPerDay, ); int days = daysSince1970; days += DAYS_OFFSET; resultYear = 400 * (days ~/ DAYS_IN_400_YEARS) - YEARS_OFFSET; days = unsafeCast(days.remainder(DAYS_IN_400_YEARS)); days--; int yd1 = days ~/ DAYS_IN_100_YEARS; days = unsafeCast(days.remainder(DAYS_IN_100_YEARS)); resultYear += 100 * yd1; days++; int yd2 = days ~/ DAYS_IN_4_YEARS; days = unsafeCast(days.remainder(DAYS_IN_4_YEARS)); resultYear += 4 * yd2; days--; int yd3 = days ~/ 365; days = unsafeCast(days.remainder(365)); resultYear += yd3; bool isLeap = (yd1 == 0 || yd2 != 0) && yd3 == 0; if (isLeap) days++; List daysUntilMonth = _DAYS_UNTIL_MONTH[isLeap ? 1 : 0]; for ( resultMonth = 12; daysUntilMonth[resultMonth - 1] > days; resultMonth-- ) { // Do nothing. } resultDay = days - daysUntilMonth[resultMonth - 1] + 1; int resultMicrosecond = localMicros % Duration.microsecondsPerMillisecond; int resultMillisecond = _flooredDivision(localMicros, Duration.microsecondsPerMillisecond) % Duration.millisecondsPerSecond; int resultSecond = _flooredDivision(localMicros, Duration.microsecondsPerSecond) % Duration.secondsPerMinute; int resultMinute = _flooredDivision( localMicros, Duration.microsecondsPerMinute, ); resultMinute %= Duration.minutesPerHour; int resultHour = _flooredDivision( localMicros, Duration.microsecondsPerHour, ); resultHour %= Duration.hoursPerDay; // In accordance with ISO 8601 a week // starts with Monday. Monday has the value 1 up to Sunday with 7. // 1970-1-1 was a Thursday. int resultWeekday = ((daysSince1970 + DateTime.thursday - DateTime.monday) % DateTime.daysPerWeek) + DateTime.monday; List list = List.filled(_YEAR_INDEX + 1, 0); list[_MICROSECOND_INDEX] = resultMicrosecond; list[_MILLISECOND_INDEX] = resultMillisecond; list[_SECOND_INDEX] = resultSecond; list[_MINUTE_INDEX] = resultMinute; list[_HOUR_INDEX] = resultHour; list[_DAY_INDEX] = resultDay; list[_WEEKDAY_INDEX] = resultWeekday; list[_MONTH_INDEX] = resultMonth; list[_YEAR_INDEX] = resultYear; return list; } List get _parts { return __parts ??= _computeUpperPart(_localDateInUtcMicros); } @patch DateTime add(Duration duration) { return DateTime._withValue(_value + duration.inMicroseconds, isUtc: isUtc); } @patch DateTime subtract(Duration duration) { return DateTime._withValue(_value - duration.inMicroseconds, isUtc: isUtc); } @patch Duration difference(DateTime other) { return Duration(microseconds: _value - other.microsecondsSinceEpoch); } @patch int get millisecondsSinceEpoch => _flooredDivision(_value, Duration.microsecondsPerMillisecond); @patch int get microsecondsSinceEpoch => _value; @patch int get microsecond => _parts[_MICROSECOND_INDEX]; @patch int get millisecond => _parts[_MILLISECOND_INDEX]; @patch int get second => _parts[_SECOND_INDEX]; @patch int get minute => _parts[_MINUTE_INDEX]; @patch int get hour => _parts[_HOUR_INDEX]; @patch int get day => _parts[_DAY_INDEX]; @patch int get weekday => _parts[_WEEKDAY_INDEX]; @patch int get month => _parts[_MONTH_INDEX]; @patch int get year => _parts[_YEAR_INDEX]; /// Returns the amount of microseconds in UTC that represent the same values /// as this [DateTime]. /// /// Say `t` is the result of this function, then /// * `this.year == new DateTime.fromMicrosecondsSinceEpoch(t, true).year`, /// * `this.month == new DateTime.fromMicrosecondsSinceEpoch(t, true).month`, /// * `this.day == new DateTime.fromMicrosecondsSinceEpoch(t, true).day`, /// * `this.hour == new DateTime.fromMicrosecondsSinceEpoch(t, true).hour`, /// * ... /// /// Daylight savings is computed as if the date was computed in [1970..2037]. /// If this [DateTime] lies outside this range then a year with similar /// properties (leap year, weekdays) is used instead. int get _localDateInUtcMicros { int micros = _value; if (isUtc) return micros; int offset = _timeZoneOffsetInSeconds(micros) * Duration.microsecondsPerSecond; return micros + offset; } static int _flooredDivision(int a, int b) { return (a - (a < 0 ? b - 1 : 0)) ~/ b; } // Returns the days since 1970 for the start of the given [year]. // [year] may be before epoch. static int _dayFromYear(int year) { return 365 * (year - 1970) + _flooredDivision(year - 1969, 4) - _flooredDivision(year - 1901, 100) + _flooredDivision(year - 1601, 400); } static bool _isLeapYear(int y) { // (y % 16 == 0) matches multiples of 400, and is faster than % 400. return (y % 4 == 0) && ((y % 16 == 0) || (y % 100 != 0)); } /// Converts the given broken down date to microseconds. static int? _brokenDownDateToValue( int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, bool isUtc, ) { // Simplify calculations by working with zero-based month. --month; // Deal with under and overflow. if (month >= 12) { year += month ~/ 12; month = month % 12; } else if (month < 0) { int realMonth = month % 12; year += (month - realMonth) ~/ 12; month = realMonth; } // First compute the seconds in UTC, independent of the [isUtc] flag. If // necessary we will add the time-zone offset later on. int days = day - 1; days += _DAYS_UNTIL_MONTH[_isLeapYear(year) ? 1 : 0][month]; days += _dayFromYear(year); int microsecondsSinceEpoch = days * Duration.microsecondsPerDay + hour * Duration.microsecondsPerHour + minute * Duration.microsecondsPerMinute + second * Duration.microsecondsPerSecond + millisecond * Duration.microsecondsPerMillisecond + microsecond; if (!isUtc) { // Since [_timeZoneOffsetInSeconds] will crash if the input is far out of // the valid range we do a preliminary test that weeds out values that can // not become valid even with timezone adjustments. // The timezone adjustment is always less than a day, so adding a security // margin of one day should be enough. if (microsecondsSinceEpoch.abs() > _maxMillisecondsSinceEpoch * Duration.microsecondsPerMillisecond + Duration.microsecondsPerDay) { return null; } microsecondsSinceEpoch -= _toLocalTimeOffset(microsecondsSinceEpoch); } if (microsecondsSinceEpoch.abs() > _maxMillisecondsSinceEpoch * Duration.microsecondsPerMillisecond) { return null; } return microsecondsSinceEpoch; } @patch static DateTime? _finishParse( int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, bool isUtc, ) { final value = _brokenDownDateToValue( year, month, day, hour, minute, second, millisecond, microsecond, isUtc, ); if (value == null) return null; return DateTime._withValue(value, isUtc: isUtc); } static int _weekDay(y) { // 1/1/1970 was a Thursday. return (_dayFromYear(y) + 4) % 7; } /// Returns a year in the range 2008-2035 matching /// * leap year, and /// * week day of first day. /// /// Leap seconds are ignored. /// Adapted from V8's date implementation. See ECMA 262 - 15.9.1.9. static int _equivalentYear(int year) { // Returns year y so that _weekDay(y) == _weekDay(year). // _weekDay returns the week day (in range 0 - 6). // 1/1/1956 was a Sunday (i.e. weekday 0). 1956 was a leap-year. // 1/1/1967 was a Sunday (i.e. weekday 0). // Without leap years a subsequent year has a week day + 1 (for example // 1/1/1968 was a Monday). With leap-years it jumps over one week day // (e.g. 1/1/1957 was a Tuesday). // After 12 years the weekdays have advanced by 12 days + 3 leap days = // 15 days. 15 % 7 = 1. So after 12 years the week day has always // (now independently of leap-years) advanced by one. // weekDay * 12 gives thus a year starting with the wanted weekDay. int recentYear = (_isLeapYear(year) ? 1956 : 1967) + (_weekDay(year) * 12); // Close to the year 2008 the calendar cycles every 4 * 7 years (4 for the // leap years, 7 for the weekdays). // Find the year in the range 2008..2037 that is equivalent mod 28. return 2008 + (recentYear - 2008) % 28; } /// Returns the UTC year for the corresponding [secondsSinceEpoch]. /// It is relatively fast for values in the range 0 to year 2098. /// /// Code is adapted from V8. static int _yearsFromSecondsSinceEpoch(int secondsSinceEpoch) { const int DAYS_IN_4_YEARS = 4 * 365 + 1; const int DAYS_IN_100_YEARS = 25 * DAYS_IN_4_YEARS - 1; const int DAYS_YEAR_2098 = DAYS_IN_100_YEARS + 6 * DAYS_IN_4_YEARS; int days = secondsSinceEpoch ~/ Duration.secondsPerDay; if (days > 0 && days < DAYS_YEAR_2098) { // According to V8 this fast case works for dates from 1970 to 2099. return 1970 + (4 * days + 2) ~/ DAYS_IN_4_YEARS; } int micros = secondsSinceEpoch * Duration.microsecondsPerSecond; return _computeUpperPart(micros)[_YEAR_INDEX]; } /// Returns a date in seconds that is equivalent to the given /// date in microseconds [microsecondsSinceEpoch]. An equivalent /// date has the same fields (`month`, `day`, etc.) as the given /// date, but the `year` is in the range [1901..2038]. /// /// * The time since the beginning of the year is the same. /// * If the given date is in a leap year then the returned /// seconds are in a leap year, too. /// * The week day of given date is the same as the one for the /// returned date. static int _equivalentSeconds(int microsecondsSinceEpoch) { const int CUT_OFF_SECONDS = 0x7FFFFFFF; int secondsSinceEpoch = _flooredDivision( microsecondsSinceEpoch, Duration.microsecondsPerSecond, ); if (secondsSinceEpoch.abs() > CUT_OFF_SECONDS) { int year = _yearsFromSecondsSinceEpoch(secondsSinceEpoch); int days = _dayFromYear(year); int equivalentYear = _equivalentYear(year); int equivalentDays = _dayFromYear(equivalentYear); int diffDays = equivalentDays - days; secondsSinceEpoch += diffDays * Duration.secondsPerDay; } return secondsSinceEpoch; } static int _timeZoneOffsetInSeconds(int microsecondsSinceEpoch) { int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); return _timeZoneOffsetInSecondsForClampedSeconds(equivalentSeconds); } static String _timeZoneName(int microsecondsSinceEpoch) { int equivalentSeconds = _equivalentSeconds(microsecondsSinceEpoch); return _timeZoneNameForClampedSeconds(equivalentSeconds); } /// Finds the local time corresponding to a UTC date and time. /// /// The [microsecondsSinceEpoch] represents a particular /// calendar date and clock time in UTC. /// This methods returns a (usually different) point in time /// where the local time had the same calendar date and clock /// time (if such a time exists, otherwise it finds the "best" /// substitute). /// /// A valid result is a point in time `microsecondsSinceEpoch - offset` /// where the local time zone offset is `+offset`. /// /// In some cases there are two valid results, due to a time zone /// change setting the clock back (for example exiting from daylight /// saving time). In that case, we return the *earliest* valid result. /// /// In some cases there are no valid results, due to a time zone /// change setting the clock forward (for example entering daylight /// saving time). In that case, we return the time which would have /// been correct in the earlier time zone (so asking for 2:30 AM /// when clocks move directly from 2:00 to 3:00 will give the /// time that *would have been* 2:30 in the earlier time zone, /// which is now 3:30 in the local time zone). /// /// Returns the point in time as a number of microseconds since epoch. static int _toLocalTimeOffset(int microsecondsSinceEpoch) { // Argument is the UTC time corresponding to the desired // calendar date/wall time. // We now need to find an UTC time where the difference // from `microsecondsSinceEpoch` is the same as the // local time offset at that time. That is, we want to // find `adjustment` in microseconds such that: // // _timeZoneOffsetInSeconds(microsecondsSinceEpoch - offset) // * Duration.microsecondsPerSecond == offset // // Such an offset might not exist, if that wall time // is skipped when a time zone change moves the clock forwards. // In that case we pick a time after the switch which would be // correct in the previous time zone. // Also, there might be more than one solution if a time zone // change moves the clock backwards and the same wall clock // time occurs twice in the same day. // In that case we pick the one in the time zone prior to // the switch. // Start with the time zone at the current microseconds since // epoch. It's within one day of the real time we're looking for. int offset = _timeZoneOffsetInSeconds(microsecondsSinceEpoch) * Duration.microsecondsPerSecond; // If offset is 0 (we're right around the UTC+0, and) // we have found one solution. if (offset != 0) { // If not, try to find an actual solution in the time zone // we just discovered. int offset2 = _timeZoneOffsetInSeconds(microsecondsSinceEpoch - offset) * Duration.microsecondsPerSecond; if (offset2 != offset) { // Also not a solution. We have found a second time zone // within the same day. We assume that's all there are. // Try again with the new time zone. int offset3 = _timeZoneOffsetInSeconds(microsecondsSinceEpoch - offset2) * Duration.microsecondsPerSecond; // Either offset3 is a solution (equal to offset2), // or we have found two different time zones and no solution. // In the latter case we choose the lower offset (latter time). return (offset2 <= offset3 ? offset2 : offset3); } // We have found one solution and one time zone. offset = offset2; } // Try to see if there is an earlier time zone which also // has a solution. // Pretends time zone changes are always at most two hours. // (Double daylight saving happened, fx, in part of Canada in 1988). int offset4 = _timeZoneOffsetInSeconds( microsecondsSinceEpoch - offset - 2 * Duration.microsecondsPerHour, ) * Duration.microsecondsPerSecond; if (offset4 > offset) { // The time zone at the earlier time had a greater // offset, so it's possible that the desired wall clock // occurs in that time zone too. if (offset4 == offset + 2 * Duration.microsecondsPerHour) { // A second and earlier solution, so use that. return offset4; } // The time zone differs one hour earlier, but not by one // hour, so check again in that time zone. int offset5 = _timeZoneOffsetInSeconds(microsecondsSinceEpoch - offset4) * Duration.microsecondsPerSecond; if (offset5 == offset4) { // Found a second solution earlier than the first solution, so use that. return offset4; } } // Did not find a solution in the earlier time // zone, so just use the original result. return offset; } } ‚hNN<0"E<+7PBA''"" #! E02AD<; '"92  '">  "&$EB' %$"  A C -- 2 "L/ $0 > J I +. #6O35==8-<=0N%0#C? )? '= 16B .<OM(H#)&-'$(*G A$;22(($"*&$A $P )P (J $E , 5 5 + + ' % - ) 'O3NPLN PJ8"K.+D!&!+-,#I>;& ? %")M<A !-.22<PQ:Q-)M-L'L# ! *  $5 &5 F)=7E/LJ&LF:HPO(H,FDB->)B>,.&5A%29,>DHIA;+?@AJ5DGICFFC@A1I>= <8;4B79DC*B@*>BF;(:#AF*A@-I,@I"N (9>'B7@1G* Q3.dart:core-patch/date_patch.dartÀeÀh³Eorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/double.dart¤A// 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"; /// A double-precision floating point number. /// /// Representation of Dart doubles containing double specific constants /// and operations and specializations of operations inherited from /// [num]. Dart doubles are 64-bit floating-point numbers as specified in the /// IEEE 754 standard. /// /// The [double] type is contagious. Operations on [double]s return /// [double] results. /// /// It is a compile-time error for a class to attempt to extend or implement /// double. /// /// **See also:** /// * [num] the super class for [double]. /// * [Built-in number types](https://dart.dev/language/built-in-types#numbers) /// * [Number representation](https://dart.dev/resources/language/number-representation) abstract final class double extends num { static const double nan = 0.0 / 0.0; static const double infinity = 1.0 / 0.0; static const double negativeInfinity = -infinity; static const double minPositive = 5e-324; static const double maxFinite = 1.7976931348623157e+308; double remainder(num other); double operator +(num other); double operator -(num other); double operator *(num other); double operator %(num other); double operator /(num other); int operator ~/(num other); double operator -(); double abs(); /// The sign of the double's numerical value. /// /// Returns -1.0 if the value is less than zero, /// +1.0 if the value is greater than zero, /// and the value itself if it is -0.0, 0.0 or NaN. double get sign; /// Returns the integer closest to this number. /// /// Rounds away from zero when there is no closest integer: /// `(3.5).round() == 4` and `(-3.5).round() == -4`. /// /// Throws an [UnsupportedError] if this number is not finite /// (NaN or an infinity). /// ```dart /// print(3.0.round()); // 3 /// print(3.25.round()); // 3 /// print(3.5.round()); // 4 /// print(3.75.round()); // 4 /// print((-3.5).round()); // -4 /// ``` int round(); /// Returns the greatest integer no greater than this number. /// /// Rounds the number towards negative infinity. /// /// Throws an [UnsupportedError] if this number is not finite /// (NaN or infinity). /// ```dart /// print(1.99999.floor()); // 1 /// print(2.0.floor()); // 2 /// print(2.99999.floor()); // 2 /// print((-1.99999).floor()); // -2 /// print((-2.0).floor()); // -2 /// print((-2.00001).floor()); // -3 /// ``` int floor(); /// Returns the least integer that is not smaller than this number. /// /// Rounds the number towards infinity. /// /// Throws an [UnsupportedError] if this number is not finite /// (NaN or an infinity). /// ```dart /// print(1.99999.ceil()); // 2 /// print(2.0.ceil()); // 2 /// print(2.00001.ceil()); // 3 /// print((-1.99999).ceil()); // -1 /// print((-2.0).ceil()); // -2 /// print((-2.00001).ceil()); // -2 /// ``` int ceil(); /// Returns the integer obtained by discarding any fractional /// part of this number. /// /// Rounds the number towards zero. /// /// Throws an [UnsupportedError] if this number is not finite /// (NaN or an infinity). /// ```dart /// print(2.00001.truncate()); // 2 /// print(1.99999.truncate()); // 1 /// print(0.5.truncate()); // 0 /// print((-0.5).truncate()); // 0 /// print((-1.5).truncate()); // -1 /// print((-2.5).truncate()); // -2 /// ``` int truncate(); /// Returns the integer double value closest to `this`. /// /// Rounds away from zero when there is no closest integer: /// `(3.5).roundToDouble() == 4` and `(-3.5).roundToDouble() == -4`. /// /// If this is already an integer valued double, including `-0.0`, or it is not /// a finite value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. /// This means that for a value `d` in the range `-0.5 < d < 0.0`, /// the result is `-0.0`. /// ```dart /// print(3.0.roundToDouble()); // 3.0 /// print(3.25.roundToDouble()); // 3.0 /// print(3.5.roundToDouble()); // 4.0 /// print(3.75.roundToDouble()); // 4.0 /// print((-3.5).roundToDouble()); // -4.0 /// ``` double roundToDouble(); /// Returns the greatest integer double value no greater than `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is not /// a finite value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. /// ```dart /// print(1.99999.floorToDouble()); // 1.0 /// print(2.0.floorToDouble()); // 2.0 /// print(2.99999.floorToDouble()); // 2.0 /// print((-1.99999).floorToDouble()); // -2.0 /// print((-2.0).floorToDouble()); // -2.0 /// print((-2.00001).floorToDouble()); // -3.0 /// ``` double floorToDouble(); /// Returns the least integer double value no smaller than `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is not /// a finite value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. /// ```dart /// print(1.99999.ceilToDouble()); // 2.0 /// print(2.0.ceilToDouble()); // 2.0 /// print(2.00001.ceilToDouble()); // 3.0 /// print((-1.99999).ceilToDouble()); // -1.0 /// print((-2.0).ceilToDouble()); // -2.0 /// print((-2.00001).ceilToDouble()); // -2.0 /// ``` double ceilToDouble(); /// Returns the integer double value obtained by discarding any fractional /// digits from `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is not /// a finite value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and /// in the range `0.0 < d < 1.0` it will return 0.0. /// ```dart /// print(2.5.truncateToDouble()); // 2.0 /// print(2.00001.truncateToDouble()); // 2.0 /// print(1.99999.truncateToDouble()); // 1.0 /// print(0.5.truncateToDouble()); // 0.0 /// print((-0.5).truncateToDouble()); // -0.0 /// print((-1.5).truncateToDouble()); // -1.0 /// print((-2.5).truncateToDouble()); // -2.0 /// ``` double truncateToDouble(); /// Provide a representation of this [double] value. /// /// The representation is a number literal such that the closest double value /// to the representation's mathematical value is this [double]. /// /// Returns "NaN" for the Not-a-Number value. /// Returns "Infinity" and "-Infinity" for positive and negative Infinity. /// Returns "-0.0" for negative zero. /// /// For all doubles, `d`, converting to a string and parsing the string back /// gives the same value again: `d == double.parse(d.toString())` (except when /// `d` is NaN). String toString(); /// Parse [source] as a double literal and return its value. /// /// Accepts an optional sign (`+` or `-`) followed by either the characters /// "Infinity", the characters "NaN" or a floating-point representation. /// A floating-point representation is composed of a mantissa and an optional /// exponent part. The mantissa is either a decimal point (`.`) followed by a /// sequence of (decimal) digits, or a sequence of digits /// optionally followed by a decimal point and optionally more digits. The /// (optional) exponent part consists of the character "e" or "E", an optional /// sign, and one or more digits. /// The [source] must not be `null`. /// /// Leading and trailing whitespace is ignored. /// /// Throws a [FormatException] if the [source] string is not /// a valid double literal. /// /// Rather than throwing and immediately catching the [FormatException], /// instead use [tryParse] to handle a potential parsing error. /// /// Examples of accepted strings: /// ``` /// "3.14" /// " 3.14 \xA0" /// "0." /// ".0" /// "-1.e3" /// "1234E+7" /// "+.12e-9" /// "-NaN" /// ``` external static double parse(String source); /// Parse [source] as a double literal and return its value. /// /// Like [parse], except that this function returns `null` for invalid inputs /// instead of throwing. /// /// Example: /// ```dart /// var value = double.tryParse('3.14'); // 3.14 /// value = double.tryParse(' 3.14 \xA0'); // 3.14 /// value = double.tryParse('0.'); // 0.0 /// value = double.tryParse('.0'); // 0.0 /// value = double.tryParse('-1.e3'); // -1000.0 /// value = double.tryParse('1234E+7'); // 12340000000.0 /// value = double.tryParse('+.12e-9'); // 1.2e-10 /// value = double.tryParse('-NaN'); // NaN /// value = double.tryParse('0xFF'); // null /// value = double.tryParse(double.infinity.toString()); // Infinity /// ``` external static double? tryParse(String source); } �NN<.HDNDM *PY*',4,;     03.62>8@  # @3@##'#' F*@""&"& @&@&&"%&& :>HR8KPE)*)*- HR8KC-)-1-1 ER8KE,(,0,0 MR8KI7,00,000 7PC0M(OQ?NKPP= MAX_EXACT_DOUBLE) return null; exponent += exponentDelta; digitsSeen = true; } else if (c == _DOT && exponentDelta == 0) { exponentDelta = -1; } else if ((c | 0x20) == _e) { i++; if (i == end) return null; final int? expPart = int._tryParseSmi(str, i, end); if (expPart == null) return null; exponent += expPart; break; } else { return null; } } if (!digitsSeen) return null; // No digits. if (exponent == 0) return sign * doubleValue; const P10 = POWERS_OF_TEN; // From shared library if (exponent < 0) { int negExponent = -exponent; if (negExponent >= P10.length) return null; return sign * (doubleValue / P10[negExponent]); } if (exponent >= P10.length) return null; return sign * (doubleValue * P10[exponent]); } static double? _parse(String str) { int len = str.length; final strbase = str as _StringBase; int start = strbase._firstNonWhitespace(); if (start == len) return null; // All whitespace. int end = strbase._lastNonWhitespace() + 1; assert(start < end); var result = _tryParseDouble(str, start, end); if (result != null) return result; return _nativeParse(str, start, end); } @patch static double parse(String source) { var result = _parse(source); if (result == null) { throw FormatException("Invalid double", source); } return result; } @patch static double? tryParse(String source) => _parse(source); } xNN< .HC"#$ AD+%)F'--(+(!I2M<:#4% #<*026#26-1&(/603'* '!7 <!dart:core-patch/double_patch.dartÀeÀh³Gorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/duration.dartº¦// Copyright (c) 2011, 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"; /// A span of time, such as 27 days, 4 hours, 12 minutes, and 3 seconds. /// /// A `Duration` represents a difference from one point in time to another. The /// duration may be "negative" if the difference is from a later time to an /// earlier. /// /// Durations are context independent. For example, a duration of 2 days is /// always 48 hours, even when it is added to a `DateTime` just when the /// time zone is about to make a daylight-savings switch. (See [DateTime.add]). /// /// Despite the same name, a `Duration` object does not implement "Durations" /// as specified by ISO 8601. In particular, a duration object does not keep /// track of the individually provided members (such as "days" or "hours"), but /// only uses these arguments to compute the length of the corresponding time /// interval. /// /// To create a new `Duration` object, use this class's single constructor /// giving the appropriate arguments: /// ```dart /// const fastestMarathon = Duration(hours: 2, minutes: 3, seconds: 2); /// ``` /// The [Duration] represents a single number of microseconds, /// which is the sum of all the individual arguments to the constructor. /// /// Properties can access that single number in different ways. /// For example the [inMinutes] gives the number of whole minutes /// in the total duration, which includes the minutes that were provided /// as "hours" to the constructor, and can be larger than 59. /// /// ```dart /// const fastestMarathon = Duration(hours: 2, minutes: 0, seconds: 35); /// print(fastestMarathon.inDays); // 0 /// print(fastestMarathon.inHours); // 2 /// print(fastestMarathon.inMinutes); // 120 /// print(fastestMarathon.inSeconds); // 7235 /// print(fastestMarathon.inMilliseconds); // 7235000 /// ``` /// The duration can be negative, in which case /// all the properties derived from the duration are also non-positive. /// ```dart /// const overDayAgo = Duration(days: -1, hours: -10); /// print(overDayAgo.inDays); // -1 /// print(overDayAgo.inHours); // -34 /// print(overDayAgo.inMinutes); // -2040 /// ``` /// Use one of the properties, such as [inDays], /// to retrieve the integer value of the `Duration` in the specified time unit. /// Note that the returned value is rounded down. /// For example, /// ```dart /// const aLongWeekend = Duration(hours: 88); /// print(aLongWeekend.inDays); // 3 /// ``` /// This class provides a collection of arithmetic /// and comparison operators, /// plus a set of constants useful for converting time units. /// ```dart /// const firstHalf = Duration(minutes: 45); // 00:45:00.000000 /// const secondHalf = Duration(minutes: 45); // 00:45:00.000000 /// const overTime = Duration(minutes: 30); // 00:30:00.000000 /// final maxGameTime = firstHalf + secondHalf + overTime; /// print(maxGameTime.inMinutes); // 120 /// /// // The duration of the firstHalf and secondHalf is the same, returns 0. /// var result = firstHalf.compareTo(secondHalf); /// print(result); // 0 /// /// // Duration of overTime is shorter than firstHalf, returns < 0. /// result = overTime.compareTo(firstHalf); /// print(result); // < 0 /// /// // Duration of secondHalf is longer than overTime, returns > 0. /// result = secondHalf.compareTo(overTime); /// print(result); // > 0 /// ``` /// /// **See also:** /// * [DateTime] to represent a point in time. /// * [Stopwatch] to measure time-spans. class Duration implements Comparable { /// The number of microseconds per millisecond. static const int microsecondsPerMillisecond = 1000; /// The number of milliseconds per second. static const int millisecondsPerSecond = 1000; /// The number of seconds per minute. /// /// Notice that some minutes of official clock time might /// differ in length because of leap seconds. /// The [Duration] and [DateTime] classes ignore leap seconds /// and consider all minutes to have 60 seconds. static const int secondsPerMinute = 60; /// The number of minutes per hour. static const int minutesPerHour = 60; /// The number of hours per day. /// /// Notice that some days may differ in length because /// of time zone changes due to daylight saving. /// The [Duration] class is time zone agnostic and /// considers all days to have 24 hours. static const int hoursPerDay = 24; /// The number of microseconds per second. static const int microsecondsPerSecond = microsecondsPerMillisecond * millisecondsPerSecond; /// The number of microseconds per minute. static const int microsecondsPerMinute = microsecondsPerSecond * secondsPerMinute; /// The number of microseconds per hour. static const int microsecondsPerHour = microsecondsPerMinute * minutesPerHour; /// The number of microseconds per day. static const int microsecondsPerDay = microsecondsPerHour * hoursPerDay; /// The number of milliseconds per minute. static const int millisecondsPerMinute = millisecondsPerSecond * secondsPerMinute; /// The number of milliseconds per hour. static const int millisecondsPerHour = millisecondsPerMinute * minutesPerHour; /// The number of milliseconds per day. static const int millisecondsPerDay = millisecondsPerHour * hoursPerDay; /// The number of seconds per hour. static const int secondsPerHour = secondsPerMinute * minutesPerHour; /// The number of seconds per day. static const int secondsPerDay = secondsPerHour * hoursPerDay; /// The number of minutes per day. static const int minutesPerDay = minutesPerHour * hoursPerDay; /// An empty duration, representing zero time. static const Duration zero = Duration(seconds: 0); /// The total microseconds of this [Duration] object. final int _duration; /// Creates a new [Duration] object whose value /// is the sum of all individual parts. /// /// Individual parts can be larger than the number of those /// parts in the next larger unit. /// For example, [hours] can be greater than 23. /// If this happens, the value overflows into the next larger /// unit, so 26 [hours] is the same as 2 [hours] and /// one more [days]. /// Likewise, values can be negative, in which case they /// underflow and subtract from the next larger unit. /// /// If the total number of microseconds cannot be represented /// as an integer value, the number of microseconds might overflow /// and be truncated to a smaller number of bits, /// or it might lose precision. /// /// All arguments are 0 by default. /// ```dart /// const duration = Duration(days: 1, hours: 8, minutes: 56, seconds: 59, /// milliseconds: 30, microseconds: 10); /// print(duration); // 32:56:59.030010 /// ``` const Duration({ int days = 0, int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0, int microseconds = 0, }) : this._microseconds( microseconds + microsecondsPerMillisecond * milliseconds + microsecondsPerSecond * seconds + microsecondsPerMinute * minutes + microsecondsPerHour * hours + microsecondsPerDay * days, ); // Fast path internal direct constructor to avoids the optional arguments // and [_microseconds] recomputation. // The `+ 0` prevents -0.0 on the web, if the incoming duration happens to be -0.0. const Duration._microseconds(int duration) : _duration = duration + 0; /// Adds this Duration and [other] and /// returns the sum as a new Duration object. Duration operator +(Duration other) { return Duration._microseconds(_duration + other._duration); } /// Subtracts [other] from this Duration and /// returns the difference as a new Duration object. Duration operator -(Duration other) { return Duration._microseconds(_duration - other._duration); } /// Multiplies this Duration by the given [factor] and returns the result /// as a new Duration object. /// /// Note that when [factor] is a double, and the duration is greater than /// 53 bits, precision is lost because of double-precision arithmetic. Duration operator *(num factor) { return Duration._microseconds((_duration * factor).round()); } /// Divides this Duration by the given [quotient] and returns the truncated /// result as a new Duration object. /// /// The [quotient] must not be `0`. Duration operator ~/(int quotient) { // By doing the check here instead of relying on "~/" below we get the // exception even with dart2js. if (quotient == 0) throw IntegerDivisionByZeroException(); return Duration._microseconds(_duration ~/ quotient); } /// Whether this [Duration] is shorter than [other]. bool operator <(Duration other) => this._duration < other._duration; /// Whether this [Duration] is longer than [other]. bool operator >(Duration other) => this._duration > other._duration; /// Whether this [Duration] is shorter than or equal to [other]. bool operator <=(Duration other) => this._duration <= other._duration; /// Whether this [Duration] is longer than or equal to [other]. bool operator >=(Duration other) => this._duration >= other._duration; /// The number of entire days spanned by this [Duration]. /// /// For example, a duration of four days and three hours /// has four entire days. /// ```dart /// const duration = Duration(days: 4, hours: 3); /// print(duration.inDays); // 4 /// ``` int get inDays => _duration ~/ Duration.microsecondsPerDay; /// The number of entire hours spanned by this [Duration]. /// /// The returned value can be greater than 23. /// For example, a duration of four days and three hours /// has 99 entire hours. /// ```dart /// const duration = Duration(days: 4, hours: 3); /// print(duration.inHours); // 99 /// ``` int get inHours => _duration ~/ Duration.microsecondsPerHour; /// The number of whole minutes spanned by this [Duration]. /// /// The returned value can be greater than 59. /// For example, a duration of three hours and 12 minutes /// has 192 minutes. /// ```dart /// const duration = Duration(hours: 3, minutes: 12); /// print(duration.inMinutes); // 192 /// ``` int get inMinutes => _duration ~/ Duration.microsecondsPerMinute; /// The number of whole seconds spanned by this [Duration]. /// /// The returned value can be greater than 59. /// For example, a duration of three minutes and 12 seconds /// has 192 seconds. /// ```dart /// const duration = Duration(minutes: 3, seconds: 12); /// print(duration.inSeconds); // 192 /// ``` int get inSeconds => _duration ~/ Duration.microsecondsPerSecond; /// The number of whole milliseconds spanned by this [Duration]. /// /// The returned value can be greater than 999. /// For example, a duration of three seconds and 125 milliseconds /// has 3125 milliseconds. /// ```dart /// const duration = Duration(seconds: 3, milliseconds: 125); /// print(duration.inMilliseconds); // 3125 /// ``` int get inMilliseconds => _duration ~/ Duration.microsecondsPerMillisecond; /// The number of whole microseconds spanned by this [Duration]. /// /// The returned value can be greater than 999999. /// For example, a duration of three seconds, 125 milliseconds and /// 369 microseconds has 3125369 microseconds. /// ```dart /// const duration = Duration(seconds: 3, milliseconds: 125, /// microseconds: 369); /// print(duration.inMicroseconds); // 3125369 /// ``` int get inMicroseconds => _duration; /// Whether this [Duration] has the same length as [other]. /// /// Durations have the same length if they have the same number /// of microseconds, as reported by [inMicroseconds]. bool operator ==(Object other) => other is Duration && _duration == other.inMicroseconds; int get hashCode => _duration.hashCode; /// Compares this [Duration] to [other], returning zero if the values are equal. /// /// Returns a negative integer if this [Duration] is shorter than /// [other], or a positive integer if it is longer. /// /// A negative [Duration] is always considered shorter than a positive one. /// /// It is always the case that `duration1.compareTo(duration2) < 0` iff /// `(someDate + duration1).compareTo(someDate + duration2) < 0`. int compareTo(Duration other) => _duration.compareTo(other._duration); /// Returns a string representation of this [Duration]. /// /// Returns a string with hours, minutes, seconds, and microseconds, in the /// following format: `H:MM:SS.mmmmmm`. For example, /// ```dart /// var d = const Duration(days: 1, hours: 1, minutes: 33, microseconds: 500); /// print(d.toString()); // 25:33:00.000500 /// /// d = const Duration(hours: 1, minutes: 10, microseconds: 500); /// print(d.toString()); // 1:10:00.000500 /// ``` String toString() { var microseconds = inMicroseconds; var sign = ""; var negative = microseconds < 0; var hours = microseconds ~/ microsecondsPerHour; microseconds = microseconds.remainder(microsecondsPerHour); // Correcting for being negative after first division, instead of before, // to avoid negating min-int, -(2^31-1), of a native int64. if (negative) { hours = 0 - hours; // Not using `-hours` to avoid creating -0.0 on web. microseconds = 0 - microseconds; sign = "-"; } var minutes = microseconds ~/ microsecondsPerMinute; microseconds = microseconds.remainder(microsecondsPerMinute); var minutesPadding = minutes < 10 ? "0" : ""; var seconds = microseconds ~/ microsecondsPerSecond; microseconds = microseconds.remainder(microsecondsPerSecond); var secondsPadding = seconds < 10 ? "0" : ""; // Padding up to six digits for microseconds. var microsecondsText = microseconds.toString().padLeft(6, "0"); return "$sign$hours:" "$minutesPadding$minutes:" "$secondsPadding$seconds." "$microsecondsText"; } /// Whether this [Duration] is negative. /// /// A negative [Duration] represents the difference from a later time to an /// earlier time. bool get isNegative => _duration < 0; /// Creates a new [Duration] representing the absolute length of this /// [Duration]. /// /// The returned [Duration] has the same length as this one, but is always /// positive where possible. Duration abs() => Duration._microseconds(_duration.abs()); /// Creates a new [Duration] with the opposite direction of this [Duration]. /// /// The returned [Duration] has the same length as this one, but will have the /// opposite sign (as reported by [isNegative]) as this one where possible. // Using subtraction helps dart2js avoid negative zeros. Duration operator -() => Duration._microseconds(0 - _duration); } �ˆNN<IPL LIPNMPNK& H?I@BI> I()-.60H 7$&*1P2 .%3> @A?;)L2D,D-/)126-1(<0@3*&(#935+%-+:-+0+Q*K-+0+Q*K&G%A%A1582*>%3@7;8@E4"&M-* 9//+( L(VI)0(@/7(@L LI$AN'&'K$?:7G6GCIBI<;4# >=1;4% @>1<8( D>1>:( DC2D@. NC5E1?1 '>B8$>*SD6NJDI:N7Q.D- '%5@N@N'9B29B22D##+N(HM=OQN;Bdart:core/duration.dartÀfIÀfKÀh³Corg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/enum.dartš// Copyright (c) 2021, 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"; /// An enumerated value. /// /// This class is implemented by all types and values /// introduced using an `enum` declaration. /// Non-platform classes cannot extend or mix in this class. /// Concrete classes cannot implement the interface. /// /// The identifier used to name an `enum` value is available as a [String], /// via the [EnumName.name] extension property on the `enum` value. @Since("2.14") abstract interface class Enum { /// A numeric identifier for the enumerated value. /// /// The values of a single enumeration are numbered /// consecutively from zero to one less than the /// number of values. /// This is also the index of the value in the /// enumerated type's static `values` list. int get index; /// The value's "name". /// /// The name of a value is a string containing the /// source identifier used to declare the value. /// /// The name occurs in the [toString] of the /// enum value, after the enum class name and a `.`. /// It is exposed by then [EnumName.name] extension getter, /// which is an extension to allow `enum` declarations to have /// an element named `name` without causing a name conflict. /// /// Given an enum declaration like /// ```dart /// enum MyEnum { /// value1, /// value2 /// } /// ``` /// the `toString` method of that class may be implemented /// as /// ```dart /// String toString() => "MyEnum.$_name"; /// ``` String get _name; /// Compares two enum values by their [index]. /// /// A generic [Comparator] function for enum types which /// orders enum values by their [index] value, which corresponds /// to the source order of the enum element declarations in /// the `enum` declaration. /// /// Example: /// ```dart /// enum Season { spring, summer, autumn, winter } /// /// void main() { /// var relationByIndex = /// Enum.compareByIndex(Season.spring, Season.summer); // < 0 /// relationByIndex = /// Enum.compareByIndex(Season.summer, Season.spring); // > 0 /// relationByIndex = /// Enum.compareByIndex(Season.spring, Season.winter); // < 0 /// relationByIndex = /// Enum.compareByIndex(Season.winter, Season.spring); // > 0 /// } /// ``` @Since("2.15") static int compareByIndex(T value1, T value2) => value1.index - value2.index; /// Compares enum values by name. /// /// The [EnumName.name] of an enum value is a string /// representing the source name used to declare that enum value. /// /// This [Comparator] compares two enum values by comparing their names, /// and can be used to sort enum values by their names. /// The comparison uses [String.compareTo], and is therefore case sensitive. /// /// Example: /// ```dart /// enum Season { spring, summer, autumn, winter } /// /// void main() { /// var seasons = [...Season.values]..sort(Enum.compareByName); /// print(seasons); /// // [Season.autumn, Season.spring, Season.summer, Season.winter] /// } /// ``` @Since("2.15") static int compareByName(T value1, T value2) => value1.name.compareTo(value2.name); } /// Superclass of all enum class implementations. @pragma('dyn-module:language-impl:extendable') @pragma('dyn-module:language-impl:callable') abstract class _Enum implements Enum { // See http://dartbug.com/51657 for discussion of dart2js pragma. @pragma('dart2js:noElision') @pragma('wasm:entry-point') final int index; @pragma('dyn-module:language-impl:callable') @pragma('wasm:entry-point') final String _name; @pragma('dyn-module:language-impl:callable') const _Enum(this.index, this._name); /// The result of [toString]. /// /// Each enum class can override this method to provide the /// string returned by toString, rather than overriding toString itself. @Since("2.19") @pragma('dyn-module:language-impl:can-be-overridden') String _enumToString(); @override @pragma('dyn-module:language-impl:can-be-overridden') String toString() => _enumToString(); } /// Access to the name of an enum value. /// /// This method is declared as an extension method /// instead of an instance method in order to allow /// enum values to have the name `name`. @Since("2.15") extension EnumName on Enum { /// The name of the enum value. /// /// The name is a string containing the source identifier used /// to declare the enum value. /// /// For example, given a declaration like: /// ```dart /// enum MyEnum { /// value1, /// value2 /// } /// ``` /// the result of `MyEnum.value1.name` is the string `"value1"`. String get name => _name; } /// Access enum values by name. /// /// Extensions on a collection of enum values, /// intended for use on the `values` list of an enum type, /// which allows looking up a value by its name. /// /// Since enum classes are expected to be relatively small, /// lookup of [byName] is performed by linearly iterating through the values /// and comparing their name to the provided name. /// If a more efficient lookup is needed, perhaps because the lookup operation /// happens very often, consider building a map instead using [asNameMap]: /// ```dart /// static myEnumNameMap = MyEnum.values.asNameMap(); /// ``` /// and then use that for lookups. @Since("2.15") extension EnumByName on Iterable { /// Finds the enum value in this list with name [name]. /// /// Goes through this collection looking for an enum with /// name [name], as reported by [EnumName.name]. /// Returns the first value with the given name. Such a value must be found. T byName(String name) { for (var value in this) { if (value._name == name) return value; } throw ArgumentError.value(name, "name", "No enum value with that name"); } /// Creates a map from the names of enum values to the values. /// /// The collection that this method is called on is expected to have /// enums with distinct names, like the `values` list of an enum class. /// Only one value for each name can occur in the created map, /// so if two or more enum values have the same name (either being the /// same value, or being values of different enum type), at most one of /// them will be represented in the returned map. Map asNameMap() => { for (var value in this) value._name: value, }; } €ÄNN<6,=5LD 5631.53/7>A?% = . 1;C>5FFFF C#$7DK:O5DH B*2/-'D//' >K8 8()34)"A!- C /;1(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(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; } ‚˜NN<;G@04F-<9A3I,4B(3&@FD58FN:F=1A$3$7K297</?AEF8J=;.$8NC/= *$K=9FEB'?'H"JDQCKDMJ&0EG%.>"AJ!EI=)K E5EGKL $"2&OFND*L:4B@# HKJL8O%$&>0;7=I&:$A<#I,:/K);;/K&:BFDF?AKEGHDFDKDFJAA0EJ10#0HD?K;JJQ;.* DDE.?GLN"'D)F3'JJ-LH)!:E==4 4FM"?K1.:.RJAM4#&1IRJMID.K;:=PC0C.N(# ()!*-'2>FA?H(K9DB51PD+'):=JKCE& ) FGEC)@A/F0!.DOQ%2; 6":82@0(&?2)&dart:core/errors.dartÀeÀh³œœÀdÙWorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/errors_patch.dart½^// 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 "core_patch.dart"; @patch class Error { @patch static String _objectToString(Object object) { return Object._toString(object); } @patch static String _stringToSafeString(String string) { return json.encode(string); } @patch StackTrace? get stackTrace => _stackTrace; @pragma("vm:entry-point") StackTrace? _stackTrace; @patch @pragma("vm:external-name", "Error_throwWithStackTrace") external static Never _throw(Object error, StackTrace stackTrace); } class _AssertionError extends Error implements AssertionError { @pragma("vm:entry-point") _AssertionError._create( this._failedAssertion, this._url, this._line, this._column, this.message, ); // AssertionError_throwNew in errors.cc fishes the assertion source code // out of the script. It expects a Dart stack frame from class // _AssertionError. Thus we need a Dart stub that calls the native code. @pragma("vm:entry-point", "call") static Never _throwNew( int assertionStart, int assertionEnd, Object? message, ) { _doThrowNew(assertionStart, assertionEnd, message); } @pragma("vm:entry-point", "call") @pragma("vm:external-name", "AssertionError_throwNewSource") external static Never _throwNewSource( String failedAssertion, String? scriptUrl, int line, int column, Object? message, ); @pragma("vm:external-name", "AssertionError_throwNew") external static Never _doThrowNew( int assertionStart, int assertionEnd, Object? message, ); String get _messageString { final msg = message; if (msg == null) return "is not true."; if (msg is String) return msg; return Error.safeToString(msg); } String toString() { if (_url == null) { if (message == null) return _failedAssertion.trim(); return "'${_failedAssertion.trim()}': $_messageString"; } var columnInfo = ""; if (_column > 0) { // Only add column information if it is valid. columnInfo = " pos $_column"; } return "'$_url': Failed assertion: line $_line$columnInfo: " "'$_failedAssertion': $_messageString"; } final String _failedAssertion; final String? _url; final int _line; final int _column; final Object? message; } class _TypeError extends Error implements TypeError { @pragma("vm:entry-point") _TypeError._create(this._url, this._line, this._column, this._message); @pragma("vm:entry-point", "call") @pragma("vm:external-name", "TypeError_throwNew") external static Never _throwNew( int location, Object srcValue, _Type dstType, String dstName, ); String toString() => _message; final String? _url; final int? _line; final int? _column; final String _message; } @pragma("vm:entry-point") class _InternalError { @pragma("vm:entry-point") const _InternalError(this._msg); String toString() => "InternalError: '${_msg}'"; final String _msg; } @patch @pragma("vm:entry-point") class UnsupportedError { static Never _throwNew(String msg) { throw UnsupportedError(msg); } } @patch @pragma("vm:entry-point") class StateError { @pragma("vm:entry-point") static Never _throwNew(String msg) { throw StateError(msg); } } @patch class NoSuchMethodError { final Object? _receiver; final Invocation _invocation; @patch factory NoSuchMethodError.withInvocation( Object? receiver, Invocation invocation, ) = NoSuchMethodError._withInvocation; NoSuchMethodError._withInvocation(this._receiver, this._invocation); static Never _throwNewInvocation(Object? receiver, Invocation invocation) { throw NoSuchMethodError.withInvocation(receiver, invocation); } // The compiler emits a call to _throwNew when it cannot resolve a static // method at compile time. The receiver is actually the literal class of the // unresolved method. @pragma("vm:entry-point", "call") static Never _throwNew( Object receiver, String memberName, int invocationType, int typeArgumentsLength, Object? typeArguments, List? arguments, List? argumentNames, ) { throw NoSuchMethodError._withType( receiver, memberName, invocationType, typeArgumentsLength, typeArguments, arguments, argumentNames, ); } // Helper to build a map of named arguments. static Map _NamedArgumentsMap( List arguments, List argumentNames, ) { Map namedArguments = Map(); int numPositionalArguments = arguments.length - argumentNames.length; for (int i = 0; i < argumentNames.length; i++) { final argValue = arguments[numPositionalArguments + i]; namedArguments[Symbol(argumentNames[i])] = argValue; } return namedArguments; } // Constructor called from Exceptions::ThrowByType(kNoSuchMethod) and from // _throwNew above, taking a TypeArguments object rather than an unpacked list // of types, as well as a list of all arguments and a list of names, rather // than a separate list of positional arguments and a map of named arguments. @pragma("vm:entry-point") NoSuchMethodError._withType( this._receiver, String memberName, int invocationType, int typeArgumentsLength, // Needed with all-dynamic (null) typeArguments. Object? typeArguments, List? arguments, List? argumentNames, ) : this._invocation = _InvocationMirror._withType( Symbol(memberName), invocationType, _InvocationMirror._unpackTypeArguments( typeArguments, typeArgumentsLength, ), argumentNames != null ? arguments!.sublist(0, arguments.length - argumentNames.length) : arguments, argumentNames != null ? _NamedArgumentsMap(arguments!, argumentNames) : null, ); @pragma("vm:external-name", "NoSuchMethodError_existingMethodSignature") external static String? _existingMethodSignature( Object? receiver, String methodName, int invocationType, ); @patch String toString() { final localInvocation = _invocation; if (localInvocation is _InvocationMirror) { var internalName = localInvocation.memberName as internal.Symbol; String memberName = internal.Symbol.computeUnmangledName(internalName); var level = (localInvocation._type >> _InvocationMirror._LEVEL_SHIFT) & _InvocationMirror._LEVEL_MASK; var kind = localInvocation._type & _InvocationMirror._KIND_MASK; if (kind == _InvocationMirror._LOCAL_VAR) { return "NoSuchMethodError: Cannot assign to final variable '$memberName'"; } StringBuffer? typeArgumentsBuf = null; final typeArguments = localInvocation.typeArguments; if ((typeArguments != null) && (typeArguments.length > 0)) { final argsBuf = StringBuffer(); argsBuf.write("<"); for (int i = 0; i < typeArguments.length; i++) { if (i > 0) { argsBuf.write(", "); } argsBuf.write(Error.safeToString(typeArguments[i])); } argsBuf.write(">"); typeArgumentsBuf = argsBuf; } StringBuffer argumentsBuf = StringBuffer(); var positionalArguments = localInvocation.positionalArguments; int argumentCount = 0; if (positionalArguments != null) { for (; argumentCount < positionalArguments.length; argumentCount++) { if (argumentCount > 0) { argumentsBuf.write(", "); } argumentsBuf.write( Error.safeToString(positionalArguments[argumentCount]), ); } } var namedArguments = localInvocation.namedArguments; if (namedArguments != null) { namedArguments.forEach((Symbol key, value) { if (argumentCount > 0) { argumentsBuf.write(", "); } var internalName = key as internal.Symbol; argumentsBuf.write( internal.Symbol.computeUnmangledName(internalName), ); argumentsBuf.write(": "); argumentsBuf.write(Error.safeToString(value)); argumentCount++; }); } String? existingSig = _existingMethodSignature( _receiver, memberName, localInvocation._type, ); String argsMsg = existingSig != null ? " with matching arguments" : ""; String kindBuf = "function"; if (kind >= 0 && kind < 5) { kindBuf = (const [ "method", "getter", "setter", "getter or setter", "variable", ])[kind]; } StringBuffer msgBuf = StringBuffer("NoSuchMethodError: "); bool isTypeCall = false; switch (level) { case _InvocationMirror._DYNAMIC: { if (_receiver == null) { if (existingSig != null) { msgBuf.writeln( "The null object does not have a $kindBuf " "'$memberName'$argsMsg.", ); } else { msgBuf.writeln( "The $kindBuf '$memberName' was called on null.", ); } } else { if (_receiver is _Closure) { msgBuf.writeln( "Closure call with mismatched arguments: " "function '$memberName'", ); } else if (_receiver is _Type && memberName == "call") { isTypeCall = true; String name = _receiver.toString(); msgBuf.writeln( "Attempted to use type '$name' as a function. " "Since types do not define a method 'call', this is not " "possible. Did you intend to call the $name constructor and " "forget the 'new' operator?", ); } else { msgBuf.writeln( "Class '${_receiver.runtimeType}' has no instance " "$kindBuf '$memberName'$argsMsg.", ); } } break; } case _InvocationMirror._SUPER: { msgBuf.writeln( "Super class of class '${_receiver.runtimeType}' has " "no instance $kindBuf '$memberName'$argsMsg.", ); memberName = "super.$memberName"; break; } case _InvocationMirror._STATIC: { msgBuf.writeln( "No static $kindBuf '$memberName'$argsMsg " "declared in class '$_receiver'.", ); break; } case _InvocationMirror._CONSTRUCTOR: { msgBuf.writeln( "No constructor '$memberName'$argsMsg declared " "in class '$_receiver'.", ); memberName = "new $memberName"; break; } case _InvocationMirror._TOP_LEVEL: { msgBuf.writeln( "No top-level $kindBuf '$memberName'$argsMsg " "declared.", ); break; } } if (level == _InvocationMirror._TOP_LEVEL) { msgBuf.writeln("Receiver: top-level"); } else { msgBuf.writeln("Receiver: ${Error.safeToString(_receiver)}"); } if (kind == _InvocationMirror._METHOD) { String m = isTypeCall ? "$_receiver" : "$memberName"; msgBuf.write("Tried calling: $m"); if (typeArgumentsBuf != null) { msgBuf.write(typeArgumentsBuf); } msgBuf.write("($argumentsBuf)"); } else if (argumentCount == 0) { msgBuf.write("Tried calling: $memberName"); } else if (kind == _InvocationMirror._SETTER) { msgBuf.write("Tried calling: $memberName$argumentsBuf"); } else { msgBuf.write("Tried calling: $memberName = $argumentsBuf"); } if (existingSig != null) { msgBuf.write("\nFound: $memberName$existingSig"); } return msgBuf.toString(); } return _toStringPlain(_receiver, localInvocation); } /// Creates a string representation of an invocation. /// /// Used for situations where there is no extra information available /// about the failed invocation than the [Invocation] object and receiver, /// which includes errors created using [NoSuchMethodError.withInvocation]. static String _toStringPlain(Object? receiver, Invocation invocation) { var name = _symbolToString(invocation.memberName); var receiverType = "${receiver.runtimeType}"; if (invocation.isAccessor) { return "NoSuchMethodError: $receiverType has no $name " "${invocation.isGetter ? "getter" : "setter"}"; } var buffer = StringBuffer("NoSuchMethodError")..write(": "); buffer.write("$receiverType has no $name method accepting arguments "); var separator = ""; if (invocation.typeArguments.isNotEmpty) { buffer.write("<"); for (var type in invocation.typeArguments) { buffer ..write(separator) ..write("_"); separator = ", "; } buffer.write(">"); separator = ""; } buffer.write("("); for (var argument in invocation.positionalArguments) { buffer ..write(separator) ..write("_"); separator = ", "; } if (invocation.namedArguments.isNotEmpty) { buffer ..write(separator) ..write("{"); separator = ""; for (var name in invocation.namedArguments.keys) { buffer ..write(separator) ..write(_symbolToString(name)) ..write(": _"); separator = ","; } buffer.write("}"); } buffer.write(")"); return buffer.toString(); } static String _symbolToString(Symbol symbol) { if (symbol is internal.Symbol) { return internal.Symbol.computeUnmangledName(symbol); } return "$symbol"; } } @pragma("vm:entry-point") class _CompileTimeError extends Error { final String _errorMsg; _CompileTimeError(this._errorMsg); String toString() => _errorMsg; } /// Used by Fasta to report a runtime error when a final field with an /// initializer is also initialized in a generative constructor. /// /// Note: in strong mode, this is a compile-time error and this class becomes /// obsolete. class _DuplicatedFieldInitializerError extends Error { final String _name; _DuplicatedFieldInitializerError(this._name); toString() => "Error: field '$_name' is already initialized."; } // Implementations needed to implement the `_stackTrace` member added // in the @patch class of [Error]. @patch class OutOfMemoryError { StackTrace? get _stackTrace => throw UnsupportedError('OutOfMemoryError._stackTrace'); void set _stackTrace(StackTrace? _) { throw UnsupportedError('OutOfMemoryError._stackTrace'); } } @patch class StackOverflowError { StackTrace? get _stackTrace => throw UnsupportedError('StackOverflowError._stackTrace'); void set _stackTrace(StackTrace? _) { throw UnsupportedError('StackOverflowError._stackTrace'); } } @patch class IntegerDivisionByZeroException { StackTrace? get _stackTrace => throw UnsupportedError('IntegerDivisionByZeroException._stackTrace'); void set _stackTrace(StackTrace? _) { throw UnsupportedError('IntegerDivisionByZeroException._stackTrace'); } } ‚ NN< 1% 5  - ;E@KAK$8$?)9%,#$;>5$A0!6J$4#!#3'!'  ,)GNBLO$'/2BJ5>;MQNPN60 M< K4 )0HNF)G2S-;C(9! ? $2E)N#& D ;$5#& 5@ $9 6 N##A) %) >, D+ =,G#4 BLP0 F5 ' E=. ( :1 - ?(, + = 3/F/>+(* )'46AD!: 78HMNJ72!>:AL/3; 0 9)1%;(%"GAN70AF#!>(<!@(>'!L(J!dart:core-patch/errors_patch.dartÀeÀh³Iorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/exceptions.dart™x// 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"; // Exceptions are thrown either by the VM or from Dart code. /// A marker interface implemented by all core library exceptions. /// /// An [Exception] is intended to convey information to the user about a failure, /// so that the error can be addressed programmatically. It is intended to be /// caught, and it should contain useful data fields. /// /// Creating instances of [Exception] directly with `Exception("message")` /// is discouraged in library code since it doesn't give users a precise /// type they can catch. It may be reasonable to use instances of this /// class in tests or during development. /// /// For failures that are not intended to be caught, use [Error] /// and its subclasses. @pragma('flutter:keep-to-string-in-subtypes') abstract interface class Exception { factory Exception([message]) => _Exception(message); } /// Default implementation of [Exception] which carries a message. class _Exception implements Exception { final dynamic message; _Exception([this.message]); String toString() { Object? message = this.message; if (message == null) return "Exception"; return "Exception: $message"; } } /// Exception thrown when a string or some other data does not have an expected /// format and cannot be parsed or processed. @pragma("vm:entry-point") class FormatException implements Exception { /// A message describing the format error. final String message; /// The actual source input which caused the error. /// /// This is usually a [String], but can be other types too. /// If it is a string, parts of it may be included in the [toString] message. /// /// The source is `null` if omitted or unknown. final dynamic source; /// The offset in [source] where the error was detected. /// /// A zero-based offset into the source that marks the format error causing /// this exception to be created. If `source` is a string, this should be a /// string index in the range `0 <= offset <= source.length`. /// /// If input is a string, the [toString] method may represent this offset as /// a line and character position. The offset should be inside the string, /// or at the end of the string. /// /// May be omitted. If present, [source] should also be present if possible. final int? offset; /// Creates a new `FormatException` with an optional error [message]. /// /// Optionally also supply the actual [source] with the incorrect format, /// and the [offset] in the format where a problem was detected. @pragma("vm:entry-point") const FormatException([this.message = "", this.source, this.offset]); /// Returns a description of the format exception. /// /// The description always contains the [message]. /// /// If [source] is present and is a string, the description will contain /// (at least a part of) the source. /// If [offset] is also provided, the part of the source included will /// contain that offset, and the offset will be marked. /// /// If the source is a string and it contains a line break before offset, /// only the line containing offset will be included, and its line number /// will also be part of the description. Line and character offsets are /// 1-based. String toString() { String report = "FormatException"; Object? message = this.message; if (message != null && "" != message) { report = "$report: $message"; } int? offset = this.offset; Object? source = this.source; if (source is String) { if (offset != null && (offset < 0 || offset > source.length)) { offset = null; } // Source is string and offset is null or valid. if (offset == null) { if (source.length > 78) { source = source.substring(0, 75) + "..."; } return "$report\n$source"; } int lineNum = 1; int lineStart = 0; bool previousCharWasCR = false; for (int i = 0; i < offset; i++) { int char = source.codeUnitAt(i); if (char == 0x0a) { if (lineStart != i || !previousCharWasCR) { lineNum++; } lineStart = i + 1; previousCharWasCR = false; } else if (char == 0x0d) { lineNum++; lineStart = i + 1; previousCharWasCR = true; } } if (lineNum > 1) { report += " (at line $lineNum, character ${offset - lineStart + 1})\n"; } else { report += " (at character ${offset + 1})\n"; } int lineEnd = source.length; for (int i = offset; i < source.length; i++) { int char = source.codeUnitAt(i); if (char == 0x0a || char == 0x0d) { lineEnd = i; break; } } int length = lineEnd - lineStart; int start = lineStart; int end = lineEnd; String prefix = ""; String postfix = ""; if (length > 78) { // Can't show entire line. Try to anchor at the nearest end, if // one is within reach. int index = offset - lineStart; if (index < 75) { end = start + 75; postfix = "..."; } else if (end - offset < 75) { start = end - 75; prefix = "..."; } else { // Neither end is near, just pick an area around the offset. start = offset - 36; end = offset + 36; prefix = postfix = "..."; } } String slice = source.substring(start, end); int markOffset = offset - start + prefix.length; return "$report$prefix$slice$postfix\n${" " * markOffset}^\n"; } else { // The source is not a string. if (offset != null) { report += " (at offset $offset)"; } return report; } } } // Exception thrown when doing integer division with a zero divisor. // TODO(30743): Should be removed, and division by zero should just throw an // [UnsupportedError]. @Deprecated("Use UnsupportedError instead") class IntegerDivisionByZeroException implements Exception, UnsupportedError { String? get message => "Division resulted in non-finite value"; StackTrace? get stackTrace => null; @pragma("vm:entry-point") const IntegerDivisionByZeroException(); String toString() => "IntegerDivisionByZeroException"; } €¸NN<=CRN6KIG*A.%7C($-"P.--6>P2;NN@OM#OHLCH55K'I:LLK'$,$"F7"4 #&))6 %#$ P5#5), (H ((G$ 37E %*EM,NB&*9dart:core/exceptions.dartÀeÀh³ÀdÙGorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/function.dart è// Copyright (c) 2011, 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"; /// A function value. /// /// The `Function` class is a supertype of all *function types*, and contains /// no values itself. All objects that implement `Function` /// have a function type as their runtime type. /// /// The `Function` type does not carry information about the /// parameter signatures or return type of a function. /// To express a more precise function type, use the function type syntax, /// which is the `Function` keyword followed by a parameter list, /// or a type argument list and a parameter list, and which can also have /// an optional return type. /// /// The function type syntax mirrors the definition of a function, /// with the function name replaced by the word "Function". /// /// Example: /// ```dart /// String numberToString(int n) => "$n"; /// String Function(int n) fun = numberToString; // Type annotation /// assert(fun is String Function(int)); // Type check. /// List functions = [fun]; // Type argument. /// ``` /// The type `String Function(int)` is the type of a function /// that takes one positional `int` argument and returns a `String`. /// /// Example with generic function type: /// ```dart /// T id(T value) => value; /// X Function(X) anotherId = id; // Parameter name may be omitted. /// int Function(int) intId = id; /// ``` /// /// A function type can be used anywhere a type is allowed, /// and is often used for functions taking other functions, "callbacks", /// as arguments. /// /// ```dart /// void doSomething(String Function(int) callback) { /// print(callback(1)); /// } /// ``` /// /// A function type has all the members declared by [Object], /// since function types are subtypes of [Object]. /// /// A function type also has a `call` method with a signature /// that has the same function type as the function type itself. /// Calling the `call` method behaves just as calling the function. /// This is mainly used to conditionally call a nullable function value. /// ```dart /// String Function(int) fun = (n) => "$n"; /// String Function(int) fun2 = fun.call; // Valid. /// print(fun2.call(1)); // Prints "1". /// /// String Function(int)? maybeFun = Random().nextBool() ? fun : null; /// print(maybeFun?.call(1)); // Prints "1" or "null". /// ``` /// /// The [Function] type has a number of special features which are not visible /// in this `class` declaration. /// /// The `Function` type itself allows any function to be assigned to it, /// since it is a supertype of any function type, /// but does not say how the function can be called. /// /// However, a value with the static type `Function` *can* still be called /// like a function. /// ```dart /// Function f = (int x) => "$x"; /// print(f(1)); // Prints "1". /// /// f("not", "one", "int"); // Throws! No static warning. /// ``` /// Such an invocation is a *dynamic* invocation, /// precisely as if the function value had been statically typed as [dynamic], /// and is precisely as unsafe as any other dynamic invocation. /// Checks will be performed at run-time to ensure that the argument /// list matches the function's parameters, and if not the call will /// fail with an [Error]. /// There is no static type checking for such a call, any argument list /// is accepted and checked at runtime. /// /// Like every function type has a `call` method with its own function type, /// the `Function` type has a special `call` member /// which acts as if it is a method with a function type of `Function` /// (which is not a method signature which can be expressed in normal /// Dart code). /// ```dart /// Function fun = (int x) => "$x"; /// /// var fun2 = fun.call; // Inferred type of `fun2` is `Function`. /// /// print(fun2.call(1)); // Prints "1"; /// /// Function? maybeFun = Random().nextBool() ? fun : null; /// print(maybeFun?.call(1)); // Prints "1" or "null". /// ``` abstract final class Function { /// Dynamically call [function] with the specified arguments. /// /// Acts the same as dynamically calling [function] with /// positional arguments corresponding to the elements of [positionalArguments] /// and named arguments corresponding to the elements of [namedArguments]. /// /// This includes giving the same errors if [function] /// expects different parameters. /// /// Example: /// ```dart /// void printWineDetails(int vintage, {String? country, String? name}) { /// print('Name: $name, Country: $country, Vintage: $vintage'); /// } /// /// void main() { /// Function.apply( /// printWineDetails, [2018], {#country: 'USA', #name: 'Dominus Estate'}); /// } /// /// // Output of the example is: /// // Name: Dominus Estate, Country: USA, Vintage: 2018 /// ``` /// /// If [positionalArguments] is null, it's considered an empty list. /// If [namedArguments] is omitted or null, it is considered an empty map. /// /// ```dart /// void helloWorld() { /// print('Hello world!'); /// } /// /// void main() { /// Function.apply(helloWorld, null); /// } /// // Output of the example is: /// // Hello world! /// ``` external static apply( Function function, List? positionalArguments, [ Map? namedArguments, ]); /// A hash code value that is compatible with `operator==`. int get hashCode; /// Test whether another object is equal to this function. /// /// Function objects are only equal to other function objects (an object /// satisfying `object is Function`), and never to non-function objects. /// /// Some function objects are considered equal by `==` because they are /// recognized as representing the "same function": /// /// - It is the same object. Static and top-level functions are compile time /// constants when used as values, so referring to the same function twice /// always yields the same object, as does referring to a local function /// declaration twice in the same scope where it was declared. /// /// ```dart /// void main() { /// assert(identical(print, print)); /// int add(int x, int y) => x + y; /// assert(identical(add, add)); /// } /// ``` /// /// - The functions are same member method extracted from the same object. /// Repeatedly extracting ("tearing off") the same instance method of the /// same object to a function value gives equal, but not necessarily /// identical, function values. /// /// ```dart /// var o = Object(); /// assert(o.toString == o.toString); /// ``` /// /// - Instantiations of equal generic functions with the *same* types /// yields equal results. /// /// ```dart /// T id(T value) => value; /// assert(id == id); /// ``` /// /// (If the function is a constant and the type arguments are known at /// compile-time, the results may also be identical.) /// /// Different evaluations of function literals are not guaranteed or required /// to give rise to identical or equal function objects. For example: /// /// ```dart /// var functions = []; /// for (var i = 0; i < 2; i++) { /// functions.add((x) => x); /// } /// print(identical(functions[0], functions[1])); // 'true' or 'false' /// print(functions[0] == functions[1]); // 'true' or 'false' /// ``` /// /// If the distinct values are identical, they are always equal. /// /// If the function values are equal, they are guaranteed to behave /// indistinguishably for all arguments. /// /// If two functions values behave differently, they are never equal or /// identical. /// /// The reason to not require a specific equality or identity of the values /// of a function expression is to allow compiler optimizations. If a /// function expression does not depend on surrounding variables, an /// implementation can safely be shared between multiple evaluations. For /// example: /// /// ```dart /// List ints = [6, 2, 5, 1, 4, 3]; /// ints.sort((x, y) => x - y); /// print(ints); /// ``` /// /// A compiler can convert the closure `(x, y) => x - y` into a top-level /// function. bool operator ==(Object other); } €èNN<N<0=7KBJC< *D8D>E( G'3>ADI ,4(G7O!I25K " :2O@EEH(M4GF $C(;7 @;RM9$LDS#; GM*# **>=KKJ6OOMC+*' MNI$* H#$ K:PH$$!I@ CF+JNHGL+" L"dart:core/function.dartYorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/function_patch.dart…Ú// 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 "core_patch.dart"; @patch class Function { // TODO(regis): Pass type arguments to generic functions. Wait for API spec. @pragma("vm:external-name", "Function_apply") external static _apply(List arguments, List names); @patch static apply( Function function, List? positionalArguments, [ Map? namedArguments, ]) { final int numPositionalArguments = 1 + // Function is first implicit argument. (positionalArguments?.length ?? 0); final int numNamedArguments = namedArguments?.length ?? 0; final int numArguments = numPositionalArguments + numNamedArguments; final List arguments = List.filled(numArguments, null); arguments[0] = function; if (positionalArguments != null) { arguments.setRange(1, numPositionalArguments, positionalArguments); } final List names = List.filled(numNamedArguments, null); int argumentIndex = numPositionalArguments; int nameIndex = 0; if (numNamedArguments > 0) { namedArguments?.forEach((name, value) { arguments[argumentIndex++] = value; names[nameIndex++] = internal.Symbol.getName(name as internal.Symbol); }); } return _apply(arguments, names); } } *NN<O0H **'4,?IE'JF0!.,O %#dart:core-patch/function_patch.dartÀeÀh³Borg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/int.dartÀBo// 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"; /// An integer number. /// /// The default implementation of `int` is 64-bit two's complement integers /// with operations that wrap to that range on overflow. /// /// **Note:** When compiling to JavaScript, integers are restricted to values /// that can be represented exactly by double-precision floating point values. /// The available integer values include all integers between -2^53 and 2^53, /// and some integers with larger magnitude. That includes some integers larger /// than 2^63. /// The behavior of the operators and methods in the [int] /// class therefore sometimes differs between the Dart VM and Dart code /// compiled to JavaScript. For example, the bitwise operators truncate their /// operands to 32-bit integers when compiled to JavaScript. /// /// Classes cannot extend, implement, or mix in `int`. /// /// **See also:** /// * [num] the super class for [int]. /// * [Built-in number types](https://dart.dev/language/built-in-types#numbers) /// * [Number representation](https://dart.dev/resources/language/number-representation) abstract final class int extends num { /// Integer value for [name] in the compilation configuration environment. /// /// The compilation configuration environment is provided by the /// surrounding tools which are compiling or running the Dart program. /// The environment is a mapping from a set of string keys to their associated /// string value. /// The string value, or lack of a value, associated with a [name] /// must be consistent across all calls to [String.fromEnvironment], /// `int.fromEnvironment`, [bool.fromEnvironment] and [bool.hasEnvironment] /// in a single program. /// The string values can be directly accessed using /// [String.fromEnvironment]. /// /// This constructor looks up the string value for [name], /// then attempts to parse it as an integer, using the same syntax rules as /// [int.parse]/[int.tryParse]. That is, it accepts decimal numerals /// and hexadecimal numerals with a `0x` prefix, and it accepts a leading /// minus sign. /// If there is no value associated with [name] in the compilation /// configuration environment, or if the associated string value cannot /// be parsed as an integer, the value of the constructor invocation /// is the [defaultValue] integer, which defaults to the integer zero. /// /// The result is effectively the same as that of: /// ```dart template:expression /// int.tryParse(const String.fromEnvironment(name, defaultValue: "")) /// ?? defaultValue /// ``` /// except that the constructor invocation can be a constant value. /// /// Example: /// ```dart /// const defaultPort = int.fromEnvironment("defaultPort", defaultValue: 80); /// ``` /// In order to check whether a value is there at all, use /// [bool.hasEnvironment]. Example: /// ```dart /// const int? maybeDeclared = bool.hasEnvironment("defaultPort") /// ? int.fromEnvironment("defaultPort") /// : null; /// ``` /// /// The string value, or lack of a value, associated with a [name] /// must be consistent across all calls to [String.fromEnvironment], /// `int.fromEnvironment`, [bool.fromEnvironment] and [bool.hasEnvironment] /// in a single program. /// /// This constructor is only guaranteed to work when invoked as `const`. /// It may work as a non-constant invocation on some platforms which /// have access to compiler options at run-time, but most ahead-of-time /// compiled platforms will not have this information. external const factory int.fromEnvironment( String name, { int defaultValue = 0, }); /// Bit-wise and operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with only the bits set that are set in /// both `this` and [other] /// /// If both operands are negative, the result is negative, otherwise /// the result is non-negative. /// ```dart /// print((2 & 1).toRadixString(2)); // 0010 & 0001 -> 0000 /// print((3 & 1).toRadixString(2)); // 0011 & 0001 -> 0001 /// print((10 & 2).toRadixString(2)); // 1010 & 0010 -> 0010 /// ``` int operator &(int other); /// Bit-wise or operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with the bits set that are set in either /// of `this` and [other] /// /// If both operands are non-negative, the result is non-negative, /// otherwise the result is negative. /// /// Example: /// ```dart /// print((2 | 1).toRadixString(2)); // 0010 | 0001 -> 0011 /// print((3 | 1).toRadixString(2)); // 0011 | 0001 -> 0011 /// print((10 | 2).toRadixString(2)); // 1010 | 0010 -> 1010 /// ``` int operator |(int other); /// Bit-wise exclusive-or operator. /// /// Treating both `this` and [other] as sufficiently large two's component /// integers, the result is a number with the bits set that are set in one, /// but not both, of `this` and [other] /// /// If the operands have the same sign, the result is non-negative, /// otherwise the result is negative. /// /// Example: /// ```dart /// print((2 ^ 1).toRadixString(2)); // 0010 ^ 0001 -> 0011 /// print((3 ^ 1).toRadixString(2)); // 0011 ^ 0001 -> 0010 /// print((10 ^ 2).toRadixString(2)); // 1010 ^ 0010 -> 1000 /// ``` int operator ^(int other); /// The bit-wise negate operator. /// /// Treating `this` as a sufficiently large two's component integer, /// the result is a number with the opposite bits set. /// /// This maps any integer `x` to `-x - 1`. int operator ~(); /// Shift the bits of this integer to the left by [shiftAmount]. /// /// Shifting to the left makes the number larger, effectively multiplying /// the number by `pow(2, shiftAmount)`. /// /// There is no limit on the size of the result. It may be relevant to /// limit intermediate values by using the "and" operator with a suitable /// mask. /// /// It is an error if [shiftAmount] is negative. /// /// Example: /// ```dart /// print((3 << 1).toRadixString(2)); // 0011 -> 0110 /// print((9 << 2).toRadixString(2)); // 1001 -> 100100 /// print((10 << 3).toRadixString(2)); // 1010 -> 1010000 /// ``` int operator <<(int shiftAmount); /// Shift the bits of this integer to the right by [shiftAmount]. /// /// Shifting to the right makes the number smaller and drops the least /// significant bits, effectively doing an integer division by /// `pow(2, shiftAmount)`. /// /// It is an error if [shiftAmount] is negative. /// /// Example: /// ```dart /// print((3 >> 1).toRadixString(2)); // 0011 -> 0001 /// print((9 >> 2).toRadixString(2)); // 1001 -> 0010 /// print((10 >> 3).toRadixString(2)); // 1010 -> 0001 /// print((-6 >> 2).toRadixString); // 111...1010 -> 111...1110 == -2 /// print((-85 >> 3).toRadixString); // 111...10101011 -> 111...11110101 == -11 /// ``` int operator >>(int shiftAmount); /// Bitwise unsigned right shift by [shiftAmount] bits. /// /// The least significant [shiftAmount] bits are dropped, /// the remaining bits (if any) are shifted down, /// and zero-bits are shifted in as the new most significant bits. /// /// The [shiftAmount] must be non-negative. /// /// Example: /// ```dart /// print((3 >>> 1).toRadixString(2)); // 0011 -> 0001 /// print((9 >>> 2).toRadixString(2)); // 1001 -> 0010 /// print(((-9) >>> 2).toRadixString(2)); // 111...1011 -> 001...1110 (> 0) /// ``` int operator >>>(int shiftAmount); /// Returns this integer to the power of [exponent] modulo [modulus]. /// /// The [exponent] must be non-negative and [modulus] must be /// positive. int modPow(int exponent, int modulus); /// Returns the modular multiplicative inverse of this integer /// modulo [modulus]. /// /// The [modulus] must be positive. /// /// It is an error if no modular inverse exists. int modInverse(int modulus); /// Returns the greatest common divisor of this integer and [other]. /// /// If either number is non-zero, the result is the numerically greatest /// integer dividing both `this` and `other`. /// /// The greatest common divisor is independent of the order, /// so `x.gcd(y)` is always the same as `y.gcd(x)`. /// /// For any integer `x`, `x.gcd(x)` is `x.abs()`. /// /// If both `this` and `other` is zero, the result is also zero. /// /// Example: /// ```dart /// print(4.gcd(2)); // 2 /// print(8.gcd(4)); // 4 /// print(10.gcd(12)); // 2 /// print(10.gcd(0)); // 10 /// print((-2).gcd(-3)); // 1 /// ``` int gcd(int other); /// Returns true if and only if this integer is even. bool get isEven; /// Returns true if and only if this integer is odd. bool get isOdd; /// Returns the minimum number of bits required to store this integer. /// /// The number of bits excludes the sign bit, which gives the natural length /// for non-negative (unsigned) values. Negative values are complemented to /// return the bit position of the first bit that differs from the sign bit. /// /// To find the number of bits needed to store the value as a signed value, /// add one, i.e. use `x.bitLength + 1`. /// ```dart /// x.bitLength == (-x-1).bitLength; /// /// 3.bitLength == 2; // 00000011 /// 2.bitLength == 2; // 00000010 /// 1.bitLength == 1; // 00000001 /// 0.bitLength == 0; // 00000000 /// (-1).bitLength == 0; // 11111111 /// (-2).bitLength == 1; // 11111110 /// (-3).bitLength == 2; // 11111101 /// (-4).bitLength == 2; // 11111100 /// ``` int get bitLength; /// Returns the least significant [width] bits of this integer as a /// non-negative number (i.e. unsigned representation). The returned value has /// zeros in all bit positions higher than [width]. /// ```dart /// (-1).toUnsigned(5) == 31 // 11111111 -> 00011111 /// ``` /// This operation can be used to simulate arithmetic from low level languages. /// For example, to increment an 8 bit quantity: /// ```dart /// q = (q + 1).toUnsigned(8); /// ``` /// `q` will count from `0` up to `255` and then wrap around to `0`. /// /// If the input fits in [width] bits without truncation, the result is the /// same as the input. The minimum width needed to avoid truncation of `x` is /// given by `x.bitLength`, i.e. /// ```dart /// x == x.toUnsigned(x.bitLength); /// ``` int toUnsigned(int width); /// Returns the least significant [width] bits of this integer, extending the /// highest retained bit to the sign. This is the same as truncating the value /// to fit in [width] bits using an signed 2-s complement representation. The /// returned value has the same bit value in all positions higher than [width]. /// /// ```dart /// // V--sign bit-V /// 16.toSigned(5) == -16; // 00010000 -> 11110000 /// 239.toSigned(5) == 15; // 11101111 -> 00001111 /// // ^ ^ /// ``` /// This operation can be used to simulate arithmetic from low level languages. /// For example, to increment an 8 bit signed quantity: /// ```dart /// q = (q + 1).toSigned(8); /// ``` /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to /// `127`. /// /// If the input value fits in [width] bits without truncation, the result is /// the same as the input. The minimum width needed to avoid truncation of `x` /// is `x.bitLength + 1`, i.e. /// ```dart /// x == x.toSigned(x.bitLength + 1); /// ``` int toSigned(int width); /// Return the negative value of this integer. /// /// The result of negating an integer always has the opposite sign, except /// for zero, which is its own negation. int operator -(); /// Returns the absolute value of this integer. /// /// For any integer `value`, /// the result is the same as `value < 0 ? -value : value`. /// /// Integer overflow may cause the result of `-value` to stay negative. int abs(); /// Returns the sign of this integer. /// /// Returns 0 for zero, -1 for values less than zero and /// +1 for values greater than zero. int get sign; /// Returns `this`. int round(); /// Returns `this`. int floor(); /// Returns `this`. int ceil(); /// Returns `this`. int truncate(); /// Returns `this.toDouble()`. double roundToDouble(); /// Returns `this.toDouble()`. double floorToDouble(); /// Returns `this.toDouble()`. double ceilToDouble(); /// Returns `this.toDouble()`. double truncateToDouble(); /// Returns a string representation of this integer. /// /// The returned string is parsable by [parse]. /// For any `int` `i`, it is guaranteed that /// `i == int.parse(i.toString())`. String toString(); /// Converts this [int] to a string representation in the given [radix]. /// /// In the string representation, lower-case letters are used for digits above /// '9', with 'a' being 10 and 'z' being 35. /// /// The [radix] argument must be an integer in the range 2 to 36. /// /// Example: /// ```dart /// // Binary (base 2). /// print(12.toRadixString(2)); // 1100 /// print(31.toRadixString(2)); // 11111 /// print(2021.toRadixString(2)); // 11111100101 /// print((-12).toRadixString(2)); // -1100 /// // Octal (base 8). /// print(12.toRadixString(8)); // 14 /// print(31.toRadixString(8)); // 37 /// print(2021.toRadixString(8)); // 3745 /// // Hexadecimal (base 16). /// print(12.toRadixString(16)); // c /// print(31.toRadixString(16)); // 1f /// print(2021.toRadixString(16)); // 7e5 /// // Base 36. /// print((35 * 36 + 1).toRadixString(36)); // z1 /// ``` String toRadixString(int radix); /// Parse [source] as a, possibly signed, integer literal and return its value. /// /// The [source] must be a non-empty sequence of base-[radix] digits, /// optionally prefixed with a minus or plus sign ('-' or '+'). /// /// The [radix] must be in the range 2..36. The digits used are /// first the decimal digits 0..9, and then the letters 'a'..'z' with /// values 10 through 35. Also accepts upper-case letters with the same /// values as the lower-case ones. /// /// If no [radix] is given then it defaults to 10. In this case, the [source] /// digits may also start with `0x`, in which case the number is interpreted /// as a hexadecimal integer literal, /// When `int` is implemented by 64-bit signed integers, /// hexadecimal integer literals may represent values larger than /// 263, in which case the value is parsed as if it is an /// *unsigned* number, and the resulting value is the corresponding /// signed integer value. /// /// For any int `n` and valid radix `r`, it is guaranteed that /// `n == int.parse(n.toRadixString(r), radix: r)`. /// /// If the [source] string does not contain a valid integer literal, /// optionally prefixed by a sign, a [FormatException] is thrown. /// /// Rather than throwing and immediately catching the [FormatException], /// instead use [tryParse] to handle a potential parsing error. /// /// Example: /// ```dart /// var value = int.tryParse(text); /// if (value == null) { /// // handle the problem /// // ... /// } /// ``` external static int parse(String source, {int? radix}); /// Parse [source] as a, possibly signed, integer literal. /// /// Like [parse] except that this function returns `null` where a /// similar call to [parse] would throw a [FormatException]. /// /// Example: /// ```dart /// print(int.tryParse('2021')); // 2021 /// print(int.tryParse('1f')); // null /// // From binary (base 2) value. /// print(int.tryParse('1100', radix: 2)); // 12 /// print(int.tryParse('00011111', radix: 2)); // 31 /// print(int.tryParse('011111100101', radix: 2)); // 2021 /// // From octal (base 8) value. /// print(int.tryParse('14', radix: 8)); // 12 /// print(int.tryParse('37', radix: 8)); // 31 /// print(int.tryParse('3745', radix: 8)); // 2021 /// // From hexadecimal (base 16) value. /// print(int.tryParse('c', radix: 16)); // 12 /// print(int.tryParse('1f', radix: 16)); // 31 /// print(int.tryParse('7e5', radix: 16)); // 2021 /// // From base 35 value. /// print(int.tryParse('y1', radix: 35)); // 1191 == 34 * 35 + 1 /// print(int.tryParse('z1', radix: 35)); // null /// // From base 36 value. /// print(int.tryParse('y1', radix: 36)); // 1225 == 34 * 36 + 1 /// print(int.tryParse('z1', radix: 36)); // 1261 == 35 * 36 + 1 /// ``` external static int? tryParse(String source, {int? radix}); } �ÆNN<L9NONP;HN=7'PY'MCIQEGN7 =NGLEJGI5"I FP =&D/ EGNKGJ9.MNG">>? MPE(>>? &MN*F(??@ $G9-CL+IL 38:< $DIA3889HR $:<4E.99N %H@)A&3GK0?74C 87IOOON+'(((((((( FR6; R3! GNQ#& PQPR4884 R: O PR!( 1M+2>J (;'!!!!72/&KQ/D*+3.((, (),4 #RHBBHJ%PO(;DGFA6GDKB& :=D?+)%37=$115+125C4CC >dart:core/int.dart`org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm_shared/lib/integers_patch.dartÀCv// 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. import "dart:_internal" show has63BitSmis, patch, unsafeCast; import "dart:typed_data" show Int64List; /// VM implementation of int. @patch @pragma('vm:deeply-immutable') @pragma("vm:entry-point") class int { @patch @pragma("vm:external-name", "Integer_fromEnvironment") external const factory int.fromEnvironment( String name, { int defaultValue = 0, }); /// Tries parsing the [string] range from [start] to [end] as a decimal Smi. /// /// Returns `null` if it's not *definitely* a Smi value. /// Not optimizing the parse-error path. static int? _tryParseSmi(String string, int start, int end) { assert(start < end); int index = start; int sign = 1; int char = string.codeUnitAt(index); // Check for leading '+' or '-'. if (0x2b == char || 0x2d == char) { index++; sign = 0x2c - char; // -1 for '-', +1 for '+'. if (index == end) { return null; // No digits after sign. } char = string.codeUnitAt(index); } // Skip leading zeros. while (0x30 == char) { index++; if (index == end) return 0; char = string.codeUnitAt(index); } // If not too long to be a Smi, parse digits. int smiLimit = has63BitSmis ? 18 : 9; if (end - index <= smiLimit) { int result = 0; while (true) { int digit = 0x30 ^ char; if (9 >= digit) { result += digit; index++; if (index < end) { result *= 10; char = string.codeUnitAt(index); } else { return sign * result; } } else { break; } } } return null; // May not fit into a Smi, or contains non-digit. } @patch static int parse(String source, {int? radix, int onError(String source)?}) { if (source.isEmpty) { return _handleFormatError(onError, source, 0, radix, null) as int; } if (radix == null || radix == 10) { // Try parsing immediately, without trimming whitespace. int? result = _tryParseSmi(source, 0, source.length); if (result != null) return result; } else if (radix < 2 || radix > 36) { throw RangeError("Radix $radix not in range 2..36"); } // Split here so improve odds of parse being inlined and the checks omitted. return _parse(unsafeCast<_StringBase>(source), radix, onError) as int; } static int? _parse( _StringBase source, int? radix, int? Function(String)? onError, ) { int end = source._lastNonWhitespace() + 1; if (end == 0) { return _handleFormatError(onError, source, source.length, radix, null); } int start = source._firstNonWhitespace(); int first = source.codeUnitAt(start); int sign = 1; if (first == 0x2b /* + */ || first == 0x2d /* - */ ) { sign = 0x2c - first; // -1 if '-', +1 if '+'. start++; if (start == end) { return _handleFormatError(onError, source, end, radix, null); } first = source.codeUnitAt(start); } if (radix == null) { // check for 0x prefix. if (first == 0x30 /* 0 */ ) { start++; if (start == end) return 0; first = source.codeUnitAt(start); if ((first | 0x20) == 0x78 /* x */ ) { start++; if (start == end) { return _handleFormatError(onError, source, start, null, null); } return _parseRadix(source, 16, start, end, sign, sign > 0, onError); } } radix = 10; } return _parseRadix(source, radix, start, end, sign, false, onError); } @patch static int? tryParse(String source, {int? radix}) { if (source.isEmpty) return null; if (radix == null || radix == 10) { // Try parsing immediately, without trimming whitespace. int? result = _tryParseSmi(source, 0, source.length); if (result != null) return result; } else if (radix < 2 || radix > 36) { throw RangeError("Radix $radix not in range 2..36"); } return _parse(unsafeCast<_StringBase>(source), radix, _kNull); } static Null _kNull(_) => null; static int? _handleFormatError( int? Function(String)? onError, String source, int? index, int? radix, String? message, ) { if (onError != null) return onError(source); message ??= radix == null ? "Invalid number" : "Invalid radix-$radix number"; throw FormatException(message, source, index); } static int? _parseRadix( String source, int radix, int start, int end, int sign, bool allowU64, int? Function(String)? onError, ) { assert(start < end); // Skip leading zeros. while (start < end && source.codeUnitAt(start) == 0x30) { start++; } int length = end - start; if (length == 0) return 0; List parseBlockSizeTable = has63BitSmis ? _parseLimits64 : _parseLimits32; int tableIndex = (radix - 2) * 2; int blockSize = parseBlockSizeTable[tableIndex]; if (length <= blockSize) { _Smi? smi = _parseBlock(source, radix, start, end); if (smi != null) { return sign * smi; } return _handleFormatError(onError, source, start, radix, null); } // Parse blocks of blockSize. If the length of input is not // a multiple, parse the shorter block first, so all later blocks // have the same length, all multiplication is with the same factor // and all overflow handling is in one place. int result = 0; // Repeated subtraction can be cheaper than modulo, especially // because digit count generally tends towards smaller rather // than larger. int smallBlockSize = length; while (true) { int smallerBlockSize = smallBlockSize - blockSize; if (smallerBlockSize >= 0) { smallBlockSize = smallerBlockSize; } else { break; } } while (smallBlockSize >= blockSize) smallBlockSize -= blockSize; if (smallBlockSize > 0) { int blockEnd = start + smallBlockSize; int? smi = _parseBlock(source, radix, start, blockEnd); if (smi == null) { return _handleFormatError(onError, source, start, radix, null); } result = sign * smi; start = blockEnd; } // Parse blocks of `blockSize` length into smis, // multiply result by radix ** blockSize and add the smi. // Check for overflow *before* adding. int multiplier = parseBlockSizeTable[tableIndex + 1]; List overflowLimits = has63BitSmis ? _overflowLimits64 : _overflowLimits32; // Overflow table has double the row length of the parseTable. tableIndex <<= 1; int positiveOverflowLimit = overflowLimits[tableIndex + 0]; int negativeOverflowLimit = overflowLimits[tableIndex + 1]; int blockEnd = start + blockSize; do { int? smi = _parseBlock(source, radix, start, blockEnd); if (smi != null) { if (result >= positiveOverflowLimit) { // May overflow when adding a smi. if ((result > positiveOverflowLimit) || (smi > overflowLimits[tableIndex + 2])) { if (allowU64) { assert(radix == 16 && sign > 0); int unsignedLimit = has63BitSmis ? 0xF : 0xFFFFFFFFF; if (blockEnd == end && result <= unsignedLimit) { return (result * multiplier) + smi; } } return _handleFormatError( onError, source, null, radix, "Positive input exceeds the limit of integer", ); } } else if (result <= negativeOverflowLimit) { // Only if sign is `-1`. if ((result < negativeOverflowLimit) || (smi > overflowLimits[tableIndex + 3])) { return _handleFormatError( onError, source, null, radix, "Negative input exceeds the limit of integer", ); } } result = (result * multiplier) + (sign * smi); start = blockEnd; blockEnd = start + blockSize; } else { return _handleFormatError(onError, source, start, radix, null); } } while (blockEnd <= end); return result; } // Parse block of digits into a Smi. static _Smi? _parseBlock(String source, int radix, int start, int end) { _Smi result = unsafeCast<_Smi>(0); if (radix <= 10) { for (int i = start; i < end; i++) { int digit = source.codeUnitAt(i) ^ 0x30; if (digit < radix) { result = unsafeCast<_Smi>(radix * result + digit); } else { return null; } } } else { for (int i = start; i < end; i++) { int char = source.codeUnitAt(i); int digit = char ^ 0x30; if (digit > 9) { digit = (char | 0x20) - (0x61 - 10); if (digit < 10 || digit >= radix) return null; } result = unsafeCast<_Smi>(radix * result + digit); } } return result; } // For each radix, 2-36, how many digits are guaranteed to fit in a smi, // and magnitude of such a block (radix ** digit-count). // Table for 31-bit smis. All numbers are smis. static const _parseLimits32 = [ // dart format off 30, 1073741824, // radix: 2 18, 387420489, 15, 1073741824, 12, 244140625, // radix: 5 11, 362797056, 10, 282475249, 10, 1073741824, 9, 387420489, 9, 1000000000, // radix: 10 8, 214358881, 8, 429981696, 8, 815730721, 7, 105413504, 7, 170859375, // radix: 15 7, 268435456, 7, 410338673, 7, 612220032, 7, 893871739, 6 , 64000000, // radix: 20 6 , 85766121, 6, 113379904, 6, 148035889, 6, 191102976, 6, 244140625, // radix: 25 6, 308915776, 6, 387420489, 6, 481890304, 6, 594823321, 6, 729000000, // radix: 30 6, 887503681, 6, 1073741824, 5, 39135393, 5, 45435424, 5, 52521875, // radix: 35 5, 60466176, // dart format on ]; // Table for 64-bit smis. All numbers are smis if smis are 63 bits. static const _parseLimits64 = [ // dart format off 62, 4611686018427387904, // radix: 2 39, 4052555153018976267, 30, 1152921504606846976, 26, 1490116119384765625, // radix: 5 23, 789730223053602816, 22, 3909821048582988049, 20, 1152921504606846976, 19, 1350851717672992089, 18, 1000000000000000000, // radix: 10 17, 505447028499293771, 17, 2218611106740436992, 16, 665416609183179841, 16, 2177953337809371136, 15, 437893890380859375, // radix: 15 15, 1152921504606846976, 15, 2862423051509815793, 14, 374813367582081024, 14, 799006685782884121, 14, 1638400000000000000, // radix: 20 14, 3243919932521508681, 13, 282810057883082752, 13, 504036361936467383, 13, 876488338465357824, 13, 1490116119384765625, // radix: 25 13, 2481152873203736576, 13, 4052555153018976267, 12, 232218265089212416, 12, 353814783205469041, 12, 531441000000000000, // radix: 30 12, 787662783788549761, 12, 1152921504606846976, 12, 1667889514952984961, 12, 2386420683693101056, 12, 3379220508056640625, // radix: 35 11, 131621703842267136, // dart format on ]; /// Overflow limits for `_parseRadix` calculation. /// /// The expression /// ``` /// result = (result * multiplier) + (sign * smi) /// ``` /// in `_parseRadix()` may overflow 64-bit integers. In such case, /// `int.parse()` should stop with an error. /// /// The table contains int64 overflow limits for `result` and `smi`. /// For each radix from `_parseLimits`, this table contains at /// index `(radix - 2) * 4`: /// /// * `[index]` = positive limit for `result`. /// * `[index + 1]` = negative limit for `result`. /// * `[index + 2]` = limit for `smi` if result is exactly at positive limit. /// * `[index + 3]` = limit for `smi` if result is exactly at negative limit. static const List _overflowLimits32 = [ // dart format off 0x1ffffffff, -0x200000000, 0x3fffffff, 0x0, // radix: 2 0x58b040ea4, -0x58b040ea4, 0x2d0ef3b, 0x2d0ef3c, 0x1ffffffff, -0x200000000, 0x3fffffff, 0x0, 0x8cbccc096, -0x8cbccc096, 0xdedb489, 0xdedb48a, // radix: 5 0x5eb537522, -0x5eb537522, 0xb0d4fff, 0xb0d5000, 0x79a357391, -0x79a357391, 0xc495a7e, 0xc495a7f, 0x1ffffffff, -0x200000000, 0x3fffffff, 0x0, 0x58b040ea4, -0x58b040ea4, 0x2d0ef3b, 0x2d0ef3c, 0x225c17d04, -0x225c17d04, 0x32f2d7ff, 0x32f2d800, // radix: 10 0xa04a6c518, -0xa04a6c518, 0x1a3c9e7, 0x1a3c9e8, 0x4fe8e6ad5, -0x4fe8e6ad5, 0x30affff, 0x30b0000, 0x2a1f158bf, -0x2a1f158bf, 0x1bb69f60, 0x1bb69f61, 0x145f3b3bb0, -0x145f3b3bb0, 0x33497ff, 0x3349800, 0xc9197a2b3, -0xc9197a2b3, 0x43a9362, 0x43a9363, // radix: 15 0x7ffffffff, -0x800000000, 0xfffffff, 0x0, 0x53bc2daf5, -0x53bc2daf5, 0x100050da, 0x100050db, 0x381f89143, -0x381f89143, 0x1f532a7f, 0x1f532a80, 0x267071285, -0x267071285, 0x2561c18, 0x2561c19, 0x218def416b, -0x218def416b, 0x343cfff, 0x343d000, // radix: 20 0x1909f102c7, -0x1909f102c7, 0x1fa6fe0, 0x1fa6fe1, 0x12f0cb4ca1, -0x12f0cb4ca1, 0x42c8dbf, 0x42c8dc0, 0xe81aa6ffb, -0xe81aa6ffb, 0x18ecdf4, 0x18ecdf5, 0xb3cc0705f, -0xb3cc0705f, 0x5e3ffff, 0x5e40000, 0x8cbccc096, -0x8cbccc096, 0xdedb489, 0xdedb48a, // radix: 25 0x6f3a14e59, -0x6f3a14e59, 0xfeaebbf, 0xfeaebc0, 0x58b040ea4, -0x58b040ea4, 0x2d0ef3b, 0x2d0ef3c, 0x474d4f50e, -0x474d4f50e, 0x11911fff, 0x11912000, 0x39c3bd9d4, -0x39c3bd9d4, 0x1ae7304b, 0x1ae7304c, 0x2f21f8a22, -0x2f21f8a22, 0x231277f, 0x2312780, // radix: 30 0x26b70cb40, -0x26b70cb40, 0x1e04a4bf, 0x1e04a4c0, 0x1ffffffff, -0x200000000, 0x3fffffff, 0x0, 0x36df890826, -0x36df890826, 0xedf019, 0xedf01a, 0x2f43b7c97a, -0x2f43b7c97a, 0x1878cbf, 0x1878cc0, 0x28e32d909e, -0x28e32d909e, 0x305eb45, 0x305eb46, // radix: 35 0x2383f4becf, -0x2383f4becf, 0x3d63ff, 0x3d6400, // dart format on ]; /// Same table as [_overflowLimits32], but for 63-bit smis. /// /// The table contains int64 overflow limits for `result` and `smi`. /// For each radix from `_parseLimits`, this table contains at /// index `(radix - 2) * 4`: /// /// * `[index]` = positive limit for `result`. /// * `[index + 1]` = negative limit for `result`. /// * `[index + 2]` = limit for `smi` if result is exactly at positive limit. /// * `[index + 3]` = limit for `smi` if result is exactly at negative limit. static const List _overflowLimits64 = [ // dart format off 0x1, -0x2, 0x3fffffffffffffff, 0x0, // radix: 2 0x2, -0x2, 0xf84dd1e8f400fe9, 0xf84dd1e8f400fea, 0x7, -0x8, 0xfffffffffffffff, 0x0, 0x6, -0x6, 0x3ec43b4d3ecc3a9, 0x3ec43b4d3ecc3aa, // radix: 5 0xb, -0xb, 0x7717602637fffff, 0x771760263800000, 0x2, -0x2, 0x137b0cf15fbb3ddd, 0x137b0cf15fbb3dde, 0x7, -0x8, 0xfffffffffffffff, 0x0, 0x6, -0x6, 0xf84dd1e8f400fe9, 0xf84dd1e8f400fea, 0x9, -0x9, 0x31993af1d7bffff, 0x31993af1d7c0000, // radix: 10 0x12, -0x12, 0x1bd3ee663694eb9, 0x1bd3ee663694eba, 0x4, -0x4, 0x4d7a3cfffffffff, 0x4d7a3d000000000, 0xd, -0xd, 0x7f38c8d9de428b2, 0x7f38c8d9de428b3, 0x4, -0x4, 0x7196bea09fbffff, 0x7196bea09fc0000, 0x15, -0x15, 0x620e5ca93c5964, 0x620e5ca93c5965, // radix: 15 0x7, -0x8, 0xfffffffffffffff, 0x0, 0x3, -0x3, 0x8d3e433859a722c, 0x8d3e433859a722d, 0x18, -0x18, 0x3297d7904d9ffff, 0x3297d7904da0000, 0xb, -0xb, 0x606f02db83d49ec, 0x606f02db83d49ed, 0x5, -0x5, 0xe502b672fffffff, 0xe502b6730000000, // radix: 20 0x2, -0x2, 0x25f690044c7e216d, 0x25f690044c7e216e, 0x20, -0x20, 0x26838061f13ffff, 0x26838061f140000, 0x12, -0x12, 0x21774c9a823b921, 0x21774c9a823b922, 0xa, -0xa, 0x65ce0ffffffffff, 0x65ce10000000000, 0x6, -0x6, 0x3ec43b4d3ecc3a9, 0x3ec43b4d3ecc3aa, // radix: 25 0x3, -0x3, 0x18b385d295c11fff, 0x18b385d295c12000, 0x2, -0x2, 0xf84dd1e8f400fe9, 0xf84dd1e8f400fea, 0x27, -0x27, 0x250ce02f8ffffff, 0x250ce02f9000000, 0x1a, -0x1a, 0x55ee8f97a91685, 0x55ee8f97a91686, 0x11, -0x11, 0x29f04d866aaefff, 0x29f04d866aaf000, // radix: 30 0xb, -0xb, 0x7c24195c05eb874, 0x7c24195c05eb875, 0x7, -0x8, 0xfffffffffffffff, 0x0, 0x5, -0x5, 0xc44549d7324d07a, 0xc44549d7324d07b, 0x3, -0x3, 0x1ca531188f7bcfff, 0x1ca531188f7bd000, 0x2, -0x2, 0x223531b41f23471d, 0x223531b41f23471e, // radix: 35 0x46, -0x46, 0x23010a4a7fffff, 0x23010a4a800000, // dart format on ]; } �éNN<>) 9.O;+@)%(5.'"'2*#!-"  C OI(?<)*;QK$/N.*;4F($$*/K O I 6%(?<)*;C!"$1)3 $>1&5:F@FH2CB!9#+E->H5>+:,C@@& >/-28/D@4'= 6#28'= 7&H'K'*1=  *)!/9 ;K;2"  !!!!!!F"))******5 6 E/GA15PP/E99E9999F9999F9999F9999F9999F9999F9>GA15PP/E99E9999F9999F9999F9999F9999F9999F9#dart:core-patch/integers_patch.dartÀeÀh³Iorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/invocation.dart•V// 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"; /// Representation of the invocation of a member on an object. /// /// This is the type of objects passed to [Object.noSuchMethod] when /// an object doesn't support the member invocation that was attempted /// on it. abstract class Invocation { Invocation(); /// Creates an invocation corresponding to a method invocation. /// /// The method invocation has no type arguments. /// If the named arguments are omitted, they default to no named arguments. @pragma("wasm:entry-point") factory Invocation.method( Symbol memberName, Iterable? positionalArguments, [ Map? namedArguments, ]) => _Invocation.method(memberName, null, positionalArguments, namedArguments); /// Creates an invocation corresponding to a generic method invocation. /// /// If [typeArguments] is `null` or empty, the constructor is equivalent to /// calling [Invocation.method] with the remaining arguments. /// All the individual type arguments must be non-null. /// /// If the named arguments are omitted, they default to no named arguments. @pragma("wasm:entry-point") factory Invocation.genericMethod( Symbol memberName, Iterable? typeArguments, Iterable? positionalArguments, [ Map? namedArguments, ]) => _Invocation.method( memberName, typeArguments, positionalArguments, namedArguments, ); /// Creates an invocation corresponding to a getter invocation. @pragma("wasm:entry-point") factory Invocation.getter(Symbol name) = _Invocation.getter; /// Creates an invocation corresponding to a setter invocation. /// /// This constructor accepts any [Symbol] as [memberName], but remember that /// *actual setter names* end in `=`, so the invocation corresponding /// to `object.member = value` is /// ```dart /// Invocation.setter(const Symbol("member="), value) /// ``` @pragma("wasm:entry-point") factory Invocation.setter(Symbol memberName, Object? argument) = _Invocation.setter; /// The name of the invoked member. Symbol get memberName; /// An unmodifiable view of the type arguments of the call. /// /// If the member is a getter, setter or operator, /// the type argument list is always empty. List get typeArguments => const []; /// An unmodifiable view of the positional arguments of the call. /// /// If the member is a getter, the positional arguments list is /// always empty. List get positionalArguments; /// An unmodifiable view of the named arguments of the call. /// /// If the member is a getter, setter or operator, /// the named arguments map is always empty. Map get namedArguments; /// Whether the invocation was a method call. bool get isMethod; /// Whether the invocation was a getter call. /// If so, all three types of arguments lists are empty. bool get isGetter; /// Whether the invocation was a setter call. /// /// If so, [positionalArguments] has exactly one positional /// argument, [namedArguments] is empty, and typeArguments is /// empty. bool get isSetter; /// Whether the invocation was a getter or a setter call. bool get isAccessor => isGetter || isSetter; } /// Implementation of [Invocation] used by its factory constructors. class _Invocation implements Invocation { final Symbol memberName; final List typeArguments; // Positional arguments is `null` for getters only. final List? _positional; // Named arguments is `null` for accessors only. final Map? _named; _Invocation.method( this.memberName, Iterable? types, Iterable? positional, Map? named, ) : typeArguments = _ensureNonNullTypes(types), _positional = positional == null ? const [] : List.unmodifiable(positional), _named = (named == null || named.isEmpty) ? const {} : Map.unmodifiable(named); _Invocation.getter(this.memberName) : typeArguments = const [], _positional = null, _named = null; _Invocation.setter(this.memberName, Object? argument) : typeArguments = const [], _positional = List.unmodifiable([argument]), _named = null; List get positionalArguments => _positional ?? const []; Map get namedArguments => _named ?? const {}; bool get isMethod => _named != null; bool get isGetter => _positional == null; bool get isSetter => _positional != null && _named == null; bool get isAccessor => _named == null; /// Checks that the elements of [types] are not null. static List _ensureNonNullTypes(Iterable? types) { if (types == null) return const []; List typeArguments = List.unmodifiable(types); for (int i = 0; i < typeArguments.length; i++) { if (typeArguments[i] == null) { throw ArgumentError.value( types, "types", "Type arguments must be non-null, was null at index $i.", ); } } return typeArguments; } } € NN<?EG B3N.*QJN@:N$#.*B?BOH$8 C&>5.2DB)?5/+00;0>@ )8A.?5&#D dart:core/invocation.dartÀeÀh³Gorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/iterator.dart�Ö// 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"; /// An interface for getting items, one at a time, from an object. /// /// The for-in construct transparently uses `Iterator` to test for the end /// of the iteration, and to get each item (or _element_). /// /// If the object iterated over is changed during the iteration, the /// behavior is unspecified. /// /// The `Iterator` is initially positioned before the first element. /// Before accessing the first element the iterator must thus be advanced using /// [moveNext] to point to the first element. /// If no element is left, then [moveNext] returns false, /// and all further calls to [moveNext] will also return false. /// /// The [current] value must not be accessed before calling [moveNext] /// or after a call to [moveNext] has returned false. /// /// A typical usage of an `Iterator` looks as follows: /// ```dart /// var it = obj.iterator; /// while (it.moveNext()) { /// use(it.current); /// } /// ``` /// **See also:** /// [Iteration](https://dart.dev/libraries/dart-core#iteration) /// in the [introduction to `dart:core`](https://dart.dev/libraries/dart-core). abstract interface class Iterator { /// Advances the iterator to the next element of the iteration. /// /// Should be called before reading [current]. /// If the call to `moveNext` returns `true`, /// then [current] will contain the next element of the iteration /// until `moveNext` is called again. /// If the call returns `false`, there are no further elements /// and [current] should not be used any more. /// /// It is safe to call [moveNext] after it has already returned `false`, /// but it must keep returning `false` and not have any other effect. /// /// A call to `moveNext` may throw for various reasons, /// including a concurrent change to an underlying collection. /// If that happens, the iterator may be in an inconsistent /// state, and any further behavior of the iterator is unspecified, /// including the effect of reading [current]. /// ```dart /// final colors = ['blue', 'yellow', 'red']; /// final colorsIterator = colors.iterator; /// print(colorsIterator.moveNext()); // true /// print(colorsIterator.moveNext()); // true /// print(colorsIterator.moveNext()); // true /// print(colorsIterator.moveNext()); // false /// ``` bool moveNext(); /// The current element. /// /// If the iterator has not yet been moved to the first element /// ([moveNext] has not been called yet), /// or if the iterator has been moved past the last element of the [Iterable] /// ([moveNext] has returned false), /// then [current] is unspecified. /// An [Iterator] may either throw or return an iterator specific default value /// in that case. /// /// The `current` getter should keep its value until the next call to /// [moveNext], even if an underlying collection changes. /// After a successful call to `moveNext`, the user doesn't need to cache /// the current value, but can keep reading it from the iterator. /// ```dart /// final colors = ['blue', 'yellow', 'red']; /// var colorsIterator = colors.iterator; /// while (colorsIterator.moveNext()) { /// print(colorsIterator.current); /// } /// ``` /// The output of the example is: /// ``` /// blue /// yellow /// red /// ``` E get current; } \NN<CK;EEP.:@G67 @P'B10D(A1KH:A>F10.0001 B,P'%RH implements EfficientLengthIterable, HideEfficientLengthIterable {} /// An indexable collection of objects with a length. /// /// Subclasses of this class implement different kinds of lists. /// The most common kinds of lists are: /// /// * **Fixed-length list** /// /// An error occurs when attempting to use operations /// that can change the length of the list. /// /// * **Growable list** /// /// Full implementation of the API defined in this class. /// /// The default growable list, as created by `[]`, keeps /// an internal buffer, and grows that buffer when necessary. This guarantees /// that a sequence of [add] operations will each execute in amortized constant /// time. Setting the length directly may take time proportional to the new /// length, and may change the internal capacity so that a following add /// operation will need to immediately increase the buffer capacity. /// Other list implementations may have different performance behavior. /// /// Example of fixed-length list: /// ```dart /// final fixedLengthList = List.filled(5, 0); // Creates fixed-length list. /// print(fixedLengthList); // [0, 0, 0, 0, 0] /// fixedLengthList[0] = 87; /// fixedLengthList.setAll(1, [1, 2, 3]); /// print(fixedLengthList); // [87, 1, 2, 3, 0] /// // Fixed length list length can't be changed or increased /// fixedLengthList.length = 0; // Throws /// fixedLengthList.add(499); // Throws /// ``` /// /// Example of growable list: /// ```dart /// final growableList = ['A', 'B']; // Creates growable list. /// ``` /// To add data to the growable list, use [operator[]=], [add] or [addAll]. /// ``` /// growableList[0] = 'G'; /// print(growableList); // [G, B] /// growableList.add('X'); /// growableList.addAll({'C', 'B'}); /// print(growableList); // [G, B, X, C, B] /// ``` /// To check whether, and where, the element is in the list, use [indexOf] or /// [lastIndexOf]. /// ``` /// final indexA = growableList.indexOf('A'); // -1 (not in the list) /// final firstIndexB = growableList.indexOf('B'); // 1 /// final lastIndexB = growableList.lastIndexOf('B'); // 4 /// ``` /// To remove an element from the growable list, use [remove], [removeAt], /// [removeLast], [removeRange] or [removeWhere]. /// ``` /// growableList.remove('C'); /// growableList.removeLast(); /// print(growableList); // [G, B, X] /// ``` /// To insert an element at position in the list, use [insert] or [insertAll]. /// ``` /// growableList.insert(1, 'New'); /// print(growableList); // [G, New, B, X] /// ``` /// To replace a range of elements in the list, use [fillRange], [replaceRange] /// or [setRange]. /// ``` /// growableList.replaceRange(0, 2, ['AB', 'A']); /// print(growableList); // [AB, A, B, X] /// growableList.fillRange(2, 4, 'F'); /// print(growableList); // [AB, A, F, F] /// ``` /// To sort the elements of the list, use [sort]. /// ``` /// growableList.sort((a, b) => a.compareTo(b)); /// print(growableList); // [A, AB, F, F] /// ``` /// To shuffle the elements of this list randomly, use [shuffle]. /// ``` /// growableList.shuffle(); /// print(growableList); // e.g. [AB, F, A, F] /// ``` /// To find the first element satisfying some predicate, or give a default /// value if none do, use [firstWhere]. /// ``` /// bool isVowel(String char) => char.length == 1 && "AEIOU".contains(char); /// final firstVowel = growableList.firstWhere(isVowel, orElse: () => ''); // '' /// ``` /// There are similar [lastWhere] and [singleWhere] methods. /// /// A list is an [Iterable] and supports all its methods, including /// [where], [map], [whereType] and [toList]. /// /// Lists are [Iterable]. Iteration occurs over values in index order. Changing /// the values does not affect iteration, but changing the valid /// indices—that is, changing the list's length—between iteration /// steps causes a [ConcurrentModificationError]. This means that only growable /// lists can throw ConcurrentModificationError. If the length changes /// temporarily and is restored before continuing the iteration, the iterator /// might not detect it. /// /// It is generally not allowed to modify the list's length (adding or removing /// elements) while an operation on the list is being performed, /// for example during a call to [forEach] or [sort]. /// Changing the list's length while it is being iterated, either by iterating it /// directly or through iterating an [Iterable] that is backed by the list, will /// break the iteration. abstract interface class List implements Iterable, _ListIterable { /// Creates a list of the given length with [fill] at each position. /// /// The [length] must be a non-negative integer. /// /// Example: /// ```dart /// final zeroList = List.filled(3, 0, growable: true); // [0, 0, 0] /// ``` /// /// The created list is fixed-length if [growable] is false (the default) /// and growable if [growable] is true. /// If the list is growable, increasing its [length] will *not* initialize /// new entries with [fill]. /// After being created and filled, the list is no different from any other /// growable or fixed-length list created /// using `[]` or other [List] constructors. /// /// All elements of the created list share the same [fill] value. /// ```dart /// final shared = List.filled(3, []); /// shared[0].add(499); /// print(shared); // [[499], [499], [499]] /// ``` /// You can use [List.generate] to create a list with a fixed length /// and a new object at each position. /// ```dart /// final unique = List.generate(3, (_) => []); /// unique[0].add(499); /// print(unique); // [[499], [], []] /// ``` external factory List.filled(int length, E fill, {bool growable = false}); /// Creates a new empty list. /// /// If [growable] is `false`, which is the default, /// the list is a fixed-length list of length zero. /// If [growable] is `true`, the list is growable and equivalent to `[]`. /// ```dart /// final growableList = List.empty(growable: true); // [] /// growableList.add(1); // [1] /// /// final fixedLengthList = List.empty(growable: false); /// fixedLengthList.add(1); // error /// ``` external factory List.empty({bool growable = false}); /// Creates a list containing all [elements]. /// /// The [Iterator] of [elements] provides the order of the elements. /// /// All the [elements] should be instances of [E]. /// /// Example: /// ```dart /// final numbers = [1, 2, 3]; /// final listFrom = List.from(numbers); /// print(listFrom); // [1, 2, 3] /// ``` /// The `elements` iterable itself may have any element type, so this /// constructor can be used to down-cast a `List`, for example as: /// ```dart import:convert /// const jsonArray = ''' /// [{"text": "foo", "value": 1, "status": true}, /// {"text": "bar", "value": 2, "status": false}] /// '''; /// final List dynamicList = jsonDecode(jsonArray); /// final List> fooData = /// List.from(dynamicList.where((x) => x is Map && x['text'] == 'foo')); /// print(fooData); // [{text: foo, value: 1, status: true}] /// ``` /// /// This constructor creates a growable list when [growable] is true; /// otherwise, it returns a fixed-length list. external factory List.from(Iterable elements, {bool growable = true}); /// Creates a list from [elements]. /// /// The [Iterator] of [elements] provides the order of the elements. /// /// This constructor creates a growable list when [growable] is true; /// otherwise, it returns a fixed-length list. /// ```dart /// final numbers = [1, 2, 3]; /// final listOf = List.of(numbers); /// print(listOf); // [1, 2, 3] /// ``` external factory List.of(Iterable elements, {bool growable = true}); /// Generates a list of values. /// /// Creates a list with [length] positions and fills it with values created by /// calling [generator] for each index in the range `0` .. `length - 1` /// in increasing order. /// ```dart /// final growableList = /// List.generate(3, (int index) => index * index, growable: true); /// print(growableList); // [0, 1, 4] /// /// final fixedLengthList = /// List.generate(3, (int index) => index * index, growable: false); /// print(fixedLengthList); // [0, 1, 4] /// ``` /// The created list is fixed-length if [growable] is set to false. /// /// The [length] must be non-negative. external factory List.generate( int length, E generator(int index), { bool growable = true, }); /// Creates an unmodifiable list containing all [elements]. /// /// The [Iterator] of [elements] provides the order of the elements. /// /// An unmodifiable list cannot have its length or elements changed. /// If the elements are themselves immutable, then the resulting list /// is also immutable. /// ```dart /// final numbers = [1, 2, 3]; /// final unmodifiableList = List.unmodifiable(numbers); // [1, 2, 3] /// unmodifiableList[1] = 87; // Throws. /// ``` external factory List.unmodifiable(Iterable elements); /// Adapts [source] to be a `List`. /// /// Any time the list would produce an element that is not a [T], /// the element access will throw. /// /// Any time a [T] value is attempted stored into the adapted list, /// the store will throw unless the value is also an instance of [S]. /// /// If all accessed elements of [source] are actually instances of [T], /// and if all elements stored into the returned list are actually instance /// of [S], /// then the returned list can be used as a `List`. /// /// Methods which accept `Object?` as argument, like [contains] and [remove], /// will pass the argument directly to the this list's method /// without any checks. static List castFrom(List source) => CastList(source); /// Copy a range of one list into another list. /// /// This is a utility function that can be used to implement methods like /// [setRange]. /// /// The range from [start] to [end] must be a valid range of [source], /// and there must be room for `end - start` elements from position [at]. /// If [start] is omitted, it defaults to zero. /// If [end] is omitted, it defaults to [source].length. /// /// If [source] and [target] are the same list, overlapping source and target /// ranges are respected so that the target range ends up containing the /// initial content of the source range. /// Otherwise the order of element copying is not guaranteed. static void copyRange( List target, int at, List source, [ int? start, int? end, ]) { start ??= 0; end = RangeError.checkValidRange(start, end, source.length); int length = end - start; if (target.length < at + length) { throw ArgumentError.value( target, "target", "Not big enough to hold $length elements at position $at", ); } if (!identical(source, target) || start >= at) { for (int i = 0; i < length; i++) { target[at + i] = source[start + i]; } } else { for (int i = length; --i >= 0;) { target[at + i] = source[start + i]; } } } /// Write the elements of an iterable into a list. /// /// This is a utility function that can be used to implement methods like /// [setAll]. /// /// The elements of [source] are written into [target] from position [at]. /// The [source] must not contain more elements after writing the last /// position of [target]. /// /// If the source is a list, the [copyRange] function is likely to be more /// efficient. static void writeIterable(List target, int at, Iterable source) { RangeError.checkValueInInterval(at, 0, target.length, "at"); int index = at; int targetLength = target.length; for (var element in source) { if (index == targetLength) { throw IndexError.withLength(index, targetLength, indexable: target); } target[index] = element; index++; } } /// Returns a view of this list as a list of [R] instances. /// /// If this list contains only instances of [R], all read operations /// will work correctly. If any operation tries to read an element /// that is not an instance of [R], the access will throw instead. /// /// Elements added to the list (e.g., by using [add] or [addAll]) /// must be instances of [R] to be valid arguments to the adding function, /// and they must also be instances of [E] to be accepted by /// this list as well. /// /// Methods which accept `Object?` as argument, like [contains] and [remove], /// will pass the argument directly to the this list's method /// without any checks. /// That means that you can do `listOfStrings.cast().remove("a")` /// successfully, even if it looks like it shouldn't have any effect. /// /// Typically implemented as `List.castFrom(this)`. List cast(); /// The object at the given [index] in the list. /// /// The [index] must be a valid index of this list, /// which means that `index` must be non-negative and /// less than [length]. E operator [](int index); /// Sets the value at the given [index] in the list to [value]. /// /// The [index] must be a valid index of this list, /// which means that `index` must be non-negative and /// less than [length]. void operator []=(int index, E value); /// The first element of the list. /// /// The list must be non-empty when accessing its first element. /// /// The first element of a list can be modified, unlike an [Iterable]. /// A `list.first` is equivalent to `list[0]`, /// both for getting and setting the value. /// /// ```dart /// final numbers = [1, 2, 3]; /// print(numbers.first); // 1 /// numbers.first = 10; /// print(numbers.first); // 10 /// numbers.clear(); /// numbers.first; // Throws. /// ``` void set first(E value); /// The last element of the list. /// /// The list must be non-empty when accessing its last element. /// /// The last element of a list can be modified, unlike an [Iterable]. /// A `list.last` is equivalent to `theList[theList.length - 1]`, /// both for getting and setting the value. /// /// ```dart /// final numbers = [1, 2, 3]; /// print(numbers.last); // 3 /// numbers.last = 10; /// print(numbers.last); // 10 /// numbers.clear(); /// numbers.last; // Throws. /// ``` void set last(E value); /// The number of objects in this list. /// /// The valid indices for a list are `0` through `length - 1`. /// ```dart /// final numbers = [1, 2, 3]; /// print(numbers.length); // 3 /// ``` int get length; /// Setting the `length` changes the number of elements in the list. /// /// The list must be growable. /// If [newLength] is greater than current length, /// new entries are initialized to `null`, /// so [newLength] must not be greater than the current length /// if the element type [E] is non-nullable. /// /// ```dart /// final maybeNumbers = [1, null, 3]; /// maybeNumbers.length = 5; /// print(maybeNumbers); // [1, null, 3, null, null] /// maybeNumbers.length = 2; /// print(maybeNumbers); // [1, null] /// /// final numbers = [1, 2, 3]; /// numbers.length = 1; /// print(numbers); // [1] /// numbers.length = 5; // Throws, cannot add `null`s. /// ``` set length(int newLength); /// Adds [value] to the end of this list, /// extending the length by one. /// /// The list must be growable. /// /// ```dart /// final numbers = [1, 2, 3]; /// numbers.add(4); /// print(numbers); // [1, 2, 3, 4] /// ``` void add(E value); /// Appends all objects of [iterable] to the end of this list. /// /// Extends the length of the list by the number of objects in [iterable]. /// The list must be growable. /// /// ```dart /// final numbers = [1, 2, 3]; /// numbers.addAll([4, 5, 6]); /// print(numbers); // [1, 2, 3, 4, 5, 6] /// ``` void addAll(Iterable iterable); /// An [Iterable] of the objects in this list in reverse order. /// ```dart /// final numbers = ['two', 'three', 'four']; /// final reverseOrder = numbers.reversed; /// print(reverseOrder.toList()); // [four, three, two] /// ``` Iterable get reversed; /// Sorts this list according to the order specified by the [compare] function. /// /// The [compare] function must act as a [Comparator]. /// ```dart /// final numbers = ['two', 'three', 'four']; /// // Sort from shortest to longest. /// numbers.sort((a, b) => a.length.compareTo(b.length)); /// print(numbers); // [two, four, three] /// ``` /// The default [List] implementations use [Comparable.compare] if /// [compare] is omitted. /// ```dart /// final numbers = [13, 2, -11, 0]; /// numbers.sort(); /// print(numbers); // [-11, 0, 2, 13] /// ``` /// In that case, the elements of the list must be [Comparable] to /// each other. /// /// A [Comparator] may compare objects as equal (return zero), even if they /// are distinct objects. /// The sort function is not guaranteed to be stable, so distinct objects /// that compare as equal may occur in any order in the result: /// ```dart /// final numbers = ['one', 'two', 'three', 'four']; /// numbers.sort((a, b) => a.length.compareTo(b.length)); /// print(numbers); // [one, two, four, three] OR [two, one, four, three] /// ``` void sort([int compare(E a, E b)?]); /// Shuffles the elements of this list randomly. /// ```dart /// final numbers = [1, 2, 3, 4, 5]; /// numbers.shuffle(); /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. /// ``` void shuffle([Random? random]); /// The first index of [element] in this list. /// /// Searches the list from index [start] to the end of the list. /// The first time an object `o` is encountered so that `o == element`, /// the index of `o` is returned. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// print(notes.indexOf('re')); // 1 /// /// final indexWithStart = notes.indexOf('re', 2); // 3 /// ``` /// Returns -1 if [element] is not found. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final index = notes.indexOf('fa'); // -1 /// ``` int indexOf(E element, [int start = 0]); /// The first index in the list that satisfies the provided [test]. /// /// Searches the list from index [start] to the end of the list. /// The first time an object `o` is encountered so that `test(o)` is true, /// the index of `o` is returned. /// /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final first = notes.indexWhere((note) => note.startsWith('r')); // 1 /// final second = notes.indexWhere((note) => note.startsWith('r'), 2); // 3 /// ``` /// /// Returns -1 if [element] is not found. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final index = notes.indexWhere((note) => note.startsWith('k')); // -1 /// ``` int indexWhere(bool test(E element), [int start = 0]); /// The last index in the list that satisfies the provided [test]. /// /// Searches the list from index [start] to 0. /// The first time an object `o` is encountered so that `test(o)` is true, /// the index of `o` is returned. /// If [start] is omitted, it defaults to the [length] of the list. /// /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final first = notes.lastIndexWhere((note) => note.startsWith('r')); // 3 /// final second = notes.lastIndexWhere((note) => note.startsWith('r'), /// 2); // 1 /// ``` /// /// Returns -1 if [element] is not found. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final index = notes.lastIndexWhere((note) => note.startsWith('k')); /// print(index); // -1 /// ``` int lastIndexWhere(bool test(E element), [int? start]); /// The last index of [element] in this list. /// /// Searches the list backwards from index [start] to 0. /// /// The first time an object `o` is encountered so that `o == element`, /// the index of `o` is returned. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// const startIndex = 2; /// final index = notes.lastIndexOf('re', startIndex); // 1 /// ``` /// If [start] is not provided, this method searches from the end of the /// list. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final index = notes.lastIndexOf('re'); // 3 /// ``` /// Returns -1 if [element] is not found. /// ```dart /// final notes = ['do', 're', 'mi', 're']; /// final index = notes.lastIndexOf('fa'); // -1 /// ``` int lastIndexOf(E element, [int? start]); /// Removes all objects from this list; the length of the list becomes zero. /// /// The list must be growable. /// /// ```dart /// final numbers = [1, 2, 3]; /// numbers.clear(); /// print(numbers.length); // 0 /// print(numbers); // [] /// ``` void clear(); /// Inserts [element] at position [index] in this list. /// /// This increases the length of the list by one and shifts all objects /// at or after the index towards the end of the list. /// /// The list must be growable. /// The [index] value must be non-negative and no greater than [length]. /// /// ```dart /// final numbers = [1, 2, 3, 4]; /// const index = 2; /// numbers.insert(index, 10); /// print(numbers); // [1, 2, 10, 3, 4] /// ``` void insert(int index, E element); /// Inserts all objects of [iterable] at position [index] in this list. /// /// This increases the length of the list by the length of [iterable] and /// shifts all later objects towards the end of the list. /// /// The list must be growable. /// The [index] value must be non-negative and no greater than [length]. /// ```dart /// final numbers = [1, 2, 3, 4]; /// final insertItems = [10, 11]; /// numbers.insertAll(2, insertItems); /// print(numbers); // [1, 2, 10, 11, 3, 4] /// ``` void insertAll(int index, Iterable iterable); /// Overwrites elements with the objects of [iterable]. /// /// The elements of [iterable] are written into this list, /// starting at position [index]. /// This operation does not increase the length of the list. /// /// The [index] must be non-negative and no greater than [length]. /// /// The [iterable] must not have more elements than what can fit from [index] /// to [length]. /// /// If `iterable` is based on this list, its values may change _during_ the /// `setAll` operation. /// ```dart /// final list = ['a', 'b', 'c', 'd']; /// list.setAll(1, ['bee', 'sea']); /// print(list); // [a, bee, sea, d] /// ``` void setAll(int index, Iterable iterable); /// Removes the first occurrence of [value] from this list. /// /// Returns true if [value] was in the list, false otherwise. /// The list must be growable. /// /// ```dart /// final parts = ['head', 'shoulders', 'knees', 'toes']; /// final retVal = parts.remove('head'); // true /// print(parts); // [shoulders, knees, toes] /// ``` /// The method has no effect if [value] was not in the list. /// ```dart /// final parts = ['shoulders', 'knees', 'toes']; /// // Note: 'head' has already been removed. /// final retVal = parts.remove('head'); // false /// print(parts); // [shoulders, knees, toes] /// ``` bool remove(Object? value); /// Removes the object at position [index] from this list. /// /// This method reduces the length of `this` by one and moves all later objects /// down by one position. /// /// Returns the removed value. /// /// The [index] must be in the range `0 ≤ index < length`. /// The list must be growable. /// ```dart /// final parts = ['head', 'shoulder', 'knees', 'toes']; /// final retVal = parts.removeAt(2); // knees /// print(parts); // [head, shoulder, toes] /// ``` E removeAt(int index); /// Removes and returns the last object in this list. /// /// The list must be growable and non-empty. /// ```dart /// final parts = ['head', 'shoulder', 'knees', 'toes']; /// final retVal = parts.removeLast(); // toes /// print(parts); // [head, shoulder, knees] /// ``` E removeLast(); /// Removes all objects from this list that satisfy [test]. /// /// An object `o` satisfies [test] if `test(o)` is true. /// ```dart /// final numbers = ['one', 'two', 'three', 'four']; /// numbers.removeWhere((item) => item.length == 3); /// print(numbers); // [three, four] /// ``` /// The list must be growable. void removeWhere(bool test(E element)); /// Removes all objects from this list that fail to satisfy [test]. /// /// An object `o` satisfies [test] if `test(o)` is true. /// ```dart /// final numbers = ['one', 'two', 'three', 'four']; /// numbers.retainWhere((item) => item.length == 3); /// print(numbers); // [one, two] /// ``` /// The list must be growable. void retainWhere(bool test(E element)); /// Returns the concatenation of this list and [other]. /// /// Returns a new list containing the elements of this list followed by /// the elements of [other]. /// /// The default behavior is to return a normal growable list. /// Some list types may choose to return a list of the same type as themselves /// (see [Uint8List.+]); List operator +(List other); /// Returns a new list containing the elements between [start] and [end]. /// /// The new list is a `List` containing the elements of this list at /// positions greater than or equal to [start] and less than [end] in the same /// order as they occur in this list. /// /// ```dart /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; /// print(colors.sublist(1, 3)); // [green, blue] /// ``` /// /// If [end] is omitted, it defaults to the [length] of this list. /// /// ```dart /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; /// print(colors.sublist(3)); // [orange, pink] /// ``` /// /// The `start` and `end` positions must satisfy the relations /// 0 ≤ `start` ≤ `end` ≤ [length]. /// If `end` is equal to `start`, then the returned list is empty. List sublist(int start, [int? end]); /// Creates an [Iterable] that iterates over a range of elements. /// /// The returned iterable iterates over the elements of this list /// with positions greater than or equal to [start] and less than [end]. /// /// The provided range, [start] and [end], must be valid at the time /// of the call. /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. /// An empty range (with `end == start`) is valid. /// /// The returned [Iterable] behaves like `skip(start).take(end - start)`. /// That is, it does *not* break if this list changes size, it just /// ends early if it reaches the end of the list early /// (if `end`, or even `start`, becomes greater than [length]). /// ```dart /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; /// final firstRange = colors.getRange(0, 3); /// print(firstRange.join(', ')); // red, green, blue /// /// final secondRange = colors.getRange(2, 5); /// print(secondRange.join(', ')); // blue, orange, pink /// ``` Iterable getRange(int start, int end); /// Writes some elements of [iterable] into a range of this list. /// /// Copies the objects of [iterable], skipping [skipCount] objects first, /// into the range from [start], inclusive, to [end], exclusive, of this list. /// ```dart /// final list1 = [1, 2, 3, 4]; /// final list2 = [5, 6, 7, 8, 9]; /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items /// // of list1. /// const skipCount = 3; /// list1.setRange(1, 3, list2, skipCount); /// print(list1); // [1, 8, 9, 4] /// ``` /// The provided range, given by [start] and [end], must be valid. /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. /// An empty range (with `end == start`) is valid. /// /// The [iterable] must have enough objects to fill the range from `start` /// to `end` after skipping [skipCount] objects. /// /// If `iterable` is this list, the operation correctly copies the elements /// originally in the range from `skipCount` to `skipCount + (end - start)` to /// the range `start` to `end`, even if the two ranges overlap. /// /// If `iterable` depends on this list in some other way, no guarantees are /// made. void setRange(int start, int end, Iterable iterable, [int skipCount = 0]); /// Removes a range of elements from the list. /// /// Removes the elements with positions greater than or equal to [start] /// and less than [end], from the list. /// This reduces the list's length by `end - start`. /// /// The provided range, given by [start] and [end], must be valid. /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. /// An empty range (with `end == start`) is valid. /// /// The list must be growable. /// ```dart /// final numbers = [1, 2, 3, 4, 5]; /// numbers.removeRange(1, 4); /// print(numbers); // [1, 5] /// ``` void removeRange(int start, int end); /// Overwrites a range of elements with [fillValue]. /// /// Sets the positions greater than or equal to [start] and less than [end], /// to [fillValue]. /// /// The provided range, given by [start] and [end], must be valid. /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. /// An empty range (with `end == start`) is valid. /// /// If the element type is not nullable, the [fillValue] must be provided and /// must be non-`null`. /// /// Example: /// ```dart /// final words = List.filled(5, 'old'); /// print(words); // [old, old, old, old, old] /// words.fillRange(1, 3, 'new'); /// print(words); // [old, new, new, old, old] /// ``` void fillRange(int start, int end, [E? fillValue]); /// Replaces a range of elements with the elements of [replacements]. /// /// Removes the objects in the range from [start] to [end], /// then inserts the elements of [replacements] at [start]. /// ```dart /// final numbers = [1, 2, 3, 4, 5]; /// final replacements = [6, 7]; /// numbers.replaceRange(1, 4, replacements); /// print(numbers); // [1, 6, 7, 5] /// ``` /// The provided range, given by [start] and [end], must be valid. /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. /// An empty range (with `end == start`) is valid. /// /// The operation `list.replaceRange(start, end, replacements)` /// is roughly equivalent to: /// ```dart /// final numbers = [1, 2, 3, 4, 5]; /// numbers.removeRange(1, 4); /// final replacements = [6, 7]; /// numbers.insertAll(1, replacements); /// print(numbers); // [1, 6, 7, 5] /// ``` /// but may be more efficient. /// /// The list must be growable. /// This method does not work on fixed-length lists, even when [replacements] /// has the same number of elements as the replaced range. In that case use /// [setRange] instead. void replaceRange(int start, int end, Iterable replacements); /// An unmodifiable [Map] view of this list. /// /// The map uses the indices of this list as keys and the corresponding objects /// as values. The `Map.keys` [Iterable] iterates the indices of this list /// in numerical order. /// ```dart /// var words = ['fee', 'fi', 'fo', 'fum']; /// var map = words.asMap(); // {0: fee, 1: fi, 2: fo, 3: fum} /// map.keys.toList(); // [0, 1, 2, 3] /// ``` Map asMap(); /// Whether this list is equal to [other]. /// /// Lists are, by default, only equal to themselves. /// Even if [other] is also a list, the equality comparison /// does not compare the elements of the two lists. bool operator ==(Object other); } ƒzNN<G M6A(8.<9NPLIEH" R/*0>++ GL#%,NF8;K2&O#+P2*'*21*B/K(MQ=D.PANPGNPA6RQLG3L L*MN,/D)/ G)2( M 66O=";' 80G5&0$ HE67 ?1O? H1I&GH1&," J"QJO(P+ F)">GGH&H+ 9)D%FHJN9P@K2LIL2;PK+@ A'!C 5), (,5LMIMMA&"#M>GEEDM?P@IH<368B68)%CI1.&!" $BHD.& ! *A&" G!5-A//7(&9 ,#!&& AM!&!, %B8-: R98(<, E,) ENLB? K 62 ,63 ,O!&" :J9!K)!* %JL<!K)$). 3:=$?EPN1&' 0>@!D30 ?<040 =R!=!C1. 8/C1/ >;?7' !*F;?7$ !*:J@Q%LJQ(I4 EI2 A&E*DDKGP5LF9BI081; ,DLQ'*H.$ EP5M3NQBN P1K*7EP5!,! (7OEP5P+1$1 6H>>,#0& EP5B ,!#*& !!PNC/RM6B) -7>6"dart:core/list.dartVorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/array_patch.dartˆ// 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. import 'dart:_internal' show makeFixedListUnmodifiable, makeListFixedLength, patch; @patch class List { @patch factory List.empty({bool growable = false}) { return growable ? [] : _List(0); } @patch @pragma("vm:prefer-inline") factory List.filled(int length, E fill, {bool growable = false}) => growable ? _GrowableList.filled(length, fill) : _List.filled(length, fill); @patch factory List.from(Iterable elements, {bool growable = true}) { // If elements is an Iterable, we won't need a type-test for each // element. if (elements is Iterable) { return List.of(elements, growable: growable); } List list = _GrowableList(0); for (E e in elements) { list.add(e); } if (growable) return list; return makeListFixedLength(list); } @patch @pragma("vm:prefer-inline") factory List.of(Iterable elements, {bool growable = true}) => growable ? _GrowableList.of(elements) : _List.of(elements); @patch @pragma("vm:prefer-inline") factory List.generate( int length, E generator(int index), { bool growable = true, }) => growable ? _GrowableList.generate(length, generator) : _List.generate(length, generator); @patch factory List.unmodifiable(Iterable elements) { final result = List.from(elements, growable: false); return makeFixedListUnmodifiable(result); } } // Used by Dart_ListLength. @pragma("vm:entry-point", "call") int _listLength(List list) => list.length; // Used by Dart_ListGetRange, Dart_ListGetAsBytes. @pragma("vm:entry-point", "call") Object? _listGetAt(List list, int index) => list[index]; // Used by Dart_ListSetAt, Dart_ListSetAsBytes. @pragma("vm:entry-point", "call") void _listSetAt(List list, int index, Object? value) { list[index] = value; } HNN<@ 0+ O.' AI#4(& CH 5. 1<."+3"90"7 dart:core-patch/array_patch.dartÀeÀh³Borg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/map.dartÀP// Copyright (c) 2011, 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"; /// A collection of key/value pairs, from which you retrieve a value /// using its associated key. /// /// There is a finite number of keys in the map, /// and each key has exactly one value associated with it. /// /// Maps, and their keys and values, can be iterated. /// The order of iteration is defined by the individual type of map. /// Examples: /// /// * The plain [HashMap] is unordered (no order is guaranteed), /// * the [LinkedHashMap] iterates in key insertion order, /// * and a sorted map like [SplayTreeMap] iterates the keys in sorted order. /// /// It is generally not allowed to modify the map (add or remove keys) while /// an operation is being performed on the map, for example in functions called /// during a [forEach] call. /// Modifying the map while iterating the keys or values /// may also break the iteration. /// /// It is generally not allowed to modify the equality of keys (and thus not /// their hashcode) while they are in the map. Some specialized subtypes may be /// more permissive, in which case they should document this behavior. /// /// Key equality must be an equality relation. If the key stored in a map /// and the key used for lookup do not agree on whether the two are equal, /// so equality is not *symmetric*, then lookup behavior is unspecified. abstract interface class Map { /// Creates an empty [LinkedHashMap]. /// /// This constructor is equivalent to the non-const map literal `{}`. /// /// A `LinkedHashMap` requires the keys to implement compatible /// `operator==` and `hashCode`. /// It iterates in key insertion order. // TODO: @Deprecated("Use literal {} instead") external factory Map(); /// Creates a [LinkedHashMap] with the same keys and values as [other]. /// /// The keys must all be instances of [K] and the values of [V]. /// The [other] map itself can have any type, unlike for [Map.of], /// and the key and value types are checked (and can fail) at run-time. /// /// Prefer using [Map.of] when possible, and only use `Map.from` /// to create a new map with more precise types than the original, /// and when it's known that all the keys and values have those /// more precise types. /// /// A `LinkedHashMap` requires the keys to implement compatible /// `operator==` and `hashCode`. /// It iterates in key insertion order. /// ```dart /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth', 4: 'Mars'}; /// final mapFrom = Map.from(planets); /// print(mapFrom); // {1: Mercury, 2: Venus, 3: Earth, 4: Mars} /// ``` factory Map.from(Map other) = LinkedHashMap.from; /// Creates a [LinkedHashMap] with the same keys and values as [other]. /// /// A `LinkedHashMap` requires the keys to implement compatible /// `operator==` and `hashCode`, and it allows `null` as a key. /// It iterates in key insertion order. /// ```dart /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// final mapOf = Map.of(planets); /// print(mapOf); // {1: Mercury, 2: Venus, 3: Earth} /// ``` factory Map.of(Map other) = LinkedHashMap.of; /// Creates an unmodifiable hash-based map containing the entries of [other]. /// /// The keys must all be instances of [K] and the values of [V]. /// The [other] map itself can have any type. /// /// The map requires the keys to implement compatible /// `operator==` and `hashCode`. /// The created map iterates keys in a fixed order, /// preserving the order provided by [other]. /// /// The resulting map behaves like the result of [Map.from], /// except that the map returned by this constructor is not modifiable. /// ```dart /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// final unmodifiableMap = Map.unmodifiable(planets); /// unmodifiableMap[4] = 'Mars'; // Throws /// ``` external factory Map.unmodifiable(Map other); /// Creates an identity map with the default implementation, [LinkedHashMap]. /// /// An identity map uses [identical] for equality and [identityHashCode] /// for hash codes of keys instead of the intrinsic [Object.==] and /// [Object.hashCode] of the keys. /// /// The map iterates in key insertion order. factory Map.identity() = LinkedHashMap.identity; /// Creates a Map instance in which the keys and values are computed from the /// [iterable]. /// /// For each element of the [iterable], a key/value pair is computed /// by applying [key] and [value] respectively to the element of the iterable. /// /// Equivalent to the map literal: /// ```dart /// {for (var v in iterable) key(v): value(v)} /// ``` /// The literal is generally preferable because it allows /// for a more precise typing. /// /// The example below creates a new map from a list of integers. /// The keys of `map` are the `list` values converted to strings, /// and the values of the `map` are the squares of the `list` values: /// ```dart /// final numbers = [1, 2, 3]; /// final map = Map.fromIterable(numbers, /// key: (item) => item.toString(), /// value: (item) => item * item); /// print(map); // {1: 1, 2: 4, 3: 9} /// ``` /// If no values are specified for [key] and [value], /// the default is the identity function. /// In that case, the iterable element must be assignable to the /// key or value type of the created map. /// /// In the following example, the keys and corresponding values of `map` /// are the `list` values directly: /// ```dart /// final numbers = [1, 2, 3]; /// final map = Map.fromIterable(numbers); /// print(map); // {1: 1, 2: 2, 3: 3} /// ``` /// The keys computed by the source [iterable] do not need to be unique. /// The last occurrence of a key will overwrite /// the value of any previous occurrence. /// /// The created map is a [LinkedHashMap]. /// A `LinkedHashMap` requires the keys to implement compatible /// `operator==` and `hashCode`. /// It iterates in key insertion order. factory Map.fromIterable( Iterable iterable, { K key(dynamic element)?, V value(dynamic element)?, }) = LinkedHashMap.fromIterable; /// Creates a map associating the given [keys] to the given [values]. /// /// The map construction iterates over [keys] and [values] simultaneously, /// and adds an entry to the map for each pair of key and value. /// ```dart /// final rings = [false, false, true, true]; /// final planets = {'Earth', 'Mars', 'Jupiter', 'Saturn'}; /// final map = Map.fromIterables(planets, rings); /// print(map); // {Earth: false, Mars: false, Jupiter: true, Saturn: true} /// ``` /// If [keys] contains the same object multiple times, /// the value of the last occurrence overwrites any previous value. /// /// The two [Iterable]s must have the same length. /// /// The created map is a [LinkedHashMap]. /// A `LinkedHashMap` requires the keys to implement compatible /// `operator==` and `hashCode`. /// It iterates in key insertion order. factory Map.fromIterables(Iterable keys, Iterable values) = LinkedHashMap.fromIterables; /// Adapts [source] to be a `Map`. /// /// Any time the set would produce a key or value that is not a [K2] or [V2], /// the access will throw. /// /// Any time [K2] key or [V2] value is attempted added into the adapted map, /// the store will throw unless the key is also an instance of [K] and /// the value is also an instance of [V]. /// /// If all accessed entries of [source] have [K2] keys and [V2] values, /// and if all entries added to the returned map have [K] keys and [V] values, /// then the returned map can be used as a `Map`. /// /// Methods which accept `Object?` as argument, /// like [containsKey], [remove] and [operator []], /// will pass the argument directly to the this map's method /// without any checks. static Map castFrom(Map source) => CastMap(source); /// Creates a new map and adds all entries. /// /// Returns a new `Map` where all entries of [entries] /// have been added in iteration order. /// /// If multiple [entries] have the same key, /// later occurrences overwrite the value of the earlier ones. /// /// Equivalent to the map literal: /// ```dart /// {for (var e in entries) e.key: e.value} /// ``` /// Example: /// ```dart /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14}; /// final map = Map.fromEntries(moonCount.entries); /// ``` factory Map.fromEntries(Iterable> entries) => {}..addEntries(entries); /// Provides a view of this map as having [RK] keys and [RV] instances, /// if necessary. /// /// If this map is already a `Map`, it is returned unchanged. /// /// If this set contains only keys of type [RK] and values of type [RV], /// all read operations will work correctly. /// If any operation exposes a non-[RK] key or non-[RV] value, /// the operation will throw instead. /// /// Entries added to the map must be valid for both a `Map` and a /// `Map`. /// /// Methods which accept `Object?` as argument, /// like [containsKey], [remove] and [operator []], /// will pass the argument directly to the this map's method /// without any checks. /// That means that you can do `mapWithStringKeys.cast().remove("a")` /// successfully, even if it looks like it shouldn't have any effect. Map cast(); /// Whether this map contains the given [value]. /// /// Returns true if any of the values in the map are equal to `value` /// according to the `==` operator. /// ```dart /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14}; /// final moons3 = moonCount.containsValue(3); // false /// final moons82 = moonCount.containsValue(82); // true /// ``` bool containsValue(Object? value); /// Whether this map contains the given [key]. /// /// Returns true if any of the keys in the map are equal to `key` /// according to the equality used by the map. /// ```dart /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14}; /// final containsUranus = moonCount.containsKey('Uranus'); // true /// final containsPluto = moonCount.containsKey('Pluto'); // false /// ``` bool containsKey(Object? key); /// The value for the given [key], or `null` if [key] is not in the map. /// /// Some maps allow `null` as a value. /// For those maps, a lookup using this operator cannot distinguish between a /// key not being in the map, and the key being there with a `null` value. /// Methods like [containsKey] or [putIfAbsent] can be used if the distinction /// is important. V? operator [](Object? key); /// Associates the [key] with the given [value]. /// /// If the key was already in the map, its associated value is changed. /// Otherwise the key/value pair is added to the map. void operator []=(K key, V value); /// The map entries of this [Map]. Iterable> get entries; /// Returns a new map where all entries of this map are transformed by /// the given [convert] function. Map map(MapEntry convert(K key, V value)); /// Adds all key/value pairs of [newEntries] to this map. /// /// If a key of [newEntries] is already in this map, /// the corresponding value is overwritten. /// /// The operation is equivalent to doing `this[entry.key] = entry.value` /// for each [MapEntry] of the iterable. /// ```dart /// final planets = {1: 'Mercury', 2: 'Venus', /// 3: 'Earth', 4: 'Mars'}; /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; /// planets.addEntries(gasGiants.entries); /// planets.addEntries(iceGiants.entries); /// print(planets); /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, /// // 7: Uranus, 8: Neptune} /// ``` void addEntries(Iterable> newEntries); /// Updates the value for the provided [key]. /// /// Returns the new value associated with the key. /// /// If the key is present, invokes [update] with the current value /// and stores the new value in the map. /// /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] /// and adds the key with the returned value to the map. /// /// If the key is not present, [ifAbsent] must be provided. /// ```dart /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', /// 3: 'Earth'}; /// // Update value for known key value 2. /// planetsFromSun.update(2, (value) => 'Venus'); /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} /// /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', /// 3: 'Neptune'}; /// // Key value 8 is missing from list, add it using [ifAbsent]. /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} /// ``` V update(K key, V update(V value), {V ifAbsent()?}); /// Updates all values. /// /// Iterates over all entries in the map and updates them with the result /// of invoking [update]. /// ```dart /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// terrestrial.updateAll((key, value) => value.toUpperCase()); /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} /// ``` void updateAll(V update(K key, V value)); /// Removes all entries of this map that satisfy the given [test]. /// ```dart /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// terrestrial.removeWhere((key, value) => value.startsWith('E')); /// print(terrestrial); // {1: Mercury, 2: Venus} /// ``` void removeWhere(bool test(K key, V value)); /// Look up the value of [key], or add a new entry if it isn't there. /// /// Returns the value associated to [key], if there is one. /// Otherwise calls [ifAbsent] to get a new value, associates [key] to /// that value, and then returns the new value. /// /// That is, if the key is currently in the map, /// `map.putIfAbsent(key, ifAbsent)` is equivalent to `map[key]`. /// If the key is not currently in the map, /// it's instead equivalent to `map[key] = ifAbsent()` /// (but without any guarantee that the `[]` and `[]=` operators are /// actually called to achieve that effect). /// /// ```dart /// final diameters = {1.0: 'Earth'}; /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; /// /// for (final item in otherDiameters.entries) { /// diameters.putIfAbsent(item.key, () => item.value); /// } /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} /// /// // If the key already exists, the current value is returned. /// final result = diameters.putIfAbsent(0.383, () => 'Random'); /// print(result); // Mercury /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} /// ``` /// The [ifAbsent] function is allowed to modify the map, /// and if so, it behaves the same as the equivalent `map[key] = ifAbsent()`. V putIfAbsent(K key, V ifAbsent()); /// Adds all key/value pairs of [other] to this map. /// /// If a key of [other] is already in this map, its value is overwritten. /// /// The operation is equivalent to doing `this[key] = value` for each key /// and associated value in other. It iterates over [other], which must /// therefore not change during the iteration. /// ```dart /// final planets = {1: 'Mercury', 2: 'Earth'}; /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} /// ``` void addAll(Map other); /// Removes [key] and its associated value, if present, from the map. /// /// Returns the value associated with `key` before it was removed. /// Returns `null` if `key` was not in the map. /// /// Note that some maps allow `null` as a value, /// so a returned `null` value doesn't always mean that the key was absent. /// ```dart /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// final removedValue = terrestrial.remove(2); // Venus /// print(terrestrial); // {1: Mercury, 3: Earth} /// ``` V? remove(Object? key); /// Removes all entries from the map. /// /// After this, the map is empty. /// ```dart /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; /// planets.clear(); // {} /// ``` void clear(); /// Applies [action] to each key/value pair of the map. /// /// Calling `action` must not add or remove keys from the map. /// ```dart /// final planetsByMass = {0.81: 'Venus', 1: 'Earth', /// 0.11: 'Mars', 17.15: 'Neptune'}; /// /// planetsByMass.forEach((key, value) { /// print('$key: $value'); /// // 0.81: Venus /// // 1: Earth /// // 0.11: Mars /// // 17.15: Neptune /// }); /// ``` void forEach(void action(K key, V value)); /// The keys of this [Map]. /// /// The returned iterable has efficient `length` and `contains` operations, /// based on [length] and [containsKey] of the map. /// /// The order of iteration is defined by the individual `Map` implementation, /// but must be consistent between changes to the map. /// /// Modifying the map while iterating the keys may break the iteration. Iterable get keys; /// The values of this [Map]. /// /// The values are iterated in the order of their corresponding keys. /// This means that iterating [keys] and [values] in parallel will /// provide matching pairs of keys and values. /// /// The returned iterable has an efficient `length` method based on the /// [length] of the map. Its [Iterable.contains] method is based on /// `==` comparison. /// /// Modifying the map while iterating the values may break the iteration. Iterable get values; /// The number of key/value pairs in the map. int get length; /// Whether there is no key/value pair in the map. bool get isEmpty; /// Whether there is at least one key/value pair in the map. bool get isNotEmpty; } /// A key/value pair representing an entry in a [Map]. /// /// The [Map] interface contains various methods that can /// inspect or modify the map based on entry objects. /// ```dart /// final map = {'1': 'A', '2': 'B'}; /// map.addEntries([ /// MapEntry('3', 'C'), /// MapEntry('4', 'D'), /// ]); /// print(map); // {1: A, 2: B, 3: C, 4: D} /// ``` /// /// Do not extend or implement the `MapEntry` class. /// If the Dart language introduces value types, /// the `MapEntry` class will be changed to such a type, /// and will likely no longer be able to be implemented or extended /// by classes. final class MapEntry { /// The key of the entry. /// /// ```dart /// final map = {'theKey': 'theValue'}; // Map /// var entry = map.entries.first; // MapEntry /// print(entry.key); // 'theKey' /// ``` final K key; /// The value associated to [key] in a map. /// /// ```dart /// final map = {'theKey': 'theValue'}; // Map /// var entry = map.entries.first; // MapEntry /// print(entry.value); // 'theValue' /// ``` final V value; /// Creates an entry with [key] and [value]. const factory MapEntry(K key, V value) = MapEntry._; const MapEntry._(this.key, this.value); /// String representation intended for debugging only. /// /// Not guaranteed to be stable over time. String toString() => "MapEntry($key: $value)"; } ‚NN<E1;6EA;NMP9"MPGJKI%(NB#*7JCEJCEBB#*V6C :JBB*K28 <PC08#60?JK9- BPKF%/9PGQ%7 BB--J! 805E+M;>G-4AFDMP 7LOB> ,EOF4 /H>I23D.9G/5Q3;FCC F { // Factory constructing a Map from a parser generated Map literal. // [elements] contains n key-value pairs. // The keys are at position 2*n and are already type checked by the parser // in checked mode. // The values are at position 2*n+1 and are not yet type checked. @pragma("vm:entry-point", "call") factory Map._fromLiteral(List elements) { var map = LinkedHashMap(); var len = elements.length; for (int i = 1; i < len; i += 2) { map[elements[i - 1]] = elements[i]; } return map; } // Factory constructing a Map for Dart_NewMap C API // from list of keys and values. @pragma("vm:entry-point", "call") factory Map._fromKeyValues(List keys, List values) { var map = LinkedHashMap(); var len = keys.length; for (int i = 0; i < len; i++) { map[keys[i]] = values[i]; } return map; } @patch factory Map.unmodifiable(Map other) { return UnmodifiableMapView(Map.from(other)); } @patch factory Map() => LinkedHashMap(); } // Used by Dart_MapContainsKey. @pragma("vm:entry-point", "call") bool _mapContainsKey(Map map, Object? key) => map.containsKey(key); // Used by Dart_MapGetAt. @pragma("vm:entry-point", "call") Object? _mapGet(Map map, Object? key) => map[key]; // Used by Dart_MapKeys. @pragma("vm:entry-point", "call") List _mapKeys(Map map) => map.keys.toList(); ;NN<$BE,MD$,%'*6#$7%$  (= * "D"3"-dart:core-patch/map_patch.dartÀeÀh³Corg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/null.dart„—// Copyright (c) 2013, 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"; /// The reserved word `null` denotes an object that is the sole instance of /// this class. /// /// The `Null` class is the only class which does not implement `Object`. /// It is a compile-time error for a class to attempt to extend or implement /// [Null]. /// /// The language contains a number of specialized operators for working with /// `null` value. Examples: /// ```dart /// e1! // Throws if e1 is null. /// e2 ?? e3 // Same as e2, unless e2 is null, then use value of e3 /// x ??= e4 // Same as x unless x is null, then same as `x = e4`. /// e5?.foo() // call `foo` on e5, unless e5 is null. /// [...? e6] // spreads e6 into the list literal, unless e6 is null. /// ``` @pragma("vm:entry-point") final class Null { factory Null._uninstantiable() { throw UnsupportedError('class Null cannot be instantiated'); } external int get hashCode; /// Returns the string `"null"`. String toString() => "null"; } #NN<LJM M 'ED6F#A#dart:core/null.dartÀeÀh³\org-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm_shared/lib/null_patch.dart�ó// Copyright (c) 2013, 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. import "dart:_internal" show patch; @patch @pragma('vm:deeply-immutable') @pragma("vm:entry-point") class Null { static const _HASH_CODE = 2011; // The year Dart was announced and a prime. @patch int get hashCode => _HASH_CODE; int get _identityHashCode => _HASH_CODE; } NN<$ N "+dart:core-patch/null_patch.dartÀeÀh³Borg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/num.dartÀ]o// 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"; /// An integer or floating-point number. /// /// It is a compile-time error for any type other than [int] or [double] /// to attempt to extend or implement `num`. /// /// **See also:** /// * [int]: An integer number. /// * [double]: A double-precision floating point number. /// * [Built-in number types](https://dart.dev/language/built-in-types#numbers) /// * [Number representation](https://dart.dev/resources/language/number-representation) sealed class num implements Comparable { /// Test whether this value is numerically equal to `other`. /// /// If both operands are [double]s, they are equal if they have the same /// representation, except that: /// /// * zero and minus zero (0.0 and -0.0) are considered equal. They /// both have the numerical value zero. /// * NaN is not equal to anything, including NaN. If either operand is /// NaN, the result is always false. /// /// If one operand is a [double] and the other is an [int], they are equal if /// the double has an integer value (finite with no fractional part) and /// the numbers have the same numerical value. /// /// If both operands are integers, they are equal if they have the same value. /// /// Returns false if [other] is not a [num]. /// /// Notice that the behavior for NaN is non-reflexive. This means that /// equality of double values is not a proper equality relation, as is /// otherwise required of `operator==`. Using NaN in, e.g., a [HashSet] /// will fail to work. The behavior is the standard IEEE-754 equality of /// doubles. /// /// If you can avoid NaN values, the remaining doubles do have a proper /// equality relation, and can be used safely. /// /// Use [compareTo] for a comparison that distinguishes zero and minus zero, /// and that considers NaN values as equal. bool operator ==(Object other); /// Returns a hash code for a numerical value. /// /// The hash code is compatible with equality. It returns the same value /// for an [int] and a [double] with the same numerical value, and therefore /// the same value for the doubles zero and minus zero. /// /// No guarantees are made about the hash code of NaN values. int get hashCode; /// Compares this to `other`. /// /// Returns a negative number if `this` is less than `other`, zero if they are /// equal, and a positive number if `this` is greater than `other`. /// /// The ordering represented by this method is a total ordering of [num] /// values. All distinct doubles are non-equal, as are all distinct integers, /// but integers are equal to doubles if they have the same numerical /// value. /// /// For doubles, the `compareTo` operation is different from the partial /// ordering given by [operator==], [operator<] and [operator>]. For example, /// IEEE doubles impose that `0.0 == -0.0` and all comparison operations on /// NaN return false. /// /// This function imposes a complete ordering for doubles. When using /// `compareTo`, the following properties hold: /// /// - All NaN values are considered equal, and greater than any numeric value. /// - -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero /// negative value. /// - Negative infinity is less than all other values and positive infinity is /// greater than all non-NaN values. /// - All other values are compared using their numeric value. /// /// Examples: /// ```dart /// print(1.compareTo(2)); // => -1 /// print(2.compareTo(1)); // => 1 /// print(1.compareTo(1)); // => 0 /// /// // The following comparisons yield different results than the /// // corresponding comparison operators. /// print((-0.0).compareTo(0.0)); // => -1 /// print(double.nan.compareTo(double.nan)); // => 0 /// print(double.infinity.compareTo(double.nan)); // => -1 /// /// // -0.0, and NaN comparison operators have rules imposed by the IEEE /// // standard. /// print(-0.0 == 0.0); // => true /// print(double.nan == double.nan); // => false /// print(double.infinity < double.nan); // => false /// print(double.nan < double.infinity); // => false /// print(double.nan == double.infinity); // => false /// ``` int compareTo(num other); /// Adds [other] to this number. /// /// The result is an [int], as described by [int.+], /// if both this number and [other] is an integer, /// otherwise the result is a [double]. num operator +(num other); /// Subtracts [other] from this number. /// /// The result is an [int], as described by [int.-], /// if both this number and [other] is an integer, /// otherwise the result is a [double]. num operator -(num other); /// Multiplies this number by [other]. /// /// The result is an [int], as described by [int.*], /// if both this number and [other] are integers, /// otherwise the result is a [double]. num operator *(num other); /// Euclidean modulo of this number by [other]. /// /// Returns the remainder of the Euclidean division. /// The Euclidean division of two integers `a` and `b` /// yields two integers `q` and `r` such that /// `a == b * q + r` and `0 <= r < b.abs()`. /// /// The Euclidean division is only defined for integers, but can be easily /// extended to work with doubles. In that case, `q` is still an integer, /// but `r` may have a non-integer value that still satisfies `0 <= r < |b|`. /// /// The sign of the returned value `r` is always positive. /// /// See [remainder] for the remainder of the truncating division. /// /// The result is an [int], as described by [int.%], /// if both this number and [other] are integers, /// otherwise the result is a [double]. /// /// Example: /// ```dart /// print(5 % 3); // 2 /// print(-5 % 3); // 1 /// print(5 % -3); // 2 /// print(-5 % -3); // 1 /// ``` num operator %(num other); /// Divides this number by [other]. double operator /(num other); /// Truncating division operator. /// /// Performs truncating division of this number by [other]. /// Truncating division is division where a fractional result /// is converted to an integer by rounding towards zero. /// /// If both operands are [int]s, then [other] must not be zero. /// Then `a ~/ b` corresponds to `a.remainder(b)` /// such that `a == (a ~/ b) * b + a.remainder(b)`. /// /// If either operand is a [double], then the other operand is converted /// to a double before performing the division and truncation of the result. /// Then `a ~/ b` is equivalent to `(a / b).truncate()`. /// This means that the intermediate result of the double division /// must be a finite integer (not an infinity or [double.nan]). int operator ~/(num other); /// The negation of this value. /// /// The negation of a number is a number of the same kind /// (`int` or `double`) representing the negation of the /// numbers numerical value (the result of subtracting the /// number from zero), if that value *exists*. /// /// Negating a double gives a number with the same magnitude /// as the original value (`number.abs() == (-number).abs()`), /// and the opposite sign (`-(number.sign) == (-number).sign`). /// /// Negating an integer, `-number`, is equivalent to subtracting /// it from zero, `0 - number`. /// /// (Both properties generally also hold for the other type, /// but with a few edge case exceptions). num operator -(); /// The remainder of the truncating division of `this` by [other]. /// /// The result `r` of this operation satisfies: /// `this == (this ~/ other) * other + r`. /// As a consequence, the remainder `r` has the same sign as the dividend /// `this`. /// /// The result is an [int], as described by [int.remainder], /// if both this number and [other] are integers, /// otherwise the result is a [double]. /// /// Example: /// ```dart /// print(5.remainder(3)); // 2 /// print(-5.remainder(3)); // -2 /// print(5.remainder(-3)); // 2 /// print(-5.remainder(-3)); // -2 /// ``` num remainder(num other); /// Whether this number is numerically smaller than [other]. /// /// Returns `true` if this number is smaller than [other]. /// Returns `false` if this number is greater than or equal to [other] /// or if either value is a NaN value like [double.nan]. bool operator <(num other); /// Whether this number is numerically smaller than or equal to [other]. /// /// Returns `true` if this number is smaller than or equal to [other]. /// Returns `false` if this number is greater than [other] /// or if either value is a NaN value like [double.nan]. bool operator <=(num other); /// Whether this number is numerically greater than [other]. /// /// Returns `true` if this number is greater than [other]. /// Returns `false` if this number is smaller than or equal to [other] /// or if either value is a NaN value like [double.nan]. bool operator >(num other); /// Whether this number is numerically greater than or equal to [other]. /// /// Returns `true` if this number is greater than or equal to [other]. /// Returns `false` if this number is smaller than [other] /// or if either value is a NaN value like [double.nan]. bool operator >=(num other); /// Whether this number is a Not-a-Number value. /// /// Is `true` if this number is the [double.nan] value /// or any other of the possible [double] NaN values. /// Is `false` if this number is an integer, /// a finite double or an infinite double ([double.infinity] /// or [double.negativeInfinity]). /// /// All numbers satisfy exactly one of [isInfinite], [isFinite] /// and `isNaN`. bool get isNaN; /// Whether this number is negative. /// /// A number is negative if it's smaller than zero, /// or if it is the double `-0.0`. /// This precludes a NaN value like [double.nan] from being negative. bool get isNegative; /// Whether this number is positive infinity or negative infinity. /// /// Only satisfied by [double.infinity] and [double.negativeInfinity]. /// /// All numbers satisfy exactly one of `isInfinite`, [isFinite] /// and [isNaN]. bool get isInfinite; /// Whether this number is finite. /// /// The only non-finite numbers are NaN values, positive infinity, and /// negative infinity. All integers are finite. /// /// All numbers satisfy exactly one of [isInfinite], `isFinite` /// and [isNaN]. bool get isFinite; /// The absolute value of this number. /// /// The absolute value is the value itself, if the value is non-negative, /// and `-value` if the value is negative. /// /// Integer overflow may cause the result of `-value` to stay negative. /// /// ```dart /// print((2).abs()); // 2 /// print((-2.5).abs()); // 2.5 /// ``` num abs(); /// Negative one, zero or positive one depending on the sign and /// numerical value of this number. /// /// The value minus one if this number is less than zero, /// plus one if this number is greater than zero, /// and zero if this number is equal to zero. /// /// Returns NaN if this number is a [double] NaN value. /// /// Returns a number of the same type as this number. /// For doubles, `(-0.0).sign` is `-0.0`. /// /// The result satisfies: /// ```dart /// n == n.sign * n.abs() /// ``` /// for all numbers `n` (except NaN, because NaN isn't `==` to itself). num get sign; /// The integer closest to this number. /// /// Rounds away from zero when there is no closest integer: /// `(3.5).round() == 4` and `(-3.5).round() == -4`. /// /// The number must be finite (see [isFinite]). /// /// If the value is greater than the highest representable positive integer, /// the result is that highest positive integer. /// If the value is smaller than the highest representable negative integer, /// the result is that highest negative integer. int round(); /// The greatest integer no greater than this number. /// /// Rounds fractional values towards negative infinity. /// /// The number must be finite (see [isFinite]). /// /// If the value is greater than the highest representable positive integer, /// the result is that highest positive integer. /// If the value is smaller than the highest representable negative integer, /// the result is that highest negative integer. int floor(); /// The least integer no smaller than `this`. /// /// Rounds fractional values towards positive infinity. /// /// The number must be finite (see [isFinite]). /// /// If the value is greater than the highest representable positive integer, /// the result is that highest positive integer. /// If the value is smaller than the highest representable negative integer, /// the result is that highest negative integer. int ceil(); /// The integer obtained by discarding any fractional digits from `this`. /// /// Rounds fractional values towards zero. /// /// The number must be finite (see [isFinite]). /// /// If the value is greater than the highest representable positive integer, /// the result is that highest positive integer. /// If the value is smaller than the highest representable negative integer, /// the result is that highest negative integer. int truncate(); /// The double integer value closest to this value. /// /// Rounds away from zero when there is no closest integer: /// `(3.5).roundToDouble() == 4` and `(-3.5).roundToDouble() == -4`. /// /// If this is already an integer valued double, including `-0.0`, or it is a /// non-finite double value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. /// This means that for a value `d` in the range `-0.5 < d < 0.0`, /// the result is `-0.0`. double roundToDouble(); /// Returns the greatest double integer value no greater than `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is a /// non-finite double value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. double floorToDouble(); /// Returns the least double integer value no smaller than `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is a /// non-finite double value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. double ceilToDouble(); /// Returns the double integer value obtained by discarding any fractional /// digits from the double value of `this`. /// /// If this is already an integer valued double, including `-0.0`, or it is a /// non-finite double value, the value is returned unmodified. /// /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and /// in the range `0.0 < d < 1.0` it will return 0.0. double truncateToDouble(); /// Returns this [num] clamped to be in the range [lowerLimit]-[upperLimit]. /// /// The comparison is done using [compareTo] and therefore takes `-0.0` into /// account. This also implies that [double.nan] is treated as the maximal /// double value. /// /// The arguments [lowerLimit] and [upperLimit] must form a valid range where /// `lowerLimit.compareTo(upperLimit) <= 0`. /// /// Example: /// ```dart /// var result = 10.5.clamp(5, 10.0); // 10.0 /// result = 0.75.clamp(5, 10.0); // 5 /// result = (-10).clamp(-5, 5.0); // -5 /// result = (-0.0).clamp(-5, 5.0); // -0.0 /// ``` num clamp(num lowerLimit, num upperLimit); /// Truncates this [num] to an integer and returns the result as an [int]. /// /// Equivalent to [truncate]. int toInt(); /// This number as a [double]. /// /// If an integer number is not precisely representable as a [double], /// an approximation is returned. double toDouble(); /// A decimal-point string-representation of this number. /// /// Converts this number to a [double] /// before computing the string representation, /// as by [toDouble]. /// /// If the absolute value of `this` is greater than or equal to `10^21`, then /// this methods returns an exponential representation computed by /// `this.toStringAsExponential()`. Otherwise the result /// is the closest string representation with exactly [fractionDigits] digits /// after the decimal point. If [fractionDigits] equals 0, then the decimal /// point is omitted. /// /// The parameter [fractionDigits] must be an integer satisfying: /// `0 <= fractionDigits <= 20`. /// /// Examples: /// ```dart /// 1.toStringAsFixed(3); // 1.000 /// (4321.12345678).toStringAsFixed(3); // 4321.123 /// (4321.12345678).toStringAsFixed(5); // 4321.12346 /// 123456789012345.toStringAsFixed(3); // 123456789012345.000 /// 10000000000000000.toStringAsFixed(4); // 10000000000000000.0000 /// 5.25.toStringAsFixed(0); // 5 /// ``` String toStringAsFixed(int fractionDigits); /// An exponential string-representation of this number. /// /// Converts this number to a [double] /// before computing the string representation. /// /// If [fractionDigits] is given, then it must be an integer satisfying: /// `0 <= fractionDigits <= 20`. In this case the string contains exactly /// [fractionDigits] after the decimal point. Otherwise, without the parameter, /// the returned string uses the shortest number of digits that accurately /// represent this number. /// /// If [fractionDigits] equals 0, then the decimal point is omitted. /// Examples: /// ```dart /// 1.toStringAsExponential(); // 1e+0 /// 1.toStringAsExponential(3); // 1.000e+0 /// 123456.toStringAsExponential(); // 1.23456e+5 /// 123456.toStringAsExponential(3); // 1.235e+5 /// 123.toStringAsExponential(0); // 1e+2 /// ``` String toStringAsExponential([int? fractionDigits]); /// A string representation with [precision] significant digits. /// /// Converts this number to a [double] /// and returns a string representation of that value /// with exactly [precision] significant digits. /// /// The parameter [precision] must be an integer satisfying: /// `1 <= precision <= 21`. /// /// Examples: /// ```dart /// 1.toStringAsPrecision(2); // 1.0 /// 1e15.toStringAsPrecision(3); // 1.00e+15 /// 1234567.toStringAsPrecision(3); // 1.23e+6 /// 1234567.toStringAsPrecision(9); // 1234567.00 /// 12345678901234567890.toStringAsPrecision(20); // 12345678901234567168 /// 12345678901234567890.toStringAsPrecision(14); // 1.2345678901235e+19 /// 0.00000012345.toStringAsPrecision(15); // 1.23450000000000e-7 /// 0.0000012345.toStringAsPrecision(15); // 0.00000123450000000000 /// ``` String toStringAsPrecision(int precision); /// The shortest string that correctly represents this number. /// /// All [double]s in the range `10^-6` (inclusive) to `10^21` (exclusive) /// are converted to their decimal representation with at least one digit /// after the decimal point. For all other doubles, /// except for special values like `NaN` or `Infinity`, this method returns an /// exponential representation (see [toStringAsExponential]). /// /// Returns `"NaN"` for [double.nan], `"Infinity"` for [double.infinity], and /// `"-Infinity"` for [double.negativeInfinity]. /// /// An [int] is converted to a decimal representation with no decimal point. /// /// Examples: /// ```dart /// (0.000001).toString(); // "0.000001" /// (0.0000001).toString(); // "1e-7" /// (111111111111111111111.0).toString(); // "111111111111111110000.0" /// (100000000000000000000.0).toString(); // "100000000000000000000.0" /// (1000000000000000000000.0).toString(); // "1e+21" /// (1111111111111111111111.0).toString(); // "1.1111111111111111e+21" /// 1.toString(); // "1" /// 111111111111111111111.toString(); // "111111111111111110000" /// 100000000000000000000.toString(); // "100000000000000000000" /// 1000000000000000000000.toString(); // "1000000000000000000000" /// 1111111111111111111111.toString(); // "1111111111111111111111" /// 1.234e5.toString(); // 123400 /// 1234.5e6.toString(); // 1234500000 /// 12.345e67.toString(); // 1.2345e+68 /// ``` /// Note: the conversion may round the output if the returned string /// is accurate enough to uniquely identify the input-number. /// For example the most precise representation of the [double] `9e59` equals /// `"899999999999999918767229449717619953810131273674690656206848"`, but /// this method returns the shorter (but still uniquely identifying) `"9e59"`. String toString(); /// Parses a string containing a number literal into a number. /// /// The method first tries to read the [input] as integer (similar to /// [int.parse] without a radix). /// If that fails, it tries to parse the [input] as a double (similar to /// [double.parse]). /// If that fails, too, it throws a [FormatException]. /// /// Rather than throwing and immediately catching the [FormatException], /// instead use [tryParse] to handle a potential parsing error. /// /// For any number `n`, this function satisfies /// `identical(n, num.parse(n.toString()))` (except when `n` is a NaN `double` /// with a payload). /// /// The [onError] parameter is deprecated and will be removed. /// Instead of `num.parse(string, (string) { ... })`, /// you should use `num.tryParse(string) ?? (...)`. /// /// Examples: /// ```dart /// var value = num.parse('2021'); // 2021 /// value = num.parse('3.14'); // 3.14 /// value = num.parse(' 3.14 \xA0'); // 3.14 /// value = num.parse('0.'); // 0.0 /// value = num.parse('.0'); // 0.0 /// value = num.parse('-1.e3'); // -1000.0 /// value = num.parse('1234E+7'); // 12340000000.0 /// value = num.parse('+.12e-9'); // 1.2e-10 /// value = num.parse('-NaN'); // NaN /// value = num.parse('0xFF'); // 255 /// value = num.parse(double.infinity.toString()); // Infinity /// value = num.parse('1f'); // Throws. /// ``` static num parse(String input, [@deprecated num onError(String input)?]) { num? result = tryParse(input); if (result != null) return result; throw FormatException(input); } /// Parses a string containing a number literal into a number. /// /// Like [parse], except that this function returns `null` for invalid inputs /// instead of throwing. /// /// Examples: /// ```dart /// var value = num.tryParse('2021'); // 2021 /// value = num.tryParse('3.14'); // 3.14 /// value = num.tryParse(' 3.14 \xA0'); // 3.14 /// value = num.tryParse('0.'); // 0.0 /// value = num.tryParse('.0'); // 0.0 /// value = num.tryParse('-1.e3'); // -1000.0 /// value = num.tryParse('1234E+7'); // 12340000000.0 /// value = num.tryParse('+.12e-9'); // 1.2e-10 /// value = num.tryParse('-NaN'); // NaN /// value = num.tryParse('0xFF'); // 255 /// value = num.tryParse(double.infinity.toString()); // Infinity /// value = num.tryParse('1f'); // null /// ``` static num? tryParse(String input) { String source = input.trim(); // TODO(lrn): Optimize to detect format and result type in one check. return int.tryParse(source) ?? double.tryParse(source); } } ‚\NN<)I- :PY.?K#H.L+PK1Q/IIJKJ1O."1KO:@ QFKPH KPNH2QQQ)A&%%D-.8=K%4889 #75**75*)74*2790/MLP=D74* & $>@;B46KO;EB"<;=1?ABC"?,E2-L?4*"$#% ?=I;KI=;?=I;KI=;398/?%B'6%HEIB%I2B)L-J" C&<40:8, J*>82O3O38:2O3O30:2O3O3L-2O3O36>HPAKPEHPAKCEPAKEM.PAKI7OOMP/0)+. -M !I$<)2PE;PND#&79BF$ .;)2KLRMG/353/ 7C)83?-214LKDG -ALL6Q@P3O,(JJ8IDDEE&** G@PLQAH$K9KB2QA86-)0&&-5/((A* M#'"AP0,3))082++D* '"J<dart:core/num.dartUorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/core_patch.dart‹Š// Copyright (c) 2013, 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. /// Note: the VM concatenates all patch files into a single patch file. This /// file is the first patch in "dart:core" which contains all the imports /// used by patches of that library. We plan to change this when we have a /// shared front end and simply use parts. import "dart:_internal" as internal show Symbol; import "dart:_internal" show allocateOneByteString, allocateTwoByteString, checkValidWeakTarget, ClassID, CodeUnits, copyRangeFromUint8ListToOneByteString, EfficientLengthIterable, FinalizerBase, FinalizerBaseMembers, FinalizerEntry, FixedLengthListBase, IterableElementError, ListIterator, Lists, POWERS_OF_TEN, SubListIterable, SystemHash, UnmodifiableListMixin, makeFixedListUnmodifiable, makeListFixedLength, patch, reachabilityFence, unsafeCast, writeIntoOneByteString, writeIntoTwoByteString; import "dart:async" show Completer, DeferredLoadException, Future, Timer, Zone; import "dart:collection" show HashMap, LinkedHashMap, LinkedList, LinkedListEntry, ListBase, MapBase, Maps, UnmodifiableMapBase, UnmodifiableMapView; import "dart:convert" show ascii, Encoding, json, latin1, utf8; import "dart:ffi" show Pointer, Struct, Union, NativePort; import "dart:isolate" show Isolate, RawReceivePort; import "dart:typed_data" show Uint8List, Uint16List, Int32List; import 'dart:_vm' show FinalThreadLocal, ThreadLocal; /// These are the additional parts of this patch library: part "array.dart"; part "double.dart"; part "double_patch.dart"; part "errors_patch.dart"; part "expando_patch.dart"; part "finalizer_patch.dart"; part "function.dart"; part "function_patch.dart"; part "growable_array.dart"; part "identical_patch.dart"; part "integers.dart"; part "invocation_mirror_patch.dart"; part "lib_prefix.dart"; part "object_patch.dart"; part "record_patch.dart"; part "regexp_patch.dart"; part "stacktrace.dart"; part "stopwatch_patch.dart"; part "string_patch.dart"; part "type_patch.dart"; part "uri_patch.dart"; part "weak_property.dart"; @patch @pragma('vm:deeply-immutable') class num { num _addFromInteger(int other); num _subFromInteger(int other); num _mulFromInteger(int other); int _truncDivFromInteger(int other); num _moduloFromInteger(int other); num _remainderFromInteger(int other); bool _greaterThanFromInteger(int other); bool _equalToInteger(int other); } @patch class StackTrace { @patch @pragma("vm:external-name", "StackTrace_current") external static StackTrace get current; } lNN<MJK+1 /!# P @;4@6:% """'%(+# 4*dart:core-patch/core_patch.dartÀeÀh³Eorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/object.dartÀJà// 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"; /// The base class for all Dart objects except `null`. /// /// Because `Object` is a root of the non-nullable Dart class hierarchy, /// every other non-`Null` Dart class is a subclass of `Object`. /// /// When you define a class, you should consider overriding [toString] /// to return a string describing an instance of that class. /// You might also need to define [hashCode] and [operator ==], as described in the /// [Implementing map keys](https://dart.dev/libraries/dart-core#implementing-map-keys) /// section of the [introduction to `dart:core`](https://dart.dev/libraries/dart-core). @pragma("vm:entry-point") @pragma('dyn-module:language-impl:extendable') class Object { /// Creates a new [Object] instance. /// /// [Object] instances have no meaningful state, and are only useful /// through their identity. An [Object] instance is equal to itself /// only. @pragma("vm:recognized", "other") const Object(); /// The equality operator. /// /// The default behavior for all [Object]s is to return true if and /// only if this object and [other] are the same object. /// /// Override this method to specify a different equality relation on /// a class. The overriding method must still be an equivalence relation. /// That is, it must be: /// /// * Total: It must return a boolean for all arguments. It should never throw. /// /// * Reflexive: For all objects `o`, `o == o` must be true. /// /// * Symmetric: For all objects `o1` and `o2`, `o1 == o2` and `o2 == o1` must /// either both be true, or both be false. /// /// * Transitive: For all objects `o1`, `o2`, and `o3`, if `o1 == o2` and /// `o2 == o3` are true, then `o1 == o3` must be true. /// /// The method should also be consistent over time, /// so whether two objects are equal should only change /// if at least one of the objects was modified. /// /// If a subclass overrides the equality operator, it should override /// the [hashCode] method as well to maintain consistency. external bool operator ==(Object other); /// The hash code for this object. /// /// A hash code is a single integer which represents the state of the object /// that affects [operator ==] comparisons. /// /// All objects have hash codes. /// The default hash code implemented by [Object] /// represents only the identity of the object, /// the same way as the default [operator ==] implementation only considers objects /// equal if they are identical (see [identityHashCode]). /// /// If [operator ==] is overridden to use the object state instead, /// the hash code must also be changed to represent that state, /// otherwise the object cannot be used in hash based data structures /// like the default [Set] and [Map] implementations. /// /// Hash codes must be the same for objects that are equal to each other /// according to [operator ==]. /// The hash code of an object should only change if the object changes /// in a way that affects equality. /// There are no further requirements for the hash codes. /// They need not be consistent between executions of the same program /// and there are no distribution guarantees. /// /// Objects that are not equal are allowed to have the same hash code. /// It is even technically allowed that all instances have the same hash code, /// but if clashes happen too often, /// it may reduce the efficiency of hash-based data structures /// like [HashSet] or [HashMap]. /// /// If a subclass overrides [hashCode], it should override the /// [operator ==] operator as well to maintain consistency. external int get hashCode; /// A string representation of this object. /// /// Some classes have a default textual representation, /// often paired with a static `parse` function (like [int.parse]). /// These classes will provide the textual representation as /// their string representation. /// /// Other classes have no meaningful textual representation /// that a program will care about. /// Such classes will typically override `toString` to provide /// useful information when inspecting the object, /// mainly for debugging or logging. external String toString(); /// Invoked when a nonexistent method or property is accessed. /// /// A dynamic member invocation can attempt to call a member which /// doesn't exist on the receiving object. Example: /// ```dart /// dynamic object = 1; /// object.add(42); // Statically allowed, run-time error /// ``` /// This invalid code will invoke the `noSuchMethod` method /// of the integer `1` with an [Invocation] representing the /// `.add(42)` call and arguments (which then throws). /// /// Classes can override [noSuchMethod] to provide custom behavior /// for such invalid dynamic invocations. /// /// A class with a non-default [noSuchMethod] invocation can also /// omit implementations for members of its interface. /// Example: /// ```dart /// class MockList implements List { /// noSuchMethod(Invocation invocation) { /// log(invocation); /// super.noSuchMethod(invocation); // Will throw. /// } /// } /// void main() { /// MockList().add(42); /// } /// ``` /// This code has no compile-time warnings or errors even though /// the `MockList` class has no concrete implementation of /// any of the `List` interface methods. /// Calls to `List` methods are forwarded to `noSuchMethod`, /// so this code will `log` an invocation similar to /// `Invocation.method(#add, [42])` and then throw. /// /// If a value is returned from `noSuchMethod`, /// it becomes the result of the original invocation. /// If the value is not of a type that can be returned by the original /// invocation, a type error occurs at the invocation. /// /// The default behavior is to throw a [NoSuchMethodError]. @pragma("vm:entry-point") @pragma("wasm:entry-point") external dynamic noSuchMethod(Invocation invocation); /// A representation of the runtime type of the object. external Type get runtimeType; /// Creates a combined hash code for a number of objects. /// /// The hash code is computed for all arguments that are actually /// supplied, even if they are `null`, by numerically combining the /// [Object.hashCode] of each argument. /// /// Example: /// ```dart /// class SomeObject { /// final Object a, b, c; /// SomeObject(this.a, this.b, this.c); /// bool operator ==(Object other) => /// other is SomeObject && a == other.a && b == other.b && c == other.c; /// int get hashCode => Object.hash(a, b, c); /// } /// ``` /// /// The computed value will be consistent when the function is called /// with the same arguments multiple times /// during the execution of a single program. /// /// The hash value generated by this function is *not* guaranteed to be stable /// over different runs of the same program, /// or between code run in different isolates of the same program. /// The exact algorithm used may differ between different platforms, /// or between different versions of the platform libraries, /// and it may depend on values that change on each program execution. /// /// The [hashAll] function gives the same result as this function when /// called with a collection containing the actual arguments /// to this function in the same order. @Since("2.14") static int hash( Object? object1, Object? object2, [ Object? object3 = sentinelValue, Object? object4 = sentinelValue, Object? object5 = sentinelValue, Object? object6 = sentinelValue, Object? object7 = sentinelValue, Object? object8 = sentinelValue, Object? object9 = sentinelValue, Object? object10 = sentinelValue, Object? object11 = sentinelValue, Object? object12 = sentinelValue, Object? object13 = sentinelValue, Object? object14 = sentinelValue, Object? object15 = sentinelValue, Object? object16 = sentinelValue, Object? object17 = sentinelValue, Object? object18 = sentinelValue, Object? object19 = sentinelValue, Object? object20 = sentinelValue, ]) { if (sentinelValue == object3) { return SystemHash.hash2(object1.hashCode, object2.hashCode, _hashSeed); } if (sentinelValue == object4) { return SystemHash.hash3( object1.hashCode, object2.hashCode, object3.hashCode, _hashSeed, ); } if (sentinelValue == object5) { return SystemHash.hash4( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, _hashSeed, ); } if (sentinelValue == object6) { return SystemHash.hash5( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, _hashSeed, ); } if (sentinelValue == object7) { return SystemHash.hash6( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, _hashSeed, ); } if (sentinelValue == object8) { return SystemHash.hash7( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, _hashSeed, ); } if (sentinelValue == object9) { return SystemHash.hash8( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, _hashSeed, ); } if (sentinelValue == object10) { return SystemHash.hash9( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, _hashSeed, ); } if (sentinelValue == object11) { return SystemHash.hash10( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, _hashSeed, ); } if (sentinelValue == object12) { return SystemHash.hash11( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, _hashSeed, ); } if (sentinelValue == object13) { return SystemHash.hash12( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, _hashSeed, ); } if (sentinelValue == object14) { return SystemHash.hash13( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, _hashSeed, ); } if (sentinelValue == object15) { return SystemHash.hash14( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, _hashSeed, ); } if (sentinelValue == object16) { return SystemHash.hash15( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, _hashSeed, ); } if (sentinelValue == object17) { return SystemHash.hash16( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, object16.hashCode, _hashSeed, ); } if (sentinelValue == object18) { return SystemHash.hash17( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, object16.hashCode, object17.hashCode, _hashSeed, ); } if (sentinelValue == object19) { return SystemHash.hash18( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, object16.hashCode, object17.hashCode, object18.hashCode, _hashSeed, ); } if (sentinelValue == object20) { return SystemHash.hash19( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, object16.hashCode, object17.hashCode, object18.hashCode, object19.hashCode, _hashSeed, ); } return SystemHash.hash20( object1.hashCode, object2.hashCode, object3.hashCode, object4.hashCode, object5.hashCode, object6.hashCode, object7.hashCode, object8.hashCode, object9.hashCode, object10.hashCode, object11.hashCode, object12.hashCode, object13.hashCode, object14.hashCode, object15.hashCode, object16.hashCode, object17.hashCode, object18.hashCode, object19.hashCode, object20.hashCode, _hashSeed, ); } /// Creates a combined hash code for a sequence of objects. /// /// The hash code is computed for elements in [objects], /// even if they are `null`, /// by numerically combining the [Object.hashCode] of each element /// in iteration order. /// /// The result of `hashAll([o])` is not `o.hashCode`. /// /// Example: /// ```dart /// class SomeObject { /// final List path; /// SomeObject(this.path); /// bool operator ==(Object other) { /// if (other is SomeObject) { /// if (path.length != other.path.length) return false; /// for (int i = 0; i < path.length; i++) { /// if (path[i] != other.path[i]) return false; /// } /// return true; /// } /// return false; /// } /// /// int get hashCode => Object.hashAll(path); /// } /// ``` /// /// The computed value will be consistent when the function is called /// again with objects that have the same hash codes in the same order /// during an execution of a single program. /// /// The hash value generated by this function is *not* guaranteed to be stable /// over different runs of the same program, /// or between code run in different isolates of the same program. /// The exact algorithm used may differ between different platforms, /// or between different versions of the platform libraries, /// and it may depend on values that change on each program execution. @Since("2.14") static int hashAll(Iterable objects) { int hash = _hashSeed; for (var object in objects) { hash = SystemHash.combine(hash, object.hashCode); } return SystemHash.finish(hash); } /// Creates a combined hash code for a collection of objects. /// /// The hash code is computed for elements in [objects], /// even if they are `null`, /// by numerically combining the [Object.hashCode] of each element /// in an order independent way. /// /// The result of `hashAllUnordered({o})` is not `o.hashCode`. /// /// Example: /// ```dart /// bool setEquals(Set set1, Set set2) { /// var hashCode1 = Object.hashAllUnordered(set1); /// var hashCode2 = Object.hashAllUnordered(set2); /// if (hashCode1 != hashCode2) return false; /// // Compare elements ... /// } /// ``` /// /// The computed value will be consistent when the function is called /// again with objects that have the same hash codes /// during an execution of a single program, /// even if the objects are not necessarily in the same order, /// /// The hash value generated by this function is *not* guaranteed to be stable /// over different runs of the same program. /// The exact algorithm used may differ between different platforms, /// or between different versions of the platform libraries, /// and it may depend on values that change on each program execution. @Since("2.14") static int hashAllUnordered(Iterable objects) { int sum = 0; int count = 0; const int mask = 0x3FFFFFFF; for (var object in objects) { int objectHash = SystemHash.smear(object.hashCode); sum = (sum + objectHash) & mask; count += 1; } return SystemHash.hash2(sum, count, 0); } } // A per-isolate seed for hash code computations. @pragma("wasm:entry-point") final int _hashSeed = identityHashCode(Object); ‚YNN<7IAG=TXX/'GF $F;GLS@R0M<6:3H=+%O.#42V<FBH8K"J&.:F?#>&A5'AE6< >?9E,D9-.9  C=+?7628I9>8:!<DF*,*Q2 H-0Q/EG?II?*%%%%%%%&&&&&&&&&&&$N$ $ $ $ $ $ % %  %  %  %  %  %  %  %  %  %  >;E8!)%@4:  2 HI/Q/EG?I2"8$@;E#A4772  H7/AQ/G?I;!":',20dart:core/object.dartÀeÀh³œœWorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/_internal/vm/lib/object_patch.dart‹¦// 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 "core_patch.dart"; @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", "dart:core#_Smi") @pragma("vm:external-name", "Object_getHash") external int _getHash(obj); @patch @pragma("vm:entry-point") class Object { // The VM has its own implementation of equals. @patch @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:exact-result-type", bool) @pragma("vm:prefer-inline") @pragma("vm:external-name", "Object_equals") external bool operator ==(Object other); @patch int get hashCode => _getHash(this); int get _identityHashCode => _getHash(this); @patch @pragma("vm:external-name", "Object_toString") external String toString(); // A statically dispatched version of Object.toString. @pragma("vm:external-name", "Object_toString") external static String _toString(obj); @patch @pragma("vm:entry-point", "call") dynamic noSuchMethod(Invocation invocation) { // TODO(regis): Remove temp constructor identifier 'withInvocation'. throw NoSuchMethodError.withInvocation(this, invocation); } @patch @pragma("vm:recognized", "asm-intrinsic") // Result type is either "dart:core#_Type" or "dart:core#_FunctionType". @pragma("vm:external-name", "Object_runtimeType") external Type get runtimeType; @pragma("vm:recognized", "asm-intrinsic") @pragma("vm:entry-point", "call") @pragma("vm:exact-result-type", bool) @pragma("vm:external-name", "Object_haveSameRuntimeType") external static bool _haveSameRuntimeType(a, b); // Call this function instead of inlining instanceof, thus collecting // type feedback and reducing code size of unoptimized code. @pragma("vm:entry-point", "call") @pragma("vm:external-name", "Object_instanceOf") external bool _instanceOf( instantiatorTypeArguments, functionTypeArguments, type, ); // Group of functions for implementing fast simple instance of. @pragma("vm:entry-point", "call") @pragma("vm:external-name", "Object_simpleInstanceOf") external bool _simpleInstanceOf(type); @pragma("vm:entry-point", "call") bool _simpleInstanceOfTrue(type) => true; @pragma("vm:entry-point", "call") bool _simpleInstanceOfFalse(type) => false; } // Used by DartLibraryCalls::Equals. @pragma("vm:entry-point", "call") bool _objectEquals(Object? o1, Object? o2) => o1 == o2; // Used by DartLibraryCalls::HashCode. @pragma("vm:entry-point", "call") int _objectHashCode(Object? obj) => obj.hashCode; // Used by DartLibraryCalls::ToString. @pragma("vm:entry-point", "call") String _objectToString(Object? obj) => obj.toString(); // Used by DartEntry::InvokeNoSuchMethod. @pragma("vm:entry-point", "call") dynamic _objectNoSuchMethod(Object? obj, Invocation invocation) => obj.noSuchMethod(invocation); ZNN<*2.2 ,(/+ &/ 191) $0I> ,K4!,$(<3H?$3 B$9)$,$.%"8'"2'"7*"C"!dart:core-patch/object_patch.dartÀeÀh³Forg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/pattern.dart“í// Copyright (c) 2011, 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"; /// An interface for basic searches within strings. abstract interface class Pattern { /// Matches this pattern against the string repeatedly. /// /// If [start] is provided, matching will start at that index. /// /// The returned iterable lazily finds non-overlapping matches /// of the pattern in the [string]. /// If a user only requests the first match, /// this function should not compute all possible matches. /// /// The matches are found by repeatedly finding the first match /// of the pattern in the string, initially starting from [start], /// and then from the end of the previous match (but always /// at least one position later than the *start* of the previous /// match, in case the pattern matches an empty substring). /// ```dart /// RegExp exp = RegExp(r'(\w+)'); /// var str = 'Dash is a bird'; /// Iterable matches = exp.allMatches(str, 8); /// for (final Match m in matches) { /// String match = m[0]!; /// print(match); /// } /// ``` /// The output of the example is: /// ``` /// a /// bird /// ``` Iterable allMatches(String string, [int start = 0]); /// Matches this pattern against the start of `string`. /// /// Returns a match if the pattern matches a substring of [string] /// starting at [start], and `null` if the pattern doesn't match /// at that point. /// /// The [start] must be non-negative and no greater than `string.length`. /// ```dart /// final string = 'Dash is a bird'; /// /// var regExp = RegExp(r'bird'); /// var match = regExp.matchAsPrefix(string, 10); // Match found. /// /// regExp = RegExp(r'bird'); /// match = regExp.matchAsPrefix(string); // null /// ``` Match? matchAsPrefix(String string, [int start = 0]); } /// A result from searching within a string. /// /// A `Match` or an [Iterable] of `Match` objects is returned from /// the [Pattern] matching methods /// ([Pattern.allMatches] and [Pattern.matchAsPrefix]). /// /// The following example finds all matches of a [RegExp] in a [String] /// and iterates through the returned iterable of `Match` objects. /// ```dart /// final regExp = RegExp(r'(\w+)'); /// const string = 'Parse my string'; /// final matches = regExp.allMatches(string); /// for (final m in matches) { /// String match = m[0]!; /// print(match); /// } /// ``` /// The output of the example is: /// ``` /// Parse /// my /// string /// ``` /// Some patterns, regular expressions in particular, may record substrings /// that were part of the matching. These are called _groups_ in the `Match` /// object. Some patterns may never have any groups, and their matches always /// have zero [groupCount]. abstract interface class Match { /// The index in the string where the match starts. int get start; /// The index in the string after the last character of the match. int get end; /// The string matched by the given [group]. /// /// If [group] is 0, returns the entire match of the pattern. /// /// The result may be `null` if the pattern didn't assign a value to it /// as part of this match. /// ```dart import:convert /// /// final string = '[00:13.37] This is a chat message.'; /// final regExp = RegExp(r'^\[\s*(\d+):(\d+)\.(\d+)\]\s*(.*)$'); /// final match = regExp.firstMatch(string)!; /// final message = jsonEncode(match.group(0)!); // '[00:13.37] This is a chat message.' /// final hours = jsonEncode(match.group(1)!); // '00' /// final minutes = jsonEncode(match.group(2)!); // '13' /// final seconds = jsonEncode(match.group(3)!); // '37' /// final text = jsonEncode(match.group(4)!); // 'This is a chat message.' /// ``` String? group(int group); /// The string matched by the given [group]. /// /// If [group] is 0, returns the match of the pattern. /// /// Short alias for [Match.group]. String? operator [](int group); /// A list of the groups with the given indices. /// /// The list contains the strings returned by [group] for each index in /// [groupIndices]. /// ```dart import:convert /// /// final string = '[00:13.37] This is a chat message.'; /// final regExp = RegExp(r'^\[\s*(\d+):(\d+)\.(\d+)\]\s*(.*)$'); /// final match = regExp.firstMatch(string)!; /// final message = jsonEncode(match.groups([1, 2, 3, 4])); /// // ['00','13','37','This is a chat message.'] /// ``` List groups(List groupIndices); /// Returns the number of captured groups in the match. /// /// Some patterns may capture parts of the input that was used to /// compute the full match. This is the number of captured groups, /// which is also the maximal allowed argument to the [group] method. int get groupCount; /// The string on which this match was computed. String get input; /// The pattern used to search in [input]. Pattern get pattern; } €’NN<4#:AA&/=BE>C>%"8' $  >:ECL'$D 4 8-C#8HC %&/"  LMN!6E/@J;D0[9;;M /9%"3J;D0>4 0:DEH3-dart:core/pattern.dartEorg-dartlang-sdk:///flutter/third_party/dart/sdk/lib/core/record.dart™P// Copyright (c) 2022, 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"; /// A record value. /// /// The `Record` class is a supertype of all *record types*, /// but is not itself the runtime type of any object instances /// _(it's an abstract class)_. /// All objects that implement `Record` has a record type as their runtime type. /// /// A record value, described by a record type, consists of a number of fields, /// which are each either positional or named. /// /// Record values and record types are written similarly to /// argument lists and simplified function type parameter lists (no `required` /// modifier allowed, or needed, since record fields are never optional). /// Example: /// ```dart /// (int, String, {bool isValid}) triple = (1, "one", isValid: true); /// ``` /// is syntactically similar to /// ```dart /// typedef F = void Function(int, String, {bool isValid}); /// void callIt(F f) => f(1, "one", isValid: true); /// ``` /// /// Every record and record type has a *shape*, /// given by the number of positional fields and the names of named fields. /// For example: /// ```dart continued /// (double value, String name, {String isValid}) another = ( /// 3.14, "Pi", isValid: "real"); /// ``` /// is another record declaration with the same *shape* (two positional fields, /// one named field named `isValid`), but with a different type. /// The names written on the positional fields are entirely for documentation /// purposes, they have no effect on the program _(same as names on positional /// parameters in function types, like `typedef F = int Function(int value);`, /// where the identifier `value` has no effect)_. /// /// Record values are mainly destructured using patterns, like: /// ```dart continued /// switch (triple) { /// case (int value, String name, isValid: bool ok): // .... /// } /// ``` /// The individual fields can also be accessed using named getters, /// using `$1`, `$2`, etc. for positional fields, and the names themselves /// for named fields. /// ```dart continued /// int value = triple.$1; /// String name = triple.$2; /// bool ok = triple.isValid; /// ``` /// Because of that, some identifiers cannot be used as names of named fields: /// * The names of `Object` members: `hashCode`, `runtimeType`, `toString` and /// `noSuchMethod`. /// * The name of a positional getter in the same record, so `(0, $1: 0)` is /// invalid, but `(0, $2: 0)` is valid, since there is no positional field /// with getter `$2` in *that* record shape. _(It'll still be confusing, /// and should be avoided in practice.)_ /// * Also, no name starting with an underscore, `_`, is allowed. Field names /// cannot be library private. /// /// The run-time type of a record object is a record type, and as such, a /// subtype of [Record], and transitively of [Object] and its supertypes. /// /// Record values do not have a persistent [identical] behavior. /// A reference to a record object can change *at any time* to a reference /// to another record object with the same shape and field values. /// /// Other than that, a record type can only be a subtype of another record /// type with the same shape, and only if the former record type's field types /// are subtypes of the other record type's corresponding field types. /// That is, `(int, String, {bool isValid})` is a subtype of /// `(num, String, {Object isValid})`, because they have the same shape, /// and the field types are pointwise subtypes. /// Record types with different shapes are unrelated to each other. abstract final class Record { /// A `Type` object representing the runtime type of a record. /// /// The runtime type of a record is defined by the record's *shape*, /// the number of positional fields and names of named fields, /// and the runtime type of each of those fields. /// (The runtime type of the record does not depend on /// the `runtimeType` getter of its fields' values, /// which may have overridden [Object.runtimeType].) /// /// The `Type` object of a record type is only equal to another `Type` object /// for a record type, and only if the other record type has the same shape, /// and if the corresponding fields have the same types. Type get runtimeType; /// A hash-code compatible with `==`. /// /// Since [operator==] is defined in terms of the `==` operators of /// the record's field values, the hash code is also computed based on the /// [Object.hashCode] of the field values. /// /// There is no guaranteed order in which the `hashCode` of field values /// is accessed. /// It's unspecified how those values are combined, /// other than it being consistent throughout a single program execution. int get hashCode; /// Checks whether [other] has the same shape and equal fields to this record. /// /// A record is only equal to another record with the same *shape*, /// and then only when the value of every field is equal, /// occording to its `==`, to the corresponding field value of [other]. /// /// There is no guaranteed order in which field value equality is checked, /// and it's unspecified whether further fields are checked after finding /// corresponding fields which are not equal. /// It's not even guaranteed that the order is consistent within a single /// program execution. /// /// As usual, be very careful around objects which break the equality /// contract, like [double.nan] which is not equal to itself. /// For example /// ```dart /// var pair = ("a", double.nan); /// if (pair != pair) print("Oops"); /// ``` /// will print the "Oops", because `pair == pair` is defined to be equal to /// `"a" == "a" & double.nan == double.nan`, which is false. bool operator ==(Object other); /// Creates a string-representation of the record. /// /// The string representation is only intended for debugging, /// and may differ between development and production. /// There is no guaranteed format in production mode. /// /// In development mode, the string will strive to be a parenthesized /// comma separated list of field representations, where the field /// representation is the `toString` of the value for positional fields, /// and `someName:` followed by that for a named field named `someName`. String toString(); } €‘NN<=? QP/'PANOO2@?DKOOMMK+N!JJAKCKOG=I0DAGA4967PO;(FM-K6LQF. /// /// Dart provides the basic regexp matching algorithm as [matchAsPrefix], /// which checks if the regexp matches a part of the input starting /// at a specific position. /// If the regexp matches, Dart returns the details of the match as a /// [RegExpMatch]. /// /// You can build all the other methods of [RegExp] from that basic match /// check. /// /// The most common use of a regexp is to *search* for a match in the input. /// The [firstMatch] method provides this functionality. /// This method searches a string for the first position where the regexp /// matches. /// Again, if a match is found, Dart returns its details as a [RegExpMatch]. /// /// The following example finds the first match of a regular expression in /// a string. /// ```dart /// RegExp exp = RegExp(r'(\w+)'); /// String str = 'Parse my string'; /// RegExpMatch? match = exp.firstMatch(str); /// print(match![0]); // "Parse" /// ``` /// Use [allMatches] to look for all matches of a regular expression in /// a string. /// /// The following example finds all matches of a regular expression in /// a string. /// ```dart /// RegExp exp = RegExp(r'(\w+)'); /// String str = 'Parse my string'; /// Iterable matches = exp.allMatches(str); /// for (final m in matches) { /// print(m[0]); /// } /// ``` /// The output of the example is: /// ``` /// Parse /// my /// string /// ``` /// /// The preceding examples use a _raw string_, /// a specific string type that prefixes the string literal with `r`. /// Use a raw string to treat each character, including `\` and `$`, /// in a string as a literal character. Each character then gets passed /// to the [RegExp] parser. /// You should use a raw string as the argument to the /// [RegExp] constructor. /// /// **Performance Notice**: Regular expressions do not resolve issues /// magically. /// Anyone can write a regexp that performs inefficiently when /// applied to some string inputs. /// Often, such a regexp will perform well enough on small or common /// inputs, but have pathological performance on large and uncommon inputs. /// This inconsistent behavior makes performance issues harder to detect /// in testing. /// /// A regexp might not find text any faster than using `String` operations /// to inspect a string. /// The strength of regexp comes from the ability to specify /// *somewhat* complicated patterns in very few characters. /// These regexps provide reasonable efficiency in most common cases. /// This conciseness comes at a cost of readability. /// Due to their syntactic complexity, regexes cannot be considered /// self documenting. /// /// Dart regexps implement the ECMAScript RegExp specification. /// This specification provides a both common and well-known regexp behavior. /// When compiling Dart for the web, the compiled code can use the browser’s /// regexp implementation. /// /// The specification defines ECMAScript regexp behavior using *backtracking*. /// When a regexp can choose between different ways to match, /// it tries each way in the order given in the pattern. /// For example: `RegExp(r"(foo|bar)baz")` wants to check for `foo` or `bar`, /// so it checks for `foo` first. /// If continuing along that path doesn't match the input, /// the regexp implementation *backtracks*. /// The implementation resets to the original state from before /// checking for `foo`, /// forgetting all the work it has done after that, /// and then tries the next choice; `bar` in this example. /// /// The specification defines these choices /// and the order in which they must be attempted. /// If a regexp could match an input in more than one way, /// the order of the choices decides which match the regexp returns. /// Commonly used regexps order their matching choices to ensure /// a specific result. /// The ECMAScript regexp specification limits how Dart /// can implement regular expressions. /// It must be a backtracking implementation which checks choices in /// a specific order. /// Dart cannot choose a different regexp implementation, /// because then regexp matching would behave differently. /// /// The backtracking approach works, but at a cost. /// For some regexps and some inputs, /// finding a *correct* match can take a *lot* of tries. /// It can take even more tries to reject an input /// that the regexp *almost* matches. /// /// A well-known dangerous regexp pattern comes from /// nesting quantifiers like `*`: /// /// ```dart /// var re = RegExp(r"^(a*|b)*c"); /// print(re.hasMatch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); /// ``` /// /// The regexp pattern doesn't match the input string of only `a`s /// as the input doesn’t contain the required `c`. /// There exists an _exponential_ number of different ways for `(a*|b)*` /// to match all the `a`s. /// The backtracking regexp implementation tries *all* of them /// before deciding that none of those can lead to a complete match. /// Each extra `a` added to the input doubles the time the regexp /// takes to return `false`. /// (When backtracking has this exponential potential, it is called /// [“catastrophic backtrackingâ€�](https://www.google.com/search?q=regexp+catastrophic+backtracking)). /// /// Sequential quantifiers provide another dangerous pattern, /// but they provide “onlyâ€� polynomial complexity. /// /// ```dart /// // Like `\w*-\d`, but check for `b` and `c` in that order. /// var re = RegExp(r"^\w*(b)?\w*(c)?\w*-\d"); /// print(re.hasMatch("a" * 512)); /// ``` /// /// Again the input doesn’t match, but `RegExp` must try *n*3 ways /// to match the *n* `a`s _before_ deciding that. /// Doubling the input’s length increases the time to return `false` /// _eightfold_. /// This exponent increases with the number of sequential quantifiers. /// /// Both of these patterns look trivial when reduced to such simple regexps. /// However, these "trivial" patterns often arise as parts of /// more complicated regular expressions, /// where your ability to find the problem gets more difficult. /// /// In general, if a regexp has potential for *super-linear complexity*, /// you can craft an input that takes an inordinate amount of time to search. /// These patterns can then be used for [denial of service attacks](https://en.wikipedia.org/wiki/ReDoS) /// if you apply vulnerable regexp patterns to user-provided inputs. /// /// No guaranteed solution exists for this problem. /// Be careful to not use regexps with super-linear behavior /// where the program may match that regexp against inputs /// with no guaranteed match. /// /// Rules of thumb to avoid regexps with super-linear execution time include: /// /// * Whenever the regexp has a choice, try to make sure /// that the choice can be made based on the next character /// (or very limited look-ahead). /// This limits the need to perform a lot of computation along both choices. /// * When using quantifiers, ensure that the same string cannot match /// both one and more-than-one iteration of the quantifier's /// regular expression. /// (For `(a*|b)*`, the string `"aa"` can match both /// `(a*|b){1}` and `(a*|b){2}`.) /// * Most uses of Dart regular expressions *search* for a match, /// for example using [firstMatch]. /// If you do not *anchor* the pattern /// to the start of a line or input using `^`, /// this search acts as if the regexp began with an implicit `[^]*`. /// Starting your actual regular expression with `.*` /// then results in potential quadratic behavior for the search. /// Use anchors or [matchAsPrefix] where appropriate, /// or avoid starting the regexp with a quantified pattern. /// * *For experts only:* Neither Dart nor ECMAScript have general /// [“atomic groupingâ€�](https://github.com/tc39/proposal-regexp-atomic-operators). /// Other regular expression dialects use this to limit backtracking. /// If an atomic capture group succeeds once, /// the regexp cannot backtrack into the same match later. /// As lookarounds also serve as atomic groups, /// something similar can be achieved using a *lookahead*: /// `var re = RegExp(r"^(?=((a*|b)*))\1d");` /// The preceding example does the same inefficient matching of `(a*|b)*`. /// Once the regexp has matched as far as possible, /// it completes the positive lookahead. /// Then it skips what the lookahead matched using a back-reference. /// After that, it can no longer backtrack /// and try other combinations of `a`s. /// /// Try to reduce how many ways the regexp can match the same string. /// That reduces the number of possible backtracks performed /// when the regexp does not find a match. /// Several guides to [improving the performance of regular expressions](https://www.google.com/search?q=performance+of+regular+expressions) /// exist on the internet. Use these as inspirations, too. @Deprecated.implement( "This class will become 'final' in a future release. " "'Pattern' may be a more appropriate interface to implement.", ) abstract interface class RegExp implements Pattern { /// Constructs a regular expression. /// /// Throws a [FormatException] if [source] does not follow valid regular /// expression syntax. /// /// If your code enables `multiLine`, then `^` and `$` will match /// the beginning and end of a _line_, as well as matching beginning and /// end of the input, respectively. /// /// If your code disables `caseSensitive`, /// then Dart ignores the case of letters when matching. /// For example, with `caseSensitive` disable, the regexp pattern `a` /// matches both `a` and `A`. /// /// If your code enables `unicode`, then Dart treats the pattern as a /// Unicode pattern per the ECMAScript standard. /// /// If your code enables `dotAll`, then the `.` pattern will match _all_ /// characters, including line terminators. /// /// Example: /// /// ```dart /// final wordPattern = RegExp(r'(\w+)'); /// final digitPattern = RegExp(r'(\d+)'); /// ``` /// /// These examples use a _raw string_ as the argument. /// You should prefer to use a raw string as argument to the [RegExp] /// constructor, because it makes it easy to write /// the `\` and `$` characters as regexp reserved characters. /// /// The same examples written using non-raw strings would be: /// ```dart /// final wordPattern = RegExp('(\\w+)'); // Should be raw string. /// final digitPattern = RegExp('(\\d+)'); // Should be raw string. /// ``` /// Use a non-raw string only when you need to use /// string interpolation. For example: /// ```dart /// Pattern keyValuePattern(String keyIdentifier) => /// RegExp('$keyIdentifier=(\\w+)'); /// ``` /// When including a string verbatim into the regexp pattern like this, /// be careful that the string does not contain regular expression /// reserved characters. /// If that risk exists, use the [escape] function to convert those /// characters to safe versions of the reserved characters /// and match only the string itself: /// ```dart /// Pattern keyValuePattern(String anyStringKey) => /// RegExp('${RegExp.escape(anyStringKey)}=(\\w+)'); /// ``` external factory RegExp( String source, { bool multiLine = false, bool caseSensitive = true, bool unicode = false, bool dotAll = false, }); /// Creates regular expression syntax that matches the input [text]. /// /// If [text] contains regular expression reserved characters, /// the resulting regular expression matches those characters literally. /// If [text] contains no regular expression reserved characters, /// Dart returns the expression unmodified. /// /// The reserved characters in regular expressions are: /// `(`, `)`, `[`, `]`, `{`, `}`, `*`, `+`, `?`, `.`, `^`, `$`, `|` and `\`. /// /// Use this method to create a pattern to be included in a /// larger regular expression. Since a [String] is itself a [Pattern] /// which matches itself, converting the string to a regular expression /// isn't needed to search for that exact string. /// ```dart /// print(RegExp.escape('dash@example.com')); // dash@example\.com /// print(RegExp.escape('a+b')); // a\+b /// print(RegExp.escape('a*b')); // a\*b /// print(RegExp.escape('{a-b}')); // \{a-b\} /// print(RegExp.escape('a?')); // a\? /// ``` external static String escape(String text); /// Finds the first match of the regular expression in the string [input]. /// /// Returns `null` if there is no match. /// ```dart /// final string = '[00:13.37] This is a chat message.'; /// final regExp = RegExp(r'c\w*'); /// final match = regExp.firstMatch(string)!; /// print(match[0]); // chat /// ``` RegExpMatch? firstMatch(String input); Iterable allMatches(String input, [int start = 0]); /// Checks whether this regular expression has a match in the [input]. /// /// ```dart /// var string = 'Dash is a bird'; /// var regExp = RegExp(r'(humming)?bird'); /// var match = regExp.hasMatch(string); // true /// /// regExp = RegExp(r'dog'); /// match = regExp.hasMatch(string); // false /// ``` bool hasMatch(String input); /// Finds the string of the first match of this regular expression /// in [input]. /// /// Searches for a match for this regular expression in [input], /// just like [firstMatch], /// but returns only the matched substring if a match is found, /// not a [RegExpMatch]. /// /// ```dart /// var string = 'Dash is a bird'; /// var regExp = RegExp(r'(humming)?bird'); /// var match = regExp.stringMatch(string); // Match /// /// regExp = RegExp(r'dog'); /// match = regExp.stringMatch(string); // No match /// ``` String? stringMatch(String input); /// The regular expression pattern source of this `RegExp`. /// /// ```dart /// final regExp = RegExp(r'\p{L}'); /// print(regExp.pattern); // \p{L} /// ``` String get pattern; /// Whether this regular expression matches multiple lines. /// /// If the regexp does match multiple lines, the "^" and "$" characters /// match the beginning and end of lines. If not, the characters match the /// beginning and end of the input. bool get isMultiLine; /// Whether this regular expression is case sensitive. /// /// If the regular expression is not case sensitive, it will match an input /// letter with a pattern letter even if the two letters are different case /// versions of the same letter. /// ```dart /// final text = 'Parse my string'; /// var regExp = RegExp(r'STRING', caseSensitive: false); /// print(regExp.isCaseSensitive); // false /// print(regExp.hasMatch(text)); // true, matches. /// /// regExp = RegExp(r'STRING', caseSensitive: true); /// print(regExp.isCaseSensitive); // true /// print(regExp.hasMatch(text)); // false, no match. /// ``` bool get isCaseSensitive; /// Whether this regular expression uses Unicode mode. /// /// In Unicode mode, Dart treats UTF-16 surrogate pairs in the original /// string as a single code point and will not match each code unit in the /// pair separately. Otherwise, /// Dart treats the target string as a sequence of individual code /// units and does not treat surrogates as special. /// /// In Unicode mode, Dart restricts the syntax of the RegExp pattern, /// for example disallowing some unescaped uses /// of restricted regexp characters, /// and disallowing unnecessary `\`-escapes ("identity escapes"), /// which have both historically been allowed in non-Unicode mode. /// Dart also allows some pattern features, like Unicode property escapes, /// only in this mode. /// ```dart /// var regExp = RegExp(r'^\p{L}$', unicode: true); /// print(regExp.hasMatch('a')); // true /// print(regExp.hasMatch('b')); // true /// print(regExp.hasMatch('?')); // false /// print(regExp.hasMatch(r'p{L}')); // false /// /// // U+1F600 (😀), one code point, two code units. /// var smiley = '\ud83d\ude00'; /// /// regExp = RegExp(r'^.$', unicode: true); // Matches one code point. /// print(regExp.hasMatch(smiley)); // true /// regExp = RegExp(r'^..$', unicode: true); // Matches two code points. /// print(regExp.hasMatch(smiley)); // false /// /// regExp = RegExp(r'^\p{L}$', unicode: false); /// print(regExp.hasMatch('a')); // false /// print(regExp.hasMatch('b')); // false /// print(regExp.hasMatch('?')); // false /// print(regExp.hasMatch(r'p{L}')); // true /// /// regExp = RegExp(r'^.$', unicode: false); // Matches one code unit. /// print(regExp.hasMatch(smiley)); // false /// regExp = RegExp(r'^..$', unicode: false); // Matches two code units. /// print(regExp.hasMatch(smiley)); // true /// ``` bool get isUnicode; /// Whether "." in this regular expression matches line terminators. /// /// When false, the "." character matches a single character, unless that /// character terminates a line. When true, then the "." character will /// match any single character including line terminators. /// /// This feature is distinct from [isMultiLine]. They affect the behavior /// of different pattern characters, so they can be used together or /// separately. bool get isDotAll; } /// A regular expression match. /// /// Regular expression matches are [Match]es. They also include the ability /// to retrieve the names for any named capture groups and to retrieve /// matches for named capture groups by name instead of by their index. /// /// Example: /// ```dart /// const pattern = /// r'^\[(?