import ast import logging from llvmlite import ir from pythonbpf.type_deducer import ctypes_to_ir from .struct_type import StructType logger = logging.getLogger(__name__) # TODO: Shall we allow the following syntax: # struct MyStruct: # field1: int # field2: str(32) # Where int is mapped to c_uint64? # Shall we just int64, int32 and uint32 similarly? def structs_proc(tree, module, chunks): """Process all class definitions to find BPF structs""" structs_sym_tab = {} for cls_node in chunks: if is_bpf_struct(cls_node): print(f"Found BPF struct: {cls_node.name}") struct_info = process_bpf_struct(cls_node, module) structs_sym_tab[cls_node.name] = struct_info return structs_sym_tab def is_bpf_struct(cls_node): return any( isinstance(decorator, ast.Name) and decorator.id == "struct" for decorator in cls_node.decorator_list ) def process_bpf_struct(cls_node, module): """Process a single BPF struct definition""" fields = parse_struct_fields(cls_node) field_types = list(fields.values()) total_size = calc_struct_size(field_types) struct_type = ir.LiteralStructType(field_types) logger.info(f"Created struct {cls_node.name} with fields {fields.keys()}") return StructType(struct_type, fields, total_size) def parse_struct_fields(cls_node): """Parse fields of a struct class node""" fields = {} for item in cls_node.body: if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): fields[item.target.id] = get_type_from_ann(item.annotation) else: logger.error(f"Unsupported struct field: {ast.dump(item)}") raise TypeError(f"Unsupported field in {ast.dump(cls_node)}") return fields def get_type_from_ann(annotation): """Convert an AST annotation node to an LLVM IR type for struct fields""" if isinstance(annotation, ast.Call) and isinstance(annotation.func, ast.Name): if annotation.func.id == "str": # Char array # Assumes constant integer argument length = annotation.args[0].value return ir.ArrayType(ir.IntType(8), length) elif isinstance(annotation, ast.Name): # Int type, written as c_int64, c_uint32, etc. return ctypes_to_ir(annotation.id) raise TypeError(f"Unsupported annotation type: {ast.dump(annotation)}") def calc_struct_size(field_types): """Calculate total size of the struct with alignment and padding""" curr_offset = 0 for ftype in field_types: if isinstance(ftype, ir.IntType): fsize = ftype.width // 8 alignment = fsize elif isinstance(ftype, ir.ArrayType): fsize = ftype.count * (ftype.element.width // 8) alignment = ftype.element.width // 8 elif isinstance(ftype, ir.PointerType): # We won't encounter this rn, but for the future fsize = 8 alignment = 8 else: raise TypeError(f"Unsupported field type: {ftype}") padding = (alignment - (curr_offset % alignment)) % alignment curr_offset += padding + fsize final_padding = (8 - (curr_offset % 8)) % 8 return curr_offset + final_padding