blob: 035e22843b7e1142d35368618c447c51f063a46f [file] [log] [blame] [edit]
// 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;
}