Parse Versioned Code Cluster Layouts

This commit is contained in:
pancake committed 2026-09-01 14:58:59 +02:00
1 parent b45d487fd3
commit 2e85b70bda
7 files changed
+129 -32

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ cluster layouts next, then metadata/value restoration and profile detection.
- [x] **R2F-DAE-003 — Handle versioned ObjectPool entry bits**
- Model low-seven-bit, swapped low-seven-bit, and modern bitfield eras.
- Commit: `Handle Versioned ObjectPool Entry Bits`
- [ ] **R2F-DAE-004 — Parse versioned Code cluster layouts**
- [x] **R2F-DAE-004 — Parse versioned Code cluster layouts**
- Profile Code reference counts, text-offset deltas, and leading refs.
- Commit: `Parse Versioned Code Cluster Layouts`
- [ ] **R2F-DAE-005 — Support Dart 3.13 single snapshots**
+18
View File
@@ -1407,3 +1407,21 @@ that bit width but swaps those two type values. Dart 3.3 and later use the
modern layout: a four-bit type, bit 4 patchability, and bits 5-7 snapshot
behavior. A broad Dart-2-versus-Dart-3 check therefore misdecodes 3.0-3.2 and
may skip a serialized payload, shifting every following pool entry.
## Code Cluster Framing Has Independent Transitions
Code allocation and fill cannot be keyed only to a broad legacy flag. Dart
2.10-2.13 allocation writes the main count followed directly by the deferred
count. Dart 2.14 and later add a state word for every main and deferred Code.
The fill has seven object references through Dart 2.16 and six from Dart 2.17
onward. Dart 2.10-2.15 also put an instruction text-offset delta before each
main Code's payload info. Dart 3.13 adds two cluster-level references before
the per-Code records. These fields are profiled separately so a format change
in one dimension does not imply a change in another.
r2flutter keeps its existing normalized Code and instruction-table model. The
profile is applied while framing the Code stream and while recovering Code
owners; DAE's Dart 2.18 negative fill adjustment is intentionally not copied,
because it compensates for drift in DAE's own generic fill walker rather than
a serialized SDK field.
+9
View File
@@ -45,6 +45,11 @@ typedef enum {
DART_POOL_ENCODING_LOW7_SWAPPED // type[0..6], patch[7], immediate=0, tagged=1
} DartPoolEncoding;
typedef enum {
DART_CODE_ALLOC_STATE_DEFERRED = 0, // count, state words, deferred count/state words
DART_CODE_ALLOC_DEFERRED_ONLY // count, deferred count
} DartCodeAlloc;
// How the Dart version/layout for a snapshot was determined. Reported so users
// can tell whether decoding rests on an exact match or a best-effort guess.
typedef enum {
@@ -68,6 +73,10 @@ typedef struct {
DartHeaderStyle header_style;
DartRefEncoding ref_encoding;
DartPoolEncoding pool_encoding;
DartCodeAlloc code_alloc;
int code_refs;
int code_leading_refs;
bool code_has_text_offset;
} DartVerLayout;
// Lookup Dart version from a snapshot hash (MD5)
+19 -2
View File
@@ -71,6 +71,11 @@ POOL_ENCODING_NAMES = {
"low7_swapped": "DART_POOL_ENCODING_LOW7_SWAPPED",
}
CODE_ALLOC_NAMES = {
"state_deferred": "DART_CODE_ALLOC_STATE_DEFERRED",
"deferred_only": "DART_CODE_ALLOC_DEFERRED_ONLY",
}
TAG_STYLE_VALUES = {v: k for k, v in TAG_STYLE_NAMES.items()}
CID_KIND_TO_CLASS_NAME = [
@@ -620,6 +625,16 @@ def pool_encoding_for_version(version):
return POOL_ENCODING_NAMES["modern"]
def code_layout_for_version(version):
ver_tuple = parse_version(version)
return {
"alloc": CODE_ALLOC_NAMES["deferred_only" if ver_tuple < (2, 14) else "state_deferred"],
"refs": 7 if ver_tuple < (2, 17) else 6,
"leading_refs": 2 if ver_tuple >= (3, 13) else 0,
"has_text_offset": "true" if ver_tuple < (2, 16) else "false",
}
def profile_diff(existing, inferred):
diffs = []
for key in ("cws", "heap_object_tag", "max_alignment", "it_cap", "tag_style", "header_fields"):
@@ -736,10 +751,12 @@ def gen_profile_entries(offsets):
header_style = header_style_for_version(ver)
ref_encoding = ref_encoding_for_version(ver)
pool_encoding = pool_encoding_for_version(ver)
code = code_layout_for_version(ver)
lines.append(f'\t{{ "", "{ver}", {v["cws"]}, {v["heap_object_tag"]}, '
f'{v["max_alignment"]}, {v["it_cap"]}, {tag_name}, '
f'{v["header_fields"]}, {header_style}, {ref_encoding}, {pool_encoding} }}, // Dart {ver}')
lines.append("\t{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }")
f'{v["header_fields"]}, {header_style}, {ref_encoding}, {pool_encoding}, '
f'{code["alloc"]}, {code["refs"]}, {code["leading_refs"]}, {code["has_text_offset"]} }}, // Dart {ver}')
lines.append("\t{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }")
lines.append("};")
return "\n".join(lines)
+40 -4
View File
@@ -771,6 +771,15 @@ static bool modern_skip_alloc(ClusterStream *s, const ModernCidCache *cids, int
return false;
}
meta->main_count = count;
DartCodeAlloc encoding = s->ctx && s->ctx->layout? s->ctx->layout->code_alloc: DART_CODE_ALLOC_STATE_DEFERRED;
if (encoding == DART_CODE_ALLOC_DEFERRED_ONLY) {
ut64 deferred = 0;
if (!cs_read_unsigned (s, &deferred)) {
return false;
}
meta->count = count + deferred;
return true;
}
meta->discarded_codes = (ut8 *)calloc ((size_t)count + 1, 1);
for (ut64 i = 0; i < count; i++) {
ut32 state_bits = 0;
@@ -1325,10 +1334,23 @@ static bool modern_skip_fill_instance(DartCtx *ctx, ClusterStream *s, const Mode
}
static bool modern_skip_fill_code(ClusterStream *s, const ModernClusterMeta *meta) {
const int num_refs = 6;
const DartVerLayout *layout = s && s->ctx? s->ctx->layout: NULL;
const int num_refs = layout && layout->code_refs > 0? layout->code_refs: 6;
const int leading_refs = layout? layout->code_leading_refs: 0;
ut64 ref = 0;
for (int i = 0; i < leading_refs; i++) {
if (!cs_read_ref_id (s, &ref)) {
return false;
}
}
for (ut64 i = 0; i < meta->count; i++) {
ut64 ref = 0;
if (i < meta->main_count) {
if (layout && layout->code_has_text_offset) {
ut64 text_offset_delta = 0;
if (!cs_read_unsigned (s, &text_offset_delta)) {
return false;
}
}
ut64 payload = 0;
if (!cs_read_unsigned (s, &payload)) {
return false;
@@ -4252,11 +4274,25 @@ static int modern_name_scan_owner_cid(const ModernWalk *w, ut64 owner_ref) {
static bool modern_name_scan_read_code(ModernWalk *w, ClusterStream *s, const ModernClusterMeta *m) {
ModernNameScanWalk *u = (ModernNameScanWalk *)w->user;
const DartVerLayout *layout = w && w->ctx? w->ctx->layout: NULL;
const int num_refs = layout && layout->code_refs > 0? layout->code_refs: 6;
const int leading_refs = layout? layout->code_leading_refs: 0;
ut64 tmp_ref = 0;
for (int i = 0; i < leading_refs; i++) {
if (!cs_read_ref_id (s, &tmp_ref)) {
return false;
}
}
for (ut64 j = 0; j < m->count; j++) {
ut64 owner_ref = 0;
ut64 tmp_ref = 0;
ut64 slot = UT64_MAX;
if (j < m->main_count) {
if (layout && layout->code_has_text_offset) {
ut64 text_offset_delta = 0;
if (!cs_read_unsigned (s, &text_offset_delta)) {
return false;
}
}
ut64 payload = 0;
if (!cs_read_unsigned (s, &payload)) {
return false;
@@ -4269,7 +4305,7 @@ static bool modern_name_scan_read_code(ModernWalk *w, ClusterStream *s, const Mo
continue;
}
}
for (int k = 0; k < 6; k++) {
for (int k = 0; k < num_refs; k++) {
if (!cs_read_ref_id (s, &tmp_ref)) {
return false;
}
+25 -25
View File
@@ -130,31 +130,31 @@ static const struct {
// Range-start version profiles. CID tables are generated in dart_cid.c.
static const DartVerLayout version_profiles[] = {
{ "", "2.10.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 4, DART_HEADER_STYLE_210, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.10.0
{ "", "2.12.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.12.0
{ "", "2.13.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.13.0
{ "", "2.14.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.14.0
{ "", "2.15.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.15.0
{ "", "2.16.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.16.0
{ "", "2.17.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7 }, // Dart 2.17.0
{ "", "2.18.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 2.18.0
{ "", "2.18.2", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 2.18.2
{ "", "2.18.3", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 2.18.3
{ "", "2.19.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 2.19.0
{ "", "3.0.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 3.0.0
{ "", "3.1.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7 }, // Dart 3.1.0
{ "", "3.2.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7_SWAPPED }, // Dart 3.2.0
{ "", "3.3.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.3.0
{ "", "3.4.0", 8, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.4.0
{ "", "3.5.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.5.0
{ "", "3.6.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.6.0
{ "", "3.7.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.7.0
{ "", "3.8.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.8.0
{ "", "3.9.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.9.0
{ "", "3.10.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.10.0
{ "", "3.11.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.11.0
{ "", "3.12.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN }, // Dart 3.12.0
{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
{ "", "2.10.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 4, DART_HEADER_STYLE_210, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true }, // Dart 2.10.0
{ "", "2.12.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true }, // Dart 2.12.0
{ "", "2.13.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true }, // Dart 2.13.0
{ "", "2.14.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, true }, // Dart 2.14.0
{ "", "2.15.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, true }, // Dart 2.15.0
{ "", "2.16.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, false }, // Dart 2.16.0
{ "", "2.17.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 2.17.0
{ "", "2.18.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 2.18.0
{ "", "2.18.2", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 2.18.2
{ "", "2.18.3", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 2.18.3
{ "", "2.19.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 2.19.0
{ "", "3.0.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.0.0
{ "", "3.1.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.1.0
{ "", "3.2.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7_SWAPPED, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.2.0
{ "", "3.3.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.3.0
{ "", "3.4.0", 8, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.4.0
{ "", "3.5.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.5.0
{ "", "3.6.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.6.0
{ "", "3.7.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.7.0
{ "", "3.8.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.8.0
{ "", "3.9.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.9.0
{ "", "3.10.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.10.0
{ "", "3.11.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.11.0
{ "", "3.12.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false }, // Dart 3.12.0
{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const char *dart_version_from_hash(const char *hash) {
+17
View File
@@ -0,0 +1,17 @@
NAME=versioned-code-cluster-layouts
FILE=-
CMDS=<<EOF
!!python3 -c 'import sys; open(sys.argv[1], "wb").write(bytes.fromhex(sys.argv[2]))' /tmp/r2flutter-code-212.bin 50414421f5f5dcdc6d000000000000000300000000000000356239373239326232356630613731353631336237613238653037333466373770726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e74657273008284818187ce8180d1818184878080808080808081002c82
!!../bin/r2flutter -q -HHH -l 3 /tmp/r2flutter-code-212.bin 2>/dev/null | python3 -c 'import sys; lines=list(sys.stdin); code=next(x for x in lines if "alloc=code" in x); pool=next(x for x in lines if "bits=" in x); print("2.12", next(x for x in code.split() if x.startswith("fill_status=")), next(x for x in pool.split() if x.startswith("ref=")))'
!!python3 -c 'import sys; open(sys.argv[1], "wb").write(bytes.fromhex(sys.argv[2]))' /tmp/r2flutter-code-215.bin 50414421f5f5dcdc6e000000000000000300000000000000323464396434313163326639306338666265383930376639396538396434623070726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e74657273008284828785e281c080ea818184878080808080808081002c82
!!../bin/r2flutter -q -HHH -l 3 /tmp/r2flutter-code-215.bin 2>/dev/null | python3 -c 'import sys; lines=list(sys.stdin); code=next(x for x in lines if "alloc=code" in x); pool=next(x for x in lines if "bits=" in x); print("2.15", next(x for x in code.split() if x.startswith("fill_status=")), next(x for x in pool.split() if x.startswith("ref=")))'
!!python3 -c 'import sys; open(sys.argv[1], "wb").write(bytes.fromhex(sys.argv[2]))' /tmp/r2flutter-code-217.bin 50414421f5f5dcdc6d000000000000000300000000000000313434316436623133623836323366613766626636313433336162656264333170726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e7465727300828482878589e281c080ea81818780808080808081002c82
!!../bin/r2flutter -q -HHH -l 3 /tmp/r2flutter-code-217.bin 2>/dev/null | python3 -c 'import sys; lines=list(sys.stdin); code=next(x for x in lines if "alloc=code" in x); pool=next(x for x in lines if "bits=" in x); print("2.17", next(x for x in code.split() if x.startswith("fill_status=")), next(x for x in pool.split() if x.startswith("ref=")))'
!!rm -f /tmp/r2flutter-code-212.bin /tmp/r2flutter-code-215.bin /tmp/r2flutter-code-217.bin
EOF
EXPECT=<<EOF
2.12 fill_status=ok ref=300
2.15 fill_status=ok ref=300
2.17 fill_status=ok ref=300
EOF
RUN