Skip to content

Commit c9474de

Browse files
authored
Merge encoding.dart and resurrectors.dart (#30)
This merges the two concepts into one set of `Converter` and `Codec` types that all live in `codecs.dart`. Part of flutter#11
1 parent 690c5e1 commit c9474de

16 files changed

+447
-290
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
8+
import 'package:file/file.dart';
9+
import 'package:path/path.dart' as path;
10+
11+
import 'common.dart';
12+
import 'events.dart';
13+
import 'replay_directory.dart';
14+
import 'replay_file.dart';
15+
import 'replay_file_stat.dart';
16+
import 'replay_file_system.dart';
17+
import 'replay_link.dart';
18+
import 'result_reference.dart';
19+
20+
/// Converter that leaves object untouched.
21+
const Converter<dynamic, dynamic> kPassthrough = const _PassthroughConverter();
22+
23+
/// Converter that will turn an object into a [Future] of that object.
24+
const Converter<dynamic, dynamic> kFutureReviver =
25+
const _FutureDecoder<dynamic>();
26+
27+
/// Converter that will deserialize a [DateTime].
28+
const Converter<dynamic, dynamic> kDateTimeReviver = _DateTimeCodec.kDecoder;
29+
30+
/// Converter that will deserialize a [FileStat].
31+
const Converter<dynamic, dynamic> kFileStatReviver = _FileStatCodec.kDecoder;
32+
33+
/// Converter that will deserialize a [FileSystemEntityType].
34+
const Converter<dynamic, dynamic> kEntityTypeReviver =
35+
_EntityTypeCodec.kDecoder;
36+
37+
/// Converter that will deserialize a [path.Context].
38+
const Converter<dynamic, dynamic> kPathContextReviver =
39+
_PathContextCodec.kDecoder;
40+
41+
/// Converter that will deserialize a [ReplayDirectory].
42+
Converter<dynamic, dynamic> directoryReviver(ReplayFileSystemImpl fileSystem) =>
43+
new _DirectoryDecoder(fileSystem);
44+
45+
/// Converter that will deserialize a [ReplayFile].
46+
Converter<dynamic, dynamic> fileReviver(ReplayFileSystemImpl fileSystem) =>
47+
new _FileDecoder(fileSystem);
48+
49+
/// Converter that will deserialize a [ReplayLink].
50+
Converter<dynamic, dynamic> linkReviver(ReplayFileSystemImpl fileSystem) =>
51+
new _LinkDecoder(fileSystem);
52+
53+
/// Encodes an arbitrary [object] into a JSON-ready representation (a number,
54+
/// boolean, string, null, list, or map).
55+
///
56+
/// Returns a value suitable for conversion into JSON using [JsonEncoder]
57+
/// without the need for a `toEncodable` argument.
58+
dynamic encode(dynamic object) => const _GenericEncoder().convert(object);
59+
60+
typedef T _ConverterDelegate<S, T>(S input);
61+
62+
class _ForwardingConverter<S, T> extends Converter<S, T> {
63+
final _ConverterDelegate<S, T> _delegate;
64+
65+
const _ForwardingConverter(this._delegate);
66+
67+
@override
68+
T convert(S input) => _delegate(input);
69+
}
70+
71+
class _GenericEncoder extends Converter<dynamic, dynamic> {
72+
const _GenericEncoder();
73+
74+
/// Known encoders. Types not covered here will be encoded using
75+
/// [_encodeDefault].
76+
///
77+
/// When encoding an object, we will walk this map in insertion order looking
78+
/// for a matching encoder. Thus, when there are two encoders that match an
79+
/// object, the first one will win.
80+
static const Map<TypeMatcher<dynamic>, Converter<Object, Object>> _encoders =
81+
const <TypeMatcher<dynamic>, Converter<Object, Object>>{
82+
const TypeMatcher<num>(): const _PassthroughConverter(),
83+
const TypeMatcher<bool>(): const _PassthroughConverter(),
84+
const TypeMatcher<String>(): const _PassthroughConverter(),
85+
const TypeMatcher<Null>(): const _PassthroughConverter(),
86+
const TypeMatcher<Iterable<dynamic>>(): const _IterableEncoder(),
87+
const TypeMatcher<Map<dynamic, dynamic>>(): const _MapEncoder(),
88+
const TypeMatcher<Symbol>(): const _SymbolEncoder(),
89+
const TypeMatcher<DateTime>(): _DateTimeCodec.kEncoder,
90+
const TypeMatcher<Uri>(): const _ToStringEncoder(),
91+
const TypeMatcher<path.Context>(): _PathContextCodec.kEncoder,
92+
const TypeMatcher<ResultReference<dynamic>>(): const _ResultEncoder(),
93+
const TypeMatcher<LiveInvocationEvent<dynamic>>(): const _EventEncoder(),
94+
const TypeMatcher<ReplayAware>(): const _ReplayAwareEncoder(),
95+
const TypeMatcher<Encoding>(): const _EncodingEncoder(),
96+
const TypeMatcher<FileMode>(): const _FileModeEncoder(),
97+
const TypeMatcher<FileStat>(): _FileStatCodec.kEncoder,
98+
const TypeMatcher<FileSystemEntityType>(): _EntityTypeCodec.kEncoder,
99+
const TypeMatcher<FileSystemEvent>(): const _FileSystemEventEncoder(),
100+
};
101+
102+
/// Default encoder (used for types not covered in [_encoders]).
103+
static String _encodeDefault(dynamic object) => object.runtimeType.toString();
104+
105+
@override
106+
dynamic convert(dynamic input) {
107+
Converter<dynamic, dynamic> encoder =
108+
const _ForwardingConverter<dynamic, String>(_encodeDefault);
109+
for (TypeMatcher<dynamic> matcher in _encoders.keys) {
110+
if (matcher.matches(input)) {
111+
encoder = _encoders[matcher];
112+
break;
113+
}
114+
}
115+
return encoder.convert(input);
116+
}
117+
}
118+
119+
class _PassthroughConverter extends Converter<dynamic, dynamic> {
120+
const _PassthroughConverter();
121+
122+
@override
123+
dynamic convert(dynamic input) => input;
124+
}
125+
126+
class _IterableEncoder extends Converter<Iterable<dynamic>, List<dynamic>> {
127+
const _IterableEncoder();
128+
129+
@override
130+
List<dynamic> convert(Iterable<dynamic> input) {
131+
_GenericEncoder generic = const _GenericEncoder();
132+
List<dynamic> encoded = <dynamic>[];
133+
for (Object element in input) {
134+
encoded.add(generic.convert(element));
135+
}
136+
return encoded;
137+
}
138+
}
139+
140+
class _MapEncoder
141+
extends Converter<Map<dynamic, dynamic>, Map<String, dynamic>> {
142+
const _MapEncoder();
143+
144+
@override
145+
Map<String, dynamic> convert(Map<dynamic, dynamic> input) {
146+
_GenericEncoder generic = const _GenericEncoder();
147+
Map<String, dynamic> encoded = <String, dynamic>{};
148+
for (dynamic key in input.keys) {
149+
String encodedKey = generic.convert(key);
150+
encoded[encodedKey] = generic.convert(input[key]);
151+
}
152+
return encoded;
153+
}
154+
}
155+
156+
class _SymbolEncoder extends Converter<Symbol, String> {
157+
const _SymbolEncoder();
158+
159+
@override
160+
String convert(Symbol input) => getSymbolName(input);
161+
}
162+
163+
class _DateTimeCodec extends Codec<DateTime, int> {
164+
const _DateTimeCodec();
165+
166+
static int _encode(DateTime input) => input.millisecondsSinceEpoch;
167+
168+
static DateTime _decode(int input) =>
169+
new DateTime.fromMillisecondsSinceEpoch(input);
170+
171+
static const Converter<DateTime, int> kEncoder =
172+
const _ForwardingConverter<DateTime, int>(_encode);
173+
174+
static const Converter<int, DateTime> kDecoder =
175+
const _ForwardingConverter<int, DateTime>(_decode);
176+
177+
@override
178+
Converter<DateTime, int> get encoder => kEncoder;
179+
180+
@override
181+
Converter<int, DateTime> get decoder => kDecoder;
182+
}
183+
184+
class _ToStringEncoder extends Converter<Object, String> {
185+
const _ToStringEncoder();
186+
187+
@override
188+
String convert(Object input) => input.toString();
189+
}
190+
191+
class _PathContextCodec extends Codec<path.Context, Map<String, String>> {
192+
const _PathContextCodec();
193+
194+
static Map<String, String> _encode(path.Context input) {
195+
return <String, String>{
196+
'style': input.style.name,
197+
'cwd': input.current,
198+
};
199+
}
200+
201+
static path.Context _decode(Map<String, String> input) {
202+
return new path.Context(
203+
style: <String, path.Style>{
204+
'posix': path.Style.posix,
205+
'windows': path.Style.windows,
206+
'url': path.Style.url,
207+
}[input['style']],
208+
current: input['cwd'],
209+
);
210+
}
211+
212+
static const Converter<path.Context, Map<String, String>> kEncoder =
213+
const _ForwardingConverter<path.Context, Map<String, String>>(_encode);
214+
215+
static const Converter<Map<String, String>, path.Context> kDecoder =
216+
const _ForwardingConverter<Map<String, String>, path.Context>(_decode);
217+
218+
@override
219+
Converter<path.Context, Map<String, String>> get encoder => kEncoder;
220+
221+
@override
222+
Converter<Map<String, String>, path.Context> get decoder => kDecoder;
223+
}
224+
225+
class _ResultEncoder extends Converter<ResultReference<dynamic>, Object> {
226+
const _ResultEncoder();
227+
228+
@override
229+
Object convert(ResultReference<dynamic> input) => input.serializedValue;
230+
}
231+
232+
class _EventEncoder
233+
extends Converter<LiveInvocationEvent<dynamic>, Map<String, Object>> {
234+
const _EventEncoder();
235+
236+
@override
237+
Map<String, Object> convert(LiveInvocationEvent<dynamic> input) {
238+
return input.serialize();
239+
}
240+
}
241+
242+
class _ReplayAwareEncoder extends Converter<ReplayAware, String> {
243+
const _ReplayAwareEncoder();
244+
245+
@override
246+
String convert(ReplayAware input) => input.identifier;
247+
}
248+
249+
class _EncodingEncoder extends Converter<Encoding, String> {
250+
const _EncodingEncoder();
251+
252+
@override
253+
String convert(Encoding input) => input.name;
254+
}
255+
256+
class _FileModeEncoder extends Converter<FileMode, String> {
257+
const _FileModeEncoder();
258+
259+
@override
260+
String convert(FileMode input) {
261+
switch (input) {
262+
case FileMode.READ:
263+
return 'READ';
264+
case FileMode.WRITE:
265+
return 'WRITE';
266+
case FileMode.APPEND:
267+
return 'APPEND';
268+
case FileMode.WRITE_ONLY:
269+
return 'WRITE_ONLY';
270+
case FileMode.WRITE_ONLY_APPEND:
271+
return 'WRITE_ONLY_APPEND';
272+
}
273+
throw new ArgumentError('Invalid value: $input');
274+
}
275+
}
276+
277+
class _FileStatCodec extends Codec<FileStat, Map<String, Object>> {
278+
const _FileStatCodec();
279+
280+
static Map<String, Object> _encode(FileStat input) {
281+
return <String, dynamic>{
282+
'changed': const _DateTimeCodec().encode(input.changed),
283+
'modified': const _DateTimeCodec().encode(input.modified),
284+
'accessed': const _DateTimeCodec().encode(input.accessed),
285+
'type': const _EntityTypeCodec().encode(input.type),
286+
'mode': input.mode,
287+
'size': input.size,
288+
'modeString': input.modeString(),
289+
};
290+
}
291+
292+
static FileStat _decode(Map<String, Object> input) =>
293+
new ReplayFileStat(input);
294+
295+
static const Converter<FileStat, Map<String, Object>> kEncoder =
296+
const _ForwardingConverter<FileStat, Map<String, Object>>(_encode);
297+
298+
static const Converter<Map<String, Object>, FileStat> kDecoder =
299+
const _ForwardingConverter<Map<String, Object>, FileStat>(_decode);
300+
301+
@override
302+
Converter<FileStat, Map<String, Object>> get encoder => kEncoder;
303+
304+
@override
305+
Converter<Map<String, Object>, FileStat> get decoder => kDecoder;
306+
}
307+
308+
class _EntityTypeCodec extends Codec<FileSystemEntityType, String> {
309+
const _EntityTypeCodec();
310+
311+
static String _encode(FileSystemEntityType input) => input.toString();
312+
313+
static FileSystemEntityType _decode(String input) {
314+
return const <String, FileSystemEntityType>{
315+
'FILE': FileSystemEntityType.FILE,
316+
'DIRECTORY': FileSystemEntityType.DIRECTORY,
317+
'LINK': FileSystemEntityType.LINK,
318+
'NOT_FOUND': FileSystemEntityType.NOT_FOUND,
319+
}[input];
320+
}
321+
322+
static const Converter<FileSystemEntityType, String> kEncoder =
323+
const _ForwardingConverter<FileSystemEntityType, String>(_encode);
324+
325+
static const Converter<String, FileSystemEntityType> kDecoder =
326+
const _ForwardingConverter<String, FileSystemEntityType>(_decode);
327+
328+
@override
329+
Converter<FileSystemEntityType, String> get encoder => kEncoder;
330+
331+
@override
332+
Converter<String, FileSystemEntityType> get decoder => kDecoder;
333+
}
334+
335+
class _FileSystemEventEncoder
336+
extends Converter<FileSystemEvent, Map<String, Object>> {
337+
const _FileSystemEventEncoder();
338+
339+
@override
340+
Map<String, Object> convert(FileSystemEvent input) {
341+
return <String, Object>{
342+
'type': input.type,
343+
'path': input.path,
344+
};
345+
}
346+
}
347+
348+
class _FutureDecoder<T> extends Converter<T, Future<T>> {
349+
const _FutureDecoder();
350+
351+
@override
352+
Future<T> convert(T input) async => input;
353+
}
354+
355+
class _DirectoryDecoder extends Converter<String, Directory> {
356+
final ReplayFileSystemImpl fileSystem;
357+
const _DirectoryDecoder(this.fileSystem);
358+
359+
@override
360+
Directory convert(String input) => new ReplayDirectory(fileSystem, input);
361+
}
362+
363+
class _FileDecoder extends Converter<String, File> {
364+
final ReplayFileSystemImpl fileSystem;
365+
const _FileDecoder(this.fileSystem);
366+
367+
@override
368+
File convert(String input) => new ReplayFile(fileSystem, input);
369+
}
370+
371+
class _LinkDecoder extends Converter<String, Link> {
372+
final ReplayFileSystemImpl fileSystem;
373+
const _LinkDecoder(this.fileSystem);
374+
375+
@override
376+
Link convert(String input) => new ReplayLink(fileSystem, input);
377+
}

lib/src/backends/record_replay/common.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ class TypeMatcher<T> {
9292
/// opaque identifier.
9393
///
9494
/// 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.
95+
/// meaningful metadata about their state for the sake of revival. Rather, they
96+
/// derive all the information they need to operate from the recording. As such,
97+
/// they are serialized using only an opaque unique identifier. When they are
98+
/// revived during replay, their identifier allows them to find invocations in
99+
/// the recording for which they are the target.
100100
abstract class ReplayAware {
101101
/// The identifier of this object, guaranteed to be unique within a single
102102
/// recording.

0 commit comments

Comments
 (0)