73d9a8d08b
Rename DummyInterceptorConstantValue to DummyConstantValue. It was actually used as a dummy *receiver* argument and could be used against any unused parameter. Remove unused CompilerOptions argument in SSA as-checks and is-tests. Pass the checked input explicitly to facilitate testing on other inputs (e.g. to do partial redundancy elimination by pushing the operation into the arms of a phi.) Change-Id: I4b450bfcf5fc877de731bf83201e3c9248ffcb0c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/433104 Reviewed-by: Mayank Patke <fishythefish@google.com> Commit-Queue: Stephen Adams <sra@google.com>
1581 lines
52 KiB
Dart
1581 lines
52 KiB
Dart
// 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';
|
|
|
|
/// Interface handling [DataSourceReader] low-level data deserialization.
|
|
///
|
|
/// Each implementation of [DataSource] should have a corresponding
|
|
/// [DataSink] for which it deserializes data.
|
|
abstract class DataSource {
|
|
/// Deserialization of a section begin tag.
|
|
void begin(String tag);
|
|
|
|
/// Deserialization of a section end tag.
|
|
void end(String tag);
|
|
|
|
/// Deserialization of a string value.
|
|
String readString();
|
|
|
|
/// Deserialization of a non-negative integer value.
|
|
int readInt();
|
|
|
|
/// Deserialization of a non-negative 32 bit integer value. The value might
|
|
/// not be compacted as with [readInt].
|
|
int readUint32();
|
|
|
|
/// Deserialization of an enum value in [values].
|
|
E readEnum<E extends Enum>(List<E> values);
|
|
|
|
/// Returns the offset for a deferred entity and skips it in the read queue.
|
|
/// The offset can later be passed to [readAtOffset] to get the value. This
|
|
/// block can be written with either [DataSink.writeDeferred] or the
|
|
/// combination of [DataSink.startDeferred] and [DataSink.endDeferred].
|
|
int readDeferred();
|
|
|
|
/// Eagerly reads and returns the value for a deferred entity.
|
|
E readDeferredAsEager<E>(E Function() reader);
|
|
|
|
/// Calls [reader] to read a value at the provided offset in the underlying
|
|
/// data stream. Use with [readDeferred] to read a deferred value.
|
|
E readAtOffset<E>(int offset, E Function() reader);
|
|
|
|
/// The length of the underlying data source.
|
|
int get length;
|
|
|
|
/// The current offset being read from.
|
|
int get currentOffset;
|
|
|
|
/// Returns a string representation of the current state of the data source
|
|
/// useful for debugging in consistencies between serialization and
|
|
/// deserialization.
|
|
String get errorContext;
|
|
}
|
|
|
|
/// 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 useDeferredStrategy;
|
|
final bool useDataKinds;
|
|
final ValueInterner? interner;
|
|
final SerializationIndices importedIndices;
|
|
ComponentLookup? _componentLookup;
|
|
AbstractValueDomain? _abstractValueDomain;
|
|
SourceLookup? _sourceLookup;
|
|
|
|
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 currentOffset => _sourceReader.currentOffset;
|
|
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.
|
|
late int startOffset;
|
|
|
|
DataSourceReader(
|
|
this._sourceReader,
|
|
CompilerOptions options,
|
|
this.importedIndices, {
|
|
this.useDataKinds = false,
|
|
this.interner,
|
|
this.useDeferredStrategy = false,
|
|
}) {
|
|
startOffset = importedIndices.registerSource(this);
|
|
_stringIndex = importedIndices.getIndexedSource<String>();
|
|
_uriIndex = importedIndices.getIndexedSource<Uri>();
|
|
_importIndex = importedIndices.getIndexedSource<ImportEntity>();
|
|
_memberNodeIndex = importedIndices.getIndexedSource<MemberData>();
|
|
_constantIndex = importedIndices.getIndexedSource<ConstantValue>();
|
|
}
|
|
|
|
/// 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!;
|
|
}
|
|
|
|
void registerSourceLookup(SourceLookup sourceLookup) {
|
|
assert(_sourceLookup == null);
|
|
_sourceLookup = sourceLookup;
|
|
}
|
|
|
|
SourceLookup get sourceLookup => _sourceLookup!;
|
|
|
|
/// Registers a [AbstractValueDomain] with this data source to support
|
|
/// deserialization of abstract values.
|
|
void registerAbstractValueDomain(AbstractValueDomain domain) {
|
|
_abstractValueDomain = domain;
|
|
}
|
|
|
|
/// 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 Function() f) {
|
|
final lastSource = _sourceReader;
|
|
final lastComponentLookup = _componentLookup;
|
|
final lastStartOffset = startOffset;
|
|
final lastAbstractValueDomain = _abstractValueDomain;
|
|
_sourceReader = source._sourceReader;
|
|
_componentLookup = source._componentLookup;
|
|
startOffset = source.startOffset;
|
|
_abstractValueDomain = source._abstractValueDomain;
|
|
final value = f();
|
|
_sourceReader = lastSource;
|
|
_componentLookup = lastComponentLookup;
|
|
startOffset = lastStartOffset;
|
|
_abstractValueDomain = lastAbstractValueDomain;
|
|
return value;
|
|
}
|
|
|
|
E readWithOffset<E>(int offset, E Function() f) {
|
|
return _sourceReader.readAtOffset(offset, f);
|
|
}
|
|
|
|
Deferrable<E> readDeferrable<E>(
|
|
E Function(DataSourceReader source) f, {
|
|
bool cacheData = true,
|
|
}) {
|
|
return useDeferredStrategy
|
|
? Deferrable<E>.deferred(
|
|
this,
|
|
f,
|
|
_sourceReader.readDeferred(),
|
|
cacheData: cacheData,
|
|
)
|
|
: Deferrable<E>.eager(_sourceReader.readDeferredAsEager(() => f(this)));
|
|
}
|
|
|
|
Deferrable<E> readDeferrableWithArg<E, A>(
|
|
E Function(DataSourceReader source, A arg) f,
|
|
A arg, {
|
|
bool cacheData = true,
|
|
}) {
|
|
return useDeferredStrategy
|
|
? Deferrable.deferredWithArg<E, A>(
|
|
this,
|
|
f,
|
|
arg,
|
|
_sourceReader.readDeferred(),
|
|
cacheData: cacheData,
|
|
)
|
|
: Deferrable<E>.eager(
|
|
_sourceReader.readDeferredAsEager(() => f(this, arg)),
|
|
);
|
|
}
|
|
|
|
/// 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 Function() 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 readIndexed<E extends Object>(E Function() f) {
|
|
E? value = readIndexedOrNull(f);
|
|
if (value == null) throw StateError("Unexpected 'null' for $E");
|
|
return value;
|
|
}
|
|
|
|
/// Reads a reference to an [E] value from this data source. [f] is called to
|
|
/// deserialize the value at the relevant index. Use [readIndexed] if the value
|
|
/// should be cached and all reads of the index should return the same object.
|
|
E readIndexedNoCache<E extends Object>(E Function() f) {
|
|
E? value = readIndexedOrNullNoCache(f);
|
|
if (value == null) throw StateError("Unexpected 'null' for $E");
|
|
return value;
|
|
}
|
|
|
|
/// Reads a reference to a nullable [E] value from this data source. If the
|
|
/// value has not yet been deserialized, [f] is called to deserialize the
|
|
/// value itself.
|
|
E? readIndexedOrNull<E extends Object>(E Function() f) {
|
|
IndexedSource<E> source =
|
|
(_generalCaches[E] ??= importedIndices.getIndexedSource<E>())
|
|
as IndexedSource<E>;
|
|
return source.read(this, f);
|
|
}
|
|
|
|
/// Reads a reference to a nullable [E] value from this data source. [f] is
|
|
/// called to deserialize the value at the relevant index. Use
|
|
/// [readIndexedOrNull] if the value should be cached and all reads of the
|
|
/// index should return the same object.
|
|
E? readIndexedOrNullNoCache<E extends Object>(E Function() f) {
|
|
IndexedSource<E> source =
|
|
(_generalCaches[E] ??= importedIndices.getIndexedSource<E>())
|
|
as IndexedSource<E>;
|
|
return source.readWithoutCache(this, 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 Function() 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 Function() 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 Function() f) {
|
|
int count = readInt();
|
|
if (count == 0) return null;
|
|
return List<E>.generate(count, (_) => f(), growable: false);
|
|
}
|
|
|
|
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 non-negative 32 bit integer value from this data source. The value
|
|
/// might not be compacted as with [readInt].
|
|
int readUint32() {
|
|
_checkDataKind(DataKind.uint32);
|
|
return _sourceReader.readUint32();
|
|
}
|
|
|
|
/// 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() {
|
|
// Cannot use a tear-off for `_sourceReader.readString` because the data
|
|
// source may be different at the time of reading.
|
|
return _stringIndex.read(this, () => _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.
|
|
///
|
|
/// This is a convenience method to be used together with
|
|
/// [DataSinkWriter.writeStrings].
|
|
List<String> readStrings() {
|
|
return readStringsOrNull() ?? const <String>[];
|
|
}
|
|
|
|
/// Reads a list of string values from this data source. If the list would be
|
|
/// empty returns `null` instead.
|
|
///
|
|
/// This is a convenience method to be used together with
|
|
/// [DataSinkWriter.writeStrings].
|
|
List<String>? readStringsOrNull() {
|
|
int count = readInt();
|
|
if (count == 0) return null;
|
|
return List.generate(count, (_) => readString(), growable: false);
|
|
}
|
|
|
|
/// Reads a map from [Name] 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.writeNameMap].
|
|
Map<Name, V> readNameMap<V>(V Function() f) {
|
|
int count = readInt();
|
|
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.
|
|
///
|
|
/// This is a convenience method to be used together with
|
|
/// [DataSinkWriter.writeStringMap].
|
|
Map<String, V> readStringMap<V>(V Function() f) {
|
|
int count = readInt();
|
|
if (count == 0) return {};
|
|
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 extends Enum>(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(this, _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 extension type declaration node from this
|
|
/// data source.
|
|
ir.ExtensionTypeDeclaration readExtensionTypeDeclarationNode() {
|
|
_checkDataKind(DataKind.extensionTypeDeclarationNode);
|
|
return _readExtensionTypeDeclarationNode();
|
|
}
|
|
|
|
ir.ExtensionTypeDeclaration _readExtensionTypeDeclarationNode() {
|
|
LibraryData library = _readLibraryData();
|
|
String name = _readString();
|
|
return library.lookupExtensionTypeDeclaration(name)!;
|
|
}
|
|
|
|
/// Reads a reference to a kernel typedef 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(this, _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 Function() 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 Function() 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 [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 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.
|
|
///
|
|
/// This is a convenience method to be used together with
|
|
/// [DataSinkWriter.writeTreeNodeMap].
|
|
Map<K, V> readTreeNodeMap<K extends ir.TreeNode, V>(V Function() f) {
|
|
return readTreeNodeMapOrNull(f) ?? <K, V>{};
|
|
}
|
|
|
|
Map<K, V>? readTreeNodeMapOrNull<K extends ir.TreeNode, V>(V Function() 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 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 Function() 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 Function() 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.
|
|
///
|
|
/// 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);
|
|
return _readDartTypeNodeOrNull();
|
|
}
|
|
|
|
ir.DartType? _readDartTypeNodeOrNull() {
|
|
final type = _readDartTypeNode([]);
|
|
return interner?.internDartTypeNode(type) ?? type;
|
|
}
|
|
|
|
ir.DartType? _readDartTypeNode(
|
|
List<ir.StructuralParameter> 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.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);
|
|
ir.TypeParameterType typeParameterType = ir.TypeParameterType(
|
|
typeParameter,
|
|
typeParameterTypeNullability,
|
|
);
|
|
if (promotedBound == null) {
|
|
return typeParameterType;
|
|
} else {
|
|
return ir.IntersectionType(typeParameterType, promotedBound);
|
|
}
|
|
case DartTypeNodeKind.functionTypeVariable:
|
|
int index = readInt();
|
|
assert(0 <= index && index < functionTypeVariables.length);
|
|
ir.Nullability typeParameterTypeNullability = readEnum(
|
|
ir.Nullability.values,
|
|
);
|
|
ir.StructuralParameterType typeParameterType =
|
|
ir.StructuralParameterType(
|
|
functionTypeVariables[index],
|
|
typeParameterTypeNullability,
|
|
);
|
|
return typeParameterType;
|
|
case DartTypeNodeKind.functionType:
|
|
begin(functionTypeNodeTag);
|
|
int typeParameterCount = readInt();
|
|
List<ir.StructuralParameter> typeParameters =
|
|
List<ir.StructuralParameter>.generate(
|
|
typeParameterCount,
|
|
(int index) => ir.StructuralParameter(),
|
|
growable: false,
|
|
);
|
|
functionTypeVariables = List<ir.StructuralParameter>.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,
|
|
);
|
|
final namedParameters = _readNamedTypeNodes(functionTypeVariables);
|
|
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.recordType:
|
|
ir.Nullability nullability = readEnum(ir.Nullability.values);
|
|
List<ir.DartType> positional = _readDartTypeNodes(
|
|
functionTypeVariables,
|
|
);
|
|
List<ir.NamedType> named = _readNamedTypeNodes(functionTypeVariables);
|
|
return ir.RecordType(positional, named, nullability);
|
|
case DartTypeNodeKind.extensionType:
|
|
ir.ExtensionTypeDeclaration extensionTypeDeclaration =
|
|
readExtensionTypeDeclarationNode();
|
|
ir.Nullability nullability = readEnum(ir.Nullability.values);
|
|
List<ir.DartType> typeArguments = _readDartTypeNodes(
|
|
functionTypeVariables,
|
|
);
|
|
return ir.ExtensionType(
|
|
extensionTypeDeclaration,
|
|
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();
|
|
}
|
|
}
|
|
|
|
List<ir.NamedType> _readNamedTypeNodes(
|
|
List<ir.StructuralParameter> functionTypeVariables,
|
|
) {
|
|
int count = readInt();
|
|
if (count == 0) return const [];
|
|
return List<ir.NamedType>.generate(count, (index) {
|
|
String name = readString();
|
|
bool isRequired = readBool();
|
|
ir.DartType type = _readDartTypeNode(functionTypeVariables)!;
|
|
return ir.NamedType(name, type, isRequired: isRequired);
|
|
}, growable: false);
|
|
}
|
|
|
|
/// 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.StructuralParameter> 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 readIndexed<LibraryEntity>(() => JLibrary.readFromDataSource(this));
|
|
}
|
|
|
|
/// 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 Function() 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 Function() 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 readIndexed<ClassEntity>(() => JClass.readFromDataSource(this));
|
|
}
|
|
|
|
/// 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 Function() 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 Function() 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 readIndexed<MemberEntity>(() => JMember.readFromDataSource(this));
|
|
}
|
|
|
|
/// 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 Function(MemberEntity member) f,
|
|
) {
|
|
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 Function(MemberEntity member) f,
|
|
) {
|
|
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 readIndexed<TypeVariableEntity>(
|
|
() => JTypeVariable.readFromDataSource(this),
|
|
);
|
|
}
|
|
|
|
/// Reads a map from type variable 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.writeTypeVariableMap].
|
|
Map<K, V> readTypeVariableMap<K extends TypeVariableEntity, V>(
|
|
V Function() 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:
|
|
return readIndexed<Local>(() => JLocal.readFromDataSource(this));
|
|
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.
|
|
///
|
|
/// This is a convenience method to be used together with
|
|
/// [DataSinkWriter.writeLocalMap].
|
|
Map<K, V> readLocalMap<K extends Local, V>(V Function() 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 map from selectors 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.writeSelectorMap].
|
|
Map<Selector, V> readSelectorMap<V>(V Function(Selector selector) f) =>
|
|
readSelectorMapOrNull<V>(f) ?? {};
|
|
|
|
/// Reads a map from selectors 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.writeSelectorMap].
|
|
Map<Selector, V>? readSelectorMapOrNull<V>(V Function(Selector selector) f) {
|
|
int count = readInt();
|
|
if (count == 0) return null;
|
|
Map<Selector, V> map = {};
|
|
for (int i = 0; i < count; i++) {
|
|
final selector = Selector.readFromDataSource(this);
|
|
map[selector] = f(selector);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
/// Reads a constant value from this data source.
|
|
ConstantValue readConstant() {
|
|
_checkDataKind(DataKind.constant);
|
|
return _readConstant();
|
|
}
|
|
|
|
ConstantValue _readConstant() {
|
|
return _constantIndex.read(this, _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 values = readConstants();
|
|
final indexObject =
|
|
readConstantOrNull() as JavaScriptObjectConstantValue?;
|
|
return constant_system.JavaScriptSetConstant(type, values, indexObject);
|
|
case ConstantValueKind.map:
|
|
final type = readDartType() as InterfaceType;
|
|
final keyList = readConstant() as ListConstantValue;
|
|
final valueList = readConstant() as ListConstantValue;
|
|
bool onlyStringKeys = readBool();
|
|
final indexObject = onlyStringKeys
|
|
? readConstant() as JavaScriptObjectConstantValue
|
|
: null;
|
|
return constant_system.JavaScriptMapConstant(
|
|
type,
|
|
keyList,
|
|
valueList,
|
|
onlyStringKeys,
|
|
indexObject,
|
|
);
|
|
case ConstantValueKind.constructed:
|
|
final type = readDartType() as InterfaceType;
|
|
Map<FieldEntity, ConstantValue> fields =
|
|
readMemberMap<FieldEntity, ConstantValue>(
|
|
(MemberEntity member) => readConstant(),
|
|
);
|
|
return ConstructedConstantValue(type, fields);
|
|
case ConstantValueKind.record:
|
|
final shape = RecordShape.readFromDataSource(this);
|
|
final values = readConstants();
|
|
return RecordConstantValue(shape, values);
|
|
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.interceptor:
|
|
ClassEntity cls = readClass();
|
|
return InterceptorConstantValue(cls);
|
|
case ConstantValueKind.javaScriptObject:
|
|
final keys = readConstants();
|
|
final values = readConstants();
|
|
return JavaScriptObjectConstantValue(keys, values);
|
|
case ConstantValueKind.deferredGlobal:
|
|
ConstantValue constant = readConstant();
|
|
OutputUnit unit = readOutputUnitReference();
|
|
return DeferredGlobalConstantValue(constant, unit);
|
|
case ConstantValueKind.dummy:
|
|
return DummyConstantValue();
|
|
case ConstantValueKind.lateSentinel:
|
|
return LateSentinelConstantValue();
|
|
case ConstantValueKind.unreachable:
|
|
return UnreachableConstantValue();
|
|
case ConstantValueKind.jsName:
|
|
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.
|
|
///
|
|
/// 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 Function() 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 Function() 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(this, _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 Function() 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 Function() 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.
|
|
AbstractValue readAbstractValue() {
|
|
assert(
|
|
_abstractValueDomain != null,
|
|
"Can not deserialize an AbstractValue "
|
|
"without a registered AbstractValueDomain.",
|
|
);
|
|
return _abstractValueDomain!.readAbstractValueFromDataSource(this);
|
|
}
|
|
|
|
/// Reads a reference to an [OutputUnit] from this data source.
|
|
OutputUnit readOutputUnitReference() {
|
|
return readIndexed<OutputUnit>(() => OutputUnit.readFromDataSource(this));
|
|
}
|
|
|
|
/// Reads a [js.Node] value from this data source.
|
|
js.Node readJsNode() {
|
|
return JsNodeDeserializer.readFromDataSource(this);
|
|
}
|
|
|
|
/// Reads a potentially `null` [js.Node] value from this data source.
|
|
js.Node? readJsNodeOrNull() {
|
|
bool hasValue = readBool();
|
|
if (hasValue) {
|
|
return readJsNode();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Reads a [TypeRecipe] value from this data source.
|
|
TypeRecipe readTypeRecipe() {
|
|
return TypeRecipe.readFromDataSource(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}",
|
|
);
|
|
}
|
|
}
|