ocaml/stdlib/oo.ml

504 lines
13 KiB
OCaml

(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Obj
(**** Object representation ****)
let object_tag = 248
let last_id = ref 0
let new_id () =
let id = !last_id in incr last_id; id
(**** Object copy ****)
let copy (o : < .. >) : 'a =
let o = Obj.repr (o : 'a) in
let s = Obj.size o in
let r = Obj.new_block object_tag s in
Obj.set_field r 0 (Obj.field o 0);
Obj.set_field r 1 (Obj.repr !last_id); incr last_id;
for i = 2 to s - 1 do Obj.set_field r i (Obj.field o i) done;
Obj.obj r
(**** Compression options ****)
let compact_table = ref true
let copy_parent = ref true
let clean_when_copying = ref true
let retry_count = ref 3
let bucket_small_size = ref 16
(**** 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
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 <= !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 rec choose bucket i =
if (i > 0) & (!small_bucket_count > 0) then begin
let n = Random.int !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 !retry_count
else
b
let compact_buckets buckets =
for i = first_bucket to Array.length buckets - 1 do
buckets.(i) <- compact buckets.(i)
done
(**** 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 new_anonymous_method =
new_label
(**** Types ****)
type object = t array
(**** Sparse array ****)
module Vars = Map.Make(struct type t = string let compare = compare end)
type vars = int Vars.t
module Meths = Map.Make(struct type t = string let compare = compare end)
type meths = label list Meths.t
type obj_init
type table =
{ mutable buckets: bucket array;
mutable methods: meths;
mutable saved_methods: meths list;
mutable meth_defs: (label * item) list;
mutable vars: vars;
mutable saved_vars: vars list;
mutable saved_var_lst: int option list;
mutable size: int;
mutable init: obj_init list list }
let table_count = ref 0
let new_table () =
incr table_count;
{ buckets = [| |];
methods = Meths.empty;
saved_methods = [];
meth_defs = [];
vars = Vars.empty;
saved_vars = [];
saved_var_lst = [];
size = initial_object_size;
init = [[]; []]}
let copy_table array1 array2 =
incr version;
array1.buckets <- Array.copy array2.buckets;
array1.methods <- array2.methods;
array1.saved_methods <- [];
array1.meth_defs <- array2.meth_defs;
array1.vars <- array2.vars;
array1.saved_vars <- [];
array1.saved_var_lst <- array1.saved_var_lst;
array1.size <- array2.size;
array1.init <- array2.init
let resize array new_size =
let old_size = Array.length array.buckets 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
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;
if !bucket.(elem) != element then begin
if
(bucket_version !bucket < !version)
&
(!bucket.(elem) != dummy_item)
then begin
if !clean_when_copying then
bucket := new_filled_bucket buck array.meth_defs
else
bucket := copy_bucket !bucket;
array.buckets.(buck) <- !bucket
end;
!bucket.(elem) <- element
end
(**** Classes ****)
type t
type class_info =
{mutable obj_init: t;
mutable class_init: table -> unit;
mutable table: table}
let set_initializer table init =
match table.init with
l::l'::l'' ->
let i =
List.fold_right
(fun init2 init1 -> (magic init1 : obj_init -> obj_init) init2)
l init
in
table.init <- (i::l')::l''
| _ ->
invalid_arg "Fatal error in Oo.set_initializer."
let inheritance table cl vars meths =
if
!copy_parent
&& (table.methods = Meths.empty) && (table.size = initial_object_size)
&& (table.init = [[]; []])
then begin
copy_table table cl.table;
table.init <- table.init@[[]];
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
Vars.empty
vars;
table.methods <-
List.fold_left
(fun s m ->
try Meths.add m (Meths.find m table.methods) s with Not_found -> s)
Meths.empty
meths
end else begin
table.init <- []::table.init;
table.saved_vars <- table.vars::table.saved_vars;
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
Vars.empty
vars;
table.saved_methods <- table.methods::table.saved_methods;
table.methods <-
List.fold_left
(fun s m ->
try Meths.add m (Meths.find m table.methods) s with Not_found -> s)
Meths.empty
meths;
cl.class_init table;
table.vars <-
List.fold_left
(fun s v ->
try Vars.add v (Vars.find v table.vars) s with Not_found -> s)
(List.hd table.saved_vars)
vars;
table.saved_vars <- List.tl table.saved_vars;
table.methods <-
List.fold_left
(fun s m ->
try Meths.add m (Meths.find m table.methods) s with Not_found -> s)
(List.hd table.saved_methods)
meths;
table.saved_methods <- List.tl table.saved_methods
end
let get_method_labels table name =
try
Meths.find name table.methods
with Not_found ->
let label = new_anonymous_method () in
table.methods <- Meths.add name [label] table.methods;
[label]
let get_method_label table name =
List.hd (get_method_labels table name)
let set_method table name element =
List.iter
(function label ->
table.meth_defs <- (label, element) :: table.meth_defs;
put table label element)
(get_method_labels table name)
let get_method table label =
let (buck, elem) = decode label in
table.buckets.(buck).(elem)
let new_slot table =
let index = table.size in
table.size <- index + 1;
index
let get_variable table name =
try
Vars.find name table.vars
with Not_found ->
let index = new_slot table in
table.vars <- Vars.add name index table.vars;
index
let hide_variable table name =
try
let i = Vars.find name table.vars in
table.vars <- Vars.remove name table.vars;
table.saved_var_lst <- Some i::table.saved_var_lst
with Not_found ->
table.saved_var_lst <- None::table.saved_var_lst
let rec list_remove name =
function
[] ->
invalid_arg "Fatal error in Oo.get_private_variable"
| (n, _) as a::l ->
if name = n then l
else a::list_remove name l
let get_private_variable table name =
let index =
try Vars.find name table.vars with Not_found -> new_slot table
in
table.vars <-
begin match List.hd table.saved_var_lst with
None ->
Vars.remove name table.vars
| Some i ->
Vars.add name i table.vars
end;
table.saved_var_lst <- List.tl table.saved_var_lst;
index
let method_count = ref 0
let inst_var_count = ref 0
let new_object table =
let obj = Array.create table.size (magic () : t) in
obj.(0) <- (magic table.buckets : t);
obj
let create_class class_info public_methods creator class_init =
let table = new_table () in
List.iter
(function met ->
table.methods <- Meths.add met [new_method met] table.methods)
public_methods;
class_init table;
method_count := !method_count + List.length table.meth_defs;
if !compact_table then
compact_buckets table.buckets;
inst_var_count := !inst_var_count + table.size - 1;
class_info.class_init <- class_init;
class_info.table <- table;
class_info.obj_init <- creator table
(**** Objects ****)
let create_object table =
let obj = Obj.new_block object_tag table.size in
Obj.set_field obj 0 (Obj.repr table.buckets);
Obj.set_field obj 1 (Obj.repr (new_id ()));
let initialization = Obj.magic List.hd (List.hd table.init) in
initialization (Obj.obj obj)
let send obj lab =
let (buck, elem) = decode lab in
(magic obj : (object -> t) array array array).(0).(buck).(elem) obj
(**** 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
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)