From d23cad322ccb7754c2945ba11f0beb9b7c799ac8 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Thu, 2 Apr 2026 23:12:30 -0500 Subject: [PATCH 01/22] initial commit for serwalker --- crates/flutterdec-serwalker/Cargo.toml | 7 +++++++ crates/flutterdec-serwalker/src/lib.rs | 14 ++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 crates/flutterdec-serwalker/Cargo.toml create mode 100644 crates/flutterdec-serwalker/src/lib.rs diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml new file mode 100644 index 0000000..83162e5 --- /dev/null +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "flutterdec-serwalker" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From f4ebabef2d5a72fd887999ae85a8c7a732cf3091 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Thu, 2 Apr 2026 23:12:30 -0500 Subject: [PATCH 02/22] feat(serwalker): scaffold crate --- crates/flutterdec-serwalker/Cargo.toml | 7 +++++++ crates/flutterdec-serwalker/src/lib.rs | 14 ++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 crates/flutterdec-serwalker/Cargo.toml create mode 100644 crates/flutterdec-serwalker/src/lib.rs diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml new file mode 100644 index 0000000..83162e5 --- /dev/null +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "flutterdec-serwalker" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From d2b3726888b9ca536e592b2ded12676b386a2b9c Mon Sep 17 00:00:00 2001 From: kalixtez Date: Sat, 4 Apr 2026 00:19:06 -0500 Subject: [PATCH 03/22] added a few macro definitions and constant declarations --- Cargo.lock | 7 ++ Cargo.toml | 1 + crates/flutterdec-serwalker/Cargo.toml | 1 + crates/flutterdec-serwalker/src/constants.rs | 14 ++++ crates/flutterdec-serwalker/src/lib.rs | 15 +---- crates/flutterdec-serwalker/src/utils.rs | 67 ++++++++++++++++++++ rust-toolchain.toml | 2 +- 7 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/constants.rs create mode 100644 crates/flutterdec-serwalker/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 6551b5a..c413b1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,13 @@ dependencies = [ "zip", ] +[[package]] +name = "flutterdec-serwalker" +version = "0.1.0-alpha.2" +dependencies = [ + "goblin", +] + [[package]] name = "foldhash" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index ce04ace..a529bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "crates/flutterdec-disasm-arm64", "crates/flutterdec-ir", "crates/flutterdec-decompiler", + "crates/flutterdec-serwalker", ] resolver = "2" diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml index 83162e5..65fb1eb 100644 --- a/crates/flutterdec-serwalker/Cargo.toml +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -5,3 +5,4 @@ edition.workspace = true license.workspace = true [dependencies] +goblin.workspace = true \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs new file mode 100644 index 0000000..7e3e408 --- /dev/null +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -0,0 +1,14 @@ +use std::mem::size_of; + +pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); +pub const SNAPSHOT_LEN_SZ: usize = size_of::(); +pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + +pub const NUM_BASE_OBJECTS_SZ: usize = size_of::(); +pub const NUM_OBJECTS_SZ: usize = size_of::(); +pub const NUM_CLUSTERS_SZ: usize = size_of::(); + +pub const INSTR_TABLE_LEN_SZ: usize = size_of::(); +pub const INSTR_TABLE_OFFSET_SZ: usize = size_of::(); + +pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index b93cf3f..22884e4 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,14 +1,3 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +mod utils; +mod constants; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs new file mode 100644 index 0000000..146df36 --- /dev/null +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -0,0 +1,67 @@ +use std::vec; + +struct DataSnapshot +{ + clusters: Vec<&'static mut dyn Cluster>, + + num_base_objects: u64, + num_objects: u64, + num_clusters: u64, + + instr_table_len: u64, + instr_table_offset: u64, +} +trait Cluster +{ + fn is_fixed_len(&self) -> bool; + fn get_size(&self) -> usize; +} + +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER +{ + ($name:ident, $instance_size:literal) => { + struct $name + { + tags: u32, + count: u64 + } + + impl Cluster for $name + { + fn get_size(&self) -> usize + { + $instance_size + } + + fn is_fixed_len(&self) -> bool + { + true + } + } + }; +} + +macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER +{ + ($name:ident, $get_length_impl:block) => { + struct $name + { + tags: u32, + count: u64 + } + + impl Cluster for $name + { + fn get_size(&self) -> usize + $get_length_impl + + fn is_fixed_len(&self) -> bool + { + false + } + } + }; +} + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 16); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 73cb934..3fe7418 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] channel = "stable" -components = ["rustfmt", "clippy"] +components = ["rustfmt", "clippy", "rust-analyzer"] From f2049a0d9f651d9fbcf28e1a2f863046b3a631b9 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Sat, 4 Apr 2026 00:19:06 -0500 Subject: [PATCH 04/22] feat(serwalker): add macro and constant declarations --- Cargo.lock | 7 ++ Cargo.toml | 1 + crates/flutterdec-serwalker/Cargo.toml | 1 + crates/flutterdec-serwalker/src/constants.rs | 14 ++++ crates/flutterdec-serwalker/src/lib.rs | 15 +---- crates/flutterdec-serwalker/src/utils.rs | 67 ++++++++++++++++++++ rust-toolchain.toml | 2 +- 7 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/constants.rs create mode 100644 crates/flutterdec-serwalker/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 6551b5a..c413b1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,13 @@ dependencies = [ "zip", ] +[[package]] +name = "flutterdec-serwalker" +version = "0.1.0-alpha.2" +dependencies = [ + "goblin", +] + [[package]] name = "foldhash" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index ce04ace..a529bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "crates/flutterdec-disasm-arm64", "crates/flutterdec-ir", "crates/flutterdec-decompiler", + "crates/flutterdec-serwalker", ] resolver = "2" diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml index 83162e5..65fb1eb 100644 --- a/crates/flutterdec-serwalker/Cargo.toml +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -5,3 +5,4 @@ edition.workspace = true license.workspace = true [dependencies] +goblin.workspace = true \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs new file mode 100644 index 0000000..7e3e408 --- /dev/null +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -0,0 +1,14 @@ +use std::mem::size_of; + +pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); +pub const SNAPSHOT_LEN_SZ: usize = size_of::(); +pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + +pub const NUM_BASE_OBJECTS_SZ: usize = size_of::(); +pub const NUM_OBJECTS_SZ: usize = size_of::(); +pub const NUM_CLUSTERS_SZ: usize = size_of::(); + +pub const INSTR_TABLE_LEN_SZ: usize = size_of::(); +pub const INSTR_TABLE_OFFSET_SZ: usize = size_of::(); + +pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index b93cf3f..22884e4 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,14 +1,3 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +mod utils; +mod constants; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs new file mode 100644 index 0000000..146df36 --- /dev/null +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -0,0 +1,67 @@ +use std::vec; + +struct DataSnapshot +{ + clusters: Vec<&'static mut dyn Cluster>, + + num_base_objects: u64, + num_objects: u64, + num_clusters: u64, + + instr_table_len: u64, + instr_table_offset: u64, +} +trait Cluster +{ + fn is_fixed_len(&self) -> bool; + fn get_size(&self) -> usize; +} + +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER +{ + ($name:ident, $instance_size:literal) => { + struct $name + { + tags: u32, + count: u64 + } + + impl Cluster for $name + { + fn get_size(&self) -> usize + { + $instance_size + } + + fn is_fixed_len(&self) -> bool + { + true + } + } + }; +} + +macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER +{ + ($name:ident, $get_length_impl:block) => { + struct $name + { + tags: u32, + count: u64 + } + + impl Cluster for $name + { + fn get_size(&self) -> usize + $get_length_impl + + fn is_fixed_len(&self) -> bool + { + false + } + } + }; +} + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 16); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 73cb934..3fe7418 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] channel = "stable" -components = ["rustfmt", "clippy"] +components = ["rustfmt", "clippy", "rust-analyzer"] From 573351768a900e19d54bc210279b30bcfe7e6969 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 13 Apr 2026 00:26:28 -0500 Subject: [PATCH 05/22] add common stream methods --- crates/flutterdec-serwalker/src/constants.rs | 11 ++++++++- crates/flutterdec-serwalker/src/stream.rs | 20 +++++++++++++++ crates/flutterdec-serwalker/src/utils.rs | 26 ++++++++++++++++---- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/stream.rs diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index 7e3e408..b896046 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -1,6 +1,11 @@ use std::mem::size_of; pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); + +pub const END_OF_ULEB_MASK: u8 = 0x80; // last byte +pub const ULEB_EXTRACT_BYTE_DATA_MASK: u8 = 0x7f; // more bytes to follow + +/* pub const SNAPSHOT_LEN_SZ: usize = size_of::(); pub const SNAPSHOT_KIND_SZ: usize = size_of::(); @@ -11,4 +16,8 @@ pub const NUM_CLUSTERS_SZ: usize = size_of::(); pub const INSTR_TABLE_LEN_SZ: usize = size_of::(); pub const INSTR_TABLE_OFFSET_SZ: usize = size_of::(); -pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); \ No newline at end of file +pub const CLUSTER_TAGS_SZ: usize = size_of::(); +pub const CLUSTER_OBJ_COUNT_SZ: usize = size_of::(); + +pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); +*/ \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs new file mode 100644 index 0000000..d38fb0b --- /dev/null +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -0,0 +1,20 @@ +mod constants; + +struct Stream +{ + byte_stream: &[u8], + curr_stream_offset: usize, +} + +impl Stream +{ + fn seek(&self, pos: usize) + { + self.curr_stream_offset = pos; + } + + fn read_modified_uleb128(stream: &[u8]) // read a modified uleb in the current poss + { + let bytes = Vec + } +} \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 146df36..72bd0fb 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,9 +1,15 @@ -use std::vec; struct DataSnapshot { clusters: Vec<&'static mut dyn Cluster>, - + + magic_bytes: u32, + size: u64, + kind: u64, + + version: String, + features: String, + num_base_objects: u64, num_objects: u64, num_clusters: u64, @@ -23,7 +29,7 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER struct $name { tags: u32, - count: u64 + count: u64, } impl Cluster for $name @@ -63,5 +69,15 @@ macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER }; } -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 16); +// These are the objects that call ReadAllocFixedSize during deserialization, +// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects +// and alloc cluster size is tags (u32) + num_of_objects (ULEB128) + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 2); +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(StringCluster, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(MintCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(DoubleCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameterCluster, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeCluster, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeArgumentsCluster, 32); From 89135839ffa6b6fd9a428dcae6a547a47a04946d Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 13 Apr 2026 00:26:28 -0500 Subject: [PATCH 06/22] feat(serwalker): add common stream methods --- crates/flutterdec-serwalker/src/constants.rs | 11 ++++++++- crates/flutterdec-serwalker/src/stream.rs | 20 +++++++++++++++ crates/flutterdec-serwalker/src/utils.rs | 26 ++++++++++++++++---- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/stream.rs diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index 7e3e408..b896046 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -1,6 +1,11 @@ use std::mem::size_of; pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); + +pub const END_OF_ULEB_MASK: u8 = 0x80; // last byte +pub const ULEB_EXTRACT_BYTE_DATA_MASK: u8 = 0x7f; // more bytes to follow + +/* pub const SNAPSHOT_LEN_SZ: usize = size_of::(); pub const SNAPSHOT_KIND_SZ: usize = size_of::(); @@ -11,4 +16,8 @@ pub const NUM_CLUSTERS_SZ: usize = size_of::(); pub const INSTR_TABLE_LEN_SZ: usize = size_of::(); pub const INSTR_TABLE_OFFSET_SZ: usize = size_of::(); -pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); \ No newline at end of file +pub const CLUSTER_TAGS_SZ: usize = size_of::(); +pub const CLUSTER_OBJ_COUNT_SZ: usize = size_of::(); + +pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); +*/ \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs new file mode 100644 index 0000000..d38fb0b --- /dev/null +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -0,0 +1,20 @@ +mod constants; + +struct Stream +{ + byte_stream: &[u8], + curr_stream_offset: usize, +} + +impl Stream +{ + fn seek(&self, pos: usize) + { + self.curr_stream_offset = pos; + } + + fn read_modified_uleb128(stream: &[u8]) // read a modified uleb in the current poss + { + let bytes = Vec + } +} \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 146df36..72bd0fb 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,9 +1,15 @@ -use std::vec; struct DataSnapshot { clusters: Vec<&'static mut dyn Cluster>, - + + magic_bytes: u32, + size: u64, + kind: u64, + + version: String, + features: String, + num_base_objects: u64, num_objects: u64, num_clusters: u64, @@ -23,7 +29,7 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER struct $name { tags: u32, - count: u64 + count: u64, } impl Cluster for $name @@ -63,5 +69,15 @@ macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER }; } -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 16); +// These are the objects that call ReadAllocFixedSize during deserialization, +// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects +// and alloc cluster size is tags (u32) + num_of_objects (ULEB128) + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 2); +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(StringCluster, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(MintCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(DoubleCluster, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameterCluster, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeCluster, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeArgumentsCluster, 32); From dd1ecfdf7e6eb4dcb6d9e5e1279890e122a53a5f Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:24:37 -0500 Subject: [PATCH 07/22] Implemented a few stream-reading functions, implemented the base struct for fixed-alloc data types, added the ClassId enum to uniquely distinguish all class ids --- Cargo.lock | 7 + crates/flutterdec-serwalker/Cargo.toml | 3 +- .../flutterdec-serwalker/src/cluster/mod.rs | 56 +++++ crates/flutterdec-serwalker/src/constants.rs | 207 ++++++++++++++++- crates/flutterdec-serwalker/src/lib.rs | 5 + .../src/raw_object/mod.rs | 214 ++++++++++++++++++ crates/flutterdec-serwalker/src/snapshot.rs | 84 +++++++ crates/flutterdec-serwalker/src/stream.rs | 109 ++++++++- crates/flutterdec-serwalker/src/utils.rs | 107 ++++----- 9 files changed, 710 insertions(+), 82 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/cluster/mod.rs create mode 100644 crates/flutterdec-serwalker/src/raw_object/mod.rs create mode 100644 crates/flutterdec-serwalker/src/snapshot.rs diff --git a/Cargo.lock b/Cargo.lock index c413b1d..6482e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,7 @@ name = "flutterdec-serwalker" version = "0.1.0-alpha.2" dependencies = [ "goblin", + "paste", ] [[package]] @@ -571,6 +572,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "plain" version = "0.2.3" diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml index 65fb1eb..1885862 100644 --- a/crates/flutterdec-serwalker/Cargo.toml +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -5,4 +5,5 @@ edition.workspace = true license.workspace = true [dependencies] -goblin.workspace = true \ No newline at end of file +goblin.workspace = true +paste = "1.0.15" diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs new file mode 100644 index 0000000..568bdb5 --- /dev/null +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -0,0 +1,56 @@ +use crate::{constants, stream::Stream}; +use crate::DECLARE_FIXED_LENGTH_CLUSTER; +use crate::raw_object::*; +use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; + +type Smi = i32; + +pub trait Cluster +{ + fn is_fixed_len(&self) -> bool; + fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; + fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; +} + +pub fn read_cluster_alloc() +{ + let curr_ref_id: u64 = 0; +} + +pub fn read_cluster_fill() +{ + let curr_ref_id: u64 = 0; +} + +pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi +{ + let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed + + (raw_smi as Smi) >> constants::SMI_SHIFT +} + +pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_NUM], class_id: ClassId) -> Result, &str> +{ + match class_id { + IllegalCid => Err("Not a supported class (illegal class)..."), + _ => Err("Not a supported class..."), + } +} + +// These are the objects that call ReadAllocFixedSize during deserialization, +// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects +// and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) + + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { 1 + // to-do +}); +/* +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteString, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(String, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(Mint, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(Type, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeArguments, 32); +*/ \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index b896046..63b35c6 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -1,13 +1,212 @@ use std::mem::size_of; +pub const MAGIC_BYTES: u32 = 0xdcdcf5f5; + pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); +pub const SNAPSHOT_LEN_SZ: usize = size_of::(); +pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + +pub const SNAPSHOT_HEADER_SZ: usize = SNAPSHOT_MAGIC_NUMBER_SZ // 20 bytes of header + + SNAPSHOT_LEN_SZ + + SNAPSHOT_KIND_SZ; + +pub const MAX_CLUSTER_NUM: usize = 67usize; + +pub const UNSIGNED_END_OF_DATA_BYTE: u8 = 0x80u8; // last byte +pub const UNSIGNED_MAX_DATA_PER_BYTE: u8 = 0x7fu8; // more bytes to follow (for both) + +pub const SIGNED_END_OF_DATA_BYTE: u8 = 0xc0u8; // last byte -pub const END_OF_ULEB_MASK: u8 = 0x80; // last byte -pub const ULEB_EXTRACT_BYTE_DATA_MASK: u8 = 0x7f; // more bytes to follow +pub const SIGNED_M: u8 = SIGNED_END_OF_DATA_BYTE; +pub const UNSIGNED_M: u8 = UNSIGNED_END_OF_DATA_BYTE; + +pub const DATA_BITS_PER_BYTE: usize = 7usize; + +pub const SMI_SHIFT: usize = 1usize; + +pub const VERSION_HASH_LENGTH: usize = 32usize; + +pub enum ClassId { + IllegalCid = 0, + NativePointer, + FreeListElement, + ForwardingCorpse, + ObjectCid, + ClassCid, + PatchClassCid, + FunctionCid, + TypeParametersCid, + ClosureDataCid, + FfiTrampolineDataCid, + FieldCid, + ScriptCid, + LibraryCid, + NamespaceCid, + KernelProgramInfoCid, + WeakSerializationReferenceCid, + WeakArrayCid, + CodeCid, + BytecodeCid, + InstructionsCid, + InstructionsSectionCid, + InstructionsTableCid, + ObjectPoolCid, + PcDescriptorsCid, + CodeSourceMapCid, + CompressedStackMapsCid, + LocalVarDescriptorsCid, + ExceptionHandlersCid, + ContextCid, + ContextScopeCid, + SentinelCid, + SingleTargetCacheCid, + MonomorphicSmiableCallCid, + CallSiteDataCid, + UnlinkedCallCid, + ICDataCid, + MegamorphicCacheCid, + SubtypeTestCacheCid, + LoadingUnitCid, + ErrorCid, + ApiErrorCid, + LanguageErrorCid, + UnhandledExceptionCid, + UnwindErrorCid, + InstanceCid, + LibraryPrefixCid, + TypeArgumentsCid, + AbstractTypeCid, + TypeCid, + FunctionTypeCid, + RecordTypeCid, + TypeParameterCid, + FinalizerBaseCid, + FinalizerCid, + NativeFinalizerCid, + FinalizerEntryCid, + ClosureCid, + NumberCid, + IntegerCid, + SmiCid, + MintCid, + DoubleCid, + BoolCid, + Float32x4Cid, + Int32x4Cid, + Float64x2Cid, + RecordCid, + TypedDataBaseCid, + TypedDataCid, + ExternalTypedDataCid, + TypedDataViewCid, + PointerCid, + DynamicLibraryCid, + CapabilityCid, + ReceivePortCid, + SendPortCid, + StackTraceCid, + SuspendStateCid, + RegExpCid, + WeakPropertyCid, + WeakReferenceCid, + MirrorReferenceCid, + FutureOrCid, + UserTagCid, + TransferableTypedDataCid, + MapCid, + ConstMapCid, + SetCid, + ConstSetCid, + ArrayCid, + ImmutableArrayCid, + GrowableObjectArrayCid, + StringCid, + OneByteStringCid, + TwoByteStringCid, + FfiNativeFunctionCid, + FfiInt8Cid, + FfiInt16Cid, + FfiInt32Cid, + FfiInt64Cid, + FfiUint8Cid, + FfiUint16Cid, + FfiUint32Cid, + FfiUint64Cid, + FfiFloatCid, + FfiDoubleCid, + FfiVoidCid, + FfiHandleCid, + FfiBoolCid, + FfiNativeTypeCid, + FfiStructCid, + TypedDataInt8ArrayCid, + TypedDataInt8ArrayViewCid, + ExternalTypedDataInt8ArrayCid, + UnmodifiableTypedDataInt8ArrayViewCid, + TypedDataUint8ArrayCid, + TypedDataUint8ArrayViewCid, + ExternalTypedDataUint8ArrayCid, + UnmodifiableTypedDataUint8ArrayViewCid, + TypedDataUint8ClampedArrayCid, + TypedDataUint8ClampedArrayViewCid, + ExternalTypedDataUint8ClampedArrayCid, + UnmodifiableTypedDataUint8ClampedArrayViewCid, + TypedDataInt16ArrayCid, + TypedDataInt16ArrayViewCid, + ExternalTypedDataInt16ArrayCid, + UnmodifiableTypedDataInt16ArrayViewCid, + TypedDataUint16ArrayCid, + TypedDataUint16ArrayViewCid, + ExternalTypedDataUint16ArrayCid, + UnmodifiableTypedDataUint16ArrayViewCid, + TypedDataInt32ArrayCid, + TypedDataInt32ArrayViewCid, + ExternalTypedDataInt32ArrayCid, + UnmodifiableTypedDataInt32ArrayViewCid, + TypedDataUint32ArrayCid, + TypedDataUint32ArrayViewCid, + ExternalTypedDataUint32ArrayCid, + UnmodifiableTypedDataUint32ArrayViewCid, + TypedDataInt64ArrayCid, + TypedDataInt64ArrayViewCid, + ExternalTypedDataInt64ArrayCid, + UnmodifiableTypedDataInt64ArrayViewCid, + TypedDataUint64ArrayCid, + TypedDataUint64ArrayViewCid, + ExternalTypedDataUint64ArrayCid, + UnmodifiableTypedDataUint64ArrayViewCid, + TypedDataFloat32ArrayCid, + TypedDataFloat32ArrayViewCid, + ExternalTypedDataFloat32ArrayCid, + UnmodifiableTypedDataFloat32ArrayViewCid, + TypedDataFloat64ArrayCid, + TypedDataFloat64ArrayViewCid, + ExternalTypedDataFloat64ArrayCid, + UnmodifiableTypedDataFloat64ArrayViewCid, + TypedDataFloat32x4ArrayCid, + TypedDataFloat32x4ArrayViewCid, + ExternalTypedDataFloat32x4ArrayCid, + UnmodifiableTypedDataFloat32x4ArrayViewCid, + TypedDataInt32x4ArrayCid, + TypedDataInt32x4ArrayViewCid, + ExternalTypedDataInt32x4ArrayCid, + UnmodifiableTypedDataInt32x4ArrayViewCid, + TypedDataFloat64x2ArrayCid, + TypedDataFloat64x2ArrayViewCid, + ExternalTypedDataFloat64x2ArrayCid, + UnmodifiableTypedDataFloat64x2ArrayViewCid, + ByteDataViewCid, + UnmodifiableByteDataViewCid, + ByteBufferCid, + NullCid, + DynamicCid, + VoidCid, + NeverCid, + NumPredefinedCids, +} /* -pub const SNAPSHOT_LEN_SZ: usize = size_of::(); -pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + pub const NUM_BASE_OBJECTS_SZ: usize = size_of::(); pub const NUM_OBJECTS_SZ: usize = size_of::(); diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index 22884e4..1f91630 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,3 +1,8 @@ mod utils; mod constants; +mod cluster; +mod raw_object; +mod stream; + +mod snapshot; \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs new file mode 100644 index 0000000..3ac4da2 --- /dev/null +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -0,0 +1,214 @@ +// raw_object/mod.rs + +type Smi = i64; // Using i64 for Smi fields to be decompressed + +// --- Fixed-Size Objects with defined fields --- + +pub struct Mint { + pub value: i64, +} + +pub struct Double { + pub value: f64, +} + +pub struct TypeArguments<'a> { + pub instantiations: Option<&'a mut Array<'a>>, // ArrayPtr + pub length: Smi, // Smi + pub hash: Smi, // Smi + pub nullability: Smi, // Smi +} + +pub struct TypeParameter<'a> { + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub base: i16, + pub index: i16, +} + +pub struct Type<'a> { + pub arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr +} + +pub struct TypeParameters<'a> { + pub names: Option<&'a mut Array<'a>>, // ArrayPtr + pub flags: Option<&'a mut Array<'a>>, // ArrayPtr + pub bounds: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr + pub defaults: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr +} + +pub struct PatchClass<'a> { + pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr +} + +pub struct ClosureData<'a> { + pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr + pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr + pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr + pub packed_fields: u32, +} + +pub struct FfiTrampolineData<'a> { + pub signature_type: Option<&'a mut Type<'a>>, // TypePtr + pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr + pub callback_exceptional_return: Option<&'a mut Instance<'a>>, // InstancePtr + pub ffi_function_kind: u8, + pub callback_id: i32, +} + +pub struct Field<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr + pub host_offset_or_field_id: Smi, // Smi + pub guarded_list_length: Smi, // Smi + pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub kernel_offset: i32, + pub guarded_list_length_in_object_offset: i8, + pub static_type_exactness_state: i8, + pub target_offset: i32, + pub kind_bits: u32, +} + +pub struct Namespace<'a> { + pub target: Option<&'a mut Library<'a>>, // LibraryPtr + pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub owner: Option<&'a mut Library<'a>>, // LibraryPtr +} + +pub struct KernelProgramInfo<'a> { + pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr + pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub metadata_payloads: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub metadata_mappings: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr +} + +pub struct ExceptionHandlers<'a> { + pub handled_types_data: Option<&'a mut Array<'a>>, // ArrayPtr + pub packed_fields: u32, +} + +pub struct Context<'a> { + pub parent: Option<&'a mut Context<'a>>, // ContextPtr + pub num_variables: i32, +} + +pub struct UnlinkedCall { + pub can_patch_to_monomorphic: bool, +} + +pub struct String { + pub hash: Smi, // Smi + pub length: Smi, // Smi +} + +pub struct Class<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub user_name: std::string::String, // StringPtr -> Raw String + pub functions: Option<&'a mut Array<'a>>, // ArrayPtr + pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr + pub fields: Option<&'a mut Array<'a>>, // ArrayPtr + pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr + pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub library: Option<&'a mut Library<'a>>, // LibraryPtr + pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr + pub super_type: Option<&'a mut Type<'a>>, // TypePtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr + pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub direct_implementors: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub direct_subclasses: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub declaration_instance_type_arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr + pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub num_type_arguments: i16, + pub num_native_fields: u16, + pub state_bits: u32, + pub host_instance_size_in_words: i32, + pub host_type_arguments_field_offset_in_words: i32, + pub host_next_field_offset_in_words: i32, + pub target_instance_size_in_words: i32, + pub target_type_arguments_field_offset_in_words: i32, + pub target_next_field_offset_in_words: i32, + pub kernel_offset: i32, +} + +pub struct Function<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub data: Option<&'a mut Object<'a>>, // ObjectPtr + pub ic_data_array_or_bytecode: Option<&'a mut Object<'a>>, // ObjectPtr + pub code: Option<&'a mut Code<'a>>, // CodePtr + pub positional_parameter_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr + pub bitmap: u64, + pub kernel_offset: i32, + pub kind_tag: u32, +} + +pub struct Library<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub url: std::string::String, // StringPtr -> Raw String + pub private_key: std::string::String, // StringPtr -> Raw String + pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr + pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr + pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr + pub used_scripts: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr + pub imports: Option<&'a mut Array<'a>>, // ArrayPtr + pub exports: Option<&'a mut Array<'a>>, // ArrayPtr + pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr + pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr + pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub num_imports: u16, + pub load_state: i8, + pub flags: u8, + pub kernel_library_index: i32, +} + +pub struct ContextScope { + pub num_variables: i32, + pub is_implicit: bool, +} + +// Fieldless classes +// These classes either have no additional payload fields beyond the standard +// instance headers or their payloads are purely variable-length or dynamically read/overlayed on top of a byte stream + +pub struct CodeSourceMap; +pub struct CompressedStackMaps; +pub struct PcDescriptors; +pub struct ObjectPool; +pub struct OneByteString; +pub struct TwoByteString; + +// Placeholder structs for references used above that aren't defined yet +// it wouldn't compile without this, though for now they remain unimplemented... +pub struct Array<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Object<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct AbstractType<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct FunctionType<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Script<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Closure<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Instance<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct WeakArray<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedDataBase<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedData<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedDataView<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct GrowableObjectArray<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Code<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct LoadingUnit<'a> { _marker: std::marker::PhantomData<&'a ()> } diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs new file mode 100644 index 0000000..f109274 --- /dev/null +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -0,0 +1,84 @@ +use crate::cluster::{ Cluster }; +use crate::stream::{ Stream }; +use crate::constants::{self, MAGIC_BYTES}; + +enum SnapshotKind // pulled straight out of the C++ def +{ + Full, + FullCore, + FullJIT, + FullAOT, // Full + AOT code, this is the one we care about, as th + Module, + None, + Invalid +} + +impl TryFrom for SnapshotKind { + type Error = &'static str; + + fn try_from(value: u64) -> Result { + match value { + 0 => Ok(SnapshotKind::Full), + 1 => Ok(SnapshotKind::FullCore), + 2 => Ok(SnapshotKind::FullJIT), + 3 => Ok(SnapshotKind::FullAOT), + 4 => Ok(SnapshotKind::Module), + 5 => Ok(SnapshotKind::None), + 6 => Ok(SnapshotKind::Invalid), + _ => Err("Invalid snapshot kind... Either headers are corrupt, or this is not a snapshot at all."), // Handle invalid snapshot kidjns + } + } +} +struct DataSnapshot +{ + // this array will contain mutable references to all clusters, and it will be indexed using the class id + clusters: [Box; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id + + magic_bytes: u32, + size: u64, + kind: SnapshotKind, + + version_hash: String, + features: String, + + num_base_objects: u64, + num_objects: u64, + num_clusters: u64, + + instr_table_len: u64, + instr_table_offset: usize, + + start_of_alloc_area: usize, + start_of_fill_area: usize, +} + +impl DataSnapshot { + fn parse_header(&mut self, stream: &mut Stream) + { + self.magic_bytes = stream.read_u32(); + + if self.magic_bytes != MAGIC_BYTES + { + panic!("Not a snapshot...") + } + + self.size = stream.read_u64(); + self.kind = SnapshotKind::try_from(stream.read_u64()).expect("Not a valid snapshot!"); + + self.parse_version_and_features(stream); + } + + fn parse_version_and_features(&mut self, stream: &mut Stream) + { + let mut version_and_features = stream.read_c_string(); + + self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) + self.version_hash = version_and_features; + } + + fn read_clusters(&mut self, stream: &Stream) + { + let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 + + } +} \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index d38fb0b..2a9c45d 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -1,20 +1,111 @@ -mod constants; - -struct Stream +use crate::constants::{UNSIGNED_END_OF_DATA_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_MAX_DATA_PER_BYTE, DATA_BITS_PER_BYTE}; +pub struct Stream<'a> { - byte_stream: &[u8], + byte_stream: &'a [u8], curr_stream_offset: usize, } -impl Stream +impl<'a> Stream<'a> { - fn seek(&self, pos: usize) + fn seek(&mut self, pos: usize) // might be useful? + { + if self.byte_stream.len() > pos && pos > 0 { self.curr_stream_offset = pos; } + } + + pub fn advance_pos(&mut self, num_bytes: usize) + { + self.curr_stream_offset += num_bytes; + } + + pub fn get_current_pos(&self) -> usize + { + self.curr_stream_offset + } + /* + Reads a modified uleb from the current stream offset. + + Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order + to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's + implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte + has its MSb set. + */ + pub fn read_modified_leb128(&mut self, sign_marker: u8) -> u64 // 8 bytes should be enough for anything... + { + let mut idx: u8 = 0; + + let first_byte = self.byte_stream[self.curr_stream_offset]; + if first_byte > UNSIGNED_MAX_DATA_PER_BYTE // if the first byte has its MSb set + { + self.advance_pos(1); + // wrapping_sub mimics C++ unsigned underflow, giving us perfect sign-extension + // for negative numbers, while behaving normally for positive numbers. gotta get used to this :) + return (first_byte as u64).wrapping_sub(sign_marker as u64); + } + + let mut read_num: u64 = 0; + let mut byte: u8; + + loop { + byte = self.byte_stream[self.curr_stream_offset + idx as usize]; + if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { break; } // final byte + read_num |= (byte as u64) << (idx as usize * DATA_BITS_PER_BYTE); + idx += 1; + } + + self.advance_pos((idx + 1) as usize); // advance the stream position + + // Same wrapping trick for the final byte + let final_chunk = (byte as u64).wrapping_sub(sign_marker as u64); + read_num |= final_chunk << (idx as usize * DATA_BITS_PER_BYTE); + + read_num + } + + pub fn read_u64(&mut self) -> u64 { - self.curr_stream_offset = pos; + let u64_size = std::mem::size_of::(); + let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; + + let converted_slice: [u8; 8] = num_slice + .try_into() + .expect("Slice wasn't 8 bytes long..."); + + self.advance_pos(u64_size); + + u64::from_le_bytes(converted_slice) } - fn read_modified_uleb128(stream: &[u8]) // read a modified uleb in the current poss + pub fn read_u32(&mut self) -> u32 { - let bytes = Vec + let u32_size = std::mem::size_of::(); + let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; + + let converted_slice: [u8; 4] = num_slice + .try_into() + .expect("Slice wasn't 4 bytes long..."); + + self.advance_pos(u32_size); + + u32::from_le_bytes(converted_slice) + } + + /* + Panics if it isn't possible to create a stream from the utf-8 representation stored in + the byte slice. It shouldn't happen, so the best possible outcome is to assume some + logic mistake has been made and end the application. It should be a good thing to change this to an + unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order + to have static debug info. + */ + pub fn read_c_string(&mut self) -> String + { + let first_nullbyte_pos = self.byte_stream + .iter() + .position(|&b| b == 0x00) + .unwrap_or(self.byte_stream.len()); + + let raw_str = &self.byte_stream[..=first_nullbyte_pos]; + self.advance_pos(raw_str.len()); + + String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there } } \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 72bd0fb..049d66f 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,83 +1,54 @@ +use paste::paste; +use crate::cluster::Cluster; +use crate::stream::Stream; +use crate::constants::UNSIGNED_M; -struct DataSnapshot +#[macro_export] +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { - clusters: Vec<&'static mut dyn Cluster>, + ($name:ident, $fill_impl:block) => { + ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation + struct [<$name Cluster>] + { + tags: u32, + obj_count: u64, - magic_bytes: u32, - size: u64, - kind: u64, + start_of_fill: usize, + start_of_alloc: usize, - version: String, - features: String, + end_of_fill: usize, + end_of_alloc: usize, - num_base_objects: u64, - num_objects: u64, - num_clusters: u64, + objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) + } - instr_table_len: u64, - instr_table_offset: u64, -} -trait Cluster -{ - fn is_fixed_len(&self) -> bool; - fn get_size(&self) -> usize; -} + impl Cluster for [<$name Cluster>] + { + fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize // read tags and count + { + let initial_pos = stream.get_current_pos(); + self.start_of_alloc = initial_pos; -macro_rules! DECLARE_FIXED_LENGTH_CLUSTER -{ - ($name:ident, $instance_size:literal) => { - struct $name - { - tags: u32, - count: u64, - } + self.obj_count = stream.read_modified_leb128(UNSIGNED_M); - impl Cluster for $name - { - fn get_size(&self) -> usize - { - $instance_size - } + for obj_idx in 0..self.obj_count + { + self.objs.push((*last_ref_id + obj_idx, Box::<$name>::default())); + } - fn is_fixed_len(&self) -> bool - { - true - } - } - }; -} + *last_ref_id = *last_ref_id + self.obj_count; -macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER -{ - ($name:ident, $get_length_impl:block) => { - struct $name - { - tags: u32, - count: u64 - } + stream.get_current_pos() - initial_pos + } - impl Cluster for $name - { - fn get_size(&self) -> usize - $get_length_impl + fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize + $fill_impl - fn is_fixed_len(&self) -> bool - { - false + fn is_fixed_len(&self) -> bool + { + true + } } } }; -} - -// These are the objects that call ReadAllocFixedSize during deserialization, -// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects -// and alloc cluster size is tags (u32) + num_of_objects (ULEB128) - -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 2); -DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 8); -DECLARE_FIXED_LENGTH_CLUSTER!(StringCluster, 8); -DECLARE_FIXED_LENGTH_CLUSTER!(MintCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(DoubleCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameterCluster, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeCluster, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeArgumentsCluster, 32); +} \ No newline at end of file From 9d9eaaec1d0c6c09698afb674ebc95936db491c3 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:24:37 -0500 Subject: [PATCH 08/22] feat(serwalker): add stream readers, fixed-alloc base struct, ClassId enum --- Cargo.lock | 7 + crates/flutterdec-serwalker/Cargo.toml | 3 +- .../flutterdec-serwalker/src/cluster/mod.rs | 56 +++++ crates/flutterdec-serwalker/src/constants.rs | 207 ++++++++++++++++- crates/flutterdec-serwalker/src/lib.rs | 5 + .../src/raw_object/mod.rs | 214 ++++++++++++++++++ crates/flutterdec-serwalker/src/snapshot.rs | 84 +++++++ crates/flutterdec-serwalker/src/stream.rs | 109 ++++++++- crates/flutterdec-serwalker/src/utils.rs | 107 ++++----- 9 files changed, 710 insertions(+), 82 deletions(-) create mode 100644 crates/flutterdec-serwalker/src/cluster/mod.rs create mode 100644 crates/flutterdec-serwalker/src/raw_object/mod.rs create mode 100644 crates/flutterdec-serwalker/src/snapshot.rs diff --git a/Cargo.lock b/Cargo.lock index c413b1d..6482e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,7 @@ name = "flutterdec-serwalker" version = "0.1.0-alpha.2" dependencies = [ "goblin", + "paste", ] [[package]] @@ -571,6 +572,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "plain" version = "0.2.3" diff --git a/crates/flutterdec-serwalker/Cargo.toml b/crates/flutterdec-serwalker/Cargo.toml index 65fb1eb..1885862 100644 --- a/crates/flutterdec-serwalker/Cargo.toml +++ b/crates/flutterdec-serwalker/Cargo.toml @@ -5,4 +5,5 @@ edition.workspace = true license.workspace = true [dependencies] -goblin.workspace = true \ No newline at end of file +goblin.workspace = true +paste = "1.0.15" diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs new file mode 100644 index 0000000..568bdb5 --- /dev/null +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -0,0 +1,56 @@ +use crate::{constants, stream::Stream}; +use crate::DECLARE_FIXED_LENGTH_CLUSTER; +use crate::raw_object::*; +use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; + +type Smi = i32; + +pub trait Cluster +{ + fn is_fixed_len(&self) -> bool; + fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; + fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; +} + +pub fn read_cluster_alloc() +{ + let curr_ref_id: u64 = 0; +} + +pub fn read_cluster_fill() +{ + let curr_ref_id: u64 = 0; +} + +pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi +{ + let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed + + (raw_smi as Smi) >> constants::SMI_SHIFT +} + +pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_NUM], class_id: ClassId) -> Result, &str> +{ + match class_id { + IllegalCid => Err("Not a supported class (illegal class)..."), + _ => Err("Not a supported class..."), + } +} + +// These are the objects that call ReadAllocFixedSize during deserialization, +// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects +// and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) + + +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { 1 + // to-do +}); +/* +DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteString, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(String, 8); +DECLARE_FIXED_LENGTH_CLUSTER!(Mint, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(Type, 32); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeArguments, 32); +*/ \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index b896046..63b35c6 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -1,13 +1,212 @@ use std::mem::size_of; +pub const MAGIC_BYTES: u32 = 0xdcdcf5f5; + pub const SNAPSHOT_MAGIC_NUMBER_SZ: usize = size_of::(); +pub const SNAPSHOT_LEN_SZ: usize = size_of::(); +pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + +pub const SNAPSHOT_HEADER_SZ: usize = SNAPSHOT_MAGIC_NUMBER_SZ // 20 bytes of header + + SNAPSHOT_LEN_SZ + + SNAPSHOT_KIND_SZ; + +pub const MAX_CLUSTER_NUM: usize = 67usize; + +pub const UNSIGNED_END_OF_DATA_BYTE: u8 = 0x80u8; // last byte +pub const UNSIGNED_MAX_DATA_PER_BYTE: u8 = 0x7fu8; // more bytes to follow (for both) + +pub const SIGNED_END_OF_DATA_BYTE: u8 = 0xc0u8; // last byte -pub const END_OF_ULEB_MASK: u8 = 0x80; // last byte -pub const ULEB_EXTRACT_BYTE_DATA_MASK: u8 = 0x7f; // more bytes to follow +pub const SIGNED_M: u8 = SIGNED_END_OF_DATA_BYTE; +pub const UNSIGNED_M: u8 = UNSIGNED_END_OF_DATA_BYTE; + +pub const DATA_BITS_PER_BYTE: usize = 7usize; + +pub const SMI_SHIFT: usize = 1usize; + +pub const VERSION_HASH_LENGTH: usize = 32usize; + +pub enum ClassId { + IllegalCid = 0, + NativePointer, + FreeListElement, + ForwardingCorpse, + ObjectCid, + ClassCid, + PatchClassCid, + FunctionCid, + TypeParametersCid, + ClosureDataCid, + FfiTrampolineDataCid, + FieldCid, + ScriptCid, + LibraryCid, + NamespaceCid, + KernelProgramInfoCid, + WeakSerializationReferenceCid, + WeakArrayCid, + CodeCid, + BytecodeCid, + InstructionsCid, + InstructionsSectionCid, + InstructionsTableCid, + ObjectPoolCid, + PcDescriptorsCid, + CodeSourceMapCid, + CompressedStackMapsCid, + LocalVarDescriptorsCid, + ExceptionHandlersCid, + ContextCid, + ContextScopeCid, + SentinelCid, + SingleTargetCacheCid, + MonomorphicSmiableCallCid, + CallSiteDataCid, + UnlinkedCallCid, + ICDataCid, + MegamorphicCacheCid, + SubtypeTestCacheCid, + LoadingUnitCid, + ErrorCid, + ApiErrorCid, + LanguageErrorCid, + UnhandledExceptionCid, + UnwindErrorCid, + InstanceCid, + LibraryPrefixCid, + TypeArgumentsCid, + AbstractTypeCid, + TypeCid, + FunctionTypeCid, + RecordTypeCid, + TypeParameterCid, + FinalizerBaseCid, + FinalizerCid, + NativeFinalizerCid, + FinalizerEntryCid, + ClosureCid, + NumberCid, + IntegerCid, + SmiCid, + MintCid, + DoubleCid, + BoolCid, + Float32x4Cid, + Int32x4Cid, + Float64x2Cid, + RecordCid, + TypedDataBaseCid, + TypedDataCid, + ExternalTypedDataCid, + TypedDataViewCid, + PointerCid, + DynamicLibraryCid, + CapabilityCid, + ReceivePortCid, + SendPortCid, + StackTraceCid, + SuspendStateCid, + RegExpCid, + WeakPropertyCid, + WeakReferenceCid, + MirrorReferenceCid, + FutureOrCid, + UserTagCid, + TransferableTypedDataCid, + MapCid, + ConstMapCid, + SetCid, + ConstSetCid, + ArrayCid, + ImmutableArrayCid, + GrowableObjectArrayCid, + StringCid, + OneByteStringCid, + TwoByteStringCid, + FfiNativeFunctionCid, + FfiInt8Cid, + FfiInt16Cid, + FfiInt32Cid, + FfiInt64Cid, + FfiUint8Cid, + FfiUint16Cid, + FfiUint32Cid, + FfiUint64Cid, + FfiFloatCid, + FfiDoubleCid, + FfiVoidCid, + FfiHandleCid, + FfiBoolCid, + FfiNativeTypeCid, + FfiStructCid, + TypedDataInt8ArrayCid, + TypedDataInt8ArrayViewCid, + ExternalTypedDataInt8ArrayCid, + UnmodifiableTypedDataInt8ArrayViewCid, + TypedDataUint8ArrayCid, + TypedDataUint8ArrayViewCid, + ExternalTypedDataUint8ArrayCid, + UnmodifiableTypedDataUint8ArrayViewCid, + TypedDataUint8ClampedArrayCid, + TypedDataUint8ClampedArrayViewCid, + ExternalTypedDataUint8ClampedArrayCid, + UnmodifiableTypedDataUint8ClampedArrayViewCid, + TypedDataInt16ArrayCid, + TypedDataInt16ArrayViewCid, + ExternalTypedDataInt16ArrayCid, + UnmodifiableTypedDataInt16ArrayViewCid, + TypedDataUint16ArrayCid, + TypedDataUint16ArrayViewCid, + ExternalTypedDataUint16ArrayCid, + UnmodifiableTypedDataUint16ArrayViewCid, + TypedDataInt32ArrayCid, + TypedDataInt32ArrayViewCid, + ExternalTypedDataInt32ArrayCid, + UnmodifiableTypedDataInt32ArrayViewCid, + TypedDataUint32ArrayCid, + TypedDataUint32ArrayViewCid, + ExternalTypedDataUint32ArrayCid, + UnmodifiableTypedDataUint32ArrayViewCid, + TypedDataInt64ArrayCid, + TypedDataInt64ArrayViewCid, + ExternalTypedDataInt64ArrayCid, + UnmodifiableTypedDataInt64ArrayViewCid, + TypedDataUint64ArrayCid, + TypedDataUint64ArrayViewCid, + ExternalTypedDataUint64ArrayCid, + UnmodifiableTypedDataUint64ArrayViewCid, + TypedDataFloat32ArrayCid, + TypedDataFloat32ArrayViewCid, + ExternalTypedDataFloat32ArrayCid, + UnmodifiableTypedDataFloat32ArrayViewCid, + TypedDataFloat64ArrayCid, + TypedDataFloat64ArrayViewCid, + ExternalTypedDataFloat64ArrayCid, + UnmodifiableTypedDataFloat64ArrayViewCid, + TypedDataFloat32x4ArrayCid, + TypedDataFloat32x4ArrayViewCid, + ExternalTypedDataFloat32x4ArrayCid, + UnmodifiableTypedDataFloat32x4ArrayViewCid, + TypedDataInt32x4ArrayCid, + TypedDataInt32x4ArrayViewCid, + ExternalTypedDataInt32x4ArrayCid, + UnmodifiableTypedDataInt32x4ArrayViewCid, + TypedDataFloat64x2ArrayCid, + TypedDataFloat64x2ArrayViewCid, + ExternalTypedDataFloat64x2ArrayCid, + UnmodifiableTypedDataFloat64x2ArrayViewCid, + ByteDataViewCid, + UnmodifiableByteDataViewCid, + ByteBufferCid, + NullCid, + DynamicCid, + VoidCid, + NeverCid, + NumPredefinedCids, +} /* -pub const SNAPSHOT_LEN_SZ: usize = size_of::(); -pub const SNAPSHOT_KIND_SZ: usize = size_of::(); + pub const NUM_BASE_OBJECTS_SZ: usize = size_of::(); pub const NUM_OBJECTS_SZ: usize = size_of::(); diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index 22884e4..1f91630 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,3 +1,8 @@ mod utils; mod constants; +mod cluster; +mod raw_object; +mod stream; + +mod snapshot; \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs new file mode 100644 index 0000000..3ac4da2 --- /dev/null +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -0,0 +1,214 @@ +// raw_object/mod.rs + +type Smi = i64; // Using i64 for Smi fields to be decompressed + +// --- Fixed-Size Objects with defined fields --- + +pub struct Mint { + pub value: i64, +} + +pub struct Double { + pub value: f64, +} + +pub struct TypeArguments<'a> { + pub instantiations: Option<&'a mut Array<'a>>, // ArrayPtr + pub length: Smi, // Smi + pub hash: Smi, // Smi + pub nullability: Smi, // Smi +} + +pub struct TypeParameter<'a> { + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub base: i16, + pub index: i16, +} + +pub struct Type<'a> { + pub arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr +} + +pub struct TypeParameters<'a> { + pub names: Option<&'a mut Array<'a>>, // ArrayPtr + pub flags: Option<&'a mut Array<'a>>, // ArrayPtr + pub bounds: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr + pub defaults: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr +} + +pub struct PatchClass<'a> { + pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr +} + +pub struct ClosureData<'a> { + pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr + pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr + pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr + pub packed_fields: u32, +} + +pub struct FfiTrampolineData<'a> { + pub signature_type: Option<&'a mut Type<'a>>, // TypePtr + pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr + pub callback_exceptional_return: Option<&'a mut Instance<'a>>, // InstancePtr + pub ffi_function_kind: u8, + pub callback_id: i32, +} + +pub struct Field<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr + pub host_offset_or_field_id: Smi, // Smi + pub guarded_list_length: Smi, // Smi + pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub kernel_offset: i32, + pub guarded_list_length_in_object_offset: i8, + pub static_type_exactness_state: i8, + pub target_offset: i32, + pub kind_bits: u32, +} + +pub struct Namespace<'a> { + pub target: Option<&'a mut Library<'a>>, // LibraryPtr + pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub owner: Option<&'a mut Library<'a>>, // LibraryPtr +} + +pub struct KernelProgramInfo<'a> { + pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr + pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub metadata_payloads: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub metadata_mappings: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr +} + +pub struct ExceptionHandlers<'a> { + pub handled_types_data: Option<&'a mut Array<'a>>, // ArrayPtr + pub packed_fields: u32, +} + +pub struct Context<'a> { + pub parent: Option<&'a mut Context<'a>>, // ContextPtr + pub num_variables: i32, +} + +pub struct UnlinkedCall { + pub can_patch_to_monomorphic: bool, +} + +pub struct String { + pub hash: Smi, // Smi + pub length: Smi, // Smi +} + +pub struct Class<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub user_name: std::string::String, // StringPtr -> Raw String + pub functions: Option<&'a mut Array<'a>>, // ArrayPtr + pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr + pub fields: Option<&'a mut Array<'a>>, // ArrayPtr + pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr + pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub library: Option<&'a mut Library<'a>>, // LibraryPtr + pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr + pub super_type: Option<&'a mut Type<'a>>, // TypePtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr + pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub direct_implementors: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub direct_subclasses: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub declaration_instance_type_arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr + pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub num_type_arguments: i16, + pub num_native_fields: u16, + pub state_bits: u32, + pub host_instance_size_in_words: i32, + pub host_type_arguments_field_offset_in_words: i32, + pub host_next_field_offset_in_words: i32, + pub target_instance_size_in_words: i32, + pub target_type_arguments_field_offset_in_words: i32, + pub target_next_field_offset_in_words: i32, + pub kernel_offset: i32, +} + +pub struct Function<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub data: Option<&'a mut Object<'a>>, // ObjectPtr + pub ic_data_array_or_bytecode: Option<&'a mut Object<'a>>, // ObjectPtr + pub code: Option<&'a mut Code<'a>>, // CodePtr + pub positional_parameter_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr + pub bitmap: u64, + pub kernel_offset: i32, + pub kind_tag: u32, +} + +pub struct Library<'a> { + pub name: std::string::String, // StringPtr -> Raw String + pub url: std::string::String, // StringPtr -> Raw String + pub private_key: std::string::String, // StringPtr -> Raw String + pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr + pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr + pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr + pub used_scripts: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr + pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr + pub imports: Option<&'a mut Array<'a>>, // ArrayPtr + pub exports: Option<&'a mut Array<'a>>, // ArrayPtr + pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr + pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr + pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub num_imports: u16, + pub load_state: i8, + pub flags: u8, + pub kernel_library_index: i32, +} + +pub struct ContextScope { + pub num_variables: i32, + pub is_implicit: bool, +} + +// Fieldless classes +// These classes either have no additional payload fields beyond the standard +// instance headers or their payloads are purely variable-length or dynamically read/overlayed on top of a byte stream + +pub struct CodeSourceMap; +pub struct CompressedStackMaps; +pub struct PcDescriptors; +pub struct ObjectPool; +pub struct OneByteString; +pub struct TwoByteString; + +// Placeholder structs for references used above that aren't defined yet +// it wouldn't compile without this, though for now they remain unimplemented... +pub struct Array<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Object<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct AbstractType<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct FunctionType<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Script<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Closure<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Instance<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct WeakArray<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedDataBase<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedData<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct TypedDataView<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct GrowableObjectArray<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Code<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct LoadingUnit<'a> { _marker: std::marker::PhantomData<&'a ()> } diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs new file mode 100644 index 0000000..f109274 --- /dev/null +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -0,0 +1,84 @@ +use crate::cluster::{ Cluster }; +use crate::stream::{ Stream }; +use crate::constants::{self, MAGIC_BYTES}; + +enum SnapshotKind // pulled straight out of the C++ def +{ + Full, + FullCore, + FullJIT, + FullAOT, // Full + AOT code, this is the one we care about, as th + Module, + None, + Invalid +} + +impl TryFrom for SnapshotKind { + type Error = &'static str; + + fn try_from(value: u64) -> Result { + match value { + 0 => Ok(SnapshotKind::Full), + 1 => Ok(SnapshotKind::FullCore), + 2 => Ok(SnapshotKind::FullJIT), + 3 => Ok(SnapshotKind::FullAOT), + 4 => Ok(SnapshotKind::Module), + 5 => Ok(SnapshotKind::None), + 6 => Ok(SnapshotKind::Invalid), + _ => Err("Invalid snapshot kind... Either headers are corrupt, or this is not a snapshot at all."), // Handle invalid snapshot kidjns + } + } +} +struct DataSnapshot +{ + // this array will contain mutable references to all clusters, and it will be indexed using the class id + clusters: [Box; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id + + magic_bytes: u32, + size: u64, + kind: SnapshotKind, + + version_hash: String, + features: String, + + num_base_objects: u64, + num_objects: u64, + num_clusters: u64, + + instr_table_len: u64, + instr_table_offset: usize, + + start_of_alloc_area: usize, + start_of_fill_area: usize, +} + +impl DataSnapshot { + fn parse_header(&mut self, stream: &mut Stream) + { + self.magic_bytes = stream.read_u32(); + + if self.magic_bytes != MAGIC_BYTES + { + panic!("Not a snapshot...") + } + + self.size = stream.read_u64(); + self.kind = SnapshotKind::try_from(stream.read_u64()).expect("Not a valid snapshot!"); + + self.parse_version_and_features(stream); + } + + fn parse_version_and_features(&mut self, stream: &mut Stream) + { + let mut version_and_features = stream.read_c_string(); + + self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) + self.version_hash = version_and_features; + } + + fn read_clusters(&mut self, stream: &Stream) + { + let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 + + } +} \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index d38fb0b..2a9c45d 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -1,20 +1,111 @@ -mod constants; - -struct Stream +use crate::constants::{UNSIGNED_END_OF_DATA_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_MAX_DATA_PER_BYTE, DATA_BITS_PER_BYTE}; +pub struct Stream<'a> { - byte_stream: &[u8], + byte_stream: &'a [u8], curr_stream_offset: usize, } -impl Stream +impl<'a> Stream<'a> { - fn seek(&self, pos: usize) + fn seek(&mut self, pos: usize) // might be useful? + { + if self.byte_stream.len() > pos && pos > 0 { self.curr_stream_offset = pos; } + } + + pub fn advance_pos(&mut self, num_bytes: usize) + { + self.curr_stream_offset += num_bytes; + } + + pub fn get_current_pos(&self) -> usize + { + self.curr_stream_offset + } + /* + Reads a modified uleb from the current stream offset. + + Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order + to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's + implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte + has its MSb set. + */ + pub fn read_modified_leb128(&mut self, sign_marker: u8) -> u64 // 8 bytes should be enough for anything... + { + let mut idx: u8 = 0; + + let first_byte = self.byte_stream[self.curr_stream_offset]; + if first_byte > UNSIGNED_MAX_DATA_PER_BYTE // if the first byte has its MSb set + { + self.advance_pos(1); + // wrapping_sub mimics C++ unsigned underflow, giving us perfect sign-extension + // for negative numbers, while behaving normally for positive numbers. gotta get used to this :) + return (first_byte as u64).wrapping_sub(sign_marker as u64); + } + + let mut read_num: u64 = 0; + let mut byte: u8; + + loop { + byte = self.byte_stream[self.curr_stream_offset + idx as usize]; + if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { break; } // final byte + read_num |= (byte as u64) << (idx as usize * DATA_BITS_PER_BYTE); + idx += 1; + } + + self.advance_pos((idx + 1) as usize); // advance the stream position + + // Same wrapping trick for the final byte + let final_chunk = (byte as u64).wrapping_sub(sign_marker as u64); + read_num |= final_chunk << (idx as usize * DATA_BITS_PER_BYTE); + + read_num + } + + pub fn read_u64(&mut self) -> u64 { - self.curr_stream_offset = pos; + let u64_size = std::mem::size_of::(); + let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; + + let converted_slice: [u8; 8] = num_slice + .try_into() + .expect("Slice wasn't 8 bytes long..."); + + self.advance_pos(u64_size); + + u64::from_le_bytes(converted_slice) } - fn read_modified_uleb128(stream: &[u8]) // read a modified uleb in the current poss + pub fn read_u32(&mut self) -> u32 { - let bytes = Vec + let u32_size = std::mem::size_of::(); + let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; + + let converted_slice: [u8; 4] = num_slice + .try_into() + .expect("Slice wasn't 4 bytes long..."); + + self.advance_pos(u32_size); + + u32::from_le_bytes(converted_slice) + } + + /* + Panics if it isn't possible to create a stream from the utf-8 representation stored in + the byte slice. It shouldn't happen, so the best possible outcome is to assume some + logic mistake has been made and end the application. It should be a good thing to change this to an + unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order + to have static debug info. + */ + pub fn read_c_string(&mut self) -> String + { + let first_nullbyte_pos = self.byte_stream + .iter() + .position(|&b| b == 0x00) + .unwrap_or(self.byte_stream.len()); + + let raw_str = &self.byte_stream[..=first_nullbyte_pos]; + self.advance_pos(raw_str.len()); + + String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there } } \ No newline at end of file diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 72bd0fb..049d66f 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,83 +1,54 @@ +use paste::paste; +use crate::cluster::Cluster; +use crate::stream::Stream; +use crate::constants::UNSIGNED_M; -struct DataSnapshot +#[macro_export] +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { - clusters: Vec<&'static mut dyn Cluster>, + ($name:ident, $fill_impl:block) => { + ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation + struct [<$name Cluster>] + { + tags: u32, + obj_count: u64, - magic_bytes: u32, - size: u64, - kind: u64, + start_of_fill: usize, + start_of_alloc: usize, - version: String, - features: String, + end_of_fill: usize, + end_of_alloc: usize, - num_base_objects: u64, - num_objects: u64, - num_clusters: u64, + objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) + } - instr_table_len: u64, - instr_table_offset: u64, -} -trait Cluster -{ - fn is_fixed_len(&self) -> bool; - fn get_size(&self) -> usize; -} + impl Cluster for [<$name Cluster>] + { + fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize // read tags and count + { + let initial_pos = stream.get_current_pos(); + self.start_of_alloc = initial_pos; -macro_rules! DECLARE_FIXED_LENGTH_CLUSTER -{ - ($name:ident, $instance_size:literal) => { - struct $name - { - tags: u32, - count: u64, - } + self.obj_count = stream.read_modified_leb128(UNSIGNED_M); - impl Cluster for $name - { - fn get_size(&self) -> usize - { - $instance_size - } + for obj_idx in 0..self.obj_count + { + self.objs.push((*last_ref_id + obj_idx, Box::<$name>::default())); + } - fn is_fixed_len(&self) -> bool - { - true - } - } - }; -} + *last_ref_id = *last_ref_id + self.obj_count; -macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER -{ - ($name:ident, $get_length_impl:block) => { - struct $name - { - tags: u32, - count: u64 - } + stream.get_current_pos() - initial_pos + } - impl Cluster for $name - { - fn get_size(&self) -> usize - $get_length_impl + fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize + $fill_impl - fn is_fixed_len(&self) -> bool - { - false + fn is_fixed_len(&self) -> bool + { + true + } } } }; -} - -// These are the objects that call ReadAllocFixedSize during deserialization, -// whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects -// and alloc cluster size is tags (u32) + num_of_objects (ULEB128) - -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteStringCluster, 2); -DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteStringCluster, 8); -DECLARE_FIXED_LENGTH_CLUSTER!(StringCluster, 8); -DECLARE_FIXED_LENGTH_CLUSTER!(MintCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(DoubleCluster, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameterCluster, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeCluster, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeArgumentsCluster, 32); +} \ No newline at end of file From 8de437b14a06505f2436681ff26f35ffd654044b Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:29:32 -0500 Subject: [PATCH 09/22] readability change std::string::String -> String --- crates/flutterdec-serwalker/src/raw_object/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 3ac4da2..2b7c920 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -59,7 +59,7 @@ pub struct FfiTrampolineData<'a> { } pub struct Field<'a> { - pub name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -115,8 +115,8 @@ pub struct String { } pub struct Class<'a> { - pub name: std::string::String, // StringPtr -> Raw String - pub user_name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String + pub user_name: String, // StringPtr -> Raw String pub functions: Option<&'a mut Array<'a>>, // ArrayPtr pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr pub fields: Option<&'a mut Array<'a>>, // ArrayPtr @@ -147,7 +147,7 @@ pub struct Class<'a> { } pub struct Function<'a> { - pub name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr pub data: Option<&'a mut Object<'a>>, // ObjectPtr @@ -161,9 +161,9 @@ pub struct Function<'a> { } pub struct Library<'a> { - pub name: std::string::String, // StringPtr -> Raw String - pub url: std::string::String, // StringPtr -> Raw String - pub private_key: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String + pub url: String, // StringPtr -> Raw String + pub private_key: String, // StringPtr -> Raw String pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr From 3991c8119197488c7ebf93cf7d85a9f2c224e495 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:29:32 -0500 Subject: [PATCH 10/22] refactor(serwalker): use String instead of std::string::String --- crates/flutterdec-serwalker/src/raw_object/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 3ac4da2..2b7c920 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -59,7 +59,7 @@ pub struct FfiTrampolineData<'a> { } pub struct Field<'a> { - pub name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -115,8 +115,8 @@ pub struct String { } pub struct Class<'a> { - pub name: std::string::String, // StringPtr -> Raw String - pub user_name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String + pub user_name: String, // StringPtr -> Raw String pub functions: Option<&'a mut Array<'a>>, // ArrayPtr pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr pub fields: Option<&'a mut Array<'a>>, // ArrayPtr @@ -147,7 +147,7 @@ pub struct Class<'a> { } pub struct Function<'a> { - pub name: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr pub data: Option<&'a mut Object<'a>>, // ObjectPtr @@ -161,9 +161,9 @@ pub struct Function<'a> { } pub struct Library<'a> { - pub name: std::string::String, // StringPtr -> Raw String - pub url: std::string::String, // StringPtr -> Raw String - pub private_key: std::string::String, // StringPtr -> Raw String + pub name: String, // StringPtr -> Raw String + pub url: String, // StringPtr -> Raw String + pub private_key: String, // StringPtr -> Raw String pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr From bb1d0932c24eca23edf4f4ac5b2043afdc0e4110 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:38:00 -0500 Subject: [PATCH 11/22] added Default derivation for current classes --- .../src/raw_object/mod.rs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 2b7c920..23e5222 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -1,17 +1,19 @@ -// raw_object/mod.rs type Smi = i64; // Using i64 for Smi fields to be decompressed // --- Fixed-Size Objects with defined fields --- +#[derive(Default)] pub struct Mint { pub value: i64, } +#[derive(Default)] pub struct Double { pub value: f64, } +#[derive(Default)] pub struct TypeArguments<'a> { pub instantiations: Option<&'a mut Array<'a>>, // ArrayPtr pub length: Smi, // Smi @@ -19,16 +21,19 @@ pub struct TypeArguments<'a> { pub nullability: Smi, // Smi } +#[derive(Default)] pub struct TypeParameter<'a> { pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub base: i16, pub index: i16, } +#[derive(Default)] pub struct Type<'a> { pub arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr } +#[derive(Default)] pub struct TypeParameters<'a> { pub names: Option<&'a mut Array<'a>>, // ArrayPtr pub flags: Option<&'a mut Array<'a>>, // ArrayPtr @@ -36,12 +41,14 @@ pub struct TypeParameters<'a> { pub defaults: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr } +#[derive(Default)] pub struct PatchClass<'a> { pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr pub script: Option<&'a mut Script<'a>>, // ScriptPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr } +#[derive(Default)] pub struct ClosureData<'a> { pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -49,6 +56,7 @@ pub struct ClosureData<'a> { pub packed_fields: u32, } +#[derive(Default)] pub struct FfiTrampolineData<'a> { pub signature_type: Option<&'a mut Type<'a>>, // TypePtr pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr @@ -58,6 +66,7 @@ pub struct FfiTrampolineData<'a> { pub callback_id: i32, } +#[derive(Default)] pub struct Field<'a> { pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr @@ -74,6 +83,7 @@ pub struct Field<'a> { pub kind_bits: u32, } +#[derive(Default)] pub struct Namespace<'a> { pub target: Option<&'a mut Library<'a>>, // LibraryPtr pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr @@ -81,6 +91,7 @@ pub struct Namespace<'a> { pub owner: Option<&'a mut Library<'a>>, // LibraryPtr } +#[derive(Default)] pub struct KernelProgramInfo<'a> { pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr @@ -95,25 +106,30 @@ pub struct KernelProgramInfo<'a> { pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr } +#[derive(Default)] pub struct ExceptionHandlers<'a> { pub handled_types_data: Option<&'a mut Array<'a>>, // ArrayPtr pub packed_fields: u32, } +#[derive(Default)] pub struct Context<'a> { pub parent: Option<&'a mut Context<'a>>, // ContextPtr pub num_variables: i32, } +#[derive(Default)] pub struct UnlinkedCall { pub can_patch_to_monomorphic: bool, } -pub struct String { +#[derive(Default)] +pub struct String_ { pub hash: Smi, // Smi pub length: Smi, // Smi } +#[derive(Default)] pub struct Class<'a> { pub name: String, // StringPtr -> Raw String pub user_name: String, // StringPtr -> Raw String @@ -146,6 +162,7 @@ pub struct Class<'a> { pub kernel_offset: i32, } +#[derive(Default)] pub struct Function<'a> { pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr @@ -160,6 +177,7 @@ pub struct Function<'a> { pub kind_tag: u32, } +#[derive(Default)] pub struct Library<'a> { pub name: String, // StringPtr -> Raw String pub url: String, // StringPtr -> Raw String @@ -180,6 +198,7 @@ pub struct Library<'a> { pub kernel_library_index: i32, } +#[derive(Default)] pub struct ContextScope { pub num_variables: i32, pub is_implicit: bool, @@ -189,11 +208,22 @@ pub struct ContextScope { // These classes either have no additional payload fields beyond the standard // instance headers or their payloads are purely variable-length or dynamically read/overlayed on top of a byte stream +#[derive(Default)] pub struct CodeSourceMap; + +#[derive(Default)] pub struct CompressedStackMaps; + +#[derive(Default)] pub struct PcDescriptors; + +#[derive(Default)] pub struct ObjectPool; + +#[derive(Default)] pub struct OneByteString; + +#[derive(Default)] pub struct TwoByteString; // Placeholder structs for references used above that aren't defined yet From 32b99ec2c960bba2910100e9c853e075bac452c7 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:38:00 -0500 Subject: [PATCH 12/22] feat(serwalker): derive Default on current classes --- .../src/raw_object/mod.rs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 2b7c920..23e5222 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -1,17 +1,19 @@ -// raw_object/mod.rs type Smi = i64; // Using i64 for Smi fields to be decompressed // --- Fixed-Size Objects with defined fields --- +#[derive(Default)] pub struct Mint { pub value: i64, } +#[derive(Default)] pub struct Double { pub value: f64, } +#[derive(Default)] pub struct TypeArguments<'a> { pub instantiations: Option<&'a mut Array<'a>>, // ArrayPtr pub length: Smi, // Smi @@ -19,16 +21,19 @@ pub struct TypeArguments<'a> { pub nullability: Smi, // Smi } +#[derive(Default)] pub struct TypeParameter<'a> { pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub base: i16, pub index: i16, } +#[derive(Default)] pub struct Type<'a> { pub arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr } +#[derive(Default)] pub struct TypeParameters<'a> { pub names: Option<&'a mut Array<'a>>, // ArrayPtr pub flags: Option<&'a mut Array<'a>>, // ArrayPtr @@ -36,12 +41,14 @@ pub struct TypeParameters<'a> { pub defaults: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr } +#[derive(Default)] pub struct PatchClass<'a> { pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr pub script: Option<&'a mut Script<'a>>, // ScriptPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr } +#[derive(Default)] pub struct ClosureData<'a> { pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -49,6 +56,7 @@ pub struct ClosureData<'a> { pub packed_fields: u32, } +#[derive(Default)] pub struct FfiTrampolineData<'a> { pub signature_type: Option<&'a mut Type<'a>>, // TypePtr pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr @@ -58,6 +66,7 @@ pub struct FfiTrampolineData<'a> { pub callback_id: i32, } +#[derive(Default)] pub struct Field<'a> { pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr @@ -74,6 +83,7 @@ pub struct Field<'a> { pub kind_bits: u32, } +#[derive(Default)] pub struct Namespace<'a> { pub target: Option<&'a mut Library<'a>>, // LibraryPtr pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr @@ -81,6 +91,7 @@ pub struct Namespace<'a> { pub owner: Option<&'a mut Library<'a>>, // LibraryPtr } +#[derive(Default)] pub struct KernelProgramInfo<'a> { pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr @@ -95,25 +106,30 @@ pub struct KernelProgramInfo<'a> { pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr } +#[derive(Default)] pub struct ExceptionHandlers<'a> { pub handled_types_data: Option<&'a mut Array<'a>>, // ArrayPtr pub packed_fields: u32, } +#[derive(Default)] pub struct Context<'a> { pub parent: Option<&'a mut Context<'a>>, // ContextPtr pub num_variables: i32, } +#[derive(Default)] pub struct UnlinkedCall { pub can_patch_to_monomorphic: bool, } -pub struct String { +#[derive(Default)] +pub struct String_ { pub hash: Smi, // Smi pub length: Smi, // Smi } +#[derive(Default)] pub struct Class<'a> { pub name: String, // StringPtr -> Raw String pub user_name: String, // StringPtr -> Raw String @@ -146,6 +162,7 @@ pub struct Class<'a> { pub kernel_offset: i32, } +#[derive(Default)] pub struct Function<'a> { pub name: String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr @@ -160,6 +177,7 @@ pub struct Function<'a> { pub kind_tag: u32, } +#[derive(Default)] pub struct Library<'a> { pub name: String, // StringPtr -> Raw String pub url: String, // StringPtr -> Raw String @@ -180,6 +198,7 @@ pub struct Library<'a> { pub kernel_library_index: i32, } +#[derive(Default)] pub struct ContextScope { pub num_variables: i32, pub is_implicit: bool, @@ -189,11 +208,22 @@ pub struct ContextScope { // These classes either have no additional payload fields beyond the standard // instance headers or their payloads are purely variable-length or dynamically read/overlayed on top of a byte stream +#[derive(Default)] pub struct CodeSourceMap; + +#[derive(Default)] pub struct CompressedStackMaps; + +#[derive(Default)] pub struct PcDescriptors; + +#[derive(Default)] pub struct ObjectPool; + +#[derive(Default)] pub struct OneByteString; + +#[derive(Default)] pub struct TwoByteString; // Placeholder structs for references used above that aren't defined yet From a6381a0bf70cdd413b9f4eafe8eae8c1e2fcca6a Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:39:57 -0500 Subject: [PATCH 13/22] comment --- crates/flutterdec-serwalker/src/raw_object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 23e5222..24c33eb 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -124,7 +124,7 @@ pub struct UnlinkedCall { } #[derive(Default)] -pub struct String_ { +pub struct String_ { // added underscore so there's no conflict between this type and rust's String pub hash: Smi, // Smi pub length: Smi, // Smi } From 86bf19a846ff40e6f913818ca8d81fc1379cb76f Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:39:57 -0500 Subject: [PATCH 14/22] docs(serwalker): add comment --- crates/flutterdec-serwalker/src/raw_object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 23e5222..24c33eb 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -124,7 +124,7 @@ pub struct UnlinkedCall { } #[derive(Default)] -pub struct String_ { +pub struct String_ { // added underscore so there's no conflict between this type and rust's String pub hash: Smi, // Smi pub length: Smi, // Smi } From ac2a614f7eb9a1f6a568ba5ca7fe6cd7be895002 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:54:11 -0500 Subject: [PATCH 15/22] format changes using cargo fmt --all --- .../flutterdec-serwalker/src/cluster/mod.rs | 32 ++-- crates/flutterdec-serwalker/src/constants.rs | 2 +- crates/flutterdec-serwalker/src/lib.rs | 4 +- .../src/raw_object/mod.rs | 180 ++++++++++-------- crates/flutterdec-serwalker/src/snapshot.rs | 39 ++-- crates/flutterdec-serwalker/src/stream.rs | 88 ++++----- crates/flutterdec-serwalker/src/utils.rs | 11 +- 7 files changed, 188 insertions(+), 168 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index 568bdb5..c1559ca 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -1,36 +1,34 @@ -use crate::{constants, stream::Stream}; -use crate::DECLARE_FIXED_LENGTH_CLUSTER; -use crate::raw_object::*; use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; +use crate::raw_object::*; +use crate::DECLARE_FIXED_LENGTH_CLUSTER; +use crate::{constants, stream::Stream}; type Smi = i32; -pub trait Cluster -{ +pub trait Cluster { fn is_fixed_len(&self) -> bool; fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; } -pub fn read_cluster_alloc() -{ +pub fn read_cluster_alloc() { let curr_ref_id: u64 = 0; } -pub fn read_cluster_fill() -{ +pub fn read_cluster_fill() { let curr_ref_id: u64 = 0; } -pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi -{ +pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed - + (raw_smi as Smi) >> constants::SMI_SHIFT } -pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_NUM], class_id: ClassId) -> Result, &str> -{ +pub fn decide_cluster( + clusters: &mut [Box; constants::MAX_CLUSTER_NUM], + class_id: ClassId, +) -> Result, &str> { match class_id { IllegalCid => Err("Not a supported class (illegal class)..."), _ => Err("Not a supported class..."), @@ -41,8 +39,8 @@ pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_N // whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects // and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) - -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { 1 +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { + 1 // to-do }); /* @@ -53,4 +51,4 @@ DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); DECLARE_FIXED_LENGTH_CLUSTER!(Type, 32); DECLARE_FIXED_LENGTH_CLUSTER!(TypeArguments, 32); -*/ \ No newline at end of file +*/ diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index 63b35c6..6a68d72 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -219,4 +219,4 @@ pub const CLUSTER_TAGS_SZ: usize = size_of::(); pub const CLUSTER_OBJ_COUNT_SZ: usize = size_of::(); pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); -*/ \ No newline at end of file +*/ diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index 1f91630..b5aaf10 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,8 +1,8 @@ -mod utils; mod constants; +mod utils; mod cluster; mod raw_object; mod stream; -mod snapshot; \ No newline at end of file +mod snapshot; diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 24c33eb..3692170 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -1,4 +1,3 @@ - type Smi = i64; // Using i64 for Smi fields to be decompressed // --- Fixed-Size Objects with defined fields --- @@ -43,24 +42,24 @@ pub struct TypeParameters<'a> { #[derive(Default)] pub struct PatchClass<'a> { - pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr - pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr } #[derive(Default)] pub struct ClosureData<'a> { - pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr + pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr - pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr + pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr pub packed_fields: u32, } #[derive(Default)] pub struct FfiTrampolineData<'a> { - pub signature_type: Option<&'a mut Type<'a>>, // TypePtr - pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr - pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr + pub signature_type: Option<&'a mut Type<'a>>, // TypePtr + pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr pub callback_exceptional_return: Option<&'a mut Instance<'a>>, // InstancePtr pub ffi_function_kind: u8, pub callback_id: i32, @@ -68,14 +67,14 @@ pub struct FfiTrampolineData<'a> { #[derive(Default)] pub struct Field<'a> { - pub name: String, // StringPtr -> Raw String - pub owner: Option<&'a mut Object<'a>>, // ObjectPtr - pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub name: String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr - pub host_offset_or_field_id: Smi, // Smi - pub guarded_list_length: Smi, // Smi - pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr - pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub host_offset_or_field_id: Smi, // Smi + pub guarded_list_length: Smi, // Smi + pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr pub kernel_offset: i32, pub guarded_list_length_in_object_offset: i8, pub static_type_exactness_state: i8, @@ -85,25 +84,25 @@ pub struct Field<'a> { #[derive(Default)] pub struct Namespace<'a> { - pub target: Option<&'a mut Library<'a>>, // LibraryPtr - pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub owner: Option<&'a mut Library<'a>>, // LibraryPtr + pub target: Option<&'a mut Library<'a>>, // LibraryPtr + pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub owner: Option<&'a mut Library<'a>>, // LibraryPtr } #[derive(Default)] pub struct KernelProgramInfo<'a> { - pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr - pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr - pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr + pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr pub metadata_payloads: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr pub metadata_mappings: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr - pub constants: Option<&'a mut Array<'a>>, // ArrayPtr - pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr - pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr } #[derive(Default)] @@ -124,32 +123,33 @@ pub struct UnlinkedCall { } #[derive(Default)] -pub struct String_ { // added underscore so there's no conflict between this type and rust's String +pub struct String_ { + // added underscore so there's no conflict between this type and rust's String pub hash: Smi, // Smi pub length: Smi, // Smi } #[derive(Default)] pub struct Class<'a> { - pub name: String, // StringPtr -> Raw String - pub user_name: String, // StringPtr -> Raw String - pub functions: Option<&'a mut Array<'a>>, // ArrayPtr - pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr - pub fields: Option<&'a mut Array<'a>>, // ArrayPtr - pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr - pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr - pub script: Option<&'a mut Script<'a>>, // ScriptPtr - pub library: Option<&'a mut Library<'a>>, // LibraryPtr - pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr - pub super_type: Option<&'a mut Type<'a>>, // TypePtr - pub constants: Option<&'a mut Array<'a>>, // ArrayPtr - pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr - pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub name: String, // StringPtr -> Raw String + pub user_name: String, // StringPtr -> Raw String + pub functions: Option<&'a mut Array<'a>>, // ArrayPtr + pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr + pub fields: Option<&'a mut Array<'a>>, // ArrayPtr + pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr + pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub library: Option<&'a mut Library<'a>>, // LibraryPtr + pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr + pub super_type: Option<&'a mut Type<'a>>, // TypePtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr + pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr pub direct_implementors: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr pub direct_subclasses: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr pub declaration_instance_type_arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr - pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr - pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr pub num_type_arguments: i16, pub num_native_fields: u16, pub state_bits: u32, @@ -164,14 +164,14 @@ pub struct Class<'a> { #[derive(Default)] pub struct Function<'a> { - pub name: String, // StringPtr -> Raw String - pub owner: Option<&'a mut Object<'a>>, // ObjectPtr - pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr - pub data: Option<&'a mut Object<'a>>, // ObjectPtr + pub name: String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub data: Option<&'a mut Object<'a>>, // ObjectPtr pub ic_data_array_or_bytecode: Option<&'a mut Object<'a>>, // ObjectPtr - pub code: Option<&'a mut Code<'a>>, // CodePtr + pub code: Option<&'a mut Code<'a>>, // CodePtr pub positional_parameter_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr + pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr pub bitmap: u64, pub kernel_offset: i32, pub kind_tag: u32, @@ -179,19 +179,19 @@ pub struct Function<'a> { #[derive(Default)] pub struct Library<'a> { - pub name: String, // StringPtr -> Raw String - pub url: String, // StringPtr -> Raw String - pub private_key: String, // StringPtr -> Raw String - pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr - pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr - pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr + pub name: String, // StringPtr -> Raw String + pub url: String, // StringPtr -> Raw String + pub private_key: String, // StringPtr -> Raw String + pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr + pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr + pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr pub used_scripts: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr - pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr - pub imports: Option<&'a mut Array<'a>>, // ArrayPtr - pub exports: Option<&'a mut Array<'a>>, // ArrayPtr - pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr + pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr + pub imports: Option<&'a mut Array<'a>>, // ArrayPtr + pub exports: Option<&'a mut Array<'a>>, // ArrayPtr + pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr - pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr pub num_imports: u16, pub load_state: i8, pub flags: u8, @@ -228,17 +228,45 @@ pub struct TwoByteString; // Placeholder structs for references used above that aren't defined yet // it wouldn't compile without this, though for now they remain unimplemented... -pub struct Array<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Object<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct AbstractType<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct FunctionType<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Script<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Closure<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Instance<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct WeakArray<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedDataBase<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedData<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedDataView<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct GrowableObjectArray<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Code<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct LoadingUnit<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Array<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Object<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct AbstractType<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct FunctionType<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Script<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Closure<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Instance<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct WeakArray<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedDataBase<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedData<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedDataView<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct GrowableObjectArray<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Code<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct LoadingUnit<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index f109274..18cdf3a 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -1,16 +1,17 @@ -use crate::cluster::{ Cluster }; -use crate::stream::{ Stream }; +use crate::cluster::Cluster; use crate::constants::{self, MAGIC_BYTES}; +use crate::stream::Stream; -enum SnapshotKind // pulled straight out of the C++ def +enum SnapshotKind +// pulled straight out of the C++ def { - Full, - FullCore, - FullJIT, - FullAOT, // Full + AOT code, this is the one we care about, as th - Module, - None, - Invalid + Full, + FullCore, + FullJIT, + FullAOT, // Full + AOT code, this is the one we care about, as th + Module, + None, + Invalid, } impl TryFrom for SnapshotKind { @@ -29,8 +30,7 @@ impl TryFrom for SnapshotKind { } } } -struct DataSnapshot -{ +struct DataSnapshot { // this array will contain mutable references to all clusters, and it will be indexed using the class id clusters: [Box; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id @@ -53,12 +53,10 @@ struct DataSnapshot } impl DataSnapshot { - fn parse_header(&mut self, stream: &mut Stream) - { + fn parse_header(&mut self, stream: &mut Stream) { self.magic_bytes = stream.read_u32(); - if self.magic_bytes != MAGIC_BYTES - { + if self.magic_bytes != MAGIC_BYTES { panic!("Not a snapshot...") } @@ -68,17 +66,14 @@ impl DataSnapshot { self.parse_version_and_features(stream); } - fn parse_version_and_features(&mut self, stream: &mut Stream) - { + fn parse_version_and_features(&mut self, stream: &mut Stream) { let mut version_and_features = stream.read_c_string(); self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) self.version_hash = version_and_features; } - fn read_clusters(&mut self, stream: &Stream) - { + fn read_clusters(&mut self, stream: &Stream) { let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 - } -} \ No newline at end of file +} diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index 2a9c45d..a0c52e4 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -1,40 +1,42 @@ -use crate::constants::{UNSIGNED_END_OF_DATA_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_MAX_DATA_PER_BYTE, DATA_BITS_PER_BYTE}; -pub struct Stream<'a> -{ +use crate::constants::{ + DATA_BITS_PER_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_END_OF_DATA_BYTE, + UNSIGNED_MAX_DATA_PER_BYTE, +}; +pub struct Stream<'a> { byte_stream: &'a [u8], curr_stream_offset: usize, } -impl<'a> Stream<'a> -{ +impl<'a> Stream<'a> { fn seek(&mut self, pos: usize) // might be useful? { - if self.byte_stream.len() > pos && pos > 0 { self.curr_stream_offset = pos; } + if self.byte_stream.len() > pos && pos > 0 { + self.curr_stream_offset = pos; + } } - pub fn advance_pos(&mut self, num_bytes: usize) - { + pub fn advance_pos(&mut self, num_bytes: usize) { self.curr_stream_offset += num_bytes; } - pub fn get_current_pos(&self) -> usize - { + pub fn get_current_pos(&self) -> usize { self.curr_stream_offset } /* - Reads a modified uleb from the current stream offset. + Reads a modified uleb from the current stream offset. - Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order - to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's - implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte - has its MSb set. - */ + Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order + to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's + implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte + has its MSb set. + */ pub fn read_modified_leb128(&mut self, sign_marker: u8) -> u64 // 8 bytes should be enough for anything... { let mut idx: u8 = 0; - let first_byte = self.byte_stream[self.curr_stream_offset]; - if first_byte > UNSIGNED_MAX_DATA_PER_BYTE // if the first byte has its MSb set + let first_byte = self.byte_stream[self.curr_stream_offset]; + if first_byte > UNSIGNED_MAX_DATA_PER_BYTE + // if the first byte has its MSb set { self.advance_pos(1); // wrapping_sub mimics C++ unsigned underflow, giving us perfect sign-extension @@ -47,7 +49,9 @@ impl<'a> Stream<'a> loop { byte = self.byte_stream[self.curr_stream_offset + idx as usize]; - if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { break; } // final byte + if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { + break; + } // final byte read_num |= (byte as u64) << (idx as usize * DATA_BITS_PER_BYTE); idx += 1; } @@ -61,28 +65,24 @@ impl<'a> Stream<'a> read_num } - pub fn read_u64(&mut self) -> u64 - { + pub fn read_u64(&mut self) -> u64 { let u64_size = std::mem::size_of::(); - let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; + let num_slice = + &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; - let converted_slice: [u8; 8] = num_slice - .try_into() - .expect("Slice wasn't 8 bytes long..."); + let converted_slice: [u8; 8] = num_slice.try_into().expect("Slice wasn't 8 bytes long..."); self.advance_pos(u64_size); u64::from_le_bytes(converted_slice) } - pub fn read_u32(&mut self) -> u32 - { + pub fn read_u32(&mut self) -> u32 { let u32_size = std::mem::size_of::(); - let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; + let num_slice = + &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; - let converted_slice: [u8; 4] = num_slice - .try_into() - .expect("Slice wasn't 4 bytes long..."); + let converted_slice: [u8; 4] = num_slice.try_into().expect("Slice wasn't 4 bytes long..."); self.advance_pos(u32_size); @@ -90,22 +90,22 @@ impl<'a> Stream<'a> } /* - Panics if it isn't possible to create a stream from the utf-8 representation stored in - the byte slice. It shouldn't happen, so the best possible outcome is to assume some - logic mistake has been made and end the application. It should be a good thing to change this to an - unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order - to have static debug info. - */ - pub fn read_c_string(&mut self) -> String - { - let first_nullbyte_pos = self.byte_stream - .iter() - .position(|&b| b == 0x00) - .unwrap_or(self.byte_stream.len()); + Panics if it isn't possible to create a stream from the utf-8 representation stored in + the byte slice. It shouldn't happen, so the best possible outcome is to assume some + logic mistake has been made and end the application. It should be a good thing to change this to an + unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order + to have static debug info. + */ + pub fn read_c_string(&mut self) -> String { + let first_nullbyte_pos = self + .byte_stream + .iter() + .position(|&b| b == 0x00) + .unwrap_or(self.byte_stream.len()); let raw_str = &self.byte_stream[..=first_nullbyte_pos]; self.advance_pos(raw_str.len()); String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there } -} \ No newline at end of file +} diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 049d66f..4f7eb30 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,11 +1,10 @@ -use paste::paste; use crate::cluster::Cluster; -use crate::stream::Stream; use crate::constants::UNSIGNED_M; +use crate::stream::Stream; +use paste::paste; #[macro_export] -macro_rules! DECLARE_FIXED_LENGTH_CLUSTER -{ +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { ($name:ident, $fill_impl:block) => { ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation struct [<$name Cluster>] @@ -22,7 +21,7 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) } - impl Cluster for [<$name Cluster>] + impl Cluster for [<$name Cluster>] { fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize // read tags and count { @@ -51,4 +50,4 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER } } }; -} \ No newline at end of file +} From b36733ae4d7105ce3e276c11bb3a44f57b3cccff Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 14:54:11 -0500 Subject: [PATCH 16/22] style: cargo fmt --all --- .../flutterdec-serwalker/src/cluster/mod.rs | 32 ++-- crates/flutterdec-serwalker/src/constants.rs | 2 +- crates/flutterdec-serwalker/src/lib.rs | 4 +- .../src/raw_object/mod.rs | 180 ++++++++++-------- crates/flutterdec-serwalker/src/snapshot.rs | 39 ++-- crates/flutterdec-serwalker/src/stream.rs | 88 ++++----- crates/flutterdec-serwalker/src/utils.rs | 11 +- 7 files changed, 188 insertions(+), 168 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index 568bdb5..c1559ca 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -1,36 +1,34 @@ -use crate::{constants, stream::Stream}; -use crate::DECLARE_FIXED_LENGTH_CLUSTER; -use crate::raw_object::*; use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; +use crate::raw_object::*; +use crate::DECLARE_FIXED_LENGTH_CLUSTER; +use crate::{constants, stream::Stream}; type Smi = i32; -pub trait Cluster -{ +pub trait Cluster { fn is_fixed_len(&self) -> bool; fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; } -pub fn read_cluster_alloc() -{ +pub fn read_cluster_alloc() { let curr_ref_id: u64 = 0; } -pub fn read_cluster_fill() -{ +pub fn read_cluster_fill() { let curr_ref_id: u64 = 0; } -pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi -{ +pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed - + (raw_smi as Smi) >> constants::SMI_SHIFT } -pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_NUM], class_id: ClassId) -> Result, &str> -{ +pub fn decide_cluster( + clusters: &mut [Box; constants::MAX_CLUSTER_NUM], + class_id: ClassId, +) -> Result, &str> { match class_id { IllegalCid => Err("Not a supported class (illegal class)..."), _ => Err("Not a supported class..."), @@ -41,8 +39,8 @@ pub fn decide_cluster(clusters: &mut [Box; constants::MAX_CLUSTER_N // whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects // and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) - -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { 1 +DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { + 1 // to-do }); /* @@ -53,4 +51,4 @@ DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); DECLARE_FIXED_LENGTH_CLUSTER!(Type, 32); DECLARE_FIXED_LENGTH_CLUSTER!(TypeArguments, 32); -*/ \ No newline at end of file +*/ diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index 63b35c6..6a68d72 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -219,4 +219,4 @@ pub const CLUSTER_TAGS_SZ: usize = size_of::(); pub const CLUSTER_OBJ_COUNT_SZ: usize = size_of::(); pub const OBJECT_STORE_ENTRY_SIZE: usize = size_of::(); -*/ \ No newline at end of file +*/ diff --git a/crates/flutterdec-serwalker/src/lib.rs b/crates/flutterdec-serwalker/src/lib.rs index 1f91630..b5aaf10 100644 --- a/crates/flutterdec-serwalker/src/lib.rs +++ b/crates/flutterdec-serwalker/src/lib.rs @@ -1,8 +1,8 @@ -mod utils; mod constants; +mod utils; mod cluster; mod raw_object; mod stream; -mod snapshot; \ No newline at end of file +mod snapshot; diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 24c33eb..3692170 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -1,4 +1,3 @@ - type Smi = i64; // Using i64 for Smi fields to be decompressed // --- Fixed-Size Objects with defined fields --- @@ -43,24 +42,24 @@ pub struct TypeParameters<'a> { #[derive(Default)] pub struct PatchClass<'a> { - pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr - pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub wrapped_class: Option<&'a mut Class<'a>>, // ClassPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr } #[derive(Default)] pub struct ClosureData<'a> { - pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr + pub context_scope: Option<&'a mut ContextScope>, // ContextScopePtr pub parent_function: Option<&'a mut Function<'a>>, // FunctionPtr - pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr + pub closure: Option<&'a mut Closure<'a>>, // ClosurePtr pub packed_fields: u32, } #[derive(Default)] pub struct FfiTrampolineData<'a> { - pub signature_type: Option<&'a mut Type<'a>>, // TypePtr - pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr - pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr + pub signature_type: Option<&'a mut Type<'a>>, // TypePtr + pub c_signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub callback_target: Option<&'a mut Function<'a>>, // FunctionPtr pub callback_exceptional_return: Option<&'a mut Instance<'a>>, // InstancePtr pub ffi_function_kind: u8, pub callback_id: i32, @@ -68,14 +67,14 @@ pub struct FfiTrampolineData<'a> { #[derive(Default)] pub struct Field<'a> { - pub name: String, // StringPtr -> Raw String - pub owner: Option<&'a mut Object<'a>>, // ObjectPtr - pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub name: String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr - pub host_offset_or_field_id: Smi, // Smi - pub guarded_list_length: Smi, // Smi - pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr - pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub host_offset_or_field_id: Smi, // Smi + pub guarded_list_length: Smi, // Smi + pub exact_type: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr pub kernel_offset: i32, pub guarded_list_length_in_object_offset: i8, pub static_type_exactness_state: i8, @@ -85,25 +84,25 @@ pub struct Field<'a> { #[derive(Default)] pub struct Namespace<'a> { - pub target: Option<&'a mut Library<'a>>, // LibraryPtr - pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub owner: Option<&'a mut Library<'a>>, // LibraryPtr + pub target: Option<&'a mut Library<'a>>, // LibraryPtr + pub show_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub hide_names: Option<&'a mut Array<'a>>, // ArrayPtr + pub owner: Option<&'a mut Library<'a>>, // LibraryPtr } #[derive(Default)] pub struct KernelProgramInfo<'a> { - pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr - pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr - pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub kernel_component: Option<&'a mut TypedDataBase<'a>>, // TypedDataBasePtr + pub string_offsets: Option<&'a mut TypedData<'a>>, // TypedDataPtr + pub string_data: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub canonical_names: Option<&'a mut TypedData<'a>>, // TypedDataPtr pub metadata_payloads: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr pub metadata_mappings: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr - pub constants: Option<&'a mut Array<'a>>, // ArrayPtr - pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr - pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr - pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub constants_table: Option<&'a mut TypedDataView<'a>>, // TypedDataViewPtr + pub libraries_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub classes_cache: Option<&'a mut Array<'a>>, // ArrayPtr } #[derive(Default)] @@ -124,32 +123,33 @@ pub struct UnlinkedCall { } #[derive(Default)] -pub struct String_ { // added underscore so there's no conflict between this type and rust's String +pub struct String_ { + // added underscore so there's no conflict between this type and rust's String pub hash: Smi, // Smi pub length: Smi, // Smi } #[derive(Default)] pub struct Class<'a> { - pub name: String, // StringPtr -> Raw String - pub user_name: String, // StringPtr -> Raw String - pub functions: Option<&'a mut Array<'a>>, // ArrayPtr - pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr - pub fields: Option<&'a mut Array<'a>>, // ArrayPtr - pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr - pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr - pub script: Option<&'a mut Script<'a>>, // ScriptPtr - pub library: Option<&'a mut Library<'a>>, // LibraryPtr - pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr - pub super_type: Option<&'a mut Type<'a>>, // TypePtr - pub constants: Option<&'a mut Array<'a>>, // ArrayPtr - pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr - pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr + pub name: String, // StringPtr -> Raw String + pub user_name: String, // StringPtr -> Raw String + pub functions: Option<&'a mut Array<'a>>, // ArrayPtr + pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr + pub fields: Option<&'a mut Array<'a>>, // ArrayPtr + pub offset_in_words_to_field: Option<&'a mut Array<'a>>, // ArrayPtr + pub interfaces: Option<&'a mut Array<'a>>, // ArrayPtr + pub script: Option<&'a mut Script<'a>>, // ScriptPtr + pub library: Option<&'a mut Library<'a>>, // LibraryPtr + pub type_parameters: Option<&'a mut TypeParameters<'a>>, // TypeParametersPtr + pub super_type: Option<&'a mut Type<'a>>, // TypePtr + pub constants: Option<&'a mut Array<'a>>, // ArrayPtr + pub declaration_type: Option<&'a mut Type<'a>>, // TypePtr + pub invocation_dispatcher_cache: Option<&'a mut Array<'a>>, // ArrayPtr pub direct_implementors: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr pub direct_subclasses: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr pub declaration_instance_type_arguments: Option<&'a mut TypeArguments<'a>>, // TypeArgumentsPtr - pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr - pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr + pub allocation_stub: Option<&'a mut Code<'a>>, // CodePtr + pub dependent_code: Option<&'a mut WeakArray<'a>>, // WeakArrayPtr pub num_type_arguments: i16, pub num_native_fields: u16, pub state_bits: u32, @@ -164,14 +164,14 @@ pub struct Class<'a> { #[derive(Default)] pub struct Function<'a> { - pub name: String, // StringPtr -> Raw String - pub owner: Option<&'a mut Object<'a>>, // ObjectPtr - pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr - pub data: Option<&'a mut Object<'a>>, // ObjectPtr + pub name: String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub data: Option<&'a mut Object<'a>>, // ObjectPtr pub ic_data_array_or_bytecode: Option<&'a mut Object<'a>>, // ObjectPtr - pub code: Option<&'a mut Code<'a>>, // CodePtr + pub code: Option<&'a mut Code<'a>>, // CodePtr pub positional_parameter_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr + pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr pub bitmap: u64, pub kernel_offset: i32, pub kind_tag: u32, @@ -179,19 +179,19 @@ pub struct Function<'a> { #[derive(Default)] pub struct Library<'a> { - pub name: String, // StringPtr -> Raw String - pub url: String, // StringPtr -> Raw String - pub private_key: String, // StringPtr -> Raw String - pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr - pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr - pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr + pub name: String, // StringPtr -> Raw String + pub url: String, // StringPtr -> Raw String + pub private_key: String, // StringPtr -> Raw String + pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr + pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr + pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr pub used_scripts: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr - pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr - pub imports: Option<&'a mut Array<'a>>, // ArrayPtr - pub exports: Option<&'a mut Array<'a>>, // ArrayPtr - pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr + pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr + pub imports: Option<&'a mut Array<'a>>, // ArrayPtr + pub exports: Option<&'a mut Array<'a>>, // ArrayPtr + pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr - pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr pub num_imports: u16, pub load_state: i8, pub flags: u8, @@ -228,17 +228,45 @@ pub struct TwoByteString; // Placeholder structs for references used above that aren't defined yet // it wouldn't compile without this, though for now they remain unimplemented... -pub struct Array<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Object<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct AbstractType<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct FunctionType<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Script<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Closure<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Instance<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct WeakArray<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedDataBase<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedData<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct TypedDataView<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct GrowableObjectArray<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct Code<'a> { _marker: std::marker::PhantomData<&'a ()> } -pub struct LoadingUnit<'a> { _marker: std::marker::PhantomData<&'a ()> } +pub struct Array<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Object<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct AbstractType<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct FunctionType<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Script<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Closure<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Instance<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct WeakArray<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedDataBase<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedData<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct TypedDataView<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct GrowableObjectArray<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct Code<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} +pub struct LoadingUnit<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index f109274..18cdf3a 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -1,16 +1,17 @@ -use crate::cluster::{ Cluster }; -use crate::stream::{ Stream }; +use crate::cluster::Cluster; use crate::constants::{self, MAGIC_BYTES}; +use crate::stream::Stream; -enum SnapshotKind // pulled straight out of the C++ def +enum SnapshotKind +// pulled straight out of the C++ def { - Full, - FullCore, - FullJIT, - FullAOT, // Full + AOT code, this is the one we care about, as th - Module, - None, - Invalid + Full, + FullCore, + FullJIT, + FullAOT, // Full + AOT code, this is the one we care about, as th + Module, + None, + Invalid, } impl TryFrom for SnapshotKind { @@ -29,8 +30,7 @@ impl TryFrom for SnapshotKind { } } } -struct DataSnapshot -{ +struct DataSnapshot { // this array will contain mutable references to all clusters, and it will be indexed using the class id clusters: [Box; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id @@ -53,12 +53,10 @@ struct DataSnapshot } impl DataSnapshot { - fn parse_header(&mut self, stream: &mut Stream) - { + fn parse_header(&mut self, stream: &mut Stream) { self.magic_bytes = stream.read_u32(); - if self.magic_bytes != MAGIC_BYTES - { + if self.magic_bytes != MAGIC_BYTES { panic!("Not a snapshot...") } @@ -68,17 +66,14 @@ impl DataSnapshot { self.parse_version_and_features(stream); } - fn parse_version_and_features(&mut self, stream: &mut Stream) - { + fn parse_version_and_features(&mut self, stream: &mut Stream) { let mut version_and_features = stream.read_c_string(); self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) self.version_hash = version_and_features; } - fn read_clusters(&mut self, stream: &Stream) - { + fn read_clusters(&mut self, stream: &Stream) { let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 - } -} \ No newline at end of file +} diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index 2a9c45d..a0c52e4 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -1,40 +1,42 @@ -use crate::constants::{UNSIGNED_END_OF_DATA_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_MAX_DATA_PER_BYTE, DATA_BITS_PER_BYTE}; -pub struct Stream<'a> -{ +use crate::constants::{ + DATA_BITS_PER_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_END_OF_DATA_BYTE, + UNSIGNED_MAX_DATA_PER_BYTE, +}; +pub struct Stream<'a> { byte_stream: &'a [u8], curr_stream_offset: usize, } -impl<'a> Stream<'a> -{ +impl<'a> Stream<'a> { fn seek(&mut self, pos: usize) // might be useful? { - if self.byte_stream.len() > pos && pos > 0 { self.curr_stream_offset = pos; } + if self.byte_stream.len() > pos && pos > 0 { + self.curr_stream_offset = pos; + } } - pub fn advance_pos(&mut self, num_bytes: usize) - { + pub fn advance_pos(&mut self, num_bytes: usize) { self.curr_stream_offset += num_bytes; } - pub fn get_current_pos(&self) -> usize - { + pub fn get_current_pos(&self) -> usize { self.curr_stream_offset } /* - Reads a modified uleb from the current stream offset. + Reads a modified uleb from the current stream offset. - Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order - to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's - implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte - has its MSb set. - */ + Dart uses a modified LEB128 format. The normal format uses bytes with their MSb set in order + to signify that there are more bytes ahead, and the last byte has its MSb unset, whereas Dart's + implementation does the opposite. The "continuation" bit on each byte is 0, and the last byte + has its MSb set. + */ pub fn read_modified_leb128(&mut self, sign_marker: u8) -> u64 // 8 bytes should be enough for anything... { let mut idx: u8 = 0; - let first_byte = self.byte_stream[self.curr_stream_offset]; - if first_byte > UNSIGNED_MAX_DATA_PER_BYTE // if the first byte has its MSb set + let first_byte = self.byte_stream[self.curr_stream_offset]; + if first_byte > UNSIGNED_MAX_DATA_PER_BYTE + // if the first byte has its MSb set { self.advance_pos(1); // wrapping_sub mimics C++ unsigned underflow, giving us perfect sign-extension @@ -47,7 +49,9 @@ impl<'a> Stream<'a> loop { byte = self.byte_stream[self.curr_stream_offset + idx as usize]; - if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { break; } // final byte + if byte & UNSIGNED_END_OF_DATA_BYTE == UNSIGNED_END_OF_DATA_BYTE { + break; + } // final byte read_num |= (byte as u64) << (idx as usize * DATA_BITS_PER_BYTE); idx += 1; } @@ -61,28 +65,24 @@ impl<'a> Stream<'a> read_num } - pub fn read_u64(&mut self) -> u64 - { + pub fn read_u64(&mut self) -> u64 { let u64_size = std::mem::size_of::(); - let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; + let num_slice = + &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u64_size]; - let converted_slice: [u8; 8] = num_slice - .try_into() - .expect("Slice wasn't 8 bytes long..."); + let converted_slice: [u8; 8] = num_slice.try_into().expect("Slice wasn't 8 bytes long..."); self.advance_pos(u64_size); u64::from_le_bytes(converted_slice) } - pub fn read_u32(&mut self) -> u32 - { + pub fn read_u32(&mut self) -> u32 { let u32_size = std::mem::size_of::(); - let num_slice = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; + let num_slice = + &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + u32_size]; - let converted_slice: [u8; 4] = num_slice - .try_into() - .expect("Slice wasn't 4 bytes long..."); + let converted_slice: [u8; 4] = num_slice.try_into().expect("Slice wasn't 4 bytes long..."); self.advance_pos(u32_size); @@ -90,22 +90,22 @@ impl<'a> Stream<'a> } /* - Panics if it isn't possible to create a stream from the utf-8 representation stored in - the byte slice. It shouldn't happen, so the best possible outcome is to assume some - logic mistake has been made and end the application. It should be a good thing to change this to an - unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order - to have static debug info. - */ - pub fn read_c_string(&mut self) -> String - { - let first_nullbyte_pos = self.byte_stream - .iter() - .position(|&b| b == 0x00) - .unwrap_or(self.byte_stream.len()); + Panics if it isn't possible to create a stream from the utf-8 representation stored in + the byte slice. It shouldn't happen, so the best possible outcome is to assume some + logic mistake has been made and end the application. It should be a good thing to change this to an + unwrap_or_else so that we can also print the stream offset and cluster where this error occurred, in order + to have static debug info. + */ + pub fn read_c_string(&mut self) -> String { + let first_nullbyte_pos = self + .byte_stream + .iter() + .position(|&b| b == 0x00) + .unwrap_or(self.byte_stream.len()); let raw_str = &self.byte_stream[..=first_nullbyte_pos]; self.advance_pos(raw_str.len()); String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there } -} \ No newline at end of file +} diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 049d66f..4f7eb30 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,11 +1,10 @@ -use paste::paste; use crate::cluster::Cluster; -use crate::stream::Stream; use crate::constants::UNSIGNED_M; +use crate::stream::Stream; +use paste::paste; #[macro_export] -macro_rules! DECLARE_FIXED_LENGTH_CLUSTER -{ +macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { ($name:ident, $fill_impl:block) => { ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation struct [<$name Cluster>] @@ -22,7 +21,7 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) } - impl Cluster for [<$name Cluster>] + impl Cluster for [<$name Cluster>] { fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize // read tags and count { @@ -51,4 +50,4 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER } } }; -} \ No newline at end of file +} From 3436a5105653be31a6a8977ab8fb58f2a196df70 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 15:40:02 -0500 Subject: [PATCH 17/22] fixed read_c_string --- crates/flutterdec-serwalker/src/cluster/mod.rs | 2 +- crates/flutterdec-serwalker/src/stream.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index c1559ca..2003d67 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -20,7 +20,7 @@ pub fn read_cluster_fill() { } pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { - let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed + let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed (raw_smi as Smi) >> constants::SMI_SHIFT } diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index a0c52e4..db024a5 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -10,7 +10,7 @@ pub struct Stream<'a> { impl<'a> Stream<'a> { fn seek(&mut self, pos: usize) // might be useful? { - if self.byte_stream.len() > pos && pos > 0 { + if self.byte_stream.len() > pos && pos >= 0 { self.curr_stream_offset = pos; } } @@ -97,14 +97,15 @@ impl<'a> Stream<'a> { to have static debug info. */ pub fn read_c_string(&mut self) -> String { - let first_nullbyte_pos = self - .byte_stream + + let first_nullbyte_pos = + self.byte_stream[self.curr_stream_offset..] .iter() .position(|&b| b == 0x00) - .unwrap_or(self.byte_stream.len()); + .expect("Reading a string until the end of the stream? Something definitely went wrong..."); - let raw_str = &self.byte_stream[..=first_nullbyte_pos]; - self.advance_pos(raw_str.len()); + let raw_str = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + first_nullbyte_pos]; + self.advance_pos(raw_str.len() + 1); String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there } From d84b948181cf93027bb93967b50e8dbf5ec7021f Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 27 Apr 2026 15:44:07 -0500 Subject: [PATCH 18/22] fixed read_and_decompress_smi --- crates/flutterdec-serwalker/src/cluster/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index c5319d3..2003d67 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -20,11 +20,7 @@ pub fn read_cluster_fill() { } pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { -<<<<<<< HEAD let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed -======= - let raw_smi = stream.read_modified_leb128(UNSIGNED_M); // smis are always written as signed ->>>>>>> b36733ae4d7105ce3e276c11bb3a44f57b3cccff (raw_smi as Smi) >> constants::SMI_SHIFT } From 399e3324938993aadd625cefca29469bb1611513 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Wed, 29 Apr 2026 23:31:34 -0500 Subject: [PATCH 19/22] feature(serwalker): added implementations to read non null-terminated strings and ref ids, fixed minor bugs --- .../flutterdec-serwalker/src/cluster/mod.rs | 6 +- .../src/raw_object/mod.rs | 25 ++++---- crates/flutterdec-serwalker/src/snapshot.rs | 59 ++++++++++++++++--- crates/flutterdec-serwalker/src/stream.rs | 51 +++++++++++++++- 4 files changed, 112 insertions(+), 29 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index 2003d67..2781c22 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -22,7 +22,7 @@ pub fn read_cluster_fill() { pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed - (raw_smi as Smi) >> constants::SMI_SHIFT + raw_smi as Smi } pub fn decide_cluster( @@ -39,13 +39,11 @@ pub fn decide_cluster( // whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects // and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) -DECLARE_FIXED_LENGTH_CLUSTER!(OneByteString, { +DECLARE_FIXED_LENGTH_CLUSTER!(_String, { 1 // to-do }); /* -DECLARE_FIXED_LENGTH_CLUSTER!(TwoByteString, 8); -DECLARE_FIXED_LENGTH_CLUSTER!(String, 8); DECLARE_FIXED_LENGTH_CLUSTER!(Mint, 16); DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 3692170..e26c820 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -67,7 +67,7 @@ pub struct FfiTrampolineData<'a> { #[derive(Default)] pub struct Field<'a> { - pub name: String, // StringPtr -> Raw String + pub name: _String, // _StringPtr -> Raw _String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -123,16 +123,17 @@ pub struct UnlinkedCall { } #[derive(Default)] -pub struct String_ { - // added underscore so there's no conflict between this type and rust's String +pub struct _String { + // added underscore so there's no conflict between this type and rust's _String pub hash: Smi, // Smi pub length: Smi, // Smi + pub inner_string: String } #[derive(Default)] pub struct Class<'a> { - pub name: String, // StringPtr -> Raw String - pub user_name: String, // StringPtr -> Raw String + pub name: _String, // _StringPtr -> Raw _String + pub user_name: _String, // _StringPtr -> Raw _String pub functions: Option<&'a mut Array<'a>>, // ArrayPtr pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr pub fields: Option<&'a mut Array<'a>>, // ArrayPtr @@ -164,7 +165,7 @@ pub struct Class<'a> { #[derive(Default)] pub struct Function<'a> { - pub name: String, // StringPtr -> Raw String + pub name: _String, // _StringPtr -> Raw _String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr pub data: Option<&'a mut Object<'a>>, // ObjectPtr @@ -179,9 +180,9 @@ pub struct Function<'a> { #[derive(Default)] pub struct Library<'a> { - pub name: String, // StringPtr -> Raw String - pub url: String, // StringPtr -> Raw String - pub private_key: String, // StringPtr -> Raw String + pub name: _String, // _StringPtr -> Raw _String + pub url: _String, // _StringPtr -> Raw _String + pub private_key: _String, // _StringPtr -> Raw _String pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr @@ -220,12 +221,6 @@ pub struct PcDescriptors; #[derive(Default)] pub struct ObjectPool; -#[derive(Default)] -pub struct OneByteString; - -#[derive(Default)] -pub struct TwoByteString; - // Placeholder structs for references used above that aren't defined yet // it wouldn't compile without this, though for now they remain unimplemented... pub struct Array<'a> { diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index 18cdf3a..61c28fb 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -1,7 +1,8 @@ use crate::cluster::Cluster; -use crate::constants::{self, MAGIC_BYTES}; +use crate::constants::{self, MAGIC_BYTES, UNSIGNED_M}; use crate::stream::Stream; +#[derive(Default)] enum SnapshotKind // pulled straight out of the C++ def { @@ -10,6 +11,7 @@ enum SnapshotKind FullJIT, FullAOT, // Full + AOT code, this is the one we care about, as th Module, + #[default] None, Invalid, } @@ -30,9 +32,10 @@ impl TryFrom for SnapshotKind { } } } + struct DataSnapshot { // this array will contain mutable references to all clusters, and it will be indexed using the class id - clusters: [Box; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id + clusters: [Option>; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id magic_bytes: u32, size: u64, @@ -45,14 +48,43 @@ struct DataSnapshot { num_objects: u64, num_clusters: u64, - instr_table_len: u64, + instr_table_len: usize, instr_table_offset: usize, start_of_alloc_area: usize, start_of_fill_area: usize, } +impl Default for DataSnapshot { + fn default() -> Self { + const INIT_CLUSTER: Option> = None; + Self { + clusters: [INIT_CLUSTER; constants::MAX_CLUSTER_NUM], + magic_bytes: 0, + size: 0, + kind: SnapshotKind::default(), + version_hash: String::new(), + features: String::new(), + num_base_objects: 0, + num_objects: 0, + num_clusters: 0, + instr_table_len: 0, + instr_table_offset: 0, + start_of_alloc_area: 0, + start_of_fill_area: 0, + } + } +} + impl DataSnapshot { + + fn parse_version_and_features(&mut self, stream: &mut Stream) { + let mut version_and_features = stream.read_c_string(); + + self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) + self.version_hash = version_and_features; + } + fn parse_header(&mut self, stream: &mut Stream) { self.magic_bytes = stream.read_u32(); @@ -64,16 +96,25 @@ impl DataSnapshot { self.kind = SnapshotKind::try_from(stream.read_u64()).expect("Not a valid snapshot!"); self.parse_version_and_features(stream); - } - fn parse_version_and_features(&mut self, stream: &mut Stream) { - let mut version_and_features = stream.read_c_string(); + self.num_base_objects = stream.read_modified_leb128(UNSIGNED_M); + self.num_objects = stream.read_modified_leb128(UNSIGNED_M); + self.num_clusters = stream.read_modified_leb128(UNSIGNED_M); - self.features = version_and_features.split_off(constants::VERSION_HASH_LENGTH); // returns (str[hash_len..]) - self.version_hash = version_and_features; + self.instr_table_len = stream.read_modified_leb128(UNSIGNED_M) as usize; + self.instr_table_offset = stream.read_modified_leb128(UNSIGNED_M) as usize; + + self.start_of_alloc_area = stream.get_current_pos(); } - fn read_clusters(&mut self, stream: &Stream) { + fn read_clusters(&mut self, stream: &mut Stream) { let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 } + + pub fn parse_snapshot(&mut self, stream: &mut Stream) + { + println!("Now parsing the snapshot..."); + self.parse_header(stream); + } + } diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index db024a5..bda9215 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -107,6 +107,55 @@ impl<'a> Stream<'a> { let raw_str = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + first_nullbyte_pos]; self.advance_pos(raw_str.len() + 1); - String::from_utf8(raw_str.to_vec()).unwrap() // it should be horrible if for some reason a string just isn't there + String::from_utf8(raw_str.to_vec()).expect("Couldn't turn null-terminated UTF-8 bytes into a String.") // it should be horrible if for some reason a string just isn't there + } + + // read a non null-terminated string given a length + pub fn read_string(&mut self, len: usize) -> String + { + let final_pos = self.curr_stream_offset + len; + let raw_str = &self.byte_stream[self.curr_stream_offset..final_pos]; + + self.advance_pos(len); + + String::from_utf8(raw_str.to_vec()).expect("Couldn't turn UTF-8 bytes into a String.") + } + + /* + Complex object types (i.e, object types that contain other object types) + point to other objects through refids, which is essentially the core + mechanism of Dart's serialization/deserialization process, allowing the + reconstruction of all objects from the snapshot into the heap. + */ + pub fn read_ref_id(&mut self) -> u32 + { + let mut idx: usize = 0; + let mut byte: i8 = self.byte_stream[self.curr_stream_offset + idx] as i8; + let mut ref_id: i32 = 0; // as far as I know, ref_ids are up to 2^28, so 32 bits is good enough + + if byte < 0 + { + ref_id += byte as i32; + self.advance_pos(1); + return (ref_id + 128) as u32; + } + + loop + { + ref_id = ref_id << 7; + ref_id += byte as i32; + idx += 1; + + if byte < 0 + { + break + } + + byte = self.byte_stream[self.curr_stream_offset + idx] as i8; + } + + self.advance_pos(idx); + + (ref_id + 128) as u32 } } From a7623275a8c76613e6442d7e173344bdecdce51c Mon Sep 17 00:00:00 2001 From: kalixtez Date: Wed, 29 Apr 2026 23:38:32 -0500 Subject: [PATCH 20/22] refactor(serwalker): ran cargo fmt --all --- .../src/raw_object/mod.rs | 42 +++++++++---------- crates/flutterdec-serwalker/src/snapshot.rs | 7 +--- crates/flutterdec-serwalker/src/stream.rs | 35 +++++++--------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index e26c820..655e79e 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -67,7 +67,7 @@ pub struct FfiTrampolineData<'a> { #[derive(Default)] pub struct Field<'a> { - pub name: _String, // _StringPtr -> Raw _String + pub name: _String, // StringPtr -> Raw String pub owner: Option<&'a mut Object<'a>>, // ObjectPtr pub type_field: Option<&'a mut AbstractType<'a>>, // AbstractTypePtr pub initializer_function: Option<&'a mut Function<'a>>, // FunctionPtr @@ -127,13 +127,13 @@ pub struct _String { // added underscore so there's no conflict between this type and rust's _String pub hash: Smi, // Smi pub length: Smi, // Smi - pub inner_string: String + pub inner_string: String, } #[derive(Default)] pub struct Class<'a> { - pub name: _String, // _StringPtr -> Raw _String - pub user_name: _String, // _StringPtr -> Raw _String + pub name: _String, // StringPtr -> Raw String + pub user_name: _String, // StringPtr -> Raw String pub functions: Option<&'a mut Array<'a>>, // ArrayPtr pub functions_hash_table: Option<&'a mut Array<'a>>, // ArrayPtr pub fields: Option<&'a mut Array<'a>>, // ArrayPtr @@ -165,14 +165,14 @@ pub struct Class<'a> { #[derive(Default)] pub struct Function<'a> { - pub name: _String, // _StringPtr -> Raw _String - pub owner: Option<&'a mut Object<'a>>, // ObjectPtr - pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr - pub data: Option<&'a mut Object<'a>>, // ObjectPtr + pub name: _String, // StringPtr -> Raw String + pub owner: Option<&'a mut Object<'a>>, // ObjectPtr + pub signature: Option<&'a mut FunctionType<'a>>, // FunctionTypePtr + pub data: Option<&'a mut Object<'a>>, // ObjectPtr pub ic_data_array_or_bytecode: Option<&'a mut Object<'a>>, // ObjectPtr - pub code: Option<&'a mut Code<'a>>, // CodePtr + pub code: Option<&'a mut Code<'a>>, // CodePtr pub positional_parameter_names: Option<&'a mut Array<'a>>, // ArrayPtr - pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr + pub unoptimized_code: Option<&'a mut Code<'a>>, // CodePtr pub bitmap: u64, pub kernel_offset: i32, pub kind_tag: u32, @@ -180,19 +180,19 @@ pub struct Function<'a> { #[derive(Default)] pub struct Library<'a> { - pub name: _String, // _StringPtr -> Raw _String - pub url: _String, // _StringPtr -> Raw _String - pub private_key: _String, // _StringPtr -> Raw _String - pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr - pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr - pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr + pub name: _String, // StringPtr -> Raw String + pub url: _String, // StringPtr -> Raw String + pub private_key: _String, // StringPtr -> Raw String + pub dictionary: Option<&'a mut Array<'a>>, // ArrayPtr + pub metadata: Option<&'a mut Array<'a>>, // ArrayPtr + pub toplevel_class: Option<&'a mut Class<'a>>, // ClassPtr pub used_scripts: Option<&'a mut GrowableObjectArray<'a>>, // GrowableObjectArrayPtr - pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr - pub imports: Option<&'a mut Array<'a>>, // ArrayPtr - pub exports: Option<&'a mut Array<'a>>, // ArrayPtr - pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr + pub loading_unit: Option<&'a mut LoadingUnit<'a>>, // LoadingUnitPtr + pub imports: Option<&'a mut Array<'a>>, // ArrayPtr + pub exports: Option<&'a mut Array<'a>>, // ArrayPtr + pub dependencies: Option<&'a mut Array<'a>>, // ArrayPtr pub kernel_program_info: Option<&'a mut KernelProgramInfo<'a>>, // KernelProgramInfoPtr - pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr + pub loaded_scripts: Option<&'a mut Array<'a>>, // ArrayPtr pub num_imports: u16, pub load_state: i8, pub flags: u8, diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index 61c28fb..b7d7cb5 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -77,7 +77,6 @@ impl Default for DataSnapshot { } impl DataSnapshot { - fn parse_version_and_features(&mut self, stream: &mut Stream) { let mut version_and_features = stream.read_c_string(); @@ -107,14 +106,12 @@ impl DataSnapshot { self.start_of_alloc_area = stream.get_current_pos(); } - fn read_clusters(&mut self, stream: &mut Stream) { + fn parse_clusters(&mut self, stream: &mut Stream) { let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 } - pub fn parse_snapshot(&mut self, stream: &mut Stream) - { + pub fn parse_snapshot(&mut self, stream: &mut Stream) { println!("Now parsing the snapshot..."); self.parse_header(stream); } - } diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index bda9215..06c2aed 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -97,22 +97,23 @@ impl<'a> Stream<'a> { to have static debug info. */ pub fn read_c_string(&mut self) -> String { - - let first_nullbyte_pos = - self.byte_stream[self.curr_stream_offset..] + let first_nullbyte_pos = self.byte_stream[self.curr_stream_offset..] .iter() .position(|&b| b == 0x00) - .expect("Reading a string until the end of the stream? Something definitely went wrong..."); + .expect( + "Reading a string until the end of the stream? Something definitely went wrong...", + ); - let raw_str = &self.byte_stream[self.curr_stream_offset..self.curr_stream_offset + first_nullbyte_pos]; + let raw_str = &self.byte_stream + [self.curr_stream_offset..self.curr_stream_offset + first_nullbyte_pos]; self.advance_pos(raw_str.len() + 1); - String::from_utf8(raw_str.to_vec()).expect("Couldn't turn null-terminated UTF-8 bytes into a String.") // it should be horrible if for some reason a string just isn't there + String::from_utf8(raw_str.to_vec()) + .expect("Couldn't turn null-terminated UTF-8 bytes into a String.") // it should be horrible if for some reason a string just isn't there } // read a non null-terminated string given a length - pub fn read_string(&mut self, len: usize) -> String - { + pub fn read_string(&mut self, len: usize) -> String { let final_pos = self.curr_stream_offset + len; let raw_str = &self.byte_stream[self.curr_stream_offset..final_pos]; @@ -121,34 +122,30 @@ impl<'a> Stream<'a> { String::from_utf8(raw_str.to_vec()).expect("Couldn't turn UTF-8 bytes into a String.") } - /* + /* Complex object types (i.e, object types that contain other object types) point to other objects through refids, which is essentially the core mechanism of Dart's serialization/deserialization process, allowing the reconstruction of all objects from the snapshot into the heap. */ - pub fn read_ref_id(&mut self) -> u32 - { + pub fn read_ref_id(&mut self) -> u32 { let mut idx: usize = 0; let mut byte: i8 = self.byte_stream[self.curr_stream_offset + idx] as i8; let mut ref_id: i32 = 0; // as far as I know, ref_ids are up to 2^28, so 32 bits is good enough - if byte < 0 - { + if byte < 0 { ref_id += byte as i32; self.advance_pos(1); return (ref_id + 128) as u32; } - - loop - { + + loop { ref_id = ref_id << 7; ref_id += byte as i32; idx += 1; - if byte < 0 - { - break + if byte < 0 { + break; } byte = self.byte_stream[self.curr_stream_offset + idx] as i8; From e7faa9bd7e164d059d168c192e6af0325033ba1b Mon Sep 17 00:00:00 2001 From: kalixtez Date: Sat, 2 May 2026 02:36:26 -0500 Subject: [PATCH 21/22] feat(serwalker): implemented tag-decoding macros, declared all fixed-size alloc cluster types, implemented parse_clusters, implemented a macro to declare the ClassId enum and TryFrom for u32 for said enum --- .../flutterdec-serwalker/src/cluster/mod.rs | 179 +++++++- crates/flutterdec-serwalker/src/constants.rs | 400 ++++++++++-------- .../src/raw_object/mod.rs | 2 +- crates/flutterdec-serwalker/src/snapshot.rs | 70 +-- crates/flutterdec-serwalker/src/stream.rs | 7 +- crates/flutterdec-serwalker/src/utils.rs | 61 ++- 6 files changed, 485 insertions(+), 234 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index 2781c22..8bb1927 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -1,7 +1,8 @@ use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; use crate::raw_object::*; +use crate::stream::Stream; use crate::DECLARE_FIXED_LENGTH_CLUSTER; -use crate::{constants, stream::Stream}; +use crate::FFI_TYPES_LIST; type Smi = i32; @@ -12,25 +13,30 @@ pub trait Cluster { } pub fn read_cluster_alloc() { - let curr_ref_id: u64 = 0; + //let curr_ref_id: u64 = 0; } pub fn read_cluster_fill() { - let curr_ref_id: u64 = 0; + //let curr_ref_id: u64 = 0; } -pub fn read_and_decompress_smi(stream: &mut Stream) -> Smi { - let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed +pub fn read_smi(stream: &mut Stream) -> Smi { + let raw_smi = stream.read_modified_leb128(SIGNED_M); // smis are always written as signed numbers raw_smi as Smi } -pub fn decide_cluster( - clusters: &mut [Box; constants::MAX_CLUSTER_NUM], - class_id: ClassId, -) -> Result, &str> { +macro_rules! FFI_CASE_PATTERN { + ( $( $ffi_type:ident ),* ) => { + $( $ffi_type )|* + }; +} + +pub fn decide_cluster(class_id: ClassId) -> Result, &'static str> { match class_id { + // we assume compressed pointers, it supports only Android for now... IllegalCid => Err("Not a supported class (illegal class)..."), + FFI_TYPES_LIST!(FFI_CASE_PATTERN) => Err("To do..."), _ => Err("Not a supported class..."), } } @@ -39,14 +45,153 @@ pub fn decide_cluster( // whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects // and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) -DECLARE_FIXED_LENGTH_CLUSTER!(_String, { +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameters, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(PatchClass, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Function, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(ClosureData, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(FfiTrampolineData, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Field, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(Script, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Library, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(Namespace, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(KernelProgramInfo, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(UnlinkedCall, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(ICData, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(MegamorphicCache, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(SubtypeTestCache, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(LoadingUnit, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(LanguageError, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(UnhandledException, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(LibraryPrefix, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(Type, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(FunctionType, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(RecordType, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(Closure, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Double, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(Int32x4, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(GrowableObjectArray, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(TypedDataView, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(ExternalTypedData, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(StackTrace, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(RegExp, { + 1 + // to-do +}); +DECLARE_FIXED_LENGTH_CLUSTER!(WeakProperty, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Map, { + 1 + // to-do +}); + +DECLARE_FIXED_LENGTH_CLUSTER!(Set, { 1 // to-do }); -/* -DECLARE_FIXED_LENGTH_CLUSTER!(Mint, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(Double, 16); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(Type, 32); -DECLARE_FIXED_LENGTH_CLUSTER!(TypeArguments, 32); -*/ diff --git a/crates/flutterdec-serwalker/src/constants.rs b/crates/flutterdec-serwalker/src/constants.rs index 6a68d72..3cb5430 100644 --- a/crates/flutterdec-serwalker/src/constants.rs +++ b/crates/flutterdec-serwalker/src/constants.rs @@ -26,183 +26,229 @@ pub const SMI_SHIFT: usize = 1usize; pub const VERSION_HASH_LENGTH: usize = 32usize; -pub enum ClassId { - IllegalCid = 0, - NativePointer, - FreeListElement, - ForwardingCorpse, - ObjectCid, - ClassCid, - PatchClassCid, - FunctionCid, - TypeParametersCid, - ClosureDataCid, - FfiTrampolineDataCid, - FieldCid, - ScriptCid, - LibraryCid, - NamespaceCid, - KernelProgramInfoCid, - WeakSerializationReferenceCid, - WeakArrayCid, - CodeCid, - BytecodeCid, - InstructionsCid, - InstructionsSectionCid, - InstructionsTableCid, - ObjectPoolCid, - PcDescriptorsCid, - CodeSourceMapCid, - CompressedStackMapsCid, - LocalVarDescriptorsCid, - ExceptionHandlersCid, - ContextCid, - ContextScopeCid, - SentinelCid, - SingleTargetCacheCid, - MonomorphicSmiableCallCid, - CallSiteDataCid, - UnlinkedCallCid, - ICDataCid, - MegamorphicCacheCid, - SubtypeTestCacheCid, - LoadingUnitCid, - ErrorCid, - ApiErrorCid, - LanguageErrorCid, - UnhandledExceptionCid, - UnwindErrorCid, - InstanceCid, - LibraryPrefixCid, - TypeArgumentsCid, - AbstractTypeCid, - TypeCid, - FunctionTypeCid, - RecordTypeCid, - TypeParameterCid, - FinalizerBaseCid, - FinalizerCid, - NativeFinalizerCid, - FinalizerEntryCid, - ClosureCid, - NumberCid, - IntegerCid, - SmiCid, - MintCid, - DoubleCid, - BoolCid, - Float32x4Cid, - Int32x4Cid, - Float64x2Cid, - RecordCid, - TypedDataBaseCid, - TypedDataCid, - ExternalTypedDataCid, - TypedDataViewCid, - PointerCid, - DynamicLibraryCid, - CapabilityCid, - ReceivePortCid, - SendPortCid, - StackTraceCid, - SuspendStateCid, - RegExpCid, - WeakPropertyCid, - WeakReferenceCid, - MirrorReferenceCid, - FutureOrCid, - UserTagCid, - TransferableTypedDataCid, - MapCid, - ConstMapCid, - SetCid, - ConstSetCid, - ArrayCid, - ImmutableArrayCid, - GrowableObjectArrayCid, - StringCid, - OneByteStringCid, - TwoByteStringCid, - FfiNativeFunctionCid, - FfiInt8Cid, - FfiInt16Cid, - FfiInt32Cid, - FfiInt64Cid, - FfiUint8Cid, - FfiUint16Cid, - FfiUint32Cid, - FfiUint64Cid, - FfiFloatCid, - FfiDoubleCid, - FfiVoidCid, - FfiHandleCid, - FfiBoolCid, - FfiNativeTypeCid, - FfiStructCid, - TypedDataInt8ArrayCid, - TypedDataInt8ArrayViewCid, - ExternalTypedDataInt8ArrayCid, - UnmodifiableTypedDataInt8ArrayViewCid, - TypedDataUint8ArrayCid, - TypedDataUint8ArrayViewCid, - ExternalTypedDataUint8ArrayCid, - UnmodifiableTypedDataUint8ArrayViewCid, - TypedDataUint8ClampedArrayCid, - TypedDataUint8ClampedArrayViewCid, - ExternalTypedDataUint8ClampedArrayCid, - UnmodifiableTypedDataUint8ClampedArrayViewCid, - TypedDataInt16ArrayCid, - TypedDataInt16ArrayViewCid, - ExternalTypedDataInt16ArrayCid, - UnmodifiableTypedDataInt16ArrayViewCid, - TypedDataUint16ArrayCid, - TypedDataUint16ArrayViewCid, - ExternalTypedDataUint16ArrayCid, - UnmodifiableTypedDataUint16ArrayViewCid, - TypedDataInt32ArrayCid, - TypedDataInt32ArrayViewCid, - ExternalTypedDataInt32ArrayCid, - UnmodifiableTypedDataInt32ArrayViewCid, - TypedDataUint32ArrayCid, - TypedDataUint32ArrayViewCid, - ExternalTypedDataUint32ArrayCid, - UnmodifiableTypedDataUint32ArrayViewCid, - TypedDataInt64ArrayCid, - TypedDataInt64ArrayViewCid, - ExternalTypedDataInt64ArrayCid, - UnmodifiableTypedDataInt64ArrayViewCid, - TypedDataUint64ArrayCid, - TypedDataUint64ArrayViewCid, - ExternalTypedDataUint64ArrayCid, - UnmodifiableTypedDataUint64ArrayViewCid, - TypedDataFloat32ArrayCid, - TypedDataFloat32ArrayViewCid, - ExternalTypedDataFloat32ArrayCid, - UnmodifiableTypedDataFloat32ArrayViewCid, - TypedDataFloat64ArrayCid, - TypedDataFloat64ArrayViewCid, - ExternalTypedDataFloat64ArrayCid, - UnmodifiableTypedDataFloat64ArrayViewCid, - TypedDataFloat32x4ArrayCid, - TypedDataFloat32x4ArrayViewCid, - ExternalTypedDataFloat32x4ArrayCid, - UnmodifiableTypedDataFloat32x4ArrayViewCid, - TypedDataInt32x4ArrayCid, - TypedDataInt32x4ArrayViewCid, - ExternalTypedDataInt32x4ArrayCid, - UnmodifiableTypedDataInt32x4ArrayViewCid, - TypedDataFloat64x2ArrayCid, - TypedDataFloat64x2ArrayViewCid, - ExternalTypedDataFloat64x2ArrayCid, - UnmodifiableTypedDataFloat64x2ArrayViewCid, - ByteDataViewCid, - UnmodifiableByteDataViewCid, - ByteBufferCid, - NullCid, - DynamicCid, - VoidCid, - NeverCid, - NumPredefinedCids, +macro_rules! DEFINE_CLASS_ID { + ( $( $name:ident = $val:expr ),* ) => { + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[repr(u32)] + pub enum ClassId { + #[default] + IllegalCid = 0, + $( $name = $val, )* + } + + impl TryFrom for ClassId { + type Error = &'static str; + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ClassId::IllegalCid), + $( $val => Ok(ClassId::$name), )* + _ => Err("Invalid ClassId"), + } + } + } + }; +} + +DEFINE_CLASS_ID! { + NativePointer = 1, + FreeListElement = 2, + ForwardingCorpse = 3, + ObjectCid = 4, + ClassCid = 5, + PatchClassCid = 6, + FunctionCid = 7, + TypeParametersCid = 8, + ClosureDataCid = 9, + FfiTrampolineDataCid = 10, + FieldCid = 11, + ScriptCid = 12, + LibraryCid = 13, + NamespaceCid = 14, + KernelProgramInfoCid = 15, + WeakSerializationReferenceCid = 16, + WeakArrayCid = 17, + CodeCid = 18, + BytecodeCid = 19, + InstructionsCid = 20, + InstructionsSectionCid = 21, + InstructionsTableCid = 22, + ObjectPoolCid = 23, + PcDescriptorsCid = 24, + CodeSourceMapCid = 25, + CompressedStackMapsCid = 26, + LocalVarDescriptorsCid = 27, + ExceptionHandlersCid = 28, + ContextCid = 29, + ContextScopeCid = 30, + SentinelCid = 31, + SingleTargetCacheCid = 32, + MonomorphicSmiableCallCid = 33, + CallSiteDataCid = 34, + UnlinkedCallCid = 35, + ICDataCid = 36, + MegamorphicCacheCid = 37, + SubtypeTestCacheCid = 38, + LoadingUnitCid = 39, + ErrorCid = 40, + ApiErrorCid = 41, + LanguageErrorCid = 42, + UnhandledExceptionCid = 43, + UnwindErrorCid = 44, + InstanceCid = 45, + LibraryPrefixCid = 46, + TypeArgumentsCid = 47, + AbstractTypeCid = 48, + TypeCid = 49, + FunctionTypeCid = 50, + RecordTypeCid = 51, + TypeParameterCid = 52, + FinalizerBaseCid = 53, + FinalizerCid = 54, + NativeFinalizerCid = 55, + FinalizerEntryCid = 56, + ClosureCid = 57, + NumberCid = 58, + IntegerCid = 59, + SmiCid = 60, + MintCid = 61, + DoubleCid = 62, + BoolCid = 63, + Float32x4Cid = 64, + Int32x4Cid = 65, + Float64x2Cid = 66, + RecordCid = 67, + TypedDataBaseCid = 68, + TypedDataCid = 69, + ExternalTypedDataCid = 70, + TypedDataViewCid = 71, + PointerCid = 72, + DynamicLibraryCid = 73, + CapabilityCid = 74, + ReceivePortCid = 75, + SendPortCid = 76, + StackTraceCid = 77, + SuspendStateCid = 78, + RegExpCid = 79, + WeakPropertyCid = 80, + WeakReferenceCid = 81, + MirrorReferenceCid = 82, + FutureOrCid = 83, + UserTagCid = 84, + TransferableTypedDataCid = 85, + MapCid = 86, + ConstMapCid = 87, + SetCid = 88, + ConstSetCid = 89, + ArrayCid = 90, + ImmutableArrayCid = 91, + GrowableObjectArrayCid = 92, + _StringCid = 93, + OneByteStringCid = 94, + TwoByteStringCid = 95, + FfiNativeFunctionCid = 96, + FfiInt8Cid = 97, + FfiInt16Cid = 98, + FfiInt32Cid = 99, + FfiInt64Cid = 100, + FfiUint8Cid = 101, + FfiUint16Cid = 102, + FfiUint32Cid = 103, + FfiUint64Cid = 104, + FfiFloatCid = 105, + FfiDoubleCid = 106, + FfiVoidCid = 107, + FfiHandleCid = 108, + FfiBoolCid = 109, + FfiNativeTypeCid = 110, + FfiStructCid = 111, + TypedDataInt8ArrayCid = 112, + TypedDataInt8ArrayViewCid = 113, + ExternalTypedDataInt8ArrayCid = 114, + UnmodifiableTypedDataInt8ArrayViewCid = 115, + TypedDataUint8ArrayCid = 116, + TypedDataUint8ArrayViewCid = 117, + ExternalTypedDataUint8ArrayCid = 118, + UnmodifiableTypedDataUint8ArrayViewCid = 119, + TypedDataUint8ClampedArrayCid = 120, + TypedDataUint8ClampedArrayViewCid = 121, + ExternalTypedDataUint8ClampedArrayCid = 122, + UnmodifiableTypedDataUint8ClampedArrayViewCid = 123, + TypedDataInt16ArrayCid = 124, + TypedDataInt16ArrayViewCid = 125, + ExternalTypedDataInt16ArrayCid = 126, + UnmodifiableTypedDataInt16ArrayViewCid = 127, + TypedDataUint16ArrayCid = 128, + TypedDataUint16ArrayViewCid = 129, + ExternalTypedDataUint16ArrayCid = 130, + UnmodifiableTypedDataUint16ArrayViewCid = 131, + TypedDataInt32ArrayCid = 132, + TypedDataInt32ArrayViewCid = 133, + ExternalTypedDataInt32ArrayCid = 134, + UnmodifiableTypedDataInt32ArrayViewCid = 135, + TypedDataUint32ArrayCid = 136, + TypedDataUint32ArrayViewCid = 137, + ExternalTypedDataUint32ArrayCid = 138, + UnmodifiableTypedDataUint32ArrayViewCid = 139, + TypedDataInt64ArrayCid = 140, + TypedDataInt64ArrayViewCid = 141, + ExternalTypedDataInt64ArrayCid = 142, + UnmodifiableTypedDataInt64ArrayViewCid = 143, + TypedDataUint64ArrayCid = 144, + TypedDataUint64ArrayViewCid = 145, + ExternalTypedDataUint64ArrayCid = 146, + UnmodifiableTypedDataUint64ArrayViewCid = 147, + TypedDataFloat32ArrayCid = 148, + TypedDataFloat32ArrayViewCid = 149, + ExternalTypedDataFloat32ArrayCid = 150, + UnmodifiableTypedDataFloat32ArrayViewCid = 151, + TypedDataFloat64ArrayCid = 152, + TypedDataFloat64ArrayViewCid = 153, + ExternalTypedDataFloat64ArrayCid = 154, + UnmodifiableTypedDataFloat64ArrayViewCid = 155, + TypedDataFloat32x4ArrayCid = 156, + TypedDataFloat32x4ArrayViewCid = 157, + ExternalTypedDataFloat32x4ArrayCid = 158, + UnmodifiableTypedDataFloat32x4ArrayViewCid = 159, + TypedDataInt32x4ArrayCid = 160, + TypedDataInt32x4ArrayViewCid = 161, + ExternalTypedDataInt32x4ArrayCid = 162, + UnmodifiableTypedDataInt32x4ArrayViewCid = 163, + TypedDataFloat64x2ArrayCid = 164, + TypedDataFloat64x2ArrayViewCid = 165, + ExternalTypedDataFloat64x2ArrayCid = 166, + UnmodifiableTypedDataFloat64x2ArrayViewCid = 167, + ByteDataViewCid = 168, + UnmodifiableByteDataViewCid = 169, + ByteBufferCid = 170, + NullCid = 171, + DynamicCid = 172, + VoidCid = 173, + NeverCid = 174, + NumPredefinedCids = 175 +} + +#[macro_export] +macro_rules! FFI_TYPES_LIST { + ($callback:ident) => { + $callback! { + FfiNativeFunctionCid, + FfiInt8Cid, + FfiInt16Cid, + FfiInt32Cid, + FfiInt64Cid, + FfiUint8Cid, + FfiUint16Cid, + FfiUint32Cid, + FfiUint64Cid, + FfiFloatCid, + FfiDoubleCid, + FfiVoidCid, + FfiHandleCid, + FfiBoolCid, + FfiNativeTypeCid, + FfiStructCid + } + }; } /* diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 655e79e..63ea72a 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -1,4 +1,4 @@ -type Smi = i64; // Using i64 for Smi fields to be decompressed +type Smi = i32; // Using i64 for Smi fields to be decompressed // --- Fixed-Size Objects with defined fields --- diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index b7d7cb5..2374f87 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -1,6 +1,9 @@ -use crate::cluster::Cluster; -use crate::constants::{self, MAGIC_BYTES, UNSIGNED_M}; +use std::collections::HashMap; + +use crate::cluster::{self, decide_cluster, Cluster}; +use crate::constants::{self, ClassId, MAGIC_BYTES, UNSIGNED_M}; use crate::stream::Stream; +use crate::utils::{decode_tags, DecodedTags}; #[derive(Default)] enum SnapshotKind @@ -33,9 +36,11 @@ impl TryFrom for SnapshotKind { } } -struct DataSnapshot { +#[derive(Default)] +pub struct DataSnapshot { // this array will contain mutable references to all clusters, and it will be indexed using the class id - clusters: [Option>; constants::MAX_CLUSTER_NUM], // thus each cluster must have its own UNIQUE class id + clusters: HashMap>, // thus each cluster must have its own UNIQUE class id + cluster_order: Vec, // used in the fill step to know which cluster's read_fill function to call magic_bytes: u32, size: u64, @@ -55,27 +60,6 @@ struct DataSnapshot { start_of_fill_area: usize, } -impl Default for DataSnapshot { - fn default() -> Self { - const INIT_CLUSTER: Option> = None; - Self { - clusters: [INIT_CLUSTER; constants::MAX_CLUSTER_NUM], - magic_bytes: 0, - size: 0, - kind: SnapshotKind::default(), - version_hash: String::new(), - features: String::new(), - num_base_objects: 0, - num_objects: 0, - num_clusters: 0, - instr_table_len: 0, - instr_table_offset: 0, - start_of_alloc_area: 0, - start_of_fill_area: 0, - } - } -} - impl DataSnapshot { fn parse_version_and_features(&mut self, stream: &mut Stream) { let mut version_and_features = stream.read_c_string(); @@ -107,11 +91,37 @@ impl DataSnapshot { } fn parse_clusters(&mut self, stream: &mut Stream) { - let curr_ref_id: u64 = 0; // all objects are numbered starting from 0 - } + let mut curr_ref_id: u64 = 0; // all objects are numbered starting from 0 + + for _cluster_idx in 0..self.num_clusters { + let tags: u32 = stream.read_modified_leb128(UNSIGNED_M) as u32; + let decoded_tags: DecodedTags = decode_tags(tags); + let cid = decoded_tags.get_cid(); + + let mut cluster = + decide_cluster(cid).expect("Couldn't find cluster implementation for class {cid}"); - pub fn parse_snapshot(&mut self, stream: &mut Stream) { - println!("Now parsing the snapshot..."); - self.parse_header(stream); + cluster.read_alloc(&mut curr_ref_id, stream); + self.clusters.insert(cid, cluster); // hashmap takes ownership of box + self.cluster_order.push(cid); + } + + for cluster_idx in 0..self.num_clusters { + let cid = self.cluster_order[cluster_idx as usize]; + let cluster_wrapper = self.clusters.get_mut(&cid); + + let cluster = cluster_wrapper.unwrap(); // this should never panic + (*cluster).read_fill(&mut curr_ref_id, stream); + } } } + +pub fn parse_snapshot(stream: &mut Stream) -> DataSnapshot { + let mut snapshot = DataSnapshot::default(); + + println!("Now parsing the snapshot..."); + snapshot.parse_header(stream); + snapshot.parse_clusters(stream); + + snapshot +} diff --git a/crates/flutterdec-serwalker/src/stream.rs b/crates/flutterdec-serwalker/src/stream.rs index 06c2aed..35681ac 100644 --- a/crates/flutterdec-serwalker/src/stream.rs +++ b/crates/flutterdec-serwalker/src/stream.rs @@ -1,7 +1,4 @@ -use crate::constants::{ - DATA_BITS_PER_BYTE, SIGNED_END_OF_DATA_BYTE, UNSIGNED_END_OF_DATA_BYTE, - UNSIGNED_MAX_DATA_PER_BYTE, -}; +use crate::constants::{DATA_BITS_PER_BYTE, UNSIGNED_END_OF_DATA_BYTE, UNSIGNED_MAX_DATA_PER_BYTE}; pub struct Stream<'a> { byte_stream: &'a [u8], curr_stream_offset: usize, @@ -153,6 +150,6 @@ impl<'a> Stream<'a> { self.advance_pos(idx); - (ref_id + 128) as u32 + (ref_id + 128) as u32 // ref_ids are always unsigned } } diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index 4f7eb30..ee6f0c7 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -1,13 +1,12 @@ -use crate::cluster::Cluster; -use crate::constants::UNSIGNED_M; -use crate::stream::Stream; use paste::paste; +use crate::constants::ClassId; + #[macro_export] macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { ($name:ident, $fill_impl:block) => { ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation - struct [<$name Cluster>] + pub struct [<$name Cluster>] { tags: u32, obj_count: u64, @@ -18,6 +17,8 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { end_of_fill: usize, end_of_alloc: usize, + first_ref_id: u32, + objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) } @@ -51,3 +52,55 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { } }; } + +pub struct DecodedTags { + class_id: ClassId, + is_deeply_immutable: bool, // we don't really care about this for now, at least + is_canonical: bool, +} + +impl DecodedTags { + pub fn new(cid: ClassId, immut: bool, canonical: bool) -> Self { + Self { + class_id: cid, + is_deeply_immutable: immut, + is_canonical: canonical, + } + } + + pub fn get_cid(&self) -> ClassId { + self.class_id + } + + pub fn is_deeply_immutable(&self) -> bool { + self.is_deeply_immutable + } + + pub fn is_canonical(&self) -> bool { + self.is_canonical + } +} + +macro_rules! DECODE_IS_CID { + ($tags:expr) => { + ClassId::try_from(($tags >> 12) & 0xFFFFF) + }; +} +macro_rules! DECODE_IS_DEEPLY_IMMUTABLE { + ($tags:expr) => { + (($tags >> 7) & 0x1) == 1 + }; +} +macro_rules! DECODE_IS_CANONICAL { + ($tags:expr) => { + (($tags >> 1) & 0x1) == 1 + }; +} + +pub fn decode_tags(tags: u32) -> DecodedTags { + let class_id: ClassId = DECODE_IS_CID!(tags).unwrap(); + let is_deeply_immutable: bool = DECODE_IS_DEEPLY_IMMUTABLE!(tags); + let is_canonical: bool = DECODE_IS_CANONICAL!(tags); + + DecodedTags::new(class_id, is_deeply_immutable, is_canonical) +} From 5db964118d39012ce3a40f7bb4f1f17c729c4190 Mon Sep 17 00:00:00 2001 From: kalixtez Date: Mon, 4 May 2026 01:41:40 -0500 Subject: [PATCH 22/22] feat(serwalker): added forward declarations for the remaining clusters and serializable types --- .../flutterdec-serwalker/src/cluster/mod.rs | 219 +++++------------- .../src/raw_object/mod.rs | 59 +++++ crates/flutterdec-serwalker/src/snapshot.rs | 17 +- crates/flutterdec-serwalker/src/utils.rs | 59 ++++- 4 files changed, 178 insertions(+), 176 deletions(-) diff --git a/crates/flutterdec-serwalker/src/cluster/mod.rs b/crates/flutterdec-serwalker/src/cluster/mod.rs index 8bb1927..c856999 100644 --- a/crates/flutterdec-serwalker/src/cluster/mod.rs +++ b/crates/flutterdec-serwalker/src/cluster/mod.rs @@ -2,6 +2,7 @@ use crate::constants::{ClassId, ClassId::*, SIGNED_M, UNSIGNED_M}; use crate::raw_object::*; use crate::stream::Stream; use crate::DECLARE_FIXED_LENGTH_CLUSTER; +use crate::DECLARE_VARIABLE_LENGTH_CLUSTER; use crate::FFI_TYPES_LIST; type Smi = i32; @@ -9,15 +10,7 @@ type Smi = i32; pub trait Cluster { fn is_fixed_len(&self) -> bool; fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; - fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize; -} - -pub fn read_cluster_alloc() { - //let curr_ref_id: u64 = 0; -} - -pub fn read_cluster_fill() { - //let curr_ref_id: u64 = 0; + fn read_fill(&mut self, stream: &mut Stream) -> usize; } pub fn read_smi(stream: &mut Stream) -> Smi { @@ -45,153 +38,61 @@ pub fn decide_cluster(class_id: ClassId) -> Result, &'static st // whose fill cluster size is uniquely determined by sizeof(Object) * num_of_objects // and alloc cluster size is tags (MULEB128) + num_of_objects (MULEB128) -DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameters, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(PatchClass, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Function, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(ClosureData, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(FfiTrampolineData, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Field, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(Script, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Library, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(Namespace, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(KernelProgramInfo, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(UnlinkedCall, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(ICData, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(MegamorphicCache, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(SubtypeTestCache, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(LoadingUnit, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(LanguageError, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(UnhandledException, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(LibraryPrefix, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(Type, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(FunctionType, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(RecordType, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(Closure, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Double, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(Int32x4, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(GrowableObjectArray, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(TypedDataView, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(ExternalTypedData, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(StackTrace, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(RegExp, { - 1 - // to-do -}); -DECLARE_FIXED_LENGTH_CLUSTER!(WeakProperty, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Map, { - 1 - // to-do -}); - -DECLARE_FIXED_LENGTH_CLUSTER!(Set, { - 1 - // to-do -}); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameters<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(PatchClass<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Function<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(ClosureData<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(FfiTrampolineData<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Field<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Script<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Library<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Namespace<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(KernelProgramInfo<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(UnlinkedCall, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(ICData, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(MegamorphicCache, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(SubtypeTestCache, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(LoadingUnit<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(LanguageError, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(UnhandledException, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(LibraryPrefix, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Type<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(FunctionType<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(RecordType, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(TypeParameter<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Closure<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Double, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(Int32x4, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(GrowableObjectArray<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(TypedDataView<'a>, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(ExternalTypedData, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(StackTrace, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(RegExp, |_self, last_ref_id, stream| { 1 }); +DECLARE_FIXED_LENGTH_CLUSTER!(WeakProperty, |_self, last_ref_id, stream| { 1 }); + +DECLARE_VARIABLE_LENGTH_CLUSTER!(Map); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Set); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Instance<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(TypedData<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Class<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(TypeArguments<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Code<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ObjectPool); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ExceptionHandlers<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Context<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ContextScope); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Mint); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Float32x4); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Float64x2); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Record); +DECLARE_VARIABLE_LENGTH_CLUSTER!(Array<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(WeakArray<'a>); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ImmutableArray); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ConstMap); +DECLARE_VARIABLE_LENGTH_CLUSTER!(ConstSet); +DECLARE_VARIABLE_LENGTH_CLUSTER!(CodeSourceMap); +DECLARE_VARIABLE_LENGTH_CLUSTER!(CompressedStackMaps); +DECLARE_VARIABLE_LENGTH_CLUSTER!(PcDescriptors); +DECLARE_VARIABLE_LENGTH_CLUSTER!(OneByteString); +DECLARE_VARIABLE_LENGTH_CLUSTER!(TwoByteString); +DECLARE_VARIABLE_LENGTH_CLUSTER!(_String); diff --git a/crates/flutterdec-serwalker/src/raw_object/mod.rs b/crates/flutterdec-serwalker/src/raw_object/mod.rs index 63ea72a..49f0429 100644 --- a/crates/flutterdec-serwalker/src/raw_object/mod.rs +++ b/crates/flutterdec-serwalker/src/raw_object/mod.rs @@ -223,45 +223,104 @@ pub struct ObjectPool; // Placeholder structs for references used above that aren't defined yet // it wouldn't compile without this, though for now they remain unimplemented... +#[derive(Default)] pub struct Array<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct Object<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct AbstractType<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct FunctionType<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct Script<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct Closure<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct Instance<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct WeakArray<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct TypedDataBase<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct TypedData<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct TypedDataView<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct GrowableObjectArray<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct Code<'a> { _marker: std::marker::PhantomData<&'a ()>, } +#[derive(Default)] pub struct LoadingUnit<'a> { _marker: std::marker::PhantomData<&'a ()>, } + +#[derive(Default)] +pub struct ICData; +#[derive(Default)] +pub struct MegamorphicCache; +#[derive(Default)] +pub struct SubtypeTestCache; +#[derive(Default)] +pub struct LanguageError; +#[derive(Default)] +pub struct UnhandledException; +#[derive(Default)] +pub struct LibraryPrefix; +#[derive(Default)] +pub struct RecordType; +#[derive(Default)] +pub struct Int32x4; +#[derive(Default)] +pub struct ExternalTypedData; +#[derive(Default)] +pub struct StackTrace; +#[derive(Default)] +pub struct RegExp; +#[derive(Default)] +pub struct WeakProperty; +#[derive(Default)] +pub struct Map; +#[derive(Default)] +pub struct Set; +#[derive(Default)] +pub struct Float32x4; +#[derive(Default)] +pub struct Float64x2; +#[derive(Default)] +pub struct ConstMap; +#[derive(Default)] +pub struct ConstSet; +#[derive(Default)] +pub struct Record; +#[derive(Default)] +pub struct ImmutableArray; +#[derive(Default)] +pub struct OneByteString; +#[derive(Default)] +pub struct TwoByteString; diff --git a/crates/flutterdec-serwalker/src/snapshot.rs b/crates/flutterdec-serwalker/src/snapshot.rs index 2374f87..c4cbe5c 100644 --- a/crates/flutterdec-serwalker/src/snapshot.rs +++ b/crates/flutterdec-serwalker/src/snapshot.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::cluster::{self, decide_cluster, Cluster}; +use crate::cluster::{decide_cluster, Cluster}; use crate::constants::{self, ClassId, MAGIC_BYTES, UNSIGNED_M}; use crate::stream::Stream; use crate::utils::{decode_tags, DecodedTags}; @@ -58,6 +58,9 @@ pub struct DataSnapshot { start_of_alloc_area: usize, start_of_fill_area: usize, + + end_of_alloc_area: usize, + end_of_fill_area: usize, } impl DataSnapshot { @@ -76,7 +79,7 @@ impl DataSnapshot { } self.size = stream.read_u64(); - self.kind = SnapshotKind::try_from(stream.read_u64()).expect("Not a valid snapshot!"); + self.kind = SnapshotKind::try_from(stream.read_u64()).unwrap(); self.parse_version_and_features(stream); @@ -86,13 +89,12 @@ impl DataSnapshot { self.instr_table_len = stream.read_modified_leb128(UNSIGNED_M) as usize; self.instr_table_offset = stream.read_modified_leb128(UNSIGNED_M) as usize; - - self.start_of_alloc_area = stream.get_current_pos(); } fn parse_clusters(&mut self, stream: &mut Stream) { let mut curr_ref_id: u64 = 0; // all objects are numbered starting from 0 + self.start_of_alloc_area = stream.get_current_pos(); for _cluster_idx in 0..self.num_clusters { let tags: u32 = stream.read_modified_leb128(UNSIGNED_M) as u32; let decoded_tags: DecodedTags = decode_tags(tags); @@ -105,15 +107,20 @@ impl DataSnapshot { self.clusters.insert(cid, cluster); // hashmap takes ownership of box self.cluster_order.push(cid); } + self.end_of_alloc_area = stream.get_current_pos(); + self.start_of_fill_area = stream.get_current_pos(); for cluster_idx in 0..self.num_clusters { let cid = self.cluster_order[cluster_idx as usize]; let cluster_wrapper = self.clusters.get_mut(&cid); let cluster = cluster_wrapper.unwrap(); // this should never panic - (*cluster).read_fill(&mut curr_ref_id, stream); + (*cluster).read_fill(stream); } + self.end_of_fill_area = stream.get_current_pos(); } + + fn parse_roots(&mut self, stream: &mut Stream) {} } pub fn parse_snapshot(stream: &mut Stream) -> DataSnapshot { diff --git a/crates/flutterdec-serwalker/src/utils.rs b/crates/flutterdec-serwalker/src/utils.rs index ee6f0c7..4fe79b1 100644 --- a/crates/flutterdec-serwalker/src/utils.rs +++ b/crates/flutterdec-serwalker/src/utils.rs @@ -4,9 +4,9 @@ use crate::constants::ClassId; #[macro_export] macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { - ($name:ident, $fill_impl:block) => { + ($name:ident $(<$lt:lifetime>)?, |$_self:ident, $last_ref_id:ident, $stream:ident| $fill_impl:block) => { ::paste::paste! { // this is ugly, but the language doesn't support identifier concatenation - pub struct [<$name Cluster>] + pub struct [<$name Cluster>] $(<$lt>)? { tags: u32, obj_count: u64, @@ -19,38 +19,73 @@ macro_rules! DECLARE_FIXED_LENGTH_CLUSTER { first_ref_id: u32, - objs: Vec<(u64, Box<$name>)> // a pair (ref_id, object) + objs: Vec)? >> // a pair (ref_id, object) } - impl Cluster for [<$name Cluster>] + impl $(<$lt>)? Cluster for [<$name Cluster>] $(<$lt>)? // optional lifetime parameter { fn read_alloc(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize // read tags and count { - let initial_pos = stream.get_current_pos(); - self.start_of_alloc = initial_pos; + self.start_of_alloc = stream.get_current_pos(); + self.first_ref_id = *last_ref_id as u32; // later used to index the objs vector self.obj_count = stream.read_modified_leb128(UNSIGNED_M); - for obj_idx in 0..self.obj_count + for _obj_idx in 0..self.obj_count { - self.objs.push((*last_ref_id + obj_idx, Box::<$name>::default())); + self.objs.push(Box::<$name $(<$lt>)? >::default()); } *last_ref_id = *last_ref_id + self.obj_count; + self.end_of_alloc = stream.get_current_pos(); - stream.get_current_pos() - initial_pos + self.end_of_alloc - self.start_of_alloc } - fn read_fill(&mut self, last_ref_id: &mut u64, stream: &mut Stream) -> usize - $fill_impl + fn read_fill(&mut self, stream: &mut Stream) -> usize + { + self.start_of_fill = stream.get_current_pos(); + + let $_self = self; + let $stream = stream; + + let fill_size = $fill_impl; + + $_self.end_of_fill = $stream.get_current_pos(); + fill_size + } fn is_fixed_len(&self) -> bool { true } } + } - }; + } +} + +#[macro_export] +macro_rules! DECLARE_VARIABLE_LENGTH_CLUSTER { + ($name:ident $(<$lt:lifetime>)?) => { + ::paste::paste! { + pub struct [<$name Cluster>] $(<$lt>)? + { + tags: u32, + obj_count: u64, + + start_of_fill: usize, + start_of_alloc: usize, + + end_of_fill: usize, + end_of_alloc: usize, + + first_ref_id: u32, + + objs: Vec)? >> // a pair (ref_id, object) + } + } + } } pub struct DecodedTags {