blob: b3cc2c94ae45b185448d078eaab8906529a390fc [file] [log] [blame]
// Copyright (c) 2018, 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 'serialization.dart';
/// Deserialization reader
///
/// To be used with [DataSinkWriter] to read and write serialized data.
/// Deserialization format is deferred to provided [DataSource].
class DataSourceReader {
// The active [DataSource] to read data from. This can be the base DataSource
// for this reader or can be set to access data in a different serialized
// input in the case of deferred indexed data.
DataSource _sourceReader;
static final List<ir.DartType> emptyListOfDartTypes =
List<ir.DartType>.empty();
final bool enableDeferredStrategy;
final bool useDeferredStrategy;
final bool useDataKinds;
final ValueInterner? interner;
DataSourceIndices? importedIndices;
EntityReader _entityReader = const EntityReader();
ComponentLookup? _componentLookup;
EntityLookup? _entityLookup;
LocalLookup? _localLookup;
CodegenReader? _codegenReader;
late final IndexedSource<String> _stringIndex;
late final IndexedSource<Uri> _uriIndex;
late final IndexedSource<MemberData> _memberNodeIndex;
late final IndexedSource<ImportEntity> _importIndex;
late final IndexedSource<ConstantValue> _constantIndex;
final Map<Type, IndexedSource> _generalCaches = {};
ir.Member? _currentMemberContext;
MemberData? _currentMemberData;
int get length => _sourceReader.length;
/// Defines the beginning of this block in the address space created by all
/// instances of [DataSourceReader].
///
/// The amount by which the offsets for indexed values read by this reader are
/// shifted. That is the length of all the sources read before this one.
///
/// See [UnorderedIndexedSource] for more info.
int get startOffset => importedIndices?.previousSourceReader?.endOffset ?? 0;
/// Defines the end of this block in the address space created by all
/// instances of [DataSourceReader].
///
/// Indexed values read from this source will all have offsets less than this
/// value.
///
/// See [UnorderedIndexedSource] for more info.
final int endOffset;
IndexedSource<T> _createSource<T>() {
final indices = importedIndices;
if (indices == null || !indices.caches.containsKey(T)) {
return OrderedIndexedSource<T>(this._sourceReader);
} else {
final source = indices.caches[T]!.source as OrderedIndexedSource<T>;
List<T?> cacheCopy = source.cache.toList();
return OrderedIndexedSource<T>(this._sourceReader, cache: cacheCopy);
}
}
UnorderedIndexedSource<T>? _getPreviousUncreatedSource<T>() {
final previousSourceReader = importedIndices?.previousSourceReader;
if (previousSourceReader == null) return null;
return UnorderedIndexedSource<T>(previousSourceReader,
previousSource: previousSourceReader._getPreviousUncreatedSource<T>());
}
IndexedSource<T> _createUnorderedSource<T>() {
final indices = importedIndices;
if (indices != null) {
if (indices.caches.containsKey(T)) {
final index = indices.caches.remove(T);
return UnorderedIndexedSource<T>(this,
previousSource: index!.source as UnorderedIndexedSource<T>);
}
final newPreviousSource = _getPreviousUncreatedSource<T>();
if (newPreviousSource != null) {
return UnorderedIndexedSource<T>(this,
previousSource: newPreviousSource);
}
}
return UnorderedIndexedSource<T>(this);
}
DataSourceReader(this._sourceReader, CompilerOptions options,
{this.useDataKinds = false,
DataSourceIndices? importedIndices,
this.interner,
this.useDeferredStrategy = false})
: enableDeferredStrategy =
(options.features.deferredSerialization.isEnabled),
this.importedIndices = importedIndices == null
? null
: (DataSourceIndices(importedIndices.previousSourceReader)
..caches.addAll(importedIndices.caches)),
endOffset = (importedIndices?.previousSourceReader?.endOffset ?? 0) +
_sourceReader.length {
if (!enableDeferredStrategy) {
_stringIndex = _createSource<String>();
_uriIndex = _createSource<Uri>();
_importIndex = _createSource<ImportEntity>();
_memberNodeIndex = _createSource<MemberData>();
_constantIndex = _createSource<ConstantValue>();
return;
}
_stringIndex = _createUnorderedSource<String>();
_uriIndex = _createUnorderedSource<Uri>();
_importIndex = _createUnorderedSource<ImportEntity>();
_memberNodeIndex = _createUnorderedSource<MemberData>();
_constantIndex = _createUnorderedSource<ConstantValue>();
}
/// Exports [DataSourceIndices] for use in other [DataSourceReader]s and
/// [DataSinkWriter]s.
DataSourceIndices exportIndices() {
final indices = DataSourceIndices(this);
indices.caches[String] = DataSourceTypeIndices(_stringIndex);
indices.caches[Uri] = DataSourceTypeIndices(_uriIndex);
indices.caches[ImportEntity] = DataSourceTypeIndices(_importIndex);
// _memberNodeIndex needs two entries depending on if the indices will be
// consumed by a [DataSource] or [DataSink].
indices.caches[MemberData] = DataSourceTypeIndices(_memberNodeIndex);
indices.caches[ir.Member] = DataSourceTypeIndices<ir.Member?, MemberData>(
_memberNodeIndex, (MemberData? data) => data?.node);
indices.caches[ConstantValue] = DataSourceTypeIndices(_constantIndex);
_generalCaches.forEach((type, indexedSource) {
indices.caches[type] = DataSourceTypeIndices(indexedSource);
});
return indices;
}
/// Registers that the section [tag] starts.
///
/// This is used for debugging to verify that sections are correctly aligned
/// between serialization and deserialization.
void begin(String tag) {
if (useDataKinds) _sourceReader.begin(tag);
}
/// Registers that the section [tag] ends.
///
/// This is used for debugging to verify that sections are correctly aligned
/// between serialization and deserialization.
void end(String tag) {
if (useDataKinds) _sourceReader.end(tag);
}
/// Registers a [ComponentLookup] object with this data source to support
/// deserialization of references to kernel nodes.
void registerComponentLookup(ComponentLookup componentLookup) {
assert(_componentLookup == null);
_componentLookup = componentLookup;
}
ComponentLookup get componentLookup {
return _componentLookup!;
}
/// Registers an [EntityLookup] object with this data source to support
/// deserialization of references to entities.
void registerEntityLookup(EntityLookup entityLookup) {
assert(_entityLookup == null);
_entityLookup = entityLookup;
}
EntityLookup get entityLookup {
return _entityLookup!;
}
/// Registers an [EntityReader] with this data source for non-default encoding
/// of entity references.
void registerEntityReader(EntityReader reader) {
assert((reader as dynamic) != null); // TODO(48820): Remove when sound.
_entityReader = reader;
}
/// Registers a [LocalLookup] object with this data source to support
void registerLocalLookup(LocalLookup localLookup) {
assert((localLookup as dynamic) != null); // TODO(48820): Remove when sound.
_localLookup = localLookup;
}
LocalLookup get localLookup {
return _localLookup!;
}
/// Registers a [CodegenReader] with this data source to support
/// deserialization of codegen only data.
void registerCodegenReader(CodegenReader reader) {
assert((reader as dynamic) != null); // TODO(48820): Remove when sound.
assert(_codegenReader == null);
_codegenReader = reader;
}
/// Unregisters the [CodegenReader] from this data source to remove support
/// for deserialization of codegen only data.
void deregisterCodegenReader(CodegenReader reader) {
assert(_codegenReader == reader);
_codegenReader = null;
}
/// Evaluates [f] with [DataSource] for the provided [source] as the
/// temporary [DataSource] for this object. Allows deferred data to be read
/// from a file other than the one currently being read from.
E readWithSource<E>(DataSourceReader source, E f()) {
final lastSource = _sourceReader;
_sourceReader = source._sourceReader;
final value = f();
_sourceReader = lastSource;
return value;
}
E readWithOffset<E>(int offset, E f()) {
return _sourceReader.readAtOffset(offset, f);
}
Deferrable<E> readDeferrable<E>(E f(), {bool cacheData = true}) {
return enableDeferredStrategy
? (useDeferredStrategy
? Deferrable<E>.deferred(this, f, _sourceReader.readDeferred(),
cacheData: cacheData)
: Deferrable<E>.eager(_sourceReader.readDeferredAsEager(f)))
: Deferrable<E>.eager(f());
}
/// Invoke [f] in the context of [member]. This sets up support for
/// deserialization of `ir.TreeNode`s using the `readTreeNode*InContext`
/// methods.
T inMemberContext<T>(ir.Member context, T f()) {
ir.Member? oldMemberContext = _currentMemberContext;
MemberData? oldMemberData = _currentMemberData;
_currentMemberContext = context;
_currentMemberData = null;
T result = f();
_currentMemberData = oldMemberData;
_currentMemberContext = oldMemberContext;
return result;
}
MemberData get currentMemberData {
assert(_currentMemberContext != null,
"DataSink has no current member context.");
return _currentMemberData ??= _getMemberData(_currentMemberContext!);
}
/// Reads a reference to an [E] value from this data source. If the value has
/// not yet been deserialized, [f] is called to deserialize the value itself.
E readCached<E>(E f()) {
E? value = readCachedOrNull(f);
if (value == null) throw StateError("Unexpected 'null' for $E");
return value;
}
/// Reads a reference to an [E] value from this data source. If the value has
/// not yet been deserialized, [f] is called to deserialize the value itself.
E? readCachedOrNull<E>(E f()) {
IndexedSource<E> source = (_generalCaches[E] ??= (enableDeferredStrategy
? _createUnorderedSource<E>()
: _createSource<E>())) as IndexedSource<E>;
return source.read(f);
}
/// Reads a potentially `null` [E] value from this data source, calling [f] to
/// read the non-null value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeValueOrNull].
E? readValueOrNull<E>(E f()) {
bool hasValue = readBool();
if (hasValue) {
return f();
}
return null;
}
/// Reads a list of [E] values from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeList].
List<E> readList<E>(E f()) {
return readListOrNull<E>(f) ?? List<E>.empty();
}
/// Reads a list of [E] values from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeList].
List<E>? readListOrNull<E>(E f()) {
int count = readInt();
if (count == 0) return null;
final first = f();
List<E> list = List<E>.filled(count, first);
for (int i = 1; i < count; i++) {
list[i] = f();
}
return list;
}
bool readBool() {
_checkDataKind(DataKind.bool);
return _readBool();
}
/// Reads a boolean value from this data source.
bool _readBool() {
int value = _sourceReader.readInt();
assert(value == 0 || value == 1);
return value == 1;
}
/// Reads a non-negative 30 bit integer value from this data source.
int readInt() {
_checkDataKind(DataKind.uint30);
return _sourceReader.readInt();
}
/// Reads a potentially `null` non-negative integer value from this data
/// source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeIntOrNull].
int? readIntOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readInt();
}
return null;
}
/// Reads a string value from this data source.
String readString() {
_checkDataKind(DataKind.string);
return _readString();
}
String _readString() {
return _stringIndex.read(() => _sourceReader.readString())!;
}
/// Reads a potentially `null` string value from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeStringOrNull].
String? readStringOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readString();
}
return null;
}
/// Reads a list of string values from this data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeStrings].
List<String>? readStrings({bool emptyAsNull = false}) {
int count = readInt();
if (count == 0 && emptyAsNull) return null;
List<String> list = List<String>.filled(count, '');
for (int i = 0; i < count; i++) {
list[i] = readString();
}
return list;
}
/// Reads a map from [Name] values to [V] values from this data source,
/// calling [f] to read each value from the data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeNameMap].
Map<Name, V>? readNameMap<V>(V f(), {bool emptyAsNull = false}) {
int count = readInt();
if (count == 0 && emptyAsNull) return null;
Map<Name, V> map = {};
for (int i = 0; i < count; i++) {
Name key = readMemberName();
V value = f();
map[key] = value;
}
return map;
}
/// Reads a map from string values to [V] values from this data source,
/// calling [f] to read each value from the data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeStringMap].
Map<String, V>? readStringMap<V>(V f(), {bool emptyAsNull = false}) {
int count = readInt();
if (count == 0 && emptyAsNull) return null;
Map<String, V> map = {};
for (int i = 0; i < count; i++) {
String key = readString();
V value = f();
map[key] = value;
}
return map;
}
/// Reads an enum value from the list of enum [values] from this data source.
///
/// The [values] argument is intended to be the static `.values` field on
/// enum classes, for instance:
///
/// enum Foo { bar, baz }
/// ...
/// Foo foo = source.readEnum(Foo.values);
///
E readEnum<E>(List<E> values) {
_checkDataKind(DataKind.enumValue);
return _sourceReader.readEnum(values);
}
/// Reads a URI value from this data source.
Uri readUri() {
_checkDataKind(DataKind.uri);
return _readUri();
}
Uri _readUri() {
return _uriIndex.read(_doReadUri)!;
}
Uri _doReadUri() {
return Uri.parse(_readString());
}
/// Reads a reference to a kernel library node from this data source.
ir.Library readLibraryNode() {
_checkDataKind(DataKind.libraryNode);
return _readLibraryData().node;
}
LibraryData _readLibraryData() {
Uri canonicalUri = _readUri();
return componentLookup.getLibraryDataByUri(canonicalUri);
}
/// Reads a reference to a kernel class node from this data source.
ir.Class readClassNode() {
_checkDataKind(DataKind.classNode);
return _readClassData().node;
}
ClassData _readClassData() {
LibraryData library = _readLibraryData();
String name = _readString();
return library.lookupClassByName(name)!;
}
/// Reads a reference to a kernel class node from this data source.
ir.Typedef readTypedefNode() {
_checkDataKind(DataKind.typedefNode);
return _readTypedefNode();
}
ir.Typedef _readTypedefNode() {
LibraryData library = _readLibraryData();
String name = _readString();
return library.lookupTypedef(name)!;
}
/// Reads a reference to a kernel member node from this data source.
ir.Member readMemberNode() {
_checkDataKind(DataKind.memberNode);
return _readMemberData().node;
}
MemberData _readMemberData() {
return _memberNodeIndex.read(_readMemberDataInternal)!;
}
MemberData _readMemberDataInternal() {
MemberContextKind kind = _sourceReader.readEnum(MemberContextKind.values);
switch (kind) {
case MemberContextKind.cls:
ClassData cls = _readClassData();
String name = _readString();
return cls.lookupMemberDataByName(name)!;
case MemberContextKind.library:
LibraryData library = _readLibraryData();
String name = _readString();
return library.lookupMemberDataByName(name)!;
}
}
/// Reads a list of references to kernel member nodes from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberNodes].
List<E> readMemberNodes<E extends ir.Member>() {
return readMemberNodesOrNull<E>() ?? List.empty();
}
/// Reads a list of references to kernel member nodes from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberNodes].
List<E>? readMemberNodesOrNull<E extends ir.Member>() {
int count = readInt();
if (count == 0) return null;
return List<E>.generate(count, (_) => readMemberNode() as E,
growable: false);
}
/// Reads a map from kernel member nodes to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberNodeMap].
Map<K, V> readMemberNodeMap<K extends ir.Member, V>(V f()) {
return readMemberNodeMapOrNull<K, V>(f) ?? {};
}
/// Reads a map from kernel member nodes to [V] values from this data source,
/// calling [f] to read each value from the data source. `null` is returned
/// instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberNodeMap].
Map<K, V>? readMemberNodeMapOrNull<K extends ir.Member, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final node = readMemberNode() as K;
V value = f();
map[node] = value;
}
return map;
}
/// Reads a kernel name node from this data source.
ir.Name readName() {
String text = readString();
ir.Library? library = readValueOrNull(readLibraryNode);
return ir.Name(text, library);
}
/// Reads a [Name] from this data source.
Name readMemberName() {
String text = readString();
Uri? uri = readValueOrNull(readUri);
bool setter = readBool();
return Name(text, uri, isSetter: setter);
}
/// Reads a kernel library dependency node from this data source.
ir.LibraryDependency readLibraryDependencyNode() {
ir.Library library = readLibraryNode();
int index = readInt();
return library.dependencies[index];
}
/// Reads a potentially `null` kernel library dependency node from this data
/// source.
ir.LibraryDependency? readLibraryDependencyNodeOrNull() {
return readValueOrNull(readLibraryDependencyNode);
}
/// Reads a reference to a kernel tree node from this data source.
ir.TreeNode readTreeNode() {
_checkDataKind(DataKind.treeNode);
return _readTreeNode(null);
}
ir.TreeNode _readTreeNode(MemberData? memberData) {
_TreeNodeKind kind = _sourceReader.readEnum(_TreeNodeKind.values);
switch (kind) {
case _TreeNodeKind.cls:
return _readClassData().node;
case _TreeNodeKind.member:
return _readMemberData().node;
case _TreeNodeKind.functionDeclarationVariable:
final functionDeclaration =
_readTreeNode(memberData) as ir.FunctionDeclaration;
return functionDeclaration.variable;
case _TreeNodeKind.functionNode:
return _readFunctionNode(memberData);
case _TreeNodeKind.typeParameter:
return _readTypeParameter(memberData);
case _TreeNodeKind.constant:
memberData ??= _readMemberData();
final expression = _readTreeNode(memberData) as ir.ConstantExpression;
ir.Constant constant =
memberData.getConstantByIndex(expression, _sourceReader.readInt());
return ConstantReference(expression, constant);
case _TreeNodeKind.node:
memberData ??= _readMemberData();
int index = _sourceReader.readInt();
ir.TreeNode treeNode = memberData.getTreeNodeByIndex(index);
return treeNode;
}
}
/// Reads a reference to a potentially `null` kernel tree node from this data
/// source.
ir.TreeNode? readTreeNodeOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readTreeNode();
}
return null;
}
/// Reads a list of references to kernel tree nodes from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodes].
List<E> readTreeNodes<E extends ir.TreeNode>() {
return readTreeNodesOrNull<E>() ?? List.empty();
}
/// Reads a list of references to kernel tree nodes from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodes].
List<E>? readTreeNodesOrNull<E extends ir.TreeNode>() {
int count = readInt();
if (count == 0) return null;
return List<E>.generate(count, (i) => readTreeNode() as E, growable: false);
}
/// Reads a map from kernel tree nodes to [V] values from this data source,
/// calling [f] to read each value from the data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodeMap].
Map<K, V> readTreeNodeMap<K extends ir.TreeNode, V>(V f()) {
return readTreeNodeMapOrNull(f) ?? <K, V>{};
}
Map<K, V>? readTreeNodeMapOrNull<K extends ir.TreeNode, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final node = readTreeNode() as K;
V value = f();
map[node] = value;
}
return map;
}
/// Reads a reference to a kernel tree node in the known [context] from this
/// data source.
ir.TreeNode readTreeNodeInContext() {
return readTreeNodeInContextInternal(currentMemberData);
}
ir.TreeNode readTreeNodeInContextInternal(MemberData memberData) {
_checkDataKind(DataKind.treeNode);
return _readTreeNode(memberData);
}
/// Reads a reference to a potentially `null` kernel tree node in the known
/// [context] from this data source.
ir.TreeNode? readTreeNodeOrNullInContext() {
bool hasValue = readBool();
if (hasValue) {
return readTreeNodeInContextInternal(currentMemberData);
}
return null;
}
/// Reads a list of references to kernel tree nodes in the known [context]
/// from this data source. If [emptyAsNull] is `true`, `null` is returned
/// instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodesInContext].
List<E>? readTreeNodesInContext<E extends ir.TreeNode>(
{bool emptyAsNull = false}) {
int count = readInt();
if (count == 0 && emptyAsNull) return null;
return List<E>.generate(
count, (index) => readTreeNodeInContextInternal(currentMemberData) as E,
growable: false);
}
/// Reads a map from kernel tree nodes to [V] values in the known [context]
/// from this data source, calling [f] to read each value from the data
/// source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodeMapInContext].
Map<K, V> readTreeNodeMapInContext<K extends ir.TreeNode, V>(V f()) {
return readTreeNodeMapInContextOrNull<K, V>(f) ?? {};
}
/// Reads a map from kernel tree nodes to [V] values in the known [context]
/// from this data source, calling [f] to read each value from the data
/// source. `null` is returned for an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTreeNodeMapInContext].
Map<K, V>? readTreeNodeMapInContextOrNull<K extends ir.TreeNode, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final node = readTreeNodeInContextInternal(currentMemberData) as K;
V value = f();
map[node] = value;
}
return map;
}
/// Reads a reference to a kernel type parameter node from this data source.
ir.TypeParameter readTypeParameterNode() {
_checkDataKind(DataKind.typeParameterNode);
return _readTypeParameter(null);
}
ir.TypeParameter _readTypeParameter(MemberData? memberData) {
_TypeParameterKind kind = _sourceReader.readEnum(_TypeParameterKind.values);
switch (kind) {
case _TypeParameterKind.cls:
ir.Class cls = _readClassData().node;
return cls.typeParameters[_sourceReader.readInt()];
case _TypeParameterKind.functionNode:
ir.FunctionNode functionNode = _readFunctionNode(memberData);
return functionNode.typeParameters[_sourceReader.readInt()];
}
}
/// Reads a list of references to kernel type parameter nodes from this data
/// source. If [emptyAsNull] is `true`, `null` is returned instead of an empty
/// list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTypeParameterNodes].
List<ir.TypeParameter> readTypeParameterNodes() {
int count = readInt();
return List<ir.TypeParameter>.generate(
count, (index) => readTypeParameterNode(),
growable: false);
}
/// Reads a type from this data source.
DartType readDartType() {
_checkDataKind(DataKind.dartType);
final type = DartType.readFromDataSource(this, []);
return interner?.internDartType(type) ?? type;
}
/// Reads a nullable type from this data source.
DartType? readDartTypeOrNull() {
_checkDataKind(DataKind.dartType);
return DartType.readFromDataSourceOrNull(this, []);
}
/// Reads a list of types from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeDartTypes].
List<DartType> readDartTypes() {
// Share the list when empty.
return readDartTypesOrNull() ?? const [];
}
/// Reads a list of types from this data source. Returns `null` instead of an
/// empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeDartTypes].
List<DartType>? readDartTypesOrNull() {
int count = readInt();
if (count == 0) return null;
return List.generate(count, (_) => readDartType(), growable: false);
}
/// Reads a kernel type node from this data source. If [allowNull], the
/// returned type is allowed to be `null`.
ir.DartType readDartTypeNode() {
_checkDataKind(DataKind.dartTypeNode);
ir.DartType? type = readDartTypeNodeOrNull();
if (type == null) throw UnsupportedError('Unexpected `null` DartTypeNode');
return type;
}
/// Reads a kernel type node from this data source. The returned type is
/// allowed to be `null`.
ir.DartType? readDartTypeNodeOrNull() {
_checkDataKind(DataKind.dartTypeNode);
final type = _readDartTypeNode([]);
return interner?.internDartTypeNode(type) ?? type;
}
ir.DartType? _readDartTypeNode(List<ir.TypeParameter> functionTypeVariables) {
DartTypeNodeKind kind = readEnum(DartTypeNodeKind.values);
switch (kind) {
case DartTypeNodeKind.none:
return null;
case DartTypeNodeKind.voidType:
return const ir.VoidType();
case DartTypeNodeKind.invalidType:
return const ir.InvalidType();
case DartTypeNodeKind.doesNotComplete:
return const DoesNotCompleteType();
case DartTypeNodeKind.neverType:
ir.Nullability nullability = readEnum(ir.Nullability.values);
return ir.NeverType.fromNullability(nullability);
case DartTypeNodeKind.typeParameterType:
ir.TypeParameter typeParameter = readTypeParameterNode();
ir.Nullability typeParameterTypeNullability =
readEnum(ir.Nullability.values);
ir.DartType? promotedBound = _readDartTypeNode(functionTypeVariables);
return ir.TypeParameterType(
typeParameter, typeParameterTypeNullability, promotedBound);
case DartTypeNodeKind.functionTypeVariable:
int index = readInt();
assert(0 <= index && index < functionTypeVariables.length);
ir.Nullability typeParameterTypeNullability =
readEnum(ir.Nullability.values);
ir.DartType? promotedBound = _readDartTypeNode(functionTypeVariables);
return ir.TypeParameterType(functionTypeVariables[index],
typeParameterTypeNullability, promotedBound);
case DartTypeNodeKind.functionType:
begin(functionTypeNodeTag);
int typeParameterCount = readInt();
List<ir.TypeParameter> typeParameters = List<ir.TypeParameter>.generate(
typeParameterCount, (int index) => ir.TypeParameter(),
growable: false);
functionTypeVariables =
List<ir.TypeParameter>.from(functionTypeVariables)
..addAll(typeParameters);
for (int index = 0; index < typeParameterCount; index++) {
typeParameters[index].name = readString();
typeParameters[index].bound =
_readDartTypeNode(functionTypeVariables)!;
typeParameters[index].defaultType =
_readDartTypeNode(functionTypeVariables)!;
}
ir.DartType returnType = _readDartTypeNode(functionTypeVariables)!;
ir.Nullability nullability = readEnum(ir.Nullability.values);
int requiredParameterCount = readInt();
List<ir.DartType> positionalParameters =
_readDartTypeNodes(functionTypeVariables);
int namedParameterCount = readInt();
final namedParameters =
List<ir.NamedType>.generate(namedParameterCount, (index) {
String name = readString();
bool isRequired = readBool();
ir.DartType type = _readDartTypeNode(functionTypeVariables)!;
return ir.NamedType(name, type, isRequired: isRequired);
}, growable: false);
end(functionTypeNodeTag);
return ir.FunctionType(positionalParameters, returnType, nullability,
namedParameters: namedParameters,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount);
case DartTypeNodeKind.interfaceType:
ir.Class cls = readClassNode();
ir.Nullability nullability = readEnum(ir.Nullability.values);
List<ir.DartType> typeArguments =
_readDartTypeNodes(functionTypeVariables);
return ir.InterfaceType(cls, nullability, typeArguments);
case DartTypeNodeKind.thisInterfaceType:
ir.Class cls = readClassNode();
ir.Nullability nullability = readEnum(ir.Nullability.values);
List<ir.DartType> typeArguments =
_readDartTypeNodes(functionTypeVariables);
return ThisInterfaceType(cls, nullability, typeArguments);
case DartTypeNodeKind.exactInterfaceType:
ir.Class cls = readClassNode();
ir.Nullability nullability = readEnum(ir.Nullability.values);
List<ir.DartType> typeArguments =
_readDartTypeNodes(functionTypeVariables);
return ExactInterfaceType(cls, nullability, typeArguments);
case DartTypeNodeKind.typedef:
ir.Typedef typedef = readTypedefNode();
ir.Nullability nullability = readEnum(ir.Nullability.values);
List<ir.DartType> typeArguments =
_readDartTypeNodes(functionTypeVariables);
return ir.TypedefType(typedef, nullability, typeArguments);
case DartTypeNodeKind.dynamicType:
return const ir.DynamicType();
case DartTypeNodeKind.futureOrType:
ir.Nullability nullability = readEnum(ir.Nullability.values);
ir.DartType typeArgument = _readDartTypeNode(functionTypeVariables)!;
return ir.FutureOrType(typeArgument, nullability);
case DartTypeNodeKind.nullType:
return const ir.NullType();
}
}
/// Reads a list of kernel type nodes from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeDartTypeNodes].
List<ir.DartType> readDartTypeNodes() {
return readDartTypeNodesOrNull() ?? const [];
}
/// Reads a list of kernel type nodes from this data source. `null` is
/// returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeDartTypeNodes].
List<ir.DartType>? readDartTypeNodesOrNull() {
int count = readInt();
if (count == 0) return null;
return List<ir.DartType>.generate(count, (index) => readDartTypeNode(),
growable: false);
}
List<ir.DartType> _readDartTypeNodes(
List<ir.TypeParameter> functionTypeVariables) {
int count = readInt();
if (count == 0) return emptyListOfDartTypes;
return List<ir.DartType>.generate(
count, (index) => _readDartTypeNode(functionTypeVariables)!,
growable: false);
}
/// Reads a source span from this data source.
SourceSpan readSourceSpan() {
_checkDataKind(DataKind.sourceSpan);
Uri uri = _readUri();
int begin = _sourceReader.readInt();
int end = _sourceReader.readInt();
return SourceSpan(uri, begin, end);
}
/// Reads a reference to a library entity from this data source.
LibraryEntity readLibrary() {
return _entityReader.readLibraryFromDataSource(this, entityLookup);
}
/// Reads a reference to a potentially `null` library entity from this data
/// source.
LibraryEntity? readLibraryOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readLibrary();
}
return null;
}
/// Reads a library from library entities to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeLibraryMap].
Map<K, V> readLibraryMap<K extends LibraryEntity, V>(V f()) {
return readLibraryMapOrNull<K, V>(f) ?? {};
}
/// Reads a library from library entities to [V] values from this data source,
/// calling [f] to read each value from the data source. `null` is returned
/// instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeLibraryMap].
Map<K, V>? readLibraryMapOrNull<K extends LibraryEntity, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final library = readLibrary() as K;
V value = f();
map[library] = value;
}
return map;
}
/// Reads a reference to an class entity from this data source.
ClassEntity readClass() {
return _entityReader.readClassFromDataSource(this, entityLookup);
}
/// Reads a reference to a potentially `null` class entity from this data
/// source.
ClassEntity? readClassOrNull() {
bool hasClass = readBool();
if (hasClass) {
return readClass();
}
return null;
}
/// Reads a list of references to class entities from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeClasses].
List<E> readClasses<E extends ClassEntity>() {
return readClassesOrNull<E>() ?? List.empty();
}
/// Reads a list of references to class entities from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeClasses].
List<E>? readClassesOrNull<E extends ClassEntity>() {
int count = readInt();
if (count == 0) return null;
return List<E>.generate(count, (index) => readClass() as E,
growable: false);
}
/// Reads a map from class entities to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeClassMap].
Map<K, V> readClassMap<K extends ClassEntity, V>(V f()) {
return readClassMapOrNull<K, V>(f) ?? {};
}
/// Reads a map from class entities to [V] values from this data source,
/// calling [f] to read each value from the data source. `null` is returned if
/// the map is empty.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeClassMap].
Map<K, V>? readClassMapOrNull<K extends ClassEntity, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final cls = readClass() as K;
V value = f();
map[cls] = value;
}
return map;
}
/// Reads a reference to an member entity from this data source.
MemberEntity readMember() {
return _entityReader.readMemberFromDataSource(this, entityLookup);
}
/// Reads a reference to a potentially `null` member entity from this data
/// source.
MemberEntity? readMemberOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readMember();
}
return null;
}
/// Reads a list of references to member entities from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMembers].
List<E> readMembers<E extends MemberEntity>() {
return readMembersOrNull() ?? List.empty();
}
/// Reads a list of references to member entities from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMembers].
List<E>? readMembersOrNull<E extends MemberEntity>() {
int count = readInt();
if (count == 0) return null;
return List<E>.generate(count, (index) => readMember() as E,
growable: false);
}
/// Reads a map from member entities to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberMap].
Map<K, V> readMemberMap<K extends MemberEntity, V>(V f(MemberEntity member)) {
return readMemberMapOrNull<K, V>(f) ?? {};
}
/// Reads a map from member entities to [V] values from this data source,
/// calling [f] to read each value from the data source.
/// `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeMemberMap].
Map<K, V>? readMemberMapOrNull<K extends MemberEntity, V>(
V f(MemberEntity member)) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final member = readMember() as K;
V value = f(member);
map[member] = value;
}
return map;
}
/// Reads a reference to an type variable entity from this data source.
TypeVariableEntity readTypeVariable() {
return _entityReader.readTypeVariableFromDataSource(this, entityLookup);
}
/// Reads a map from type variable entities to [V] values from this data
/// source, calling [f] to read each value from the data source. If
/// [emptyAsNull] is `true`, `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeTypeVariableMap].
Map<K, V> readTypeVariableMap<K extends TypeVariableEntity, V>(V f()) {
int count = readInt();
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final node = readTypeVariable() as K;
V value = f();
map[node] = value;
}
return map;
}
/// Reads a reference to a local from this data source.
Local readLocal() {
LocalKind kind = readEnum(LocalKind.values);
switch (kind) {
case LocalKind.jLocal:
MemberEntity memberContext = readMember();
int localIndex = readInt();
return localLookup.getLocalByIndex(memberContext, localIndex);
case LocalKind.thisLocal:
ClassEntity cls = readClass();
return ThisLocal(cls);
case LocalKind.boxLocal:
ClassEntity cls = readClass();
return BoxLocal(cls);
case LocalKind.anonymousClosureLocal:
final cls = readClass() as JClosureClass;
return AnonymousClosureLocal(cls);
case LocalKind.typeVariableLocal:
TypeVariableEntity typeVariable = readTypeVariable();
return TypeVariableLocal(typeVariable);
}
}
/// Reads a reference to a potentially `null` local from this data source.
Local? readLocalOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readLocal();
}
return null;
}
/// Reads a map from locals to [V] values from this data source, calling [f]
/// to read each value from the data source. If [emptyAsNull] is `true`,
/// `null` is returned instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeLocalMap].
Map<K, V> readLocalMap<K extends Local, V>(V f()) {
int count = readInt();
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final local = readLocal() as K;
V value = f();
map[local] = value;
}
return map;
}
/// Reads a constant value from this data source.
ConstantValue readConstant() {
_checkDataKind(DataKind.constant);
return _readConstant();
}
ConstantValue _readConstant() {
return _constantIndex.read(_readConstantInternal)!;
}
ConstantValue _readConstantInternal() {
ConstantValueKind kind = _sourceReader.readEnum(ConstantValueKind.values);
switch (kind) {
case ConstantValueKind.BOOL:
bool value = readBool();
return BoolConstantValue(value);
case ConstantValueKind.INT:
BigInt value = _readBigInt();
return IntConstantValue(value);
case ConstantValueKind.DOUBLE:
double value = _readDoubleValue();
return DoubleConstantValue(value);
case ConstantValueKind.STRING:
String value = readString();
return StringConstantValue(value);
case ConstantValueKind.NULL:
return const NullConstantValue();
case ConstantValueKind.FUNCTION:
final function = readMember() as FunctionEntity;
final type = readDartType() as FunctionType;
return FunctionConstantValue(function, type);
case ConstantValueKind.LIST:
final type = readDartType() as InterfaceType;
final entries = readConstants();
return ListConstantValue(type, entries);
case ConstantValueKind.SET:
final type = readDartType() as InterfaceType;
final entries = readConstant() as MapConstantValue;
return constant_system.JavaScriptSetConstant(type, entries);
case ConstantValueKind.MAP:
final type = readDartType() as InterfaceType;
final keyList = readConstant() as ListConstantValue;
List<ConstantValue> values = readConstants();
bool onlyStringKeys = readBool();
return constant_system.JavaScriptMapConstant(
type, keyList, values, onlyStringKeys);
case ConstantValueKind.CONSTRUCTED:
final type = readDartType() as InterfaceType;
Map<FieldEntity, ConstantValue> fields =
readMemberMap<FieldEntity, ConstantValue>(
(MemberEntity member) => readConstant());
return ConstructedConstantValue(type, fields);
case ConstantValueKind.TYPE:
final representedType = readDartType();
final type = readDartType() as InterfaceType;
return TypeConstantValue(representedType, type);
case ConstantValueKind.INSTANTIATION:
List<DartType> typeArguments = readDartTypes();
final function = readConstant() as FunctionConstantValue;
return InstantiationConstantValue(typeArguments, function);
case ConstantValueKind.NON_CONSTANT:
return NonConstantValue();
case ConstantValueKind.INTERCEPTOR:
ClassEntity cls = readClass();
return InterceptorConstantValue(cls);
case ConstantValueKind.DEFERRED_GLOBAL:
ConstantValue constant = readConstant();
OutputUnit unit = readOutputUnitReference();
return DeferredGlobalConstantValue(constant, unit);
case ConstantValueKind.DUMMY_INTERCEPTOR:
return DummyInterceptorConstantValue();
case ConstantValueKind.LATE_SENTINEL:
return LateSentinelConstantValue();
case ConstantValueKind.UNREACHABLE:
return UnreachableConstantValue();
case ConstantValueKind.JS_NAME:
final name = readJsNode() as js.LiteralString;
return JsNameConstantValue(name);
}
}
/// Reads a potentially `null` constant value from this data source.
ConstantValue? readConstantOrNull() {
bool hasClass = readBool();
if (hasClass) {
return readConstant();
}
return null;
}
/// Reads a list of constant values from this data source. If [emptyAsNull] is
/// `true`, `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeConstants].
List<E> readConstants<E extends ConstantValue>() {
int count = readInt();
return List<E>.generate(count, (index) => readConstant() as E,
growable: false);
}
/// Reads a map from constant values to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeConstantMap].
Map<K, V> readConstantMap<K extends ConstantValue, V>(V f()) {
return readConstantMapOrNull<K, V>(f) ?? {};
}
/// Reads a map from constant values to [V] values from this data source,
/// calling [f] to read each value from the data source. `null` is returned
/// instead of an empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeConstantMap].
Map<K, V>? readConstantMapOrNull<K extends ConstantValue, V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<K, V> map = {};
for (int i = 0; i < count; i++) {
final key = readConstant() as K;
V value = f();
map[key] = value;
}
return map;
}
/// Reads a double value from this data source.
double readDoubleValue() {
_checkDataKind(DataKind.double);
return _readDoubleValue();
}
double _readDoubleValue() {
ByteData data = ByteData(8);
data.setUint16(0, readInt());
data.setUint16(2, readInt());
data.setUint16(4, readInt());
data.setUint16(6, readInt());
return data.getFloat64(0);
}
/// Reads an integer of arbitrary value from this data source.
///
/// This is should only when the value is not known to be a non-negative
/// 30 bit integer. Otherwise [readInt] should be used.
int readIntegerValue() {
_checkDataKind(DataKind.int);
return _readBigInt().toInt();
}
BigInt _readBigInt() {
return BigInt.parse(readString());
}
ImportEntity readImport() {
_checkDataKind(DataKind.import);
return _readImport();
}
/// Reads a import from this data source.
ImportEntity _readImport() {
return _importIndex.read(_readImportInternal)!;
}
ImportEntity _readImportInternal() {
String? name = readStringOrNull();
Uri uri = _readUri();
Uri enclosingLibraryUri = _readUri();
bool isDeferred = _readBool();
return ImportEntity(isDeferred, name, uri, enclosingLibraryUri);
}
/// Reads a potentially `null` import from this data source.
ImportEntity? readImportOrNull() {
bool hasClass = readBool();
if (hasClass) {
return readImport();
}
return null;
}
/// Reads a list of imports from this data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeImports].
List<ImportEntity> readImports() {
return readImportsOrNull() ?? const [];
}
/// Reads a list of imports from this data source.
/// `null` is returned instead of an empty list.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeImports].
List<ImportEntity>? readImportsOrNull() {
int count = readInt();
if (count == 0) return null;
return List<ImportEntity>.generate(count, (index) => readImport(),
growable: false);
}
/// Reads a map from imports to [V] values from this data source,
/// calling [f] to read each value from the data source.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeImportMap].
Map<ImportEntity, V> readImportMap<V>(V f()) {
return readImportMapOrNull<V>(f) ?? {};
}
/// Reads a map from imports to [V] values from this data source, calling [f]
/// to read each value from the data source. `null` is returned if the map is
/// empty map.
///
/// This is a convenience method to be used together with
/// [DataSinkWriter.writeImportMap].
Map<ImportEntity, V>? readImportMapOrNull<V>(V f()) {
int count = readInt();
if (count == 0) return null;
Map<ImportEntity, V> map = {};
for (int i = 0; i < count; i++) {
ImportEntity key = readImport();
V value = f();
map[key] = value;
}
return map;
}
/// Reads an [AbstractValue] from this data source.
///
/// This feature is only available a [CodegenReader] has been registered.
AbstractValue readAbstractValue() {
assert(
_codegenReader != null,
"Can not deserialize an AbstractValue "
"without a registered codegen reader.");
return _codegenReader!.readAbstractValue(this);
}
/// Reads a reference to an [OutputUnit] from this data source.
///
/// This feature is only available a [CodegenReader] has been registered.
OutputUnit readOutputUnitReference() {
assert(
_codegenReader != null,
"Can not deserialize an OutputUnit reference "
"without a registered codegen reader.");
return _codegenReader!.readOutputUnitReference(this);
}
/// Reads a [js.Node] value from this data source.
///
/// This feature is only available a [CodegenReader] has been registered.
js.Node readJsNode() {
assert(_codegenReader != null,
"Can not deserialize a JS node without a registered codegen reader.");
return _codegenReader!.readJsNode(this);
}
/// Reads a potentially `null` [js.Node] value from this data source.
///
/// This feature is only available a [CodegenReader] has been registered.
js.Node? readJsNodeOrNull() {
bool hasValue = readBool();
if (hasValue) {
return readJsNode();
}
return null;
}
/// Reads a [TypeRecipe] value from this data source.
///
/// This feature is only available a [CodegenReader] has been registered.
TypeRecipe readTypeRecipe() {
assert(_codegenReader != null,
"Can not deserialize a TypeRecipe without a registered codegen reader.");
return _codegenReader!.readTypeRecipe(this);
}
MemberData _getMemberData(ir.Member node) {
LibraryData libraryData =
componentLookup.getLibraryDataByUri(node.enclosingLibrary.importUri);
if (node.enclosingClass != null) {
final classData = libraryData.lookupClassByNode(node.enclosingClass!)!;
return classData.lookupMemberDataByNode(node)!;
} else {
return libraryData.lookupMemberDataByNode(node)!;
}
}
ir.FunctionNode _readFunctionNode(MemberData? memberData) {
_FunctionNodeKind kind = _sourceReader.readEnum(_FunctionNodeKind.values);
switch (kind) {
case _FunctionNodeKind.procedure:
final procedure = _readMemberData().node as ir.Procedure;
return procedure.function;
case _FunctionNodeKind.constructor:
final constructor = _readMemberData().node as ir.Constructor;
return constructor.function;
case _FunctionNodeKind.functionExpression:
final functionExpression =
_readTreeNode(memberData) as ir.FunctionExpression;
return functionExpression.function;
case _FunctionNodeKind.functionDeclaration:
final functionDeclaration =
_readTreeNode(memberData) as ir.FunctionDeclaration;
return functionDeclaration.function;
}
}
void _checkDataKind(DataKind expectedKind) {
if (!useDataKinds) return;
DataKind actualKind = _sourceReader.readEnum(DataKind.values);
assert(
actualKind == expectedKind,
"Invalid data kind. "
"Expected $expectedKind, "
"found $actualKind.${_sourceReader.errorContext}");
}
}