Skip to content

Commit d1716f4

Browse files
committed
bindgen: Generate signal callback overloads
Facade on/off took any callable, so misshapen handlers type-checked. Derive the stacks from the .gir: the alias name follows from the type and signal name. Undefined aliases and single-signal types are skipped.
1 parent 0646972 commit d1716f4

1 file changed

Lines changed: 53 additions & 4 deletions

File tree

‎frida/frida_bindgen/codegen.py‎

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from __future__ import annotations
22

3+
import ast
4+
import re
35
import textwrap
46
from pathlib import Path
5-
from typing import Dict, List, Optional, Tuple, Union
7+
from typing import Dict, List, Optional, Set, Tuple, Union
68

79
from frida_bindgen_core import Procedure, Type
10+
from frida_bindgen_core.naming import to_pascal_case
811

912
from .model import (
1013
Enumeration,
@@ -32,7 +35,8 @@ def read_asset(name: str) -> str:
3235

3336

3437
FACADE_TYPING_IMPORTS = (
35-
"from typing import Any, Callable, Dict, List, Literal, Mapping, NotRequired, Optional, Tuple, TypedDict, Union"
38+
"from typing import (Any, Callable, Dict, List, Literal, Mapping, NotRequired, Optional, Tuple, TypedDict, "
39+
"Union, overload)"
3640
)
3741

3842

@@ -366,6 +370,8 @@ def generate_aio_class(otype: ObjectType, model: Model) -> str:
366370
if not members:
367371
members.append(" pass")
368372

373+
members = apply_signal_overloads(members, otype, model)
374+
369375
return f"class {otype.py_name}:\n" + "\n\n".join(members)
370376

371377

@@ -461,6 +467,8 @@ def generate_py_class(otype: ObjectType, model: Model) -> str:
461467
if not members:
462468
members.append(" pass")
463469

470+
members = apply_signal_overloads(members, otype, model)
471+
464472
return f"class {otype.py_name}:\n" + "\n\n".join(members)
465473

466474

@@ -496,13 +504,54 @@ def generate_facade_init(otype: ObjectType) -> str:
496504

497505

498506
def generate_facade_signals() -> str:
499-
return """ def on(self, signal, callback):
507+
return """ def on(self, signal: str, callback: Callable[..., Any]) -> None:
500508
self._impl.on(signal, _make_signal_handler(callback))
501509
502-
def off(self, signal, callback):
510+
def off(self, signal: str, callback: Callable[..., Any]) -> None:
503511
self._impl.off(signal, callback)"""
504512

505513

514+
def facade_prelude_names(model: Model) -> Set[str]:
515+
names = set()
516+
for asset in model.customizations.facade_preludes:
517+
for node in ast.parse(read_asset(asset)).body:
518+
if isinstance(node, ast.ClassDef):
519+
names.add(node.name)
520+
elif isinstance(node, ast.Assign):
521+
names.update(t.id for t in node.targets if isinstance(t, ast.Name))
522+
return names
523+
524+
525+
def signal_callback_alias(otype: ObjectType, signal) -> str:
526+
return f"{otype.py_name}{to_pascal_case(signal.name.replace('-', '_'))}Callback"
527+
528+
529+
def signal_overload_block(otype: ObjectType, model: Model, name: str) -> Optional[str]:
530+
aliases = facade_prelude_names(model)
531+
lines = []
532+
for signal in otype.signals:
533+
alias = signal_callback_alias(otype, signal)
534+
if alias in aliases:
535+
lines.append(" @overload")
536+
lines.append(f' def {name}(self, signal: Literal["{signal.name}"], callback: {alias}) -> None: ...')
537+
return "\n".join(lines) if len(lines) > 2 else None
538+
539+
540+
def apply_signal_overloads(members: List[str], otype: ObjectType, model: Model) -> List[str]:
541+
result = []
542+
for member in members:
543+
for name in ("on", "off"):
544+
block = signal_overload_block(otype, model, name)
545+
if block is None:
546+
continue
547+
pattern = re.compile(rf"^ (?:async )?def {name}\(self, signal", re.MULTILINE)
548+
match = pattern.search(member)
549+
if match is not None:
550+
member = member[: match.start()] + block + "\n" + member[match.start() :]
551+
result.append(member)
552+
return result
553+
554+
506555
def facade_repr_property_names(otype: ObjectType) -> List[str]:
507556
names = []
508557
for method in otype.methods:

0 commit comments

Comments
 (0)