diff --git a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.cc b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.cc index a758dd15a00e5..64128234369dc 100644 --- a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.cc +++ b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.cc @@ -4,17 +4,10 @@ namespace flutter { -static std::unique_ptr DataMapping(const DartSnapshot& snapshot) { - auto ptr = snapshot.GetDataMapping(); - return std::make_unique(ptr, - Dart_SnapshotDataSize(ptr)); -} - -static std::unique_ptr InstructionsMapping( - const DartSnapshot& snapshot) { - auto ptr = snapshot.GetInstructionsMapping(); - return std::make_unique(ptr, - Dart_SnapshotInstrSize(ptr)); +static std::unique_ptr RegionMapping( + const uint8_t* region, + const SnapshotsDataHandle::RegionSizer& size) { + return std::make_unique(region, size(region)); } // The size of the snapshot data is the sum of the sizes of the blobs. @@ -83,6 +76,16 @@ BlobsIndex SnapshotsDataHandle::IndexForAbsoluteOffset(int64_t offset, std::unique_ptr SnapshotsDataHandle::createForSnapshots( const DartSnapshot& base_snapshot) { + return createForSnapshots( + base_snapshot, + [](const uint8_t* region) { return Dart_SnapshotDataSize(region); }, + [](const uint8_t* region) { return Dart_SnapshotInstrSize(region); }); +} + +std::unique_ptr SnapshotsDataHandle::createForSnapshots( + const DartSnapshot& base_snapshot, + const RegionSizer& data_size, + const RegionSizer& instructions_size) { // Order and count must match HandleDumpBlobs in // runtime/bin/analyze_snapshot.cc, which writes the data region then the // text region. One snapshot supplies both. The VM isolate's contents were @@ -95,8 +98,9 @@ std::unique_ptr SnapshotsDataHandle::createForSnapshots( // The caller must pass a snapshot resolved through the VM path. That path is // patch-blind, and this stream has to be the unpatched base the diff was // computed against. - auto data = DataMapping(base_snapshot); - auto insns = InstructionsMapping(base_snapshot); + auto data = RegionMapping(base_snapshot.GetDataMapping(), data_size); + auto insns = + RegionMapping(base_snapshot.GetInstructionsMapping(), instructions_size); // Per-blob observability for the base byte stream the updater is about to // patch against. Logged at every patch-apply attempt so customer syslogs diff --git a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.h b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.h index b304d0d032dd2..c90fca0b68978 100644 --- a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.h +++ b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle.h @@ -2,6 +2,7 @@ #define FLUTTER_SHELL_COMMON_SHOREBIRD_SNAPSHOTS_DATA_HANDLE_H_ #include +#include #include "flutter/fml/file.h" #include "flutter/runtime/dart_snapshot.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -25,11 +26,26 @@ class SnapshotsDataHandle { explicit SnapshotsDataHandle(std::vector> blobs) : blobs_(std::move(blobs)) {} + // Resolves the byte length of a snapshot region starting at `region`. + // + // Production has to ask the Dart VM. An AOT snapshot's regions arrive as + // fml::SymbolMapping, whose GetSize() is 0 because a dlsym'd symbol address + // carries no extent. Only the snapshot header knows where the region ends. + using RegionSizer = std::function; + // `base_snapshot` must come from the VM resolve path, which never returns a // patch. This stream is the base the updater diffs against. static std::unique_ptr createForSnapshots( const DartSnapshot& base_snapshot); + // As above, with the lengths supplied by the caller. A test has no + // serialized snapshot, and the VM's parser dereferences whatever a + // fabricated buffer's header bytes point at. + static std::unique_ptr createForSnapshots( + const DartSnapshot& base_snapshot, + const RegionSizer& data_size, + const RegionSizer& instructions_size); + uintptr_t Read(uint8_t* buffer, uintptr_t length); int64_t Seek(int64_t offset, int32_t whence); diff --git a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle_unittests.cc b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle_unittests.cc index 570f3dcf78355..24012fb020414 100644 --- a/engine/src/flutter/shell/common/shorebird/snapshots_data_handle_unittests.cc +++ b/engine/src/flutter/shell/common/shorebird/snapshots_data_handle_unittests.cc @@ -186,6 +186,11 @@ static fml::RefPtr MakeSnapshot( instructions.size())); } +// The regions below are fabricated strings, not serialized snapshots. +static SnapshotsDataHandle::RegionSizer SizedAt(size_t size) { + return [size](const uint8_t*) { return size; }; +} + // Guards blob count and order, which the Read/Seek tests above never touch // because they build handles through the public constructor. // @@ -198,7 +203,8 @@ TEST(SnapshotsDataHandle, CreateForSnapshotsEmitsDataThenInstructionsOnce) { const std::string instructions = "INSTRUCTIONS"; auto snapshot = MakeSnapshot(data, instructions); - auto handle = SnapshotsDataHandle::createForSnapshots(*snapshot); + auto handle = SnapshotsDataHandle::createForSnapshots( + *snapshot, SizedAt(data.size()), SizedAt(instructions.size())); EXPECT_EQ(handle->FullSize(), data.size() + instructions.size()); @@ -215,7 +221,8 @@ TEST(SnapshotsDataHandle, CreateForSnapshotsPutsDataBeforeInstructions) { const std::string instructions = "BB"; auto snapshot = MakeSnapshot(data, instructions); - auto handle = SnapshotsDataHandle::createForSnapshots(*snapshot); + auto handle = SnapshotsDataHandle::createForSnapshots( + *snapshot, SizedAt(data.size()), SizedAt(instructions.size())); uint8_t first[4] = {0, 0, 0, 0}; EXPECT_EQ(handle->Read(first, 4), 4u); @@ -234,7 +241,8 @@ TEST(SnapshotsDataHandle, CreateForSnapshotsKeepsEmptyInstructionsRegion) { const std::string instructions; auto snapshot = MakeSnapshot(data, instructions); - auto handle = SnapshotsDataHandle::createForSnapshots(*snapshot); + auto handle = SnapshotsDataHandle::createForSnapshots( + *snapshot, SizedAt(data.size()), SizedAt(instructions.size())); EXPECT_EQ(handle->FullSize(), data.size()); }