diff --git a/jscomp/lib/js.ml b/jscomp/lib/js.ml index 42f3e7dd92..caed867da9 100644 --- a/jscomp/lib/js.ml +++ b/jscomp/lib/js.ml @@ -33,6 +33,10 @@ external anything_to_string : 'a -> string = "js_anything_to_string" external anything_to_number : 'a -> float = "js_anything_to_number" +type any = Obj.t + +external erase : 'a -> any = "%identity" +external cast : any -> 'a = "%identity" type + 'a opt @@ -141,6 +145,8 @@ module Caml_obj = struct external set_tag : Obj.t -> int -> unit = "caml_obj_set_tag" external uninitialized_object : int -> int -> Obj.t = "js_uninitialized_object" external is_instance_array : Obj.t -> bool = "js_is_instance_array" (* use Array.isArray instead*) + external size_of_any : Obj.t -> 'a def = "length" [@@bs.get] + external tag_of_any : Obj.t -> 'a def = "tag" [@@bs.get] end module Caml_int64 = struct @@ -148,3 +154,4 @@ module Caml_int64 = struct external div_mod : int64 -> int64 -> int64 * int64 = "js_int64_div_mod" external to_hex : int64 -> string = "js_int64_to_hex" end + diff --git a/jscomp/runtime/.depend b/jscomp/runtime/.depend index 88f06f74a0..70b3df2c7e 100644 --- a/jscomp/runtime/.depend +++ b/jscomp/runtime/.depend @@ -15,7 +15,6 @@ caml_queue.cmi : caml_string.cmi : caml_sys.cmi : caml_utils.cmi : -caml_weak.cmi : caml_array.cmo : js.cmo caml_array.cmi caml_array.cmx : js.cmx caml_array.cmi caml_bigarray.cmo : caml_bigarray.cmi @@ -54,8 +53,6 @@ caml_sys.cmo : js.cmo caml_sys.cmi caml_sys.cmx : js.cmx caml_sys.cmi caml_utils.cmo : caml_utils.cmi caml_utils.cmx : caml_utils.cmi -caml_weak.cmo : caml_array.cmi caml_weak.cmi -caml_weak.cmx : caml_array.cmx caml_weak.cmi fn.cmo : fn.cmx : js.cmo : @@ -102,8 +99,6 @@ caml_sys.cmo : js.cmo caml_sys.cmi caml_sys.cmj : js.cmj caml_sys.cmi caml_utils.cmo : caml_utils.cmi caml_utils.cmj : caml_utils.cmi -caml_weak.cmo : caml_array.cmi caml_weak.cmi -caml_weak.cmj : caml_array.cmj caml_weak.cmi fn.cmo : fn.cmj : js.cmo : diff --git a/jscomp/runtime/caml_hash.js b/jscomp/runtime/caml_hash.js index d2f78bc11a..656ff68ff8 100644 --- a/jscomp/runtime/caml_hash.js +++ b/jscomp/runtime/caml_hash.js @@ -69,7 +69,8 @@ function caml_hash(count, _, seed, obj) { while(queue[/* length */0] !== 0 && num > 0) { var obj$1 = Caml_queue.unsafe_pop(queue); if (typeof obj$1 === "number") { - hash = mix(hash, obj$1 | 0); + var u$1 = obj$1 | 0; + hash = mix(hash, (u$1 + u$1 | 0) + 1 | 0); num = num - 1 | 0; } else if (typeof obj$1 === "string") { @@ -83,19 +84,29 @@ function caml_hash(count, _, seed, obj) { Caml_builtin_exceptions.assert_failure, [ "caml_hash.ml", - 124, + 125, 8 ] ]; } else if (typeof obj$1 !== "function") { - var tag = obj$1.tag | 0; - hash = mix(hash, tag); - var v = obj$1.length - 1 | 0; - var block = v < num ? v : num; - for(var i = 0; i<= block; ++i){ - Caml_queue.push(obj$1[i], queue); + var size = obj$1.length; + if (size) { + var obj_tag = obj$1.tag | 0; + var tag = (size << 10) | obj_tag; + if (tag === 248) { + hash = mix(hash, obj$1[1]); + } + else { + hash = mix(hash, tag); + var v = size - 1 | 0; + var block = v < num ? v : num; + for(var i = 0; i<= block; ++i){ + Caml_queue.push(obj$1[i], queue); + } + } } + } } diff --git a/jscomp/runtime/caml_hash.ml b/jscomp/runtime/caml_hash.ml index abdf4a44e8..c290d1a651 100644 --- a/jscomp/runtime/caml_hash.ml +++ b/jscomp/runtime/caml_hash.ml @@ -108,7 +108,8 @@ let caml_hash count _limit seed obj = let obj = Caml_queue.unsafe_pop queue in if Js.typeof obj = "number" then begin - hash := mix !hash (Nativeint.of_float (Obj.magic obj)); + let u = Nativeint.of_float (Obj.magic obj) in + hash := mix !hash (u +~ u +~ 1n) ; decr num ; end else if Js.typeof obj = "string" then @@ -125,13 +126,23 @@ let caml_hash count _limit seed obj = else if Js.typeof obj = "function" then () else - let tag = Obj.tag obj in - hash := mix !hash (Nativeint.of_int tag) ; - let block = - let v = Obj.size obj - 1 in if v < !num then v else !num in - for i = 0 to block do - Caml_queue.push (Obj.field obj i ) queue - done + let size = Js.Caml_obj.size_of_any obj in + match Js.from_def size with (* FIXME: generated code is buggy *) + | None -> () + | Some size -> + let obj_tag = Obj.tag obj in + let tag = (size lsl 10) lor obj_tag in + if tag = 248 (* Obj.object_tag*) then + hash := mix !hash (Nativeint.of_int (Oo.id (Obj.magic obj))) + else + begin + hash := mix !hash (Nativeint.of_int tag) ; + let block = + let v = size - 1 in if v < !num then v else !num in + for i = 0 to block do + Caml_queue.push (Obj.field obj i ) queue + done + end done; final_mix !hash diff --git a/jscomp/test/.depend b/jscomp/test/.depend index 44cdc443e0..7546423c4f 100644 --- a/jscomp/test/.depend +++ b/jscomp/test/.depend @@ -218,6 +218,10 @@ int64_test.cmo : ../stdlib/pervasives.cmi ../stdlib/nativeint.cmi mt.cmi \ int64_test.cmx : ../stdlib/pervasives.cmx ../stdlib/nativeint.cmx mt.cmx \ ../lib/js.cmx ../stdlib/int64.cmx ../stdlib/int32.cmx \ ../stdlib/format.cmx ext_array.cmx ../stdlib/array.cmx +int_hashtbl_test.cmo : mt.cmi ../stdlib/list.cmi ../stdlib/hashtbl.cmi \ + ../stdlib/array.cmi +int_hashtbl_test.cmx : mt.cmx ../stdlib/list.cmx ../stdlib/hashtbl.cmx \ + ../stdlib/array.cmx int_map.cmo : ../stdlib/map.cmi int_map.cmx : ../stdlib/map.cmx int_overflow_test.cmo : ../stdlib/string.cmi mt.cmi ../stdlib/int32.cmi \ @@ -764,6 +768,10 @@ int64_test.cmo : ../stdlib/pervasives.cmi ../stdlib/nativeint.cmi mt.cmi \ int64_test.cmj : ../stdlib/pervasives.cmj ../stdlib/nativeint.cmj mt.cmj \ ../lib/js.cmj ../stdlib/int64.cmj ../stdlib/int32.cmj \ ../stdlib/format.cmj ext_array.cmj ../stdlib/array.cmj +int_hashtbl_test.cmo : mt.cmi ../stdlib/list.cmi ../stdlib/hashtbl.cmi \ + ../stdlib/array.cmi +int_hashtbl_test.cmj : mt.cmj ../stdlib/list.cmj ../stdlib/hashtbl.cmj \ + ../stdlib/array.cmj int_map.cmo : ../stdlib/map.cmi int_map.cmj : ../stdlib/map.cmj int_overflow_test.cmo : ../stdlib/string.cmi mt.cmi ../stdlib/int32.cmi \ diff --git a/jscomp/test/hash_test.js b/jscomp/test/hash_test.js index 24d2ec9143..e3e2d49daa 100644 --- a/jscomp/test/hash_test.js +++ b/jscomp/test/hash_test.js @@ -63,23 +63,86 @@ function normalize(x) { return x & 1073741823; } -var param = $$Array.map(function (x) { - return Hashtbl.hash(x) & 1073741823; - }, test_strings); +function caml_hash(x) { + return Hashtbl.hash(x) & 1073741823; +} + +var param = $$Array.map(caml_hash, test_strings); -Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 17, characters 5-12', param, test_strings_hash_results); +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 18, characters 5-12', param, test_strings_hash_results); var param$1 = Hashtbl.hash(0) & 1073741823; -Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 23, characters 5-12', param$1, 129913994); +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 24, characters 5-12', param$1, 129913994); var param$2 = Hashtbl.hash("x") & 1073741823; -Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 26, characters 5-12', param$2, 780510073); +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 27, characters 5-12', param$2, 780510073); var param$3 = Hashtbl.hash("xy") & 1073741823; -Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 29, characters 5-12', param$3, 194127723); +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 30, characters 5-12', param$3, 194127723); + +var param$4 = Hashtbl.hash(/* A */65) & 1073741823; + +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 33, characters 5-12', param$4, 381663642); + +var param$5 = Hashtbl.hash(/* `A */[ + 65, + 3 + ]) & 1073741823; + +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 34, characters 5-12', param$5, 294279345); + +var param$6 = Hashtbl.hash(/* :: */[ + /* `A */[ + 65, + 3 + ], + /* :: */[ + /* `B */[ + 66, + 2 + ], + /* :: */[ + /* `C */[ + 67, + 3 + ], + /* [] */0 + ] + ] + ]) & 1073741823; + +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 35, characters 5-12', param$6, 1017654909); + +var param$7 = Hashtbl.hash(/* :: */[ + /* tuple */[ + /* `A */[ + 65, + "3" + ], + /* `B */[ + 66, + "2" + ] + ], + /* :: */[ + /* tuple */[ + /* `C */[ + 67, + "3" + ], + /* `D */[ + 68, + "4" + ] + ], + /* [] */0 + ] + ]) & 1073741823; + +Mt_global.collect_eq(test_id, suites, 'File "hash_test.ml", line 36, characters 5-12', param$7, 81986873); Mt.from_pair_suites("hash_test.ml", suites[0]); @@ -89,4 +152,5 @@ exports.eq = eq; exports.test_strings = test_strings; exports.test_strings_hash_results = test_strings_hash_results; exports.normalize = normalize; +exports.caml_hash = caml_hash; /* test_strings Not a pure module */ diff --git a/jscomp/test/hash_test.ml b/jscomp/test/hash_test.ml index 45cf2193e0..7757c823fa 100644 --- a/jscomp/test/hash_test.ml +++ b/jscomp/test/hash_test.ml @@ -13,9 +13,10 @@ let test_strings_hash_results = 178511779; 585018975; 544388424; 1043872806; 831138595|] let normalize x = x land 0x3FFFFFFF +let caml_hash x = Hashtbl.hash x |> normalize let () = eq __LOC__ - (test_strings |> Array.map (fun x -> normalize (Hashtbl.hash x) )) + (test_strings |> Array.map caml_hash) test_strings_hash_results @@ -28,5 +29,12 @@ let () = let () = eq __LOC__ (Hashtbl.hash "xy" |> normalize) 194127723 -let () = +let () = + eq __LOC__ (caml_hash `A) 381663642; + eq __LOC__ (caml_hash (`A 3)) 294279345; + eq __LOC__ (caml_hash [`A 3; `B 2 ; `C 3 ]) 1017654909; + eq __LOC__ (caml_hash [`A "3", `B "2" ; `C "3", `D "4"]) (81986873) + + +let () = Mt.from_pair_suites __FILE__ !suites diff --git a/jscomp/test/int_hashtbl_test.js b/jscomp/test/int_hashtbl_test.js new file mode 100644 index 0000000000..84f4c1f712 --- /dev/null +++ b/jscomp/test/int_hashtbl_test.js @@ -0,0 +1,118 @@ +// Generated CODE, PLEASE EDIT WITH CARE +'use strict'; + +var Caml_obj = require("../runtime/caml_obj"); +var Hashtbl = require("../stdlib/hashtbl"); +var Mt = require("./mt"); +var $$Array = require("../stdlib/array"); +var Caml_curry = require("../runtime/caml_curry"); +var List = require("../stdlib/list"); + +function f(H) { + var tbl = Caml_curry.app1(H[0], 17); + Caml_curry.app3(H[4], tbl, 1, /* "1" */49); + Caml_curry.app3(H[4], tbl, 2, /* "2" */50); + return List.sort(function (param, param$1) { + return Caml_obj.caml_int_compare(param[0], param$1[0]); + }, Caml_curry.app3(H[11], function (k, v, acc) { + return /* :: */[ + /* tuple */[ + k, + v + ], + acc + ]; + }, tbl, /* [] */0)); +} + +function g(H) { + return function (count) { + var tbl = Caml_curry.app1(H[0], 17); + for(var i = 0; i<= count; ++i){ + Caml_curry.app3(H[8], tbl, (i << 1), "" + i); + } + for(var i$1 = 0; i$1<= count; ++i$1){ + Caml_curry.app3(H[8], tbl, (i$1 << 1), "" + i$1); + } + var v = Caml_curry.app3(H[11], function (k, v, acc) { + return /* :: */[ + /* tuple */[ + k, + v + ], + acc + ]; + }, tbl, /* [] */0); + return $$Array.of_list(List.sort(function (param, param$1) { + return Caml_obj.caml_int_compare(param[0], param$1[0]); + }, v)); + }; +} + +var hash = Hashtbl.hash + +function equal(x, y) { + return +(x === y); +} + +var Int_hash = Hashtbl.Make(/* module */[ + equal, + hash + ]); + +var suites_000 = /* tuple */[ + "simple", + function () { + return /* Eq */{ + 0: /* :: */[ + /* tuple */[ + 1, + /* "1" */49 + ], + /* :: */[ + /* tuple */[ + 2, + /* "2" */50 + ], + /* [] */0 + ] + ], + 1: f(Int_hash), + length: 2, + tag: 0 + }; + } +]; + +var suites_001 = /* :: */[ + /* tuple */[ + "more_iterations", + function () { + return /* Eq */{ + 0: $$Array.init(1001, function (i) { + return /* tuple */[ + (i << 1), + "" + i + ]; + }), + 1: g(Int_hash)(1000), + length: 2, + tag: 0 + }; + } + ], + /* [] */0 +]; + +var suites = /* :: */[ + suites_000, + suites_001 +]; + +Mt.from_pair_suites("int_hashtbl_test.ml", suites); + +exports.f = f; +exports.g = g; +exports.Int_hash = Int_hash; +exports.suites = suites; +/* Int_hash Not a pure module */ diff --git a/jscomp/test/int_hashtbl_test.ml b/jscomp/test/int_hashtbl_test.ml new file mode 100644 index 0000000000..ce16841872 --- /dev/null +++ b/jscomp/test/int_hashtbl_test.ml @@ -0,0 +1,46 @@ +open Hashtbl + +module type S = Hashtbl.S with type key = int + +(* +let to_list (module H : S) (tbl : 'a H.t) = + H.fold (fun k v acc -> (k,v)::acc) tbl [] +*) + +let f (module H : Hashtbl.S with type key = int) = + (* let module Hashtbl = (val Hashtbl) in *) + let tbl = H.create 17 in + H.add tbl 1 '1'; + H.add tbl 2 '2'; + List.sort (fun ((a : int),_) (b,_) -> compare a b ) + @@ H.fold (fun k v acc -> (k,v)::acc) tbl [] + +let g (module H : S) count = + let tbl = H.create 17 in + for i = 0 to count do + H.replace tbl (i * 2) (string_of_int i) + done; + for i = 0 to count do + H.replace tbl (i * 2) (string_of_int i) + done; + let v = H.fold (fun k v acc -> (k,v)::acc) tbl [] in + let v = List.sort (fun (x, _) ((y : int), _) -> compare x y) @@ v in + Array.of_list v + +module Int_hash = + Hashtbl.Make( struct type t = int + let hash x = Hashtbl.hash x + let equal (x : int) y = x = y + end) + +let suites = Mt.[ + + "simple", (fun _ -> Eq ([1,'1';2,'2'], f (module Int_hash ))); + "more_iterations", + (fun _ -> + let count = 1000 in + Eq( Array.init (count + 1) (fun i -> (2 * i, string_of_int i) ), + g (module Int_hash) count)) +] + +;; Mt.from_pair_suites __FILE__ suites diff --git a/jscomp/test/mt.ml b/jscomp/test/mt.ml index 3bdcd9fd52..670a3503d3 100644 --- a/jscomp/test/mt.ml +++ b/jscomp/test/mt.ml @@ -58,3 +58,23 @@ let from_pair_suites name (suites : pair_suites) = ) ) ) +(* +Note that [require] is a file local value, +we need type [require] + +let is_top : unit -> Js.boolean = [%bs.raw{| +function (_){ +console.log('hi'); +if (typeof require === "undefined"){ + return false +} else { + console.log("hey",require.main.filename); + return require.main === module; +} +} +|}] + +let from_pair_suites_non_top name suites = + if not @@ Js.to_bool @@ is_top () then + from_pair_suites name suites +*) diff --git a/jscomp/test/mt.mli b/jscomp/test/mt.mli index 8ca53755ab..97706cc9e9 100644 --- a/jscomp/test/mt.mli +++ b/jscomp/test/mt.mli @@ -10,3 +10,4 @@ val from_pair_suites : string -> pair_suites -> unit + diff --git a/jscomp/test/test.mllib b/jscomp/test/test.mllib index eb2b035b47..ca4343b1a8 100644 --- a/jscomp/test/test.mllib +++ b/jscomp/test/test.mllib @@ -136,6 +136,7 @@ stack_test test_stack complex_while_loop hashtbl_test +int_hashtbl_test cont_int_fold_test test_incomplete test_ramification