Skip to content

Commit 690c5e1

Browse files
authored
Extract out common ReplayAware interface (#29)
This lets us unify the identifier logic across record and replay object serializations. Part of flutter#11
1 parent 6d335f0 commit 690c5e1

10 files changed

+37
-46
lines changed

lib/src/backends/record_replay/common.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@ class TypeMatcher<T> {
8888
bool matches(dynamic object) => object is T;
8989
}
9090

91+
/// Marks a class that, when serialized, will be referred to merely by an
92+
/// opaque identifier.
93+
///
94+
/// Unlike other objects, objects that are replay-aware don't need to serialize
95+
/// meaningful metadata about their state for the sake of resurrection. Rather,
96+
/// they derive all the information they need to operate from the recording.
97+
/// As such, they are serialized using only an opaque unique identifier. When
98+
/// they are resurrected during replay, their identifier allows them to find
99+
/// invocations in the recording for which they are the target.
100+
abstract class ReplayAware {
101+
/// The identifier of this object, guaranteed to be unique within a single
102+
/// recording.
103+
String get identifier;
104+
}
105+
91106
/// Tells whether two objects are equal using deep equality checking.
92107
///
93108
/// Two lists are deeply equal if they have the same runtime type, the same

lib/src/backends/record_replay/encoding.dart

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@ import 'package:path/path.dart' as p;
99

1010
import 'common.dart';
1111
import 'events.dart';
12-
import 'recording_directory.dart';
13-
import 'recording_file.dart';
14-
import 'recording_file_system_entity.dart';
15-
import 'recording_io_sink.dart';
16-
import 'recording_link.dart';
17-
import 'recording_random_access_file.dart';
18-
import 'replay_proxy_mixin.dart';
1912
import 'result_reference.dart';
2013

2114
/// Encodes an object into a JSON-ready representation.
@@ -43,13 +36,7 @@ const Map<TypeMatcher<dynamic>, _Encoder> _kEncoders =
4336
const TypeMatcher<p.Context>(): _encodePathContext,
4437
const TypeMatcher<ResultReference<dynamic>>(): _encodeResultReference,
4538
const TypeMatcher<LiveInvocationEvent<dynamic>>(): _encodeEvent,
46-
const TypeMatcher<FileSystem>(): _encodeFileSystem,
47-
const TypeMatcher<RecordingDirectory>(): _encodeFileSystemEntity,
48-
const TypeMatcher<RecordingFile>(): _encodeFileSystemEntity,
49-
const TypeMatcher<RecordingLink>(): _encodeFileSystemEntity,
50-
const TypeMatcher<RecordingIOSink>(): _encodeIOSink,
51-
const TypeMatcher<RecordingRandomAccessFile>(): _encodeRandomAccessFile,
52-
const TypeMatcher<ReplayProxyMixin>(): _encodeReplayEntity,
39+
const TypeMatcher<ReplayAware>(): _encodeReplayAwareEntity,
5340
const TypeMatcher<Encoding>(): _encodeEncoding,
5441
const TypeMatcher<FileMode>(): _encodeFileMode,
5542
const TypeMatcher<FileStat>(): _encodeFileStat,
@@ -116,25 +103,7 @@ dynamic _encodeResultReference(ResultReference<dynamic> reference) =>
116103
Map<String, dynamic> _encodeEvent(LiveInvocationEvent<dynamic> event) =>
117104
event.serialize();
118105

119-
String _encodeFileSystem(FileSystem fs) => kFileSystemEncodedValue;
120-
121-
/// Encodes a file system entity by using its `uid` as a reference identifier.
122-
/// During replay, this allows us to tie the return value of of one event to
123-
/// the object of another.
124-
String _encodeFileSystemEntity(
125-
RecordingFileSystemEntity<FileSystemEntity> entity) {
126-
return '${entity.runtimeType}@${entity.uid}';
127-
}
128-
129-
String _encodeIOSink(RecordingIOSink sink) {
130-
return '${sink.runtimeType}@${sink.uid}';
131-
}
132-
133-
String _encodeRandomAccessFile(RecordingRandomAccessFile raf) {
134-
return '${raf.runtimeType}@${raf.uid}';
135-
}
136-
137-
String _encodeReplayEntity(ReplayProxyMixin entity) => entity.identifier;
106+
String _encodeReplayAwareEntity(ReplayAware entity) => entity.identifier;
138107

139108
String _encodeEncoding(Encoding encoding) => encoding.name;
140109

lib/src/backends/record_replay/recording_file_system.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:file/file.dart';
66
import 'package:meta/meta.dart';
77

8+
import 'common.dart';
89
import 'mutable_recording.dart';
910
import 'recording.dart';
1011
import 'recording_directory.dart';
@@ -109,6 +110,9 @@ class RecordingFileSystemImpl extends FileSystem
109110
});
110111
}
111112

113+
@override
114+
String get identifier => kFileSystemEncodedValue;
115+
112116
/// The file system to which invocations will be forwarded upon recording.
113117
@override
114118
final FileSystem delegate;

lib/src/backends/record_replay/recording_file_system_entity.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ abstract class RecordingFileSystemEntity<T extends FileSystemEntity>
5050
/// A unique entity id.
5151
final int uid = newUid();
5252

53+
@override
54+
String get identifier => '$runtimeType@$uid';
55+
5356
@override
5457
final RecordingFileSystemImpl fileSystem;
5558

lib/src/backends/record_replay/recording_io_sink.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class RecordingIOSink extends Object
4646
/// A unique entity id.
4747
final int uid = newUid();
4848

49+
@override
50+
String get identifier => '$runtimeType@$uid';
51+
4952
@override
5053
MutableRecording get recording => fileSystem.recording;
5154

lib/src/backends/record_replay/recording_proxy_mixin.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66

77
import 'package:meta/meta.dart';
88

9+
import 'common.dart';
910
import 'events.dart';
1011
import 'mutable_recording.dart';
1112
import 'proxy.dart';
@@ -60,7 +61,7 @@ import 'result_reference.dart';
6061
/// Methods that return [Stream]s will be recorded immediately, but their
6162
/// return values will be recorded as a [List] that will grow as the stream
6263
/// produces data.
63-
abstract class RecordingProxyMixin implements ProxyObject {
64+
abstract class RecordingProxyMixin implements ProxyObject, ReplayAware {
6465
/// Maps method names to delegate functions.
6566
///
6667
/// Invocations of methods listed in this map will be recorded after

lib/src/backends/record_replay/recording_random_access_file.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class RecordingRandomAccessFile extends Object
6565
/// A unique entity id.
6666
final int uid = newUid();
6767

68+
@override
69+
String get identifier => '$runtimeType@$uid';
70+
6871
@override
6972
MutableRecording get recording => fileSystem.recording;
7073

lib/src/backends/record_replay/replay_file_system.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ abstract class ReplayFileSystem extends FileSystem {
6767
/// Non-exported implementation class for `ReplayFileSystem`.
6868
class ReplayFileSystemImpl extends FileSystem
6969
with ReplayProxyMixin
70-
implements ReplayFileSystem {
70+
implements ReplayFileSystem, ReplayAware {
7171
/// Creates a new `ReplayFileSystemImpl`.
7272
ReplayFileSystemImpl(this.manifest) {
7373
methods.addAll(<Symbol, Resurrector>{

lib/src/backends/record_replay/replay_proxy_mixin.dart

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ int _nextOrdinal = 0;
5757
/// });
5858
/// }
5959
/// }
60-
abstract class ReplayProxyMixin implements ProxyObject {
60+
abstract class ReplayProxyMixin implements ProxyObject, ReplayAware {
6161
/// Maps method names to [Resurrector] functions.
6262
///
6363
/// Invocations of methods listed in this map will be replayed by looking for
@@ -79,16 +79,6 @@ abstract class ReplayProxyMixin implements ProxyObject {
7979
@protected
8080
final Map<Symbol, Resurrector> properties = <Symbol, Resurrector>{};
8181

82-
/// The unique identifier of this replay object.
83-
///
84-
/// When replay-aware objects are serialized in a recording, they are done so
85-
/// using only a unique String identifier. When the objects are resurrected
86-
/// for the purpose of replay, their identifiers are used to match incoming
87-
/// invocations against recorded invocations in the [manifest] (only
88-
/// invocations whose target object matches the identifier are considered
89-
/// possible matches).
90-
String get identifier;
91-
9282
/// The manifest of recorded invocation events.
9383
///
9484
/// When invocations are received on this object, we will attempt find a

test/recording_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,9 @@ class _RecordingClass extends Object
899899
});
900900
}
901901

902+
@override
903+
String get identifier => '$runtimeType';
904+
902905
@override
903906
final MutableRecording recording;
904907

0 commit comments

Comments
 (0)