| // 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:async'; |
| |
| import 'codecs.dart'; |
| import 'common.dart'; |
| import 'recording.dart'; |
| import 'result_reference.dart'; |
| |
| /// Base class for recordable file system invocation events. |
| /// |
| /// Instances of this class will be aggregated in a [Recording] |
| abstract class InvocationEvent<T> { |
| /// The object on which the invocation occurred. Will always be non-null. |
| Object get object; |
| |
| /// The return value of the invocation if the invocation completed |
| /// successfully. |
| /// |
| /// This may be null (and will always be `null` for setters). |
| /// |
| /// If the invocation completed with an error, this value will be `null`, |
| /// and [error] will be set. |
| T get result; |
| |
| /// The error that was thrown by the invocation if the invocation completed |
| /// with an error. |
| /// |
| /// If the invocation completed successfully, this value will be `null`, and |
| /// [result] will hold the result of the invocation (which may also be |
| /// `null`). |
| /// |
| /// This field being non-null can be used as an indication that the invocation |
| /// completed with an error. |
| dynamic get error; |
| |
| /// The stopwatch value (in milliseconds) when the invocation occurred. |
| /// |
| /// This value is recorded when the invocation first occurs, not when the |
| /// delegate returns. |
| int get timestamp; |
| } |
| |
| /// A recordable invocation of a property getter on a file system object. |
| abstract class PropertyGetEvent<T> extends InvocationEvent<T> { |
| /// The property that was retrieved. |
| Symbol get property; |
| } |
| |
| /// A recordable invocation of a property setter on a file system object. |
| abstract class PropertySetEvent<T> extends InvocationEvent<Null> { |
| /// The property that was set. |
| /// |
| /// All setter property symbols will have a trailing equals sign. For example, |
| /// if the `foo` property was set, this value will be a symbol of `foo=`. |
| Symbol get property; |
| |
| /// The value to which [property] was set. This is distinct from [result], |
| /// which is always `null` for setters. |
| T get value; |
| } |
| |
| /// A recordable invocation of a method on a file system object. |
| abstract class MethodEvent<T> extends InvocationEvent<T> { |
| /// The method that was invoked. |
| Symbol get method; |
| |
| /// The positional arguments that were passed to the method. |
| List<dynamic> get positionalArguments; |
| |
| /// The named arguments that were passed to the method. |
| Map<Symbol, dynamic> get namedArguments; |
| } |
| |
| /// An [InvocationEvent] that's in the process of being recorded. |
| abstract class LiveInvocationEvent<T> implements InvocationEvent<T> { |
| /// Creates a new [LiveInvocationEvent]. |
| LiveInvocationEvent(this.object, this._result, this.error, this.timestamp); |
| |
| final dynamic _result; |
| |
| @override |
| final Object object; |
| |
| @override |
| T get result { |
| dynamic result = _result; |
| while (result is ResultReference) { |
| ResultReference<dynamic> reference = result; |
| result = reference.recordedValue; |
| } |
| return result; |
| } |
| |
| @override |
| final dynamic error; |
| |
| @override |
| final int timestamp; |
| |
| /// A [Future] that completes once [result] is ready for serialization. |
| /// |
| /// If [result] is a [Future], this future completes when [result] completes. |
| /// If [result] is a [Stream], this future completes when the stream sends a |
| /// "done" event. If [result] is neither a future nor a stream, this future |
| /// completes immediately. |
| /// |
| /// It is legal for [serialize] to be called before this future completes, |
| /// but doing so will cause incomplete results to be serialized. Results that |
| /// are unfinished futures will be serialized as `null`, and results that are |
| /// unfinished streams will be serialized as the data that has been received |
| /// thus far. |
| Future<Null> get done async { |
| dynamic result = _result; |
| while (result is ResultReference) { |
| ResultReference<dynamic> reference = result; |
| await reference.complete; |
| result = reference.recordedValue; |
| } |
| } |
| |
| /// Returns this event as a JSON-serializable object. |
| Map<String, dynamic> serialize() { |
| return <String, dynamic>{ |
| kManifestObjectKey: encode(object), |
| kManifestResultKey: encode(_result), |
| kManifestErrorKey: encode(error), |
| kManifestTimestampKey: timestamp, |
| }; |
| } |
| |
| @override |
| String toString() => serialize().toString(); |
| } |
| |
| /// A [PropertyGetEvent] that's in the process of being recorded. |
| class LivePropertyGetEvent<T> extends LiveInvocationEvent<T> |
| implements PropertyGetEvent<T> { |
| /// Creates a new [LivePropertyGetEvent]. |
| LivePropertyGetEvent( |
| Object object, this.property, T result, dynamic error, int timestamp) |
| : super(object, result, error, timestamp); |
| |
| @override |
| final Symbol property; |
| |
| @override |
| Map<String, dynamic> serialize() { |
| return <String, dynamic>{ |
| kManifestTypeKey: kGetType, |
| kManifestPropertyKey: getSymbolName(property), |
| }..addAll(super.serialize()); |
| } |
| } |
| |
| /// A [PropertySetEvent] that's in the process of being recorded. |
| class LivePropertySetEvent<T> extends LiveInvocationEvent<Null> |
| implements PropertySetEvent<T> { |
| /// Creates a new [LivePropertySetEvent]. |
| LivePropertySetEvent( |
| Object object, this.property, this.value, dynamic error, int timestamp) |
| : super(object, null, error, timestamp); |
| |
| @override |
| final Symbol property; |
| |
| @override |
| final T value; |
| |
| @override |
| Map<String, dynamic> serialize() { |
| return <String, dynamic>{ |
| kManifestTypeKey: kSetType, |
| kManifestPropertyKey: getSymbolName(property), |
| kManifestValueKey: encode(value), |
| }..addAll(super.serialize()); |
| } |
| } |
| |
| /// A [MethodEvent] that's in the process of being recorded. |
| class LiveMethodEvent<T> extends LiveInvocationEvent<T> |
| implements MethodEvent<T> { |
| /// Creates a new [LiveMethodEvent]. |
| LiveMethodEvent( |
| Object object, |
| this.method, |
| List<dynamic> positionalArguments, |
| Map<Symbol, dynamic> namedArguments, |
| T result, |
| dynamic error, |
| int timestamp, |
| ) |
| : this.positionalArguments = |
| new List<dynamic>.unmodifiable(positionalArguments), |
| this.namedArguments = |
| new Map<Symbol, dynamic>.unmodifiable(namedArguments), |
| super(object, result, error, timestamp); |
| |
| @override |
| final Symbol method; |
| |
| @override |
| final List<dynamic> positionalArguments; |
| |
| @override |
| final Map<Symbol, dynamic> namedArguments; |
| |
| @override |
| Map<String, dynamic> serialize() { |
| return <String, dynamic>{ |
| kManifestTypeKey: kInvokeType, |
| kManifestMethodKey: getSymbolName(method), |
| kManifestPositionalArgumentsKey: encode(positionalArguments), |
| kManifestNamedArgumentsKey: encode(namedArguments), |
| }..addAll(super.serialize()); |
| } |
| } |