| // 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; |
| } |