this repo has no description
at universe-builder 169 lines 5.9 kB view raw
1(** Multiple isolated execution environments. 2 3 This module provides isolated execution environments for the OCaml toplevel. 4 Each environment maintains both: 5 - The typing environment (Env.t) which tracks type bindings 6 - Runtime values (via Toploop.getvalue/setvalue) which store actual values 7 8 When switching between environments, both are saved and restored to ensure 9 complete isolation of definitions. *) 10 11module StringSet = Set.Make (String) 12module StringMap = Map.Make (String) 13 14(* Debug logging - uses the Logs module which is configured in the worker *) 15let log_debug msg = Logs.debug (fun m -> m "%s" msg) 16 17type id = string 18 19(** Runtime values are stored as a map from binding name to Obj.t. 20 We use Obj.t because Toploop.getvalue/setvalue work with Obj.t. *) 21type runtime_values = Obj.t StringMap.t 22 23type t = { 24 id : id; 25 mutable toplevel_env : Env.t option; 26 mutable runtime_values : runtime_values; 27 mutable is_setup : bool; 28 failed_cells : StringSet.t ref; 29} 30 31let default_id = "default" 32 33(* Global table of environments *) 34let environments : (id, t) Hashtbl.t = Hashtbl.create 16 35 36let create id = 37 let env = { 38 id; 39 toplevel_env = None; 40 runtime_values = StringMap.empty; 41 is_setup = false; 42 failed_cells = ref StringSet.empty; 43 } in 44 Hashtbl.replace environments id env; 45 env 46 47let get id = Hashtbl.find_opt environments id 48 49let get_or_create id = 50 match get id with 51 | Some env -> env 52 | None -> create id 53 54let destroy id = Hashtbl.remove environments id 55 56let list () = Hashtbl.fold (fun id _ acc -> id :: acc) environments [] 57 58let id env = env.id 59 60(** Get the toplevel name for a binding identifier. 61 This is used to look up runtime values via Toploop.getvalue. *) 62let toplevel_name ident = Translmod.toplevel_name ident 63 64(** Restore runtime values from the stored map. 65 This sets the values in the bytecode global table. *) 66let restore_runtime_values env_id values = 67 let count = StringMap.cardinal values in 68 if count > 0 then 69 log_debug (Printf.sprintf "[ENV] Restoring %d runtime values for env %s" count env_id); 70 StringMap.iter (fun name value -> 71 try 72 log_debug (Printf.sprintf "[ENV] setvalue %s" name); 73 Toploop.setvalue name value 74 with e -> 75 log_debug (Printf.sprintf "[ENV] setvalue %s failed: %s" name (Printexc.to_string e)) 76 ) values 77 78(** Check if an identifier is a value binding in the given environment. 79 Returns true for let-bindings, false for exceptions, modules, types, etc. *) 80let is_value_binding typing_env ident = 81 try 82 let path = Path.Pident ident in 83 let _ = Env.find_value path typing_env in 84 true 85 with Not_found -> false 86 87(** Capture runtime values for the given identifiers. 88 Only captures value bindings (not exceptions, modules, etc.). 89 Returns an updated map with the new values. *) 90let capture_runtime_values typing_env env_id base_map idents = 91 (* Filter to only value bindings to avoid "Fatal error" from Toploop.getvalue *) 92 let value_idents = List.filter (is_value_binding typing_env) idents in 93 if value_idents <> [] then 94 log_debug (Printf.sprintf "[ENV] Capturing %d value bindings for env %s (filtered from %d total)" 95 (List.length value_idents) env_id (List.length idents)); 96 List.fold_left (fun map ident -> 97 let name = toplevel_name ident in 98 try 99 let value = Toploop.getvalue name in 100 log_debug (Printf.sprintf "[ENV] captured %s" name); 101 StringMap.add name value map 102 with e -> 103 log_debug (Printf.sprintf "[ENV] could not capture %s: %s" name (Printexc.to_string e)); 104 map 105 ) base_map value_idents 106 107let with_env env f = 108 log_debug (Printf.sprintf "[ENV] with_env called for %s (has_saved_env=%b, runtime_values_count=%d)" 109 env.id (Option.is_some env.toplevel_env) (StringMap.cardinal env.runtime_values)); 110 111 (* Save current toplevel environment *) 112 let saved_typing_env = !Toploop.toplevel_env in 113 let saved_typing_env_before = 114 match env.toplevel_env with 115 | Some e -> e 116 | None -> saved_typing_env 117 in 118 119 (* Restore this environment's typing environment if we have one *) 120 (match env.toplevel_env with 121 | Some e -> Toploop.toplevel_env := e 122 | None -> ()); 123 124 (* Restore this environment's runtime values *) 125 restore_runtime_values env.id env.runtime_values; 126 127 (* Run the function *) 128 let result = 129 try f () 130 with exn -> 131 (* Capture new bindings before re-raising *) 132 let current_typing_env = !Toploop.toplevel_env in 133 let new_idents = Env.diff saved_typing_env_before current_typing_env in 134 let updated_values = capture_runtime_values current_typing_env env.id env.runtime_values new_idents in 135 env.runtime_values <- updated_values; 136 env.toplevel_env <- Some current_typing_env; 137 Toploop.toplevel_env := saved_typing_env; 138 raise exn 139 in 140 141 (* Capture new bindings that were added during execution *) 142 let current_typing_env = !Toploop.toplevel_env in 143 let new_idents = Env.diff saved_typing_env_before current_typing_env in 144 log_debug (Printf.sprintf "[ENV] Env.diff found %d new idents for %s" (List.length new_idents) env.id); 145 let updated_values = capture_runtime_values current_typing_env env.id env.runtime_values new_idents in 146 147 (* Save the updated environment state *) 148 env.runtime_values <- updated_values; 149 env.toplevel_env <- Some !Toploop.toplevel_env; 150 151 (* Restore the previous typing environment *) 152 Toploop.toplevel_env := saved_typing_env; 153 154 result 155 156let is_setup env = env.is_setup 157 158let mark_setup env = env.is_setup <- true 159 160let get_failed_cells env = !(env.failed_cells) 161 162let add_failed_cell env cell_id = 163 env.failed_cells := StringSet.add cell_id !(env.failed_cells) 164 165let remove_failed_cell env cell_id = 166 env.failed_cells := StringSet.remove cell_id !(env.failed_cells) 167 168let is_cell_failed env cell_id = 169 StringSet.mem cell_id !(env.failed_cells)