diff options
Diffstat (limited to 'stdlib')
-rw-r--r-- | stdlib/Makefile | 14 | ||||
-rw-r--r-- | stdlib/camlinternalOO.ml | 413 | ||||
-rw-r--r-- | stdlib/camlinternalOO.mli | 42 | ||||
-rw-r--r-- | stdlib/oo.ml | 2 | ||||
-rw-r--r-- | stdlib/oo.mli | 4 | ||||
-rw-r--r-- | stdlib/sys.ml | 2 |
6 files changed, 179 insertions, 298 deletions
diff --git a/stdlib/Makefile b/stdlib/Makefile index 30e78f01a..978102dd5 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -119,20 +119,24 @@ pervasives.p.cmx: pervasives.ml camlinternalOO.cmi: camlinternalOO.mli $(CAMLC) $(COMPFLAGS) -nopervasives -c camlinternalOO.mli +# camlinternalOO.cmx should not be inlined CIOO=camlinternalOO $(CIOO).cmx $(CIOO).p.cmx: $(CIOO).ml - $(MAKE) EXTRAFLAGS="-inline 0" CIOO=dummy $@ + $(MAKE) CAMLOPT="$(CAMLOPT)" OPTCOMPFLAGS="$(OPTCOMPFLAGS)" \ + EXTRAFLAGS="-inline 0" CIOO=dummy $@ # labelled modules require the -nolabels flag labelled.cmo: - $(MAKE) EXTRAFLAGS=-nolabels RUNTIME=$(RUNTIME) \ - COMPILER=$(COMPILER) $(LABELLED:.ml=.cmo) + $(MAKE) CAMLC="$(CAMLC)" COMPFLAGS="$(COMPFLAGS)" \ + EXTRAFLAGS=-nolabels $(LABELLED:.ml=.cmo) touch $@ labelled.cmx: - $(MAKE) EXTRAFLAGS=-nolabels $(LABELLED:.ml=.cmx) + $(MAKE) CAMLOPT="$(CAMLOPT)" OPTCOMPFLAGS="$(OPTCOMPFLAGS)" \ + EXTRAFLAGS=-nolabels $(LABELLED:.ml=.cmx) touch $@ labelled.p.cmx: - $(MAKE) EXTRAFLAGS=-nolabels $(LABELLED:.ml=.p.cmx) + $(MAKE) CAMLOPT="$(CAMLOPT)" OPTCOMPFLAGS="$(OPTCOMPFLAGS)" \ + EXTRAFLAGS=-nolabels $(LABELLED:.ml=.p.cmx) touch $@ .SUFFIXES: .mli .ml .cmi .cmo .cmx .p.cmx diff --git a/stdlib/camlinternalOO.ml b/stdlib/camlinternalOO.ml index 7bf5c7b02..fff08b49f 100644 --- a/stdlib/camlinternalOO.ml +++ b/stdlib/camlinternalOO.ml @@ -54,185 +54,36 @@ let params = { (**** Parameters ****) let step = Sys.word_size / 16 -let first_bucket = 0 -let bucket_size = 32 (* Must be 256 or less *) let initial_object_size = 2 -(**** Index ****) - -type label = int - -let label_count = ref 0 - -let next label = - incr label_count; - let label = label + step in - if label mod (step * bucket_size) = 0 then - label + step * (65536 - bucket_size) - else - label - -let decode label = - (label / 65536 / step, (label mod (step * bucket_size)) / step) - (**** Items ****) -type item +type item = DummyA | DummyB | DummyC of int let dummy_item = (magic () : item) -(**** Buckets ****) - -type bucket = item array - -let version = ref 0 - -let set_bucket_version (bucket : bucket) = - bucket.(bucket_size) <- (magic !version : item) - -let bucket_version bucket = - (magic bucket.(bucket_size) : int) - -let bucket_list = ref [] - -let empty_bucket = [| |] - -let new_bucket () = - let bucket = Array.create (bucket_size + 1) dummy_item in - set_bucket_version bucket; - bucket_list := bucket :: !bucket_list; - bucket - -let copy_bucket bucket = - let bucket = Array.copy bucket in - set_bucket_version bucket; - bucket.(bucket_size) <- (magic !version : item); - bucket_list := bucket :: !bucket_list; - bucket - -(**** Make a clean bucket ****) - -let new_filled_bucket pos methods = - let bucket = new_bucket () in - List.iter - (fun (lab, met) -> - let (buck, elem) = decode lab in - if buck = pos then - bucket.(elem) <- (magic met : item)) - (List.rev methods); - bucket - -(**** Bucket merging ****) - -let small_buckets = ref (Array.create 10 [| |]) -let small_bucket_count = ref 0 - -let insert_bucket bucket = - let length = Array.length !small_buckets in - if !small_bucket_count >= length then begin - let new_array = Array.create (2 * length) [| |] in - Array.blit !small_buckets 0 new_array 0 length; - small_buckets := new_array - end; - !small_buckets.(!small_bucket_count) <- bucket; - incr small_bucket_count - -let remove_bucket n = - !small_buckets.(n) <- !small_buckets.(!small_bucket_count - 1); - decr small_bucket_count - -let bucket_used b = - let n = ref 0 in - for i = 0 to bucket_size - 1 do - if b.(i) != dummy_item then incr n - done; - !n - -let small_bucket b = bucket_used b <= params.bucket_small_size - -exception Failed - -let rec except e = - function - [] -> [] - | e'::l -> if e == e' then l else e'::(except e l) - -let merge_buckets b1 b2 = - for i = 0 to bucket_size - 1 do - if - (b2.(i) != dummy_item) && (b1.(i) != dummy_item) && (b2.(i) != b1.(i)) - then - raise Failed - done; - for i = 0 to bucket_size - 1 do - if b2.(i) != dummy_item then - b1.(i) <- b2.(i) - done; - bucket_list := except b2 !bucket_list; - b1 - -let prng = Random.State.make [| 0 |];; - -let rec choose bucket i = - if (i > 0) && (!small_bucket_count > 0) then begin - let n = Random.State.int prng !small_bucket_count in - if not (small_bucket !small_buckets.(n)) then begin - remove_bucket n; choose bucket i - end else - try - merge_buckets !small_buckets.(n) bucket - with Failed -> - choose bucket (i - 1) - end else begin - insert_bucket bucket; - bucket - end - -let compact b = - if - (b != empty_bucket) && (bucket_version b = !version) && (small_bucket b) - then - choose b params.retry_count - else - b +(**** Types ****) -let compact_buckets buckets = - for i = first_bucket to Array.length buckets - 1 do - buckets.(i) <- compact buckets.(i) - done +type tag +type label = int +type closure = item +type t = DummyA | DummyB | DummyC of int +type obj = t array +external ret : (obj -> 'a) -> closure = "%identity" (**** Labels ****) -let first_label = first_bucket * 65536 * step - -let last_label = ref first_label -let methods = Hashtbl.create 101 - -let new_label () = - let label = !last_label in - last_label := next !last_label; - label - -let new_method met = - try - Hashtbl.find methods met - with Not_found -> - let label = new_label () in - Hashtbl.add methods met label; - label - -let public_method_label met = - try - Hashtbl.find methods met - with Not_found -> - invalid_arg "Oo.public_method_label" - -let new_anonymous_method = - new_label - -(**** Types ****) - -type obj = t array +let public_method_label s : tag = + let accu = ref 0 in + for i = 0 to String.length s - 1 do + accu := 223 * !accu + Char.code s.[i] + done; + (* reduce to 31 bits *) + accu := !accu land (1 lsl 31 - 1); + (* make it signed for 64 bits architectures *) + let tag = if !accu > 0x3FFFFFFF then !accu - (1 lsl 31) else !accu in + (* Printf.eprintf "%s = %d\n" s tag; flush stderr; *) + magic tag (**** Sparse array ****) @@ -247,7 +98,7 @@ type labs = bool Labs.t (* The compiler assumes that the first field of this structure is [size]. *) type table = { mutable size: int; - mutable buckets: bucket array; + mutable methods: closure array; mutable methods_by_name: meths; mutable methods_by_label: labs; mutable previous_states: @@ -258,20 +109,31 @@ type table = mutable initializers: (obj -> unit) list } let dummy_table = - { buckets = [| |]; + { methods = [| dummy_item |]; methods_by_name = Meths.empty; methods_by_label = Labs.empty; previous_states = []; hidden_meths = []; vars = Vars.empty; initializers = []; - size = initial_object_size } + size = 0 } let table_count = ref 0 -let new_table () = +let null_item : item = Obj.obj (Obj.field (Obj.repr 0n) 1) + +let rec fit_size n = + if n <= 2 then n else + fit_size ((n+1)/2) * 2 + +let new_table pub_labels = incr table_count; - { buckets = [| |]; + let len = Array.length pub_labels in + let methods = Array.create (len*2+2) null_item in + methods.(0) <- magic len; + methods.(1) <- magic (fit_size len * Sys.word_size / 8 - 1); + for i = 0 to len - 1 do methods.(i*2+3) <- magic pub_labels.(i) done; + { methods = methods; methods_by_name = Meths.empty; methods_by_label = Labs.empty; previous_states = []; @@ -281,40 +143,42 @@ let new_table () = size = initial_object_size } let resize array new_size = - let old_size = Array.length array.buckets in + let old_size = Array.length array.methods in if new_size > old_size then begin - let new_buck = Array.create new_size empty_bucket in - Array.blit array.buckets 0 new_buck 0 old_size; - array.buckets <- new_buck + let new_buck = Array.create new_size null_item in + Array.blit array.methods 0 new_buck 0 old_size; + array.methods <- new_buck end let put array label element = - let (buck, elem) = decode label in - resize array (buck + 1); - let bucket = ref (array.buckets.(buck)) in - if !bucket == empty_bucket then begin - bucket := new_bucket (); - array.buckets.(buck) <- !bucket - end; - !bucket.(elem) <- element + resize array (label + 1); + array.methods.(label) <- element (**** Classes ****) let method_count = ref 0 let inst_var_count = ref 0 -type t +(* type t *) type meth = item +let new_method table = + let index = Array.length table.methods in + resize table (index + 1); + index + let get_method_label table name = try Meths.find name table.methods_by_name with Not_found -> - let label = new_anonymous_method () in + let label = new_method table in table.methods_by_name <- Meths.add name label table.methods_by_name; table.methods_by_label <- Labs.add label true table.methods_by_label; label +let get_method_labels table names = + Array.map (get_method_label table) names + let set_method table label element = incr method_count; if Labs.find label table.methods_by_label then @@ -323,9 +187,8 @@ let set_method table label element = table.hidden_meths <- (label, element) :: table.hidden_meths let get_method table label = - try List.assoc label table.hidden_meths with Not_found -> - let (buck, elem) = decode label in - table.buckets.(buck).(elem) + try List.assoc label table.hidden_meths + with Not_found -> table.methods.(label) let to_list arr = if arr == magic 0 then [] else Array.to_list arr @@ -403,25 +266,39 @@ let new_variables table names = let get_variable table name = Vars.find name table.vars +let get_variables table names = + Array.map (get_variable table) names + let add_initializer table f = table.initializers <- f::table.initializers +(* +module Keys = Map.Make(struct type t = tag array let compare = compare end) +let key_map = ref Keys.empty +let get_key tags : item = + try magic (Keys.find tags !key_map : tag array) + with Not_found -> + key_map := Keys.add tags tags !key_map; + magic tags +*) + let create_table public_methods = - let table = new_table () in - if public_methods != magic 0 then - Array.iter - (function met -> - let lab = new_method met in - table.methods_by_name <- Meths.add met lab table.methods_by_name; - table.methods_by_label <- Labs.add lab true table.methods_by_label) - public_methods; + if public_methods == magic 0 then new_table [||] else + (* [public_methods] must be in ascending order for bytecode *) + let tags = Array.map public_method_label public_methods in + let table = new_table tags in + Array.iteri + (fun i met -> + let lab = i*2+2 in + table.methods_by_name <- Meths.add met lab table.methods_by_name; + table.methods_by_label <- Labs.add lab true table.methods_by_label) + public_methods; table let init_class table = inst_var_count := !inst_var_count + table.size - 1; - if params.compact_table then - compact_buckets table.buckets; - table.initializers <- List.rev table.initializers + table.initializers <- List.rev table.initializers; + resize table (3 + magic table.methods.(1) * 16 / Sys.word_size) let inherits cla vals virt_meths concr_meths (_, super, _, env) top = narrow cla vals virt_meths concr_meths; @@ -451,7 +328,7 @@ let create_object table = (* XXX Appel de [obj_block] *) let obj = Obj.new_block Obj.object_tag table.size in (* XXX Appel de [caml_modify] *) - Obj.set_field obj 0 (Obj.repr table.buckets); + Obj.set_field obj 0 (Obj.repr table.methods); set_id obj last_id; (Obj.obj obj) @@ -460,7 +337,7 @@ let create_object_opt obj_0 table = (* XXX Appel de [obj_block] *) let obj = Obj.new_block Obj.object_tag table.size in (* XXX Appel de [caml_modify] *) - Obj.set_field obj 0 (Obj.repr table.buckets); + Obj.set_field obj 0 (Obj.repr table.methods); set_id obj last_id; (Obj.obj obj) end @@ -490,17 +367,20 @@ let create_object_and_run_initializers obj_0 table = end (* Equivalent primitive below -let send obj lab = - let (buck, elem) = decode lab in - (magic obj : (obj -> t) array array array).(0).(buck).(elem) obj +let sendself obj lab = + (magic obj : (obj -> t) array array).(0).(lab) obj *) -external send : obj -> label -> 'a = "%send" +external send : obj -> tag -> 'a = "%send" +external sendcache : obj -> tag -> t -> int -> 'a = "%sendcache" +external sendself : obj -> label -> 'a = "%sendself" +external get_public_method : obj -> tag -> closure + = "caml_get_public_method" "noalloc" (**** table collection access ****) -type tables = Empty | Cons of table * tables * tables +type tables = Empty | Cons of closure * tables * tables type mut_tables = - {key: table; mutable data: tables; mutable next: tables} + {key: closure; mutable data: tables; mutable next: tables} external mut : tables -> mut_tables = "%identity" let build_path n keys tables = @@ -533,39 +413,61 @@ let lookup_tables root keys = (**** builtin methods ****) -type closure = item -external ret : (obj -> 'a) -> closure = "%identity" - let get_const x = ret (fun obj -> x) let get_var n = ret (fun obj -> Array.unsafe_get obj n) -let get_env e n = ret (fun obj -> Obj.field (Array.unsafe_get obj e) n) -let get_meth n = ret (fun obj -> send obj n) +let get_env e n = + ret (fun obj -> + Array.unsafe_get (Obj.magic (Array.unsafe_get obj e) : obj) n) +let get_meth n = ret (fun obj -> sendself obj n) let set_var n = ret (fun obj x -> Array.unsafe_set obj n x) let app_const f x = ret (fun obj -> f x) let app_var f n = ret (fun obj -> f (Array.unsafe_get obj n)) -let app_env f e n = ret (fun obj -> f (Obj.field (Array.unsafe_get obj e) n)) -let app_meth f n = ret (fun obj -> f (send obj n)) +let app_env f e n = + ret (fun obj -> + f (Array.unsafe_get (Obj.magic (Array.unsafe_get obj e) : obj) n)) +let app_meth f n = ret (fun obj -> f (sendself obj n)) let app_const_const f x y = ret (fun obj -> f x y) let app_const_var f x n = ret (fun obj -> f x (Array.unsafe_get obj n)) -let app_const_meth f x n = ret (fun obj -> f x (send obj n)) +let app_const_meth f x n = ret (fun obj -> f x (sendself obj n)) let app_var_const f n x = ret (fun obj -> f (Array.unsafe_get obj n) x) -let app_meth_const f n x = ret (fun obj -> f (send obj n) x) +let app_meth_const f n x = ret (fun obj -> f (sendself obj n) x) let app_const_env f x e n = - ret (fun obj -> f x (Obj.field (Array.unsafe_get obj e) n)) + ret (fun obj -> + f x (Array.unsafe_get (Obj.magic (Array.unsafe_get obj e) : obj) n)) let app_env_const f e n x = - ret (fun obj -> f (Obj.field (Array.unsafe_get obj e) n) x) -let meth_app_const n x = ret (fun obj -> (send obj n) x) + ret (fun obj -> + f (Array.unsafe_get (Obj.magic (Array.unsafe_get obj e) : obj) n) x) +let meth_app_const n x = ret (fun obj -> (sendself obj n) x) let meth_app_var n m = - ret (fun obj -> (send obj n) (Array.unsafe_get obj m)) + ret (fun obj -> (sendself obj n) (Array.unsafe_get obj m)) let meth_app_env n e m = - ret (fun obj -> (send obj n) (Obj.field (Array.unsafe_get obj e) m)) + ret (fun obj -> (sendself obj n) + (Array.unsafe_get (Obj.magic (Array.unsafe_get obj e) : obj) m)) let meth_app_meth n m = - ret (fun obj -> (send obj n) (send obj m)) -let send_const m x = ret (fun obj -> send x m) -let send_var m n = ret (fun obj -> send (Obj.obj (Array.unsafe_get obj n)) m) -let send_env m e n = - ret (fun obj -> send (Obj.obj (Obj.field (Array.unsafe_get obj e) n)) m) -let send_meth m n = ret (fun obj -> send (send obj n) m) + ret (fun obj -> (sendself obj n) (sendself obj m)) +let send_const m x c = + ret (fun obj -> sendcache x m (Array.unsafe_get obj 0) c) +let send_var m n c = + ret (fun obj -> + sendcache (Obj.magic (Array.unsafe_get obj n) : obj) m + (Array.unsafe_get obj 0) c) +let send_env m e n c = + ret (fun obj -> + sendcache + (Obj.magic (Array.unsafe_get + (Obj.magic (Array.unsafe_get obj e) : obj) n) : obj) + m (Array.unsafe_get obj 0) c) +let send_meth m n c = + ret (fun obj -> + sendcache (sendself obj n) m (Array.unsafe_get obj 0) c) +let new_cache table = + let n = new_method table in + let n = + if n mod 2 = 0 || n > 2 + magic table.methods.(1) * 16 / Sys.word_size + then n else new_method table + in + table.methods.(n) <- Obj.magic 0; + n type impl = GetConst @@ -592,9 +494,9 @@ type impl = | SendVar | SendEnv | SendMeth - | Closure of Obj.t + | Closure of closure -let method_impl i arr = +let method_impl table i arr = let next () = incr i; magic arr.(!i) in match next() with GetConst -> let x : t = next() in get_const x @@ -631,17 +533,21 @@ let method_impl i arr = let n = next() and e = next() and m = next() in meth_app_env n e m | MethAppMeth -> let n = next() and m = next() in meth_app_meth n m - | SendConst -> let m = next() and x = next() in send_const m x - | SendVar -> let m = next() and n = next () in send_var m n - | SendEnv -> - let m = next() and e = next() and n = next() in send_env m e n - | SendMeth -> let m = next() and n = next () in send_meth m n + | SendConst -> + let m = next() and x = next() in send_const m x (new_cache table) + | SendVar -> + let m = next() and n = next () in send_var m n (new_cache table) + | SendEnv -> + let m = next() and e = next() and n = next() in + send_env m e n (new_cache table) + | SendMeth -> + let m = next() and n = next () in send_meth m n (new_cache table) | Closure _ as clo -> magic clo let set_methods table methods = let len = Array.length methods and i = ref 0 in while !i < len do - let label = methods.(!i) and clo = method_impl i methods in + let label = methods.(!i) and clo = method_impl table i methods in set_method table label clo; incr i done @@ -649,35 +555,8 @@ let set_methods table methods = (**** Statistics ****) type stats = - { classes: int; labels: int; methods: int; inst_vars: int; buckets: int; - distrib : int array; small_bucket_count: int; small_bucket_max: int } - -let distrib () = - let d = Array.create 32 0 in - List.iter - (function b -> - let n = bucket_used b in - d.(n - 1) <- d.(n - 1) + 1) - !bucket_list; - d + { classes: int; methods: int; inst_vars: int; } let stats () = - { classes = !table_count; labels = !label_count; - methods = !method_count; inst_vars = !inst_var_count; - buckets = List.length !bucket_list; distrib = distrib (); - small_bucket_count = !small_bucket_count; - small_bucket_max = Array.length !small_buckets } - -let sort_buck lst = - List.map snd - (Sort.list (fun (n, _) (n', _) -> n <= n') - (List.map (function b -> (bucket_used b, b)) lst)) - -let show_buckets () = - List.iter - (function b -> - for i = 0 to bucket_size - 1 do - print_char (if b.(i) == dummy_item then '.' else '*') - done; - print_newline ()) - (sort_buck !bucket_list) + { classes = !table_count; + methods = !method_count; inst_vars = !inst_var_count; } diff --git a/stdlib/camlinternalOO.mli b/stdlib/camlinternalOO.mli index 92345c4b1..8b6c980f6 100644 --- a/stdlib/camlinternalOO.mli +++ b/stdlib/camlinternalOO.mli @@ -17,22 +17,23 @@ All functions in this module are for system use only, not for the casual user. *) -(** {6 Methods} *) - -type label -val new_method : string -> label -val public_method_label : string -> label - (** {6 Classes} *) +type tag +type label type table type meth type t type obj +type closure +val public_method_label : string -> tag +val new_method : table -> label val new_variable : table -> string -> int val new_variables : table -> string array -> int val get_variable : table -> string -> int +val get_variables : table -> string array -> int array val get_method_label : table -> string -> label +val get_method_labels : table -> string array -> label array val get_method : table -> label -> meth val set_method : table -> label -> meth -> unit val set_methods : table -> label array -> unit @@ -60,17 +61,19 @@ val create_object_opt : obj -> table -> obj val run_initializers : obj -> table -> unit val run_initializers_opt : obj -> obj -> table -> obj val create_object_and_run_initializers : obj -> table -> obj -external send : obj -> label -> t = "%send" +external send : obj -> tag -> t = "%send" +external sendcache : obj -> tag -> t -> int -> t = "%sendcache" +external sendself : obj -> label -> t = "%sendself" +external get_public_method : obj -> tag -> closure + = "caml_get_public_method" "noalloc" (** {6 Table cache} *) type tables -val lookup_tables : tables -> table array -> tables +val lookup_tables : tables -> closure array -> tables (** {6 Builtins to reduce code size} *) -open Obj -type closure val get_const : t -> closure val get_var : int -> closure val get_env : int -> int -> closure @@ -91,10 +94,10 @@ val meth_app_const : label -> t -> closure val meth_app_var : label -> int -> closure val meth_app_env : label -> int -> int -> closure val meth_app_meth : label -> label -> closure -val send_const : label -> obj -> closure -val send_var : label -> int -> closure -val send_env : label -> int -> int -> closure -val send_meth : label -> label -> closure +val send_const : tag -> obj -> int -> closure +val send_var : tag -> int -> int -> closure +val send_env : tag -> int -> int -> int -> closure +val send_meth : tag -> label -> int -> closure type impl = GetConst @@ -121,10 +124,11 @@ type impl = | SendVar | SendEnv | SendMeth - | Closure of t + | Closure of closure (** {6 Parameters} *) +(* currently disabled *) type params = { mutable compact_table : bool; mutable copy_parent : bool; @@ -138,12 +142,6 @@ val params : params type stats = { classes : int; - labels : int; methods : int; - inst_vars : int; - buckets : int; - distrib : int array; - small_bucket_count : int; - small_bucket_max : int } + inst_vars : int } val stats : unit -> stats -val show_buckets : unit -> unit diff --git a/stdlib/oo.ml b/stdlib/oo.ml index e8795d857..c9ec64ae4 100644 --- a/stdlib/oo.ml +++ b/stdlib/oo.ml @@ -15,5 +15,5 @@ let copy = CamlinternalOO.copy external id : < .. > -> int = "%field1" -let new_method = CamlinternalOO.new_method +let new_method = CamlinternalOO.public_method_label let public_method_label = CamlinternalOO.public_method_label diff --git a/stdlib/oo.mli b/stdlib/oo.mli index c18bfa51e..b3111ce85 100644 --- a/stdlib/oo.mli +++ b/stdlib/oo.mli @@ -25,5 +25,5 @@ external id : < .. > -> int = "%field1" (**/**) (** For internal use (CamlIDL) *) -val new_method : string -> CamlinternalOO.label -val public_method_label : string -> CamlinternalOO.label +val new_method : string -> CamlinternalOO.tag +val public_method_label : string -> CamlinternalOO.tag diff --git a/stdlib/sys.ml b/stdlib/sys.ml index 3d7e7e256..c6646a029 100644 --- a/stdlib/sys.ml +++ b/stdlib/sys.ml @@ -78,4 +78,4 @@ let catch_break on = (* OCaml version string, must be in the format described in sys.mli. *) -let ocaml_version = "3.07+18 (2004-05-17)";; +let ocaml_version = "3.07+19 (2004-05-26)";; |