Skip to content

Commit 293b68c

Browse files
oleavrAbhiTheModder
authored andcommitted
bindgen: Expose named constructors as factories
Emit them all rather than the first alone, and marshal a bytes argument through GBytes. Keep new as the primary constructor.
1 parent 7dcf927 commit 293b68c

1 file changed

Lines changed: 134 additions & 16 deletions

File tree

‎frida/frida_bindgen/codegen.py‎

Lines changed: 134 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -811,13 +811,22 @@ def generate_pyi_class(otype: ObjectType, model: Model) -> List[str]:
811811

812812
body = []
813813

814-
ctor = next(iter(otype.constructors), None)
815-
if ctor is not None and not ctor.throws:
816-
params = pyi_parameters(ctor.input_parameters, model)
814+
primary = primary_constructor(otype)
815+
if primary is not None and not primary.throws:
816+
params = pyi_parameters(primary.input_parameters, model)
817817
if params is not None:
818818
signature = ", ".join(["self"] + params)
819819
body.append(f" def __init__({signature}) -> None: ...")
820820

821+
for ctor in otype.constructors:
822+
if ctor is primary:
823+
continue
824+
params = pyi_parameters(ctor.input_parameters, model)
825+
if params is None:
826+
continue
827+
body.append(" @staticmethod")
828+
body.append(f" def {ctor.name}({', '.join(params)}) -> {otype.py_name}: ...")
829+
821830
if otype.name == "Object":
822831
body.append(" def on(self, signal: str, callback: Callable[..., Any]) -> None: ...")
823832
body.append(" def off(self, signal: str, callback: Callable[..., Any]) -> None: ...")
@@ -966,6 +975,8 @@ def generate_extension_c(model: Model) -> str:
966975
code += generate_interface_implementation(otype)
967976

968977
code += generate_object_type_constructor(otype)
978+
for ctor in factory_constructors(otype):
979+
code += generate_factory_function(otype, ctor, build_constructor_marshals(ctor))
969980

970981
return code
971982

@@ -1241,6 +1252,10 @@ def generate_prototypes(model: Model) -> str:
12411252
"",
12421253
f"static int {otype_cprefix}_init ({otype_cprefix} * self, PyObject * args, PyObject * kw);",
12431254
]
1255+
for ctor in factory_constructors(otype):
1256+
prototypes.append(
1257+
f"static PyObject * {otype_cprefix}_{ctor.name} (PyObject * self, PyObject * args, PyObject * kw);"
1258+
)
12441259
if implementable_interface(otype):
12451260
prototypes += [
12461261
f"static int {otype_cprefix}_traverse (PyObject * self, visitproc visit, void * arg);",
@@ -1357,6 +1372,9 @@ def generate_object_type_methods(otype: ObjectType) -> str:
13571372
functions.append(function)
13581373
entries.append(entry)
13591374

1375+
for ctor in factory_constructors(otype):
1376+
entries.append(factory_method_entry(otype, ctor))
1377+
13601378
return f"""{"".join(functions)}
13611379
static PyMethodDef {otype.c_symbol_prefix}_methods[] =
13621380
{{
@@ -2308,28 +2326,47 @@ def generate_object_type_constructor(otype: ObjectType) -> str:
23082326
if implementable_interface(otype):
23092327
return generate_interface_init(otype)
23102328

2311-
ctor = next(iter(otype.constructors), None)
2329+
ctor = primary_constructor(otype)
23122330

23132331
if ctor is None:
23142332
return generate_bare_init(otype)
23152333

2316-
marshals: Optional[List[ParamMarshal]] = None
2317-
if not ctor.throws:
2318-
marshals = []
2319-
for param in ctor.input_parameters:
2320-
marshal = build_param_marshal(param)
2321-
if marshal is None:
2322-
marshals = None
2323-
break
2324-
marshal.optional = param.optional
2325-
marshals.append(marshal)
2334+
if ctor.throws:
2335+
return generate_unconstructable_init(otype)
23262336

2337+
marshals = build_constructor_marshals(ctor)
23272338
if marshals is None:
23282339
return generate_unconstructable_init(otype)
23292340

23302341
return generate_constructor_init(otype, ctor, marshals)
23312342

23322343

2344+
def primary_constructor(otype: ObjectType) -> Optional[Procedure]:
2345+
if not otype.constructors:
2346+
return None
2347+
return next((c for c in otype.constructors if c.name == "new"), otype.constructors[0])
2348+
2349+
2350+
def factory_constructors(otype: ObjectType) -> List[Procedure]:
2351+
primary = primary_constructor(otype)
2352+
return [
2353+
ctor
2354+
for ctor in otype.constructors
2355+
if ctor is not primary and not ctor.is_async and build_constructor_marshals(ctor) is not None
2356+
]
2357+
2358+
2359+
def build_constructor_marshals(ctor: Procedure) -> Optional[List["ParamMarshal"]]:
2360+
marshals = []
2361+
for param in ctor.input_parameters:
2362+
marshal = build_param_marshal(param)
2363+
if marshal is None:
2364+
return None
2365+
marshal.optional = param.optional
2366+
marshals.append(marshal)
2367+
return marshals
2368+
2369+
23332370
def generate_interface_init(otype: ObjectType) -> str:
23342371
n = interface_impl_names(otype)
23352372
indent = constructor_indent(otype)
@@ -2430,7 +2467,88 @@ def generate_constructor_init(otype: ObjectType, ctor: Procedure, marshals: List
24302467
{generate_constructor_tail(marshals)}"""
24312468

24322469

2433-
def generate_argument_parse(marshals: List["ParamMarshal"]) -> str:
2470+
def factory_method_entry(otype: ObjectType, ctor: Procedure) -> str:
2471+
return (
2472+
f' {{ "{ctor.name}", (PyCFunction) {otype.c_symbol_prefix}_{ctor.name}, '
2473+
"METH_VARARGS | METH_KEYWORDS | METH_STATIC, NULL },"
2474+
)
2475+
2476+
2477+
def generate_factory_function(otype: ObjectType, ctor: Procedure, marshals: List["ParamMarshal"]) -> str:
2478+
indent = " " * (len(otype.c_symbol_prefix) + len(ctor.name) + 3)
2479+
2480+
decls = [line for m in marshals for line in m.decls]
2481+
decls.append(f"{otype.c_type} * handle;")
2482+
if ctor.throws:
2483+
decls.append("GError * error = NULL;")
2484+
decls.append("PyObject * result = NULL;")
2485+
decls_block = indent_c_code("\n".join(decls), 1, prologue="\n")
2486+
2487+
keyword_decl = ""
2488+
parse_block = ""
2489+
if marshals:
2490+
keywords = ", ".join([f'"{m.keyword}"' for m in marshals] + ["NULL"])
2491+
keyword_decl = f"\n static char * keywords[] = {{ {keywords} }};"
2492+
parse_block = "\n" + generate_argument_parse(marshals, on_failure="return NULL;")
2493+
2494+
post_block = ""
2495+
for m in marshals:
2496+
if m.post is not None:
2497+
post_block += indent_c_code(m.post, 1, prologue="\n") + "\n"
2498+
2499+
call_args = [m.call_arg for m in marshals]
2500+
if ctor.throws:
2501+
call_args.append("&error")
2502+
call = f"handle = ({otype.c_type} *) {ctor.c_identifier} ({', '.join(call_args)});"
2503+
2504+
error_block = ""
2505+
if ctor.throws:
2506+
error_block = """
2507+
if (error != NULL)
2508+
{
2509+
result = PyFrida_raise (error);
2510+
goto beach;
2511+
}
2512+
"""
2513+
2514+
take_handle = f"result = PyGObject_new_take_handle (g_steal_pointer (&handle), PYFRIDA_TYPE ({otype.py_name}));"
2515+
2516+
return f"""
2517+
static PyObject *
2518+
{otype.c_symbol_prefix}_{ctor.name} (PyObject * self,
2519+
{indent}PyObject * args,
2520+
{indent}PyObject * kw)
2521+
{{{keyword_decl}{decls_block}
2522+
{parse_block}{post_block}
2523+
{call}
2524+
{error_block}
2525+
{take_handle}
2526+
2527+
{generate_factory_tail(marshals, ctor.throws)}"""
2528+
2529+
2530+
def generate_factory_tail(marshals: List["ParamMarshal"], throws: bool) -> str:
2531+
cleanups = [m.cleanup for m in marshals if m.cleanup is not None]
2532+
cleanup_block = indent_c_code("\n".join(reversed(cleanups)), 1) if cleanups else ""
2533+
2534+
if throws or constructor_needs_beach(marshals):
2535+
prologue = f"{cleanup_block}\n\n" if cleanup_block else ""
2536+
return f"""beach:
2537+
{prologue} return result;
2538+
}}
2539+
"""
2540+
if cleanup_block:
2541+
return f"""{cleanup_block}
2542+
2543+
return result;
2544+
}}
2545+
"""
2546+
return """ return result;
2547+
}
2548+
"""
2549+
2550+
2551+
def generate_argument_parse(marshals: List["ParamMarshal"], on_failure: str = "return -1;") -> str:
24342552
fmt = ""
24352553
optional_started = False
24362554
for m in marshals:
@@ -2444,7 +2562,7 @@ def generate_argument_parse(marshals: List["ParamMarshal"]) -> str:
24442562
indent = " " * len(" if (!PyArg_ParseTupleAndKeywords (")
24452563
parse_args_str = (",\n" + indent).join(parse_args)
24462564
return f""" if (!PyArg_ParseTupleAndKeywords ({parse_args_str}))
2447-
return -1;
2565+
{on_failure}
24482566
"""
24492567

24502568

0 commit comments

Comments
 (0)