From 705d07ccd66201a8e6c7ea33872d3f179f04f65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Wed, 2 Sep 2026 21:25:49 +0200 Subject: [PATCH] Bind a MEOS scalar at the width its ABI defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A C type the mapping table does not name falls through to IntPtr, the pointer-width opaque handle. The `_t` spellings libclang leaves unreduced — int8_t through uint64_t — and `unsigned char` are absent from that table, so 530 of the 4825 bound functions declared a scalar at pointer width: 246 int64_t, 140 uint64_t, 38 uint32_t and 24 unsigned char arguments, and 72 int64_t, 58 uint64_t, 56 uint32_t and one unsigned char returns. A uint32_t returned as IntPtr reads eight bytes of a register the ABI defines four of, so the value carries whatever the upper half held. The enum types come from the catalog's own `enums` rather than from names listed one at a time. The five listed names covered MeosType, interpType and tempSubtype, missed IndexSearchOp, SPTreeKind, MeosPixType, MeosOper, SkipListType and nullHandleType across 43 further parameters and returns, and one of them — spanType — names no catalog enum at all. Reading the set from the catalog is what keeps the next enum from arriving as a pointer. --- tools/codegen.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tools/codegen.py b/tools/codegen.py index 86d54e1..b3fdea5 100755 --- a/tools/codegen.py +++ b/tools/codegen.py @@ -53,15 +53,32 @@ "uint32": "uint", "int64": "long", "uint64": "ulong", + "int8_t": "sbyte", + "int16_t": "short", + "int32_t": "int", + "int64_t": "long", + "uint16_t": "ushort", + "uint32_t": "uint", + "uint64_t": "ulong", + "signed char": "sbyte", + "unsigned char": "byte", + "unsigned short": "ushort", "size_t": "ulong", "ssize_t": "long", - "meosType": "int", - "MeosType": "int", - "interpType": "int", - "tempSubtype": "int", - "spanType": "int", } +# The names of the catalog's own enums, filled in by ``configure``. A C enum is +# an int at the ABI, and taking the set from the catalog is what keeps a newly +# added enum from arriving as an opaque pointer the way a hand-list leaves it. +ENUM_TYPES: set[str] = set() + + +def configure(idl: dict) -> None: + """Take from the catalog the type facts the mapping below reads.""" + ENUM_TYPES.clear() + ENUM_TYPES.update(e["name"] for e in idl.get("enums", []) if e.get("name")) + + # C pointer-to-char marshalled as managed string when StringMarshalling.Utf8 is on. def is_string_pointer(c_type: str) -> bool: t = c_type.replace("const ", "").strip() @@ -77,6 +94,8 @@ def csharp_type_for(canonical: str) -> str: # Pointer of any depth -> IntPtr (we don't propagate pointer types into C# semantics). if "*" in t or t.endswith("[]"): return "IntPtr" + if t in ENUM_TYPES: + return "int" return SCALAR_MAP.get(t, "IntPtr") # unknown scalar -> opaque pointer is safer than guessing @@ -453,6 +472,7 @@ def main(idl_path: str, dll_path: str = DLL_PATH) -> None: DLL_PATH = dll_path with open(idl_path) as fh: idl = json.load(fh) + configure(idl) funcs = idl["functions"] repo_root = Path(__file__).resolve().parent.parent out_dir = repo_root / "MEOS.NET" / "Internal"