Skip to content

Commit 8ba0290

Browse files
committed
Runtime: reimplement weak/ephemeron
1 parent 59f4693 commit 8ba0290

File tree

2 files changed

+182
-78
lines changed

2 files changed

+182
-78
lines changed

compiler/tests-jsoo/test_weak.ml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
(* Js_of_ocaml tests
2+
* http://www.ocsigen.org/js_of_ocaml/
3+
* Copyright (C) 2024 Hugo Heuzard
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, with linking exception;
8+
* either version 2.1 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18+
*)
19+
20+
let%expect_test _ =
21+
let k1 = Some 2 in
22+
let k2 = Some 3 in
23+
let k3 = Some 4 in
24+
let d = k1, k2, k3 in
25+
let e = Ephemeron.Kn.make [| k1; k2; k3 |] d in
26+
(match Ephemeron.Kn.query e [| k1; k2; k3 |] with
27+
| None -> print_endline "none"
28+
| Some d' ->
29+
assert (d = d');
30+
print_endline "found");
31+
[%expect {| found |}]
32+
33+
let%expect_test _ =
34+
let module K = struct
35+
type t = int option
36+
37+
let equal (a : t) (b : t) = a = b
38+
39+
let hash (x : t) = Hashtbl.hash x
40+
end in
41+
let module T = Ephemeron.Kn.Make (K) in
42+
let f y =
43+
let k1 = Some 2 in
44+
let k2 = Some 3 in
45+
let k3 = Some y in
46+
let d = k1, k2, k3 in
47+
let t = T.create 10 in
48+
T.add t [| k1; k2; k3 |] d;
49+
T.add t [| k2; k3; k1 |] d;
50+
T.add t [| k3; k1; k2 |] d;
51+
match T.find_opt t [| k1; k2; k3 |] with
52+
| None -> print_endline "none"
53+
| Some d' ->
54+
assert (d = d');
55+
print_endline "found"
56+
in
57+
f 3;
58+
f 2;
59+
[%expect {|
60+
found
61+
found
62+
|}]

runtime/weak.js

Lines changed: 120 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -25,79 +25,85 @@ var caml_ephe_key_offset = 3;
2525
//Provides: caml_ephe_data_offset
2626
var caml_ephe_data_offset = 2;
2727

28+
//Provides: caml_ephe_none
29+
var caml_ephe_none = { caml_ephe_none: 0 };
30+
2831
//Provides: caml_ephe_set_key
2932
//Requires: caml_invalid_argument, caml_ephe_key_offset
33+
//Requires: caml_ephe_get_data
34+
//Requires: caml_ephe_set_data_opt
3035
function caml_ephe_set_key(x, i, v) {
3136
if (i < 0 || caml_ephe_key_offset + i >= x.length)
3237
caml_invalid_argument("Weak.set");
33-
if (v instanceof Object && globalThis.WeakRef) {
34-
if (x[1].register) x[1].register(v, undefined, v);
35-
x[caml_ephe_key_offset + i] = new globalThis.WeakRef(v);
36-
} else x[caml_ephe_key_offset + i] = v;
38+
var old = caml_ephe_get_data(x);
39+
if (globalThis.WeakRef && v instanceof Object) v = new globalThis.WeakRef(v);
40+
x[caml_ephe_key_offset + i] = v;
41+
caml_ephe_set_data_opt(x, old);
3742
return 0;
3843
}
3944

4045
//Provides: caml_ephe_unset_key
4146
//Requires: caml_invalid_argument, caml_ephe_key_offset
47+
//Requires: caml_ephe_get_data
48+
//Requires: caml_ephe_set_data_opt
49+
//Requires: caml_ephe_none
4250
function caml_ephe_unset_key(x, i) {
4351
if (i < 0 || caml_ephe_key_offset + i >= x.length)
44-
caml_invalid_argument("Weak.set");
45-
if (
46-
globalThis.WeakRef &&
47-
x[caml_ephe_key_offset + i] instanceof globalThis.WeakRef &&
48-
x[1].unregister
49-
) {
50-
var old = x[caml_ephe_key_offset + i].deref();
51-
if (old !== undefined) {
52-
var count = 0;
53-
for (var j = caml_ephe_key_offset; j < x.length; j++) {
54-
var key = x[j];
55-
if (key instanceof globalThis.WeakRef) {
56-
key = key.deref();
57-
if (key === old) count++;
58-
}
59-
}
60-
if (count === 1) x[1].unregister(old);
61-
}
62-
}
63-
x[caml_ephe_key_offset + i] = undefined;
52+
caml_invalid_argument("Weak.unset");
53+
var old = caml_ephe_get_data(x);
54+
x[caml_ephe_key_offset + i] = caml_ephe_none;
55+
caml_ephe_set_data_opt(x, old);
6456
return 0;
6557
}
6658

6759
//Provides: caml_ephe_create
68-
//Requires: caml_weak_create, caml_ephe_data_offset
60+
//Requires: caml_weak_create
6961
function caml_ephe_create(n) {
70-
var x = caml_weak_create(n);
71-
return x;
62+
return caml_weak_create(n);
7263
}
7364

7465
//Provides: caml_weak_create
75-
//Requires: caml_ephe_key_offset, caml_invalid_argument,caml_ephe_data_offset
66+
//Requires: caml_ephe_key_offset, caml_invalid_argument
67+
//Requires: caml_ephe_none
7668
function caml_weak_create(n) {
7769
if (n < 0) caml_invalid_argument("Weak.create");
78-
var x = [251, "caml_ephe_list_head"];
79-
x.length = caml_ephe_key_offset + n;
70+
var alen = caml_ephe_key_offset + n;
71+
var x = new Array(alen);
72+
x[0] = 251;
73+
x[1] = "caml_ephe_list_head";
74+
for (var i = 2; i < alen; i++) {
75+
x[i] = caml_ephe_none;
76+
}
8077
return x;
8178
}
8279

8380
//Provides: caml_weak_set
84-
//Requires: caml_invalid_argument
8581
//Requires: caml_ephe_set_key, caml_ephe_unset_key
8682
function caml_weak_set(x, i, v) {
8783
if (v === 0) caml_ephe_unset_key(x, i);
8884
else caml_ephe_set_key(x, i, v[1]);
8985
return 0;
9086
}
9187
//Provides: caml_ephe_get_key
92-
//Requires: caml_ephe_key_offset, caml_invalid_argument
88+
//Requires: caml_ephe_key_offset, caml_ephe_data_offset
89+
//Requires: caml_invalid_argument
90+
//Requires: caml_ephe_none
9391
//Alias: caml_weak_get
92+
9493
function caml_ephe_get_key(x, i) {
9594
if (i < 0 || caml_ephe_key_offset + i >= x.length)
96-
caml_invalid_argument("Weak.get_key");
95+
caml_invalid_argument("Weak.get");
9796
var weak = x[caml_ephe_key_offset + i];
98-
if (globalThis.WeakRef && weak instanceof globalThis.WeakRef)
97+
if (weak === caml_ephe_none) return 0;
98+
if (globalThis.WeakRef && weak instanceof globalThis.WeakRef) {
9999
weak = weak.deref();
100-
return weak === undefined ? 0 : [0, weak];
100+
if (weak === undefined) {
101+
x[caml_ephe_key_offset + i] = caml_ephe_none;
102+
x[caml_ephe_data_offset] = caml_ephe_none;
103+
return 0;
104+
}
105+
}
106+
return [0, weak];
101107
}
102108
//Provides: caml_ephe_get_key_copy
103109
//Requires: caml_ephe_get_key,caml_ephe_key_offset
@@ -114,21 +120,34 @@ function caml_ephe_get_key_copy(x, i) {
114120
}
115121

116122
//Provides: caml_ephe_check_key mutable
117-
//Requires: caml_ephe_key_offset
123+
//Requires: caml_ephe_key_offset, caml_ephe_data_offset
124+
//Requires: caml_invalid_argument
125+
//Requires: caml_ephe_none
118126
//Alias: caml_weak_check
119127
function caml_ephe_check_key(x, i) {
128+
if (i < 0 || caml_ephe_key_offset + i >= x.length)
129+
caml_invalid_argument("Weak.check");
120130
var weak = x[caml_ephe_key_offset + i];
121-
if (globalThis.WeakRef && weak instanceof globalThis.WeakRef)
131+
if (weak === caml_ephe_none) return 0;
132+
if (globalThis.WeakRef && weak instanceof globalThis.WeakRef) {
122133
weak = weak.deref();
123-
if (weak === undefined) return 0;
124-
else return 1;
134+
if (weak === undefined) {
135+
x[caml_ephe_key_offset + i] = caml_ephe_none;
136+
x[caml_ephe_data_offset] = caml_ephe_none;
137+
return 0;
138+
}
139+
}
140+
return 1;
125141
}
126142

127143
//Provides: caml_ephe_blit_key
128144
//Requires: caml_array_blit
129145
//Requires: caml_ephe_key_offset
146+
//Requires: caml_ephe_get_data
147+
//Requires: caml_ephe_set_data_opt
130148
//Alias: caml_weak_blit
131149
function caml_ephe_blit_key(a1, i1, a2, i2, len) {
150+
var old = caml_ephe_get_data(a1);
132151
// minus one because caml_array_blit works on ocaml array
133152
caml_array_blit(
134153
a1,
@@ -137,77 +156,100 @@ function caml_ephe_blit_key(a1, i1, a2, i2, len) {
137156
caml_ephe_key_offset + i2 - 1,
138157
len,
139158
);
159+
caml_ephe_set_data_opt(a2, old);
140160
return 0;
141161
}
142162

143163
//Provides: caml_ephe_blit_data
144-
//Requires: caml_ephe_data_offset, caml_ephe_set_data, caml_ephe_unset_data
164+
//Requires: caml_ephe_get_data, caml_ephe_set_data_opt
145165
function caml_ephe_blit_data(src, dst) {
146-
var n = src[caml_ephe_data_offset];
147-
if (n === undefined) caml_ephe_unset_data(dst);
148-
else caml_ephe_set_data(dst, n);
166+
var old = caml_ephe_get_data(src);
167+
caml_ephe_set_data_opt(dst, old);
149168
return 0;
150169
}
151170

152171
//Provides: caml_ephe_get_data
153-
//Requires: caml_ephe_data_offset
172+
//Requires: caml_ephe_data_offset, caml_ephe_key_offset
173+
//Requires: caml_ephe_none
154174
function caml_ephe_get_data(x) {
155-
if (x[caml_ephe_data_offset] === undefined) return 0;
156-
else return [0, x[caml_ephe_data_offset]];
175+
var data = x[caml_ephe_data_offset];
176+
if (data === caml_ephe_none) return 0;
177+
for (var i = caml_ephe_key_offset; i < x.length; i++) {
178+
var k = x[i];
179+
if (globalThis.WeakRef && k instanceof globalThis.WeakRef) {
180+
var d = k.deref();
181+
if (d === undefined) {
182+
x[i] = caml_ephe_none;
183+
x[caml_ephe_data_offset] = caml_ephe_none;
184+
return 0;
185+
}
186+
if (globalThis.WeakMap) {
187+
data = data.get(k);
188+
if (data === undefined) {
189+
x[caml_ephe_data_offset] = caml_ephe_none;
190+
return 0;
191+
}
192+
}
193+
}
194+
}
195+
return [0, data];
157196
}
158197

159198
//Provides: caml_ephe_get_data_copy
160-
//Requires: caml_ephe_data_offset
199+
//Requires: caml_ephe_get_data
161200
//Requires: caml_obj_dup
162201
function caml_ephe_get_data_copy(x) {
163-
if (x[caml_ephe_data_offset] === undefined) return 0;
164-
else return [0, caml_obj_dup(x[caml_ephe_data_offset])];
202+
var r = caml_ephe_get_data(x);
203+
if (r === 0) return 0;
204+
var z = r[1];
205+
if (Array.isArray(z)) return [0, caml_obj_dup(z)];
206+
return z;
165207
}
166208

167209
//Provides: caml_ephe_set_data
168-
//Requires: caml_ephe_data_offset, caml_ephe_key_offset, caml_ephe_unset_data
210+
//Requires: caml_ephe_data_offset, caml_ephe_key_offset
211+
//Requires: caml_ephe_none
169212
function caml_ephe_set_data(x, data) {
170-
if (globalThis.FinalizationRegistry && globalThis.WeakRef) {
171-
if (!(x[1] instanceof globalThis.FinalizationRegistry)) {
172-
x[1] = new globalThis.FinalizationRegistry(function () {
173-
caml_ephe_unset_data(x);
174-
});
175-
//register all keys
176-
for (var j = caml_ephe_key_offset; j < x.length; j++) {
177-
var key = x[j];
178-
if (key instanceof globalThis.WeakRef) {
179-
key = key.deref();
180-
if (key) x[1].register(key, undefined, key);
181-
}
213+
for (var i = x.length - 1; i >= caml_ephe_key_offset; i--) {
214+
var k = x[i];
215+
if (globalThis.WeakRef && k instanceof globalThis.WeakRef) {
216+
var d = k.deref();
217+
if (d === undefined) {
218+
x[i] = caml_ephe_none;
219+
continue;
220+
}
221+
if (globalThis.WeakMap) {
222+
var data2 = new globalThis.WeakMap();
223+
data2.set(k, data);
224+
data = data2;
182225
}
183226
}
184227
}
185228
x[caml_ephe_data_offset] = data;
186229
return 0;
187230
}
188231

232+
//Provides: caml_ephe_set_data_opt
233+
//Requires: caml_ephe_set_data
234+
//Requires: caml_ephe_unset_data
235+
function caml_ephe_set_data_opt(x, data_opt) {
236+
if (data_opt === 0) caml_ephe_unset_data(x);
237+
else caml_ephe_set_data(x, data_opt[1]);
238+
return 0;
239+
}
240+
189241
//Provides: caml_ephe_unset_data
190-
//Requires: caml_ephe_data_offset, caml_ephe_key_offset
242+
//Requires: caml_ephe_data_offset
243+
//Requires: caml_ephe_none
191244
function caml_ephe_unset_data(x) {
192-
if (globalThis.FinalizationRegistry && globalThis.WeakRef) {
193-
if (x[1] instanceof globalThis.FinalizationRegistry) {
194-
//unregister all keys
195-
for (var j = caml_ephe_key_offset; j < x.length; j++) {
196-
var key = x[j];
197-
if (key instanceof globalThis.WeakRef) {
198-
key = key.deref();
199-
if (key) x[1].unregister(key);
200-
}
201-
}
202-
}
203-
}
204-
x[caml_ephe_data_offset] = undefined;
245+
x[caml_ephe_data_offset] = caml_ephe_none;
205246
return 0;
206247
}
207248

208249
//Provides: caml_ephe_check_data
209-
//Requires: caml_ephe_data_offset
250+
//Requires: caml_ephe_get_data
210251
function caml_ephe_check_data(x) {
211-
if (x[caml_ephe_data_offset] === undefined) return 0;
252+
var data = caml_ephe_get_data(x);
253+
if (data === 0) return 0;
212254
else return 1;
213255
}

0 commit comments

Comments
 (0)