Generates a CPython-compatible legacy wrapper for a native function. In particular, this handles unboxing the arguments, calling the native function, and then boxing the return value.
(
fn: FuncIR, emitter: Emitter, source_path: str, module_name: str
)
| 225 | |
| 226 | |
| 227 | def generate_legacy_wrapper_function( |
| 228 | fn: FuncIR, emitter: Emitter, source_path: str, module_name: str |
| 229 | ) -> None: |
| 230 | """Generates a CPython-compatible legacy wrapper for a native function. |
| 231 | |
| 232 | In particular, this handles unboxing the arguments, calling the native function, and |
| 233 | then boxing the return value. |
| 234 | """ |
| 235 | emitter.emit_line(f"{legacy_wrapper_function_header(fn, emitter.names)} {{") |
| 236 | |
| 237 | # If fn is a method, then the first argument is a self param |
| 238 | real_args = list(fn.args) |
| 239 | if fn.sig.num_bitmap_args: |
| 240 | real_args = real_args[: -fn.sig.num_bitmap_args] |
| 241 | if fn.class_name and (fn.decl.name == "__new__" or fn.decl.kind != FUNC_STATICMETHOD): |
| 242 | arg = real_args.pop(0) |
| 243 | emitter.emit_line(f"PyObject *obj_{arg.name} = self;") |
| 244 | |
| 245 | # Need to order args as: required, optional, kwonly optional, kwonly required |
| 246 | # This is because CPyArg_ParseTupleAndKeywords format string requires |
| 247 | # them grouped in that way. |
| 248 | groups = make_arg_groups(real_args) |
| 249 | reordered_args = reorder_arg_groups(groups) |
| 250 | |
| 251 | emitter.emit_line(make_static_kwlist(reordered_args)) |
| 252 | for arg in real_args: |
| 253 | emitter.emit_line( |
| 254 | "PyObject *obj_{}{};".format(arg.name, " = NULL" if arg.optional else "") |
| 255 | ) |
| 256 | |
| 257 | cleanups = [f"CPy_DECREF(obj_{arg.name});" for arg in groups[ARG_STAR] + groups[ARG_STAR2]] |
| 258 | |
| 259 | arg_ptrs: list[str] = [] |
| 260 | if groups[ARG_STAR] or groups[ARG_STAR2]: |
| 261 | arg_ptrs += [f"&obj_{groups[ARG_STAR][0].name}" if groups[ARG_STAR] else "NULL"] |
| 262 | arg_ptrs += [f"&obj_{groups[ARG_STAR2][0].name}" if groups[ARG_STAR2] else "NULL"] |
| 263 | arg_ptrs += [f"&obj_{arg.name}" for arg in reordered_args] |
| 264 | |
| 265 | emitter.emit_lines( |
| 266 | 'if (!CPyArg_ParseTupleAndKeywords(args, kw, "{}", "{}", kwlist{})) {{'.format( |
| 267 | make_format_string(None, groups), fn.name, "".join(", " + n for n in arg_ptrs) |
| 268 | ), |
| 269 | "return NULL;", |
| 270 | "}", |
| 271 | ) |
| 272 | for i in range(fn.sig.num_bitmap_args): |
| 273 | name = bitmap_name(i) |
| 274 | emitter.emit_line(f"{BITMAP_TYPE} {name} = 0;") |
| 275 | traceback_code = generate_traceback_code(fn, emitter, source_path, module_name) |
| 276 | generate_wrapper_core( |
| 277 | fn, |
| 278 | emitter, |
| 279 | groups[ARG_OPT] + groups[ARG_NAMED_OPT], |
| 280 | cleanups=cleanups, |
| 281 | traceback_code=traceback_code, |
| 282 | ) |
| 283 | |
| 284 | emitter.emit_line("}") |
no test coverage detected
searching dependent graphs…