0c17d7fc89
The return value is unused and previous changes ensure members always exist. Change-Id: I476c11261048b5fe48d0603b899e1dde9235a705 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419500 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
368 lines
9.2 KiB
Dart
368 lines
9.2 KiB
Dart
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:kernel/ast.dart';
|
|
|
|
import 'dynamic_module_kernel_metadata.dart';
|
|
import 'reference_extensions.dart';
|
|
|
|
class _EntityToIdMapper {
|
|
final Component component;
|
|
final Map<TreeNode, int> _ids;
|
|
|
|
_EntityToIdMapper(this.component)
|
|
: _ids =
|
|
(component.metadata[DynamicModuleGlobalIdRepository.repositoryTag]
|
|
as DynamicModuleGlobalIdRepository)
|
|
.mapping;
|
|
|
|
int idForClass(Class cls) {
|
|
return _ids[cls]!;
|
|
}
|
|
|
|
int idForMember(Member member) {
|
|
return _ids[member]!;
|
|
}
|
|
|
|
(int, int) idForReference(Reference reference) {
|
|
return (idForMember(reference.asMember), _flagForReference(reference));
|
|
}
|
|
|
|
static int _flagForReference(Reference reference) {
|
|
if (reference.isImplicitGetter) return 0;
|
|
if (reference.isImplicitSetter) return 1;
|
|
if (reference.isTearOffReference) return 2;
|
|
if (reference.isConstructorBodyReference) return 3;
|
|
if (reference.isInitializerReference) return 4;
|
|
if (reference.isTypeCheckerReference) return 5;
|
|
if (reference.isCheckedEntryReference) return 6;
|
|
if (reference.isUncheckedEntryReference) return 7;
|
|
if (reference.isBodyReference) return 8;
|
|
assert(reference == reference.asMember.reference);
|
|
return 9;
|
|
}
|
|
}
|
|
|
|
class _IdToEntityMapper {
|
|
final Component component;
|
|
final Map<int, TreeNode> _mapping;
|
|
|
|
static Map<int, TreeNode> _makeIdsMap(Component component) {
|
|
final mapping =
|
|
(component.metadata[DynamicModuleGlobalIdRepository.repositoryTag]
|
|
as DynamicModuleGlobalIdRepository)
|
|
.mapping;
|
|
final inverse = <int, TreeNode>{};
|
|
mapping.forEach((node, id) {
|
|
inverse[id] = node;
|
|
});
|
|
return inverse;
|
|
}
|
|
|
|
_IdToEntityMapper(this.component) : _mapping = _makeIdsMap(component);
|
|
|
|
Class classForId(int id) {
|
|
return _mapping[id] as Class;
|
|
}
|
|
|
|
Member memberForId(int id) {
|
|
return _mapping[id] as Member;
|
|
}
|
|
|
|
Reference referenceForId(int memberId, int flag) {
|
|
final member = _mapping[memberId] as Member;
|
|
return _referenceForFlag(member, flag);
|
|
}
|
|
|
|
static Reference _referenceForFlag(Member member, int flag) {
|
|
if (flag == 0) return (member as Field).getterReference;
|
|
if (flag == 1) return (member as Field).setterReference!;
|
|
if (flag == 2) return (member as Procedure).tearOffReference;
|
|
if (flag == 3) return (member as Constructor).constructorBodyReference;
|
|
if (flag == 4) return (member as Constructor).initializerReference;
|
|
if (flag == 5) return member.typeCheckerReference;
|
|
if (flag == 6) return member.checkedEntryReference;
|
|
if (flag == 7) return member.uncheckedEntryReference;
|
|
if (flag == 8) return member.bodyReference;
|
|
assert(flag == 9);
|
|
return member.reference;
|
|
}
|
|
}
|
|
|
|
class DataSerializer {
|
|
final _BinaryDataSink _sink = _BinaryDataSink();
|
|
final _EntityToIdMapper _mapper;
|
|
final Map<String, int> _stringIndexer = {};
|
|
|
|
DataSerializer(Component component) : _mapper = _EntityToIdMapper(component);
|
|
|
|
void writeNullable<T>(T? value, void Function(T value) write) {
|
|
if (value != null) {
|
|
_sink.writeBool(true);
|
|
write(value);
|
|
} else {
|
|
_sink.writeBool(false);
|
|
}
|
|
}
|
|
|
|
void writeString(String value) {
|
|
int? index = _stringIndexer[value];
|
|
if (index == null) {
|
|
index = _stringIndexer[value] = _stringIndexer.length;
|
|
_sink.writeInt(index);
|
|
_sink.writeString(value);
|
|
} else {
|
|
_sink.writeInt(index);
|
|
}
|
|
}
|
|
|
|
void writeInt(int value) {
|
|
_sink.writeInt(value);
|
|
}
|
|
|
|
void writeBool(bool value) {
|
|
_sink.writeByte(value ? 1 : 0);
|
|
}
|
|
|
|
void writeBoolList(List<bool> values) {
|
|
_sink.writeInt(values.length);
|
|
int index = 0;
|
|
for (final value in values) {
|
|
index = (index << 1) | (value ? 1 : 0);
|
|
}
|
|
_sink.writeInt(index);
|
|
}
|
|
|
|
void writeEnum<E extends Enum>(E value) {
|
|
writeInt(value.index);
|
|
}
|
|
|
|
void writeClass(Class cls) {
|
|
writeInt(_mapper.idForClass(cls));
|
|
}
|
|
|
|
void writeMember(Member member) {
|
|
final memberId = _mapper.idForMember(member);
|
|
writeInt(memberId);
|
|
}
|
|
|
|
void writeReference(Reference reference) {
|
|
final (memberId, referenceFlag) = _mapper.idForReference(reference);
|
|
writeInt(memberId);
|
|
writeInt(referenceFlag);
|
|
}
|
|
|
|
void writeMap<K, V>(Map<K, V> map, void Function(K key) writeKey,
|
|
void Function(V value) writeValue) {
|
|
writeInt(map.length);
|
|
map.forEach((key, value) {
|
|
writeKey(key);
|
|
writeValue(value);
|
|
});
|
|
}
|
|
|
|
void writeList<E>(Iterable<E> list, void Function(E value) writeValue) {
|
|
writeInt(list.length);
|
|
for (final value in list) {
|
|
writeValue(value);
|
|
}
|
|
}
|
|
|
|
Uint8List takeBytes() {
|
|
return _sink.takeBytes();
|
|
}
|
|
}
|
|
|
|
class DataDeserializer {
|
|
final _BinaryDataSource _source;
|
|
final _IdToEntityMapper _mapper;
|
|
final List<String> _stringIndexer = [];
|
|
|
|
DataDeserializer(Uint8List bytes, Component component)
|
|
: _source = _BinaryDataSource(bytes),
|
|
_mapper = _IdToEntityMapper(component);
|
|
|
|
T? readNullable<T>(T Function() read) {
|
|
return _source.readBool() ? read() : null;
|
|
}
|
|
|
|
String readString() {
|
|
final index = _source.readInt();
|
|
if (index < _stringIndexer.length) return _stringIndexer[index];
|
|
final value = _source.readString();
|
|
assert(index == _stringIndexer.length);
|
|
_stringIndexer.add(value);
|
|
return value;
|
|
}
|
|
|
|
int readInt() {
|
|
return _source.readInt();
|
|
}
|
|
|
|
bool readBool() {
|
|
return _source.readInt() == 1;
|
|
}
|
|
|
|
List<bool> readBoolList() {
|
|
final length = _source.readInt();
|
|
final values = _source.readInt();
|
|
return List.generate(length, (i) => (values >> (length - i - 1)) & 1 == 1);
|
|
}
|
|
|
|
E readEnum<E extends Enum>(List<E> values) {
|
|
int index = _source.readInt();
|
|
assert(
|
|
0 <= index && index < values.length,
|
|
"Invalid data kind index. "
|
|
"Expected one of $values, found index $index.");
|
|
return values[index];
|
|
}
|
|
|
|
Class readClass() {
|
|
return _mapper.classForId(_source.readInt());
|
|
}
|
|
|
|
Member readMember() {
|
|
return _mapper.memberForId(_source.readInt());
|
|
}
|
|
|
|
Reference readReference() {
|
|
final memberId = _source.readInt();
|
|
final flag = _source.readInt();
|
|
return _mapper.referenceForId(memberId, flag);
|
|
}
|
|
|
|
Map<K, V> readMap<K, V>(K Function() readKey, V Function() readValue) {
|
|
final length = _source.readInt();
|
|
final map = <K, V>{};
|
|
for (int i = 0; i < length; i++) {
|
|
final key = readKey();
|
|
final value = readValue();
|
|
map[key] = value;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
List<E> readList<E>(E Function() readValue) {
|
|
final length = _source.readInt();
|
|
final list = <E>[];
|
|
for (int i = 0; i < length; i++) {
|
|
list.add(readValue());
|
|
}
|
|
return list;
|
|
}
|
|
}
|
|
|
|
class _BinaryDataSink {
|
|
static const int _initSinkSize = 50 * 1024;
|
|
|
|
Uint8List _data = Uint8List(_initSinkSize);
|
|
int _length = 0;
|
|
|
|
_BinaryDataSink();
|
|
|
|
int get length => _length;
|
|
|
|
void _ensure(int size) {
|
|
// Ensure space for at least `size` additional bytes.
|
|
if (_data.length < _length + size) {
|
|
int newLength = _data.length * 2;
|
|
while (newLength < _length + size) {
|
|
newLength *= 2;
|
|
}
|
|
_data = Uint8List(newLength)..setRange(0, _data.length, _data);
|
|
}
|
|
}
|
|
|
|
void writeByte(int byte) {
|
|
assert(byte == byte & 0xFF);
|
|
_ensure(1);
|
|
_data[_length++] = byte;
|
|
}
|
|
|
|
void writeBytes(Uint8List bytes) {
|
|
_ensure(bytes.length);
|
|
_data.setRange(_length, _length += bytes.length, bytes);
|
|
}
|
|
|
|
void writeString(String value) {
|
|
final bytes = utf8.encode(value);
|
|
writeInt(bytes.length);
|
|
writeBytes(bytes);
|
|
}
|
|
|
|
void writeBool(bool value) {
|
|
writeByte(value ? 1 : 0);
|
|
}
|
|
|
|
void writeInt(int value) {
|
|
assert(value >= 0 && value >> 30 == 0);
|
|
if (value < 0x80) {
|
|
writeByte(value);
|
|
} else if (value < 0x4000) {
|
|
writeByte((value >> 8) | 0x80);
|
|
writeByte(value & 0xFF);
|
|
} else {
|
|
writeByte((value >> 24) | 0xC0);
|
|
writeByte((value >> 16) & 0xFF);
|
|
writeByte((value >> 8) & 0xFF);
|
|
writeByte(value & 0xFF);
|
|
}
|
|
}
|
|
|
|
Uint8List takeBytes() {
|
|
final result = Uint8List.sublistView(_data, 0, _length);
|
|
// Free the reference to the large data list so it can potentially be
|
|
// tree-shaken.
|
|
_data = Uint8List(0);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
class _BinaryDataSource {
|
|
int _byteOffset = 0;
|
|
final Uint8List _bytes;
|
|
|
|
_BinaryDataSource(this._bytes);
|
|
|
|
void begin(String tag) {}
|
|
|
|
void end(String tag) {}
|
|
|
|
int _readByte() => _bytes[_byteOffset++];
|
|
|
|
String readString() {
|
|
int length = readInt();
|
|
return utf8.decode(
|
|
Uint8List.sublistView(_bytes, _byteOffset, _byteOffset += length));
|
|
}
|
|
|
|
bool readBool() {
|
|
return _readByte() != 0;
|
|
}
|
|
|
|
int readInt() {
|
|
var byte = _readByte();
|
|
if (byte & 0x80 == 0) {
|
|
// 0xxxxxxx
|
|
return byte;
|
|
} else if (byte & 0x40 == 0) {
|
|
// 10xxxxxx
|
|
return ((byte & 0x3F) << 8) | _readByte();
|
|
} else {
|
|
// 11xxxxxx
|
|
return ((byte & 0x3F) << 24) |
|
|
(_readByte() << 16) |
|
|
(_readByte() << 8) |
|
|
_readByte();
|
|
}
|
|
}
|
|
|
|
int get length => _bytes.length;
|
|
int get currentOffset => _byteOffset;
|
|
}
|