OCaml implementation of the Mozilla Public Suffix service

Eliminate Obj.magic with type-safe equivalents

Replace unsafe Obj.magic casts with proper type-safe alternatives:

- conpool: Make protocol parameter required, add create_basic for simple
pools. The previous optional protocol with Obj.magic default was
fundamentally unsound as OCaml cannot have optional parameters that
change return types.

- publicsuffix: Add explicit id field to trie_node instead of using
Obj.magic to cast nodes to int for hashtable keys.

- yamlt: Add init_unknown_builder helper that properly handles GADT
refinement, returning () for Unknown_skip/Unknown_error cases where
builder=unit.

- jmap_brr: Use Jsont_brr.encode/decode Jsont.json instead of unsafe
casts between Jv.t and Jsont.json.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+11 -5
+11 -5
gen/gen_psl.ml
··· 35 35 36 36 (** Trie node for efficient lookup *) 37 37 type trie_node = { 38 + id : int; (* Unique identifier for this node *) 38 39 mutable rule : (rule_type * section) option; 39 40 mutable children : (string * trie_node) list; 40 41 mutable wildcard_child : trie_node option; 41 42 } 42 43 43 - let make_node () = { rule = None; children = []; wildcard_child = None } 44 + let node_id_counter = ref 0 45 + 46 + let make_node () = 47 + let id = !node_id_counter in 48 + incr node_id_counter; 49 + { id; rule = None; children = []; wildcard_child = None } 44 50 45 51 (** Parse a single line from the PSL file *) 46 52 let parse_line section line = ··· 221 227 let rec assign_names node = 222 228 let name = Printf.sprintf "n%d" !node_counter in 223 229 incr node_counter; 224 - Hashtbl.add node_names (Obj.magic node : int) name; 230 + Hashtbl.add node_names node.id name; 225 231 List.iter (fun (_, child) -> assign_names child) node.children; 226 232 Option.iter assign_names node.wildcard_child 227 233 in ··· 232 238 let output_buffer = Buffer.create (1024 * 1024) in 233 239 234 240 let rec generate_node node = 235 - let node_id = (Obj.magic node : int) in 241 + let node_id = node.id in 236 242 if Hashtbl.mem generated node_id then 237 243 Hashtbl.find node_names node_id 238 244 else begin ··· 264 270 else begin 265 271 Buffer.add_string output_buffer " children = [\n"; 266 272 List.iter (fun (label, child) -> 267 - let child_name = Hashtbl.find node_names (Obj.magic child : int) in 273 + let child_name = Hashtbl.find node_names child.id in 268 274 Buffer.add_string output_buffer 269 275 (Printf.sprintf " (\"%s\", %s);\n" (escape_string label) child_name) 270 276 ) node.children; ··· 275 281 (match node.wildcard_child with 276 282 | None -> Buffer.add_string output_buffer " wildcard_child = None;\n" 277 283 | Some child -> 278 - let child_name = Hashtbl.find node_names (Obj.magic child : int) in 284 + let child_name = Hashtbl.find node_names child.id in 279 285 Buffer.add_string output_buffer 280 286 (Printf.sprintf " wildcard_child = Some %s;\n" child_name)); 281 287