blob: 8ba6ba235a814a8335455666f4b36d526d83a0e5 [file] [log] [blame]
// Copyright (c) 2023, 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:collection';
import 'dart:convert';
import 'dart:typed_data';
import '../../source_map.dart';
abstract class Serializable {
void serialize(Serializer s);
}
// TODO(joshualitt): Now that we have an IR, we should consider switching to a
// visitor pattern.
class Serializer {
static bool traceEnabled = false;
// The prefix of `_data` up to `_index` contains the data serialized so far.
Uint8List _data = Uint8List(24);
int _index = 0;
// Stack traces or other serializers attached to byte positions within the
// chunk of data produced by this serializer.
late final SplayTreeMap<int, Object> _traces = SplayTreeMap();
/// Get the current offset in the serialized data.
int get offset => _index;
final SourceMapSerializer sourceMapSerializer = SourceMapSerializer();
void _ensure(int size) {
// Ensure space for at least `size` additional bytes.
if (_data.length < _index + size) {
int newLength = _data.length * 2;
while (newLength < _index + size) {
newLength *= 2;
}
_data = Uint8List(newLength)..setRange(0, _data.length, _data);
}
}
void debugTrace(Object data) {
_traces[_index] ??= data;
}
void writeByte(int byte) {
if (traceEnabled) debugTrace(StackTrace.current);
assert(byte == byte & 0xFF);
_ensure(1);
_data[_index++] = byte;
}
void writeBytes(Uint8List bytes) {
if (traceEnabled) debugTrace(StackTrace.current);
_ensure(bytes.length);
_data.setRange(_index, _index += bytes.length, bytes);
}
void writeSigned(int value) {
while (value < -0x40 || value >= 0x40) {
writeByte((value & 0x7F) | 0x80);
value >>= 7;
}
writeByte(value & 0x7F);
}
void writeUnsigned(int value) {
assert(value >= 0);
while (value >= 0x80) {
writeByte((value & 0x7F) | 0x80);
value >>= 7;
}
writeByte(value);
}
static final ByteData _f32ByteData = ByteData(4);
static final Uint8List _f32Uint8List = _f32ByteData.buffer.asUint8List();
void writeF32(double value) {
_f32ByteData.setFloat32(0, value, Endian.little);
writeBytes(_f32Uint8List);
}
static final ByteData _f64ByteData = ByteData(8);
static final Uint8List _f64Uint8List = _f64ByteData.buffer.asUint8List();
void writeF64(double value) {
_f64ByteData.setFloat64(0, value, Endian.little);
writeBytes(_f64Uint8List);
}
void writeName(String name) {
final bytes = utf8.encode(name);
writeUnsigned(bytes.length);
writeBytes(bytes);
}
void write(Serializable object) {
object.serialize(this);
}
void writeList(List<Serializable> objects) {
writeUnsigned(objects.length);
for (int i = 0; i < objects.length; i++) {
write(objects[i]);
}
}
void writeData(Serializer chunk, [List<int> watchPoints = const []]) {
if (traceEnabled) debugTrace(chunk);
for (int watchPoint in watchPoints) {
if (_index <= watchPoint && watchPoint < _index + chunk.data.length) {
int byteValue = chunk.data[watchPoint - _index];
Object trace = this;
int offset = watchPoint;
while (trace is Serializer) {
int keyOffset = trace._traces.containsKey(offset)
? offset
: trace._traces.lastKeyBefore(offset)!;
trace = trace._traces[keyOffset]!;
offset -= keyOffset;
}
String byte = byteValue.toRadixString(16).padLeft(2, '0');
print("Watch $watchPoint: 0x$byte\n$trace");
}
}
writeBytes(chunk.data);
}
Uint8List get data => Uint8List.sublistView(_data, 0, _index);
}