splice static method

Uint8List splice(
  1. Uint8List executable,
  2. Map<String, List<int>> sections
)

executable with sections carried inside it.

Throws FormatException for bytes that do not end the way a compiled Dart executable does: splicing into anything else writes a file that does not start, and says nothing until somebody runs it.

Implementation

static Uint8List splice(
  Uint8List executable,
  Map<String, List<int>> sections,
) {
  if (executable.length < 32) {
    throw const FormatException('too short to be a compiled executable');
  }
  final ByteData bytes = ByteData.sublistView(executable);
  if (bytes.getUint64(executable.length - 8, Endian.little) !=
      _snapshotMagic) {
    if (_isPortableExecutable(executable) || _isMachO(executable)) {
      return _append(executable, sections);
    }
    throw const FormatException(
      'not a compiled Dart executable: it does not end with the snapshot '
      'trailer `dart compile exe` writes, and it is not a Windows or macOS '
      'executable image',
    );
  }
  final int snapshot = bytes.getUint64(executable.length - 16, Endian.little);
  if (snapshot > executable.length - 16) {
    throw const FormatException('the snapshot trailer points past the file');
  }

  final Uint8List block = _block(sections);
  final int start = snapshot;
  final int end = start + block.length;
  final int moved = ((end + 16 + _alignment - 1) ~/ _alignment) * _alignment;

  final BytesBuilder out = BytesBuilder(copy: false)
    ..add(Uint8List.sublistView(executable, 0, snapshot))
    ..add(block)
    ..add(Uint8List(moved - 16 - end));
  final ByteData locator = ByteData(16)
    ..setUint64(0, start, Endian.little);
  final Uint8List locatorBytes = locator.buffer.asUint8List();
  locatorBytes.setRange(8, 16, ascii.encode(_locatorMagic));
  out
    ..add(locatorBytes)
    ..add(Uint8List.sublistView(executable, snapshot, executable.length - 16));
  final ByteData trailer = ByteData(16)
    ..setUint64(0, moved, Endian.little)
    ..setUint64(8, _snapshotMagic, Endian.little);
  out.add(trailer.buffer.asUint8List());
  return out.takeBytes();
}