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