ocaml/stdlib/camlinternalOO.ml

587 lines
18 KiB
OCaml

(***********************************************************************)
(* *)
(* OCaml *)
(* *)
(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 2002 Institut National de Recherche en Informatique et *)
(* en Automatique. All rights reserved. This file is distributed *)
(* under the terms of the GNU Library General Public License, with *)
(* the special exception on linking described in file ../LICENSE. *)
(* *)
(***********************************************************************)
open Obj
(**** Object representation ****)
external set_id: 'a -> 'a = "caml_set_oo_id" "noalloc"
(**** Object copy ****)
let copy o =
let o = (Obj.obj (Obj.dup (Obj.repr o))) in
set_id o
(**** Compression options ****)
(* Parameters *)
type params = {
mutable compact_table : bool;
mutable copy_parent : bool;
mutable clean_when_copying : bool;
mutable retry_count : int;
mutable bucket_small_size : int
}
let params = {
compact_table = true;
copy_parent = true;
clean_when_copying = true;
retry_count = 3;
bucket_small_size = 16
}
(**** Parameters ****)
let step = Sys.word_size / 16
let initial_object_size = 2
(**** Items ****)
type item = DummyA | DummyB | DummyC of int
let _ = [DummyA; DummyB; DummyC 0] (* to avoid warnings *)
let dummy_item = (magic () : item)
(**** Types ****)
type tag
type label = int
type closure = item
type t = DummyA | DummyB | DummyC of int
let _ = [DummyA; DummyB; DummyC 0] (* to avoid warnings *)
type obj = t array
external ret : (obj -> 'a) -> closure = "%identity"
(**** Labels ****)
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 ****)
module Vars =
Map.Make(struct type t = string let compare (x:t) y = compare x y end)
type vars = int Vars.t
module Meths =
Map.Make(struct type t = string let compare (x:t) y = compare x y end)
type meths = label Meths.t
module Labs =
Map.Make(struct type t = label let compare (x:t) y = compare x y end)
type labs = bool Labs.t
(* The compiler assumes that the first field of this structure is [size]. *)
type table =
{ mutable size: int;
mutable methods: closure array;
mutable methods_by_name: meths;
mutable methods_by_label: labs;
mutable previous_states:
(meths * labs * (label * item) list * vars *
label list * string list) list;
mutable hidden_meths: (label * item) list;
mutable vars: vars;
mutable initializers: (obj -> unit) list }
let dummy_table =
{ methods = [| dummy_item |];
methods_by_name = Meths.empty;
methods_by_label = Labs.empty;
previous_states = [];
hidden_meths = [];
vars = Vars.empty;
initializers = [];
size = 0 }
let table_count = ref 0
(* dummy_met should be a pointer, so use an atom *)
let dummy_met : item = obj (Obj.new_block 0 0)
(* if debugging is needed, this could be a good idea: *)
(* let dummy_met () = failwith "Undefined method" *)
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;
let len = Array.length pub_labels in
let methods = Array.create (len*2+2) dummy_met 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 = [];
hidden_meths = [];
vars = Vars.empty;
initializers = [];
size = initial_object_size }
let resize array new_size =
let old_size = Array.length array.methods in
if new_size > old_size then begin
let new_buck = Array.create new_size dummy_met in
Array.blit array.methods 0 new_buck 0 old_size;
array.methods <- new_buck
end
let put array label element =
resize array (label + 1);
array.methods.(label) <- element
(**** Classes ****)
let method_count = ref 0
let inst_var_count = ref 0
(* 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_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
put table label element
else
table.hidden_meths <- (label, element) :: table.hidden_meths
let get_method table label =
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
let narrow table vars virt_meths concr_meths =
let vars = to_list vars
and virt_meths = to_list virt_meths
and concr_meths = to_list concr_meths in
let virt_meth_labs = List.map (get_method_label table) virt_meths in
let concr_meth_labs = List.map (get_method_label table) concr_meths in
table.previous_states <-
(table.methods_by_name, table.methods_by_label, table.hidden_meths,
table.vars, virt_meth_labs, vars)
:: table.previous_states;
table.vars <-
Vars.fold
(fun lab info tvars ->
if List.mem lab vars then Vars.add lab info tvars else tvars)
table.vars Vars.empty;
let by_name = ref Meths.empty in
let by_label = ref Labs.empty in
List.iter2
(fun met label ->
by_name := Meths.add met label !by_name;
by_label :=
Labs.add label
(try Labs.find label table.methods_by_label with Not_found -> true)
!by_label)
concr_meths concr_meth_labs;
List.iter2
(fun met label ->
by_name := Meths.add met label !by_name;
by_label := Labs.add label false !by_label)
virt_meths virt_meth_labs;
table.methods_by_name <- !by_name;
table.methods_by_label <- !by_label;
table.hidden_meths <-
List.fold_right
(fun ((lab, _) as met) hm ->
if List.mem lab virt_meth_labs then hm else met::hm)
table.hidden_meths
[]
let widen table =
let (by_name, by_label, saved_hidden_meths, saved_vars, virt_meths, vars) =
List.hd table.previous_states
in
table.previous_states <- List.tl table.previous_states;
table.vars <-
List.fold_left
(fun s v -> Vars.add v (Vars.find v table.vars) s)
saved_vars vars;
table.methods_by_name <- by_name;
table.methods_by_label <- by_label;
table.hidden_meths <-
List.fold_right
(fun ((lab, _) as met) hm ->
if List.mem lab virt_meths then hm else met::hm)
table.hidden_meths
saved_hidden_meths
let new_slot table =
let index = table.size in
table.size <- index + 1;
index
let new_variable table name =
try Vars.find name table.vars
with Not_found ->
let index = new_slot table in
if name <> "" then table.vars <- Vars.add name index table.vars;
index
let to_array arr =
if arr = Obj.magic 0 then [||] else arr
let new_methods_variables table meths vals =
let meths = to_array meths in
let nmeths = Array.length meths and nvals = Array.length vals in
let res = Array.create (nmeths + nvals) 0 in
for i = 0 to nmeths - 1 do
res.(i) <- get_method_label table meths.(i)
done;
for i = 0 to nvals - 1 do
res.(i+nmeths) <- new_variable table vals.(i)
done;
res
let get_variable table name =
try Vars.find name table.vars with Not_found -> assert false
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 (x:t) y = compare x y 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 =
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;
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;
let init =
if top then super cla env else Obj.repr (super cla) in
widen cla;
Array.concat
[[| repr init |];
magic (Array.map (get_variable cla) (to_array vals) : int array);
Array.map
(fun nm -> repr (get_method cla (get_method_label cla nm) : closure))
(to_array concr_meths) ]
let make_class pub_meths class_init =
let table = create_table pub_meths in
let env_init = class_init table in
init_class table;
(env_init (Obj.repr 0), class_init, env_init, Obj.repr 0)
type init_table = { mutable env_init: t; mutable class_init: table -> t }
let make_class_store pub_meths class_init init_table =
let table = create_table pub_meths in
let env_init = class_init table in
init_class table;
init_table.class_init <- class_init;
init_table.env_init <- env_init
let dummy_class loc =
let undef = fun _ -> raise (Undefined_recursive_module loc) in
(Obj.magic undef, undef, undef, Obj.repr 0)
(**** Objects ****)
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.methods);
Obj.obj (set_id obj)
let create_object_opt obj_0 table =
if (Obj.magic obj_0 : bool) then obj_0 else begin
(* 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.methods);
Obj.obj (set_id obj)
end
let rec iter_f obj =
function
[] -> ()
| f::l -> f obj; iter_f obj l
let run_initializers obj table =
let inits = table.initializers in
if inits <> [] then
iter_f obj inits
let run_initializers_opt obj_0 obj table =
if (Obj.magic obj_0 : bool) then obj else begin
let inits = table.initializers in
if inits <> [] then iter_f obj inits;
obj
end
let create_object_and_run_initializers obj_0 table =
if (Obj.magic obj_0 : bool) then obj_0 else begin
let obj = create_object table in
run_initializers obj table;
obj
end
(* Equivalent primitive below
let sendself obj lab =
(magic obj : (obj -> t) array array).(0).(lab) obj
*)
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 closure * tables * tables
type mut_tables =
{key: closure; mutable data: tables; mutable next: tables}
external mut : tables -> mut_tables = "%identity"
external demut : mut_tables -> tables = "%identity"
let build_path n keys tables =
(* Be careful not to create a seemingly immutable block, otherwise it could
be statically allocated. See #5779. *)
let res = demut {key = Obj.magic 0; data = Empty; next = Empty} in
let r = ref res in
for i = 0 to n do
r := Cons (keys.(i), !r, Empty)
done;
tables.data <- !r;
res
let rec lookup_keys i keys tables =
if i < 0 then tables else
let key = keys.(i) in
let rec lookup_key tables =
if tables.key == key then lookup_keys (i-1) keys tables.data else
if tables.next <> Empty then lookup_key (mut tables.next) else
let next = Cons (key, Empty, Empty) in
tables.next <- next;
build_path (i-1) keys (mut next)
in
lookup_key (mut tables)
let lookup_tables root keys =
let root = mut root in
if root.data <> Empty then
lookup_keys (Array.length keys - 1) keys root.data
else
build_path (Array.length keys - 1) keys root
(**** builtin methods ****)
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 ->
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 (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 (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 (sendself obj n) x)
let app_const_env f x 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 (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 -> (sendself obj n : _ -> _) (Array.unsafe_get obj m))
let meth_app_env n 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 -> (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
| GetVar
| GetEnv
| GetMeth
| SetVar
| AppConst
| AppVar
| AppEnv
| AppMeth
| AppConstConst
| AppConstVar
| AppConstEnv
| AppConstMeth
| AppVarConst
| AppEnvConst
| AppMethConst
| MethAppConst
| MethAppVar
| MethAppEnv
| MethAppMeth
| SendConst
| SendVar
| SendEnv
| SendMeth
| Closure of closure
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
| GetVar -> let n = next() in get_var n
| GetEnv -> let e = next() and n = next() in get_env e n
| GetMeth -> let n = next() in get_meth n
| SetVar -> let n = next() in set_var n
| AppConst -> let f = next() and x = next() in app_const f x
| AppVar -> let f = next() and n = next () in app_var f n
| AppEnv ->
let f = next() and e = next() and n = next() in app_env f e n
| AppMeth -> let f = next() and n = next () in app_meth f n
| AppConstConst ->
let f = next() and x = next() and y = next() in app_const_const f x y
| AppConstVar ->
let f = next() and x = next() and n = next() in app_const_var f x n
| AppConstEnv ->
let f = next() and x = next() and e = next () and n = next() in
app_const_env f x e n
| AppConstMeth ->
let f = next() and x = next() and n = next() in app_const_meth f x n
| AppVarConst ->
let f = next() and n = next() and x = next() in app_var_const f n x
| AppEnvConst ->
let f = next() and e = next () and n = next() and x = next() in
app_env_const f e n x
| AppMethConst ->
let f = next() and n = next() and x = next() in app_meth_const f n x
| MethAppConst ->
let n = next() and x = next() in meth_app_const n x
| MethAppVar ->
let n = next() and m = next() in meth_app_var n m
| MethAppEnv ->
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 (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 table i methods in
set_method table label clo;
incr i
done
(**** Statistics ****)
type stats =
{ classes: int; methods: int; inst_vars: int; }
let stats () =
{ classes = !table_count;
methods = !method_count; inst_vars = !inst_var_count; }