Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion adapters/python/adapter_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,23 @@ def _r2flutter_pool(strings: List[dict]) -> List[dict]:
return out


def _r2flutter_super_name(value) -> str:
"""Normalize r2flutter superclass metadata to ProgramModel's string field."""
if isinstance(value, str):
name = value.strip()
if name:
return name
if isinstance(value, dict):
name = value.get("name")
if isinstance(name, str):
name = name.strip()
if name:
return name
# ProgramModel v3 requires a non-null superclass string. "Object" is only
# a placeholder for unresolved r2flutter metadata, not recovered data.
return "Object"


def _r2flutter_classes(classes: List[dict]) -> List[dict]:
"""Project r2flutter classes onto ProgramModel classes.

Expand All @@ -849,7 +866,7 @@ def _r2flutter_classes(classes: List[dict]) -> List[dict]:
{
"id": i,
"name": name,
"super": (c.get("super") or "Object"),
"super": _r2flutter_super_name(c.get("super")),
"lib": "",
}
)
Expand Down
100 changes: 100 additions & 0 deletions crates/flutterdec-adapter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,4 +460,104 @@ asm_dir.mkdir(parents=True, exist_ok=True)
"generic onResume in non-activity owner should not be tagged as activity handler"
);
}

#[test]
fn run_adapter_r2flutter_backend_normalizes_structured_superclass() {
let td = tempdir().expect("tempdir");
let root = td.path();
let python_dir = root.join("python");
fs::create_dir_all(&python_dir).expect("mkdir python");

let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()
.expect("canonicalize repo root");
let template_src = repo_root.join("adapters/python/adapter_template.py");
fs::copy(&template_src, python_dir.join("adapter_template.py"))
.expect("copy adapter template");

let fake_r2flutter = root.join("fake_r2flutter");
fs::write(
&fake_r2flutter,
r#"#!/usr/bin/env python3
import json
import sys

if "-jH" in sys.argv:
value = {
"dart_version": "3.6.2",
"hash": "testhash",
}
elif "-ji" in sys.argv:
value = {
"entries": [
{
"index": 0,
"address": 4096,
"name": "method.Child.test",
}
]
}
elif "-jxz" in sys.argv:
value = []
elif "-jc" in sys.argv:
value = [
{
"name": "Child",
"super": {"type_ref": 35836},
},
{
"name": "Widget",
"super": {"ref": 12, "name": "StatefulWidget"},
}
]
Comment thread
mikhail-ekzi marked this conversation as resolved.
else:
raise SystemExit(1)

print(json.dumps(value))
"#,
)
.expect("write fake r2flutter");
let mut fake_perms = fs::metadata(&fake_r2flutter)
.expect("metadata")
.permissions();
fake_perms.set_mode(0o755);
fs::set_permissions(&fake_r2flutter, fake_perms).expect("chmod fake r2flutter");

let exec = root.join("adapter_exec.py");
fs::write(
&exec,
"#!/usr/bin/env python3\nfrom pathlib import Path\nimport os\nimport sys\nroot = Path(__file__).resolve().parent\nos.environ['FLUTTERDEC_R2FLUTTER_CMD'] = str(root / 'fake_r2flutter')\nsys.path.insert(0, str(root / 'python'))\nimport adapter_template\nif __name__ == '__main__':\n raise SystemExit(adapter_template.entrypoint(default_snapshot_hash='testhash', default_version='unknown'))\n",
)
.expect("write exec");
let mut perms = fs::metadata(&exec).expect("metadata").permissions();
perms.set_mode(0o755);
fs::set_permissions(&exec, perms).expect("chmod exec");

let input_file = root.join("libapp.so");
fs::write(&input_file, b"dummy").expect("write dummy input");

let vm_data = vec![0u8; 64];
let iso_data = vec![0u8; 64];
let vm_instr = vec![0u8; 16];
let iso_instr = vec![0u8; 16];
let input = AdapterInput {
input_path: Some(&input_file),
libapp_path: None,
vm_data: &vm_data,
isolate_data: &iso_data,
vm_instr: &vm_instr,
isolate_instr: &iso_instr,
vm_instr_va: 0,
isolate_instr_va: 0,
backend: Some("r2flutter"),
};

let model = run_adapter(&exec, &input).expect("run adapter");
assert_eq!(model.classes.len(), 2);
assert_eq!(model.classes[0].name, "Child");
assert_eq!(model.classes[0].super_name, "Object");
assert_eq!(model.classes[1].name, "Widget");
assert_eq!(model.classes[1].super_name, "StatefulWidget");
}
}