| // 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. |
| |
| import 'dart:collection'; |
| |
| import 'package:compiler/src/serialization/serialization.dart'; |
| |
| /// Interface for data that may be deserialized lazily. |
| /// |
| /// This interface should be used to wrap data objects that aren't needed in |
| /// later phases of the compiler. Usage of this class should follow a set |
| /// pattern. Given a class `C` with a field `m0` of type `E` that we wish to |
| /// make deferrable: |
| /// |
| /// 1) `m0` should be replaced with an internal field, `_m1`, of type |
| /// `Deferrable<E>`. |
| /// 2) An internal constructor should be added to `C` that takes a `d` of type |
| /// `Deferrable<E>` to initialize `_m1`. This internal constructor should be |
| /// called from the readFromSource method/factory to create the instance |
| /// of `C`. `d` should be obtained using [DataSourceReader.readDeferrable]. |
| /// 3) Any existing constructors of `C` should maintain the same signature |
| /// and initialize `_m1` passing `m` to [Deferrable.eager] where m is the value |
| /// previously used to initialize `m0`. |
| /// 4) If there are external references to `m0` then `C`s interface should be |
| /// maintained. A getter `m0` should be added: `E get m0 => _m1.loaded()` |
| /// 5) If all references to `m0` were internal, they can simply be replaced |
| /// with calls to `_m1.loaded()`. |
| /// |
| /// Example class before: |
| /// |
| /// class Foo { |
| /// final Bar bar; |
| /// |
| /// Foo(this.bar); |
| /// |
| /// factory Foo.readFromSource(DataSourceReader reader) { |
| /// return Foo(Bar.readFromSource(reader)); |
| /// } |
| /// } |
| /// |
| /// After: |
| /// |
| /// class Foo { |
| /// Bar get bar => _bar.loaded(); |
| /// final Deferrable<Bar> _bar; |
| /// |
| /// Foo(Bar bar) : _bar = Deferrable.eager(bar); |
| /// Foo._deserialized(this._bar); |
| /// |
| /// factory Foo.readFromSource(DataSourceReader reader) { |
| /// return Foo._deserialized( |
| /// reader.readDeferrable(() => Bar.readFromSource(reader))); |
| /// } |
| /// } |
| abstract class Deferrable<E> { |
| E loaded(); |
| |
| factory Deferrable.deferred(DataSourceReader reader, E f(), int offset, |
| {bool cacheData = true}) => |
| cacheData |
| ? _DeferredCache(reader, f, offset) |
| : _Deferred(reader, f, offset); |
| const factory Deferrable.eager(E data) = _Eager; |
| |
| const Deferrable(); |
| } |
| |
| class _Eager<E> extends Deferrable<E> { |
| final E _data; |
| @override |
| E loaded() => _data; |
| const _Eager(this._data); |
| } |
| |
| class _Deferred<E> extends Deferrable<E> { |
| final DataSourceReader _reader; |
| final E Function() _dataLoader; |
| final int _dataOffset; |
| @override |
| E loaded() => _reader.readWithOffset(_dataOffset, _dataLoader); |
| _Deferred(this._reader, this._dataLoader, this._dataOffset); |
| } |
| |
| class _DeferredCache<E> extends Deferrable<E> { |
| final int _dataOffset; |
| // Below fields are nullable so they can be cleared after loading. |
| DataSourceReader? _reader; |
| E Function()? _dataLoader; |
| late final E _data = _loadData(); |
| |
| @override |
| E loaded() => _data; |
| |
| E _loadData() { |
| final reader = _reader!; |
| final dataLoader = _dataLoader!; |
| _reader = null; |
| _dataLoader = null; |
| return reader.readWithOffset(_dataOffset, dataLoader); |
| } |
| |
| _DeferredCache(this._reader, this._dataLoader, this._dataOffset); |
| } |
| |
| /// Implementation of [Map] in which each value of type [V] is internally |
| /// [Deferrable]. |
| /// |
| /// This map should be used when values of type [V] are expensive to |
| /// deserialize. This abstracts away the laziness allowing the deferred map to |
| /// be used as if the values were not deferred. |
| /// |
| /// The provided map can have a mix of eager and lazy [Deferrable]s if |
| /// there are a mix of expensive and cheap values. |
| class DeferrableValueMap<K, V> with MapMixin<K, V> { |
| DeferrableValueMap(this._map); |
| final Map<K, Deferrable<V>> _map; |
| @override |
| V? operator [](Object? key) { |
| return _map[key]?.loaded(); |
| } |
| |
| @override |
| void operator []=(K key, V value) { |
| _map[key] = Deferrable.eager(value); |
| } |
| |
| @override |
| void clear() { |
| _map.clear(); |
| } |
| |
| @override |
| Iterable<K> get keys => _map.keys; |
| |
| @override |
| V? remove(Object? key) { |
| return _map.remove(key)?.loaded(); |
| } |
| |
| @override |
| V putIfAbsent(K key, V ifAbsent()) { |
| return _map.putIfAbsent(key, () => Deferrable.eager(ifAbsent())).loaded(); |
| } |
| } |