on master help sign in
Merge pull request #49 from fitzgen/benches-and-fixing-unsoundness
35c85d5Nick Fitzgerald
.travis.yml+6−2
@@ -32,13 +32,17 @@ matrix:
32 rust: stable
32 rust: stable
33 addons:
33 addons:
34 firefox: latest
34 firefox: latest
35− script: wasm-pack test --firefox --headless
35+ script: wasm-pack test --firefox --headless -- --features xxx-unstable-internal-use-only
36 - name: "headless browser tests (chrome)"
36 - name: "headless browser tests (chrome)"
37 rust: stable
37 rust: stable
38 addons:
38 addons:
39 chrome: stable
39 chrome: stable
40− script: wasm-pack test --chrome --headless
40+ script: wasm-pack test --chrome --headless -- --features xxx-unstable-internal-use-only
41 - name: "native tests"
41 - name: "native tests"
42 rust: stable
42 rust: stable
43 install: echo "no install"
43 install: echo "no install"
44 script: cargo test --all
44 script: cargo test --all
45+ - name: "check native benches"
46+ rust: nightly
47+ install: echo "no install"
48+ script: cargo check --benches --features xxx-unstable-internal-use-only
Cargo.lock+418−114
@@ -1,17 +1,25 @@
1[[package]]
1[[package]]
2name = "aho-corasick"
2name = "aho-corasick"
3−version = "0.6.10"
3+version = "0.7.3"
4source = "registry+https://github.com/rust-lang/crates.io-index"
4source = "registry+https://github.com/rust-lang/crates.io-index"
5dependencies = [
5dependencies = [
6 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
6 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
7]
7]
8
8
9+[[package]]
10+name = "arrayvec"
11+version = "0.4.10"
12+source = "registry+https://github.com/rust-lang/crates.io-index"
13+dependencies = [
14+ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
15+]
16+
9[[package]]
17[[package]]
10name = "atty"
18name = "atty"
11version = "0.2.11"
19version = "0.2.11"
12source = "registry+https://github.com/rust-lang/crates.io-index"
20source = "registry+https://github.com/rust-lang/crates.io-index"
13dependencies = [
21dependencies = [
14− "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
22+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
15 "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
23 "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
16 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
24 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
17]
25]
@@ -29,7 +37,7 @@ dependencies = [
29 "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
37 "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
30 "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
38 "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
31 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
39 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
32− "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
40+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
33 "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
41 "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
34 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
42 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
35]
43]
@@ -39,10 +47,15 @@ name = "backtrace-sys"
39version = "0.1.28"
47version = "0.1.28"
40source = "registry+https://github.com/rust-lang/crates.io-index"
48source = "registry+https://github.com/rust-lang/crates.io-index"
41dependencies = [
49dependencies = [
42− "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
50+ "cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
43− "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
51+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
44]
52]
45
53
54+[[package]]
55+name = "bitflags"
56+version = "1.0.4"
57+source = "registry+https://github.com/rust-lang/crates.io-index"
58+
46[[package]]
59[[package]]
47name = "bumpalo"
60name = "bumpalo"
48version = "2.3.0"
61version = "2.3.0"
@@ -53,9 +66,14 @@ name = "byteorder"
53version = "1.3.1"
66version = "1.3.1"
54source = "registry+https://github.com/rust-lang/crates.io-index"
67source = "registry+https://github.com/rust-lang/crates.io-index"
55
68
69+[[package]]
70+name = "cast"
71+version = "0.2.2"
72+source = "registry+https://github.com/rust-lang/crates.io-index"
73+
56[[package]]
74[[package]]
57name = "cc"
75name = "cc"
58−version = "1.0.31"
76+version = "1.0.32"
59source = "registry+https://github.com/rust-lang/crates.io-index"
77source = "registry+https://github.com/rust-lang/crates.io-index"
60
78
61[[package]]
79[[package]]
@@ -63,13 +81,31 @@ name = "cfg-if"
63version = "0.1.7"
81version = "0.1.7"
64source = "registry+https://github.com/rust-lang/crates.io-index"
82source = "registry+https://github.com/rust-lang/crates.io-index"
65
83
84+[[package]]
85+name = "clap"
86+version = "2.32.0"
87+source = "registry+https://github.com/rust-lang/crates.io-index"
88+dependencies = [
89+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
90+ "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
91+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
92+]
93+
94+[[package]]
95+name = "cloudabi"
96+version = "0.0.3"
97+source = "registry+https://github.com/rust-lang/crates.io-index"
98+dependencies = [
99+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
100+]
101+
66[[package]]
102[[package]]
67name = "console_error_panic_hook"
103name = "console_error_panic_hook"
68version = "0.1.6"
104version = "0.1.6"
69source = "registry+https://github.com/rust-lang/crates.io-index"
105source = "registry+https://github.com/rust-lang/crates.io-index"
70dependencies = [
106dependencies = [
71 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
107 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
72− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
108+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
73]
109]
74
110
75[[package]]
111[[package]]
@@ -78,7 +114,89 @@ version = "0.1.2"
78source = "registry+https://github.com/rust-lang/crates.io-index"
114source = "registry+https://github.com/rust-lang/crates.io-index"
79dependencies = [
115dependencies = [
80 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
116 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
117+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
118+]
119+
120+[[package]]
121+name = "criterion"
122+version = "0.2.10"
123+source = "registry+https://github.com/rust-lang/crates.io-index"
124+dependencies = [
125+ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
126+ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
127+ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
128+ "criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
129+ "csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
130+ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
131+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
132+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
133+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
134+ "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
135+ "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
136+ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
137+ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
138+ "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
139+ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
140+ "tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
141+ "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
142+]
143+
144+[[package]]
145+name = "criterion-plot"
146+version = "0.3.0"
147+source = "registry+https://github.com/rust-lang/crates.io-index"
148+dependencies = [
149+ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
150+ "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
151+ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
152+]
153+
154+[[package]]
155+name = "crossbeam-deque"
156+version = "0.2.0"
157+source = "registry+https://github.com/rust-lang/crates.io-index"
158+dependencies = [
159+ "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
160+ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
161+]
162+
163+[[package]]
164+name = "crossbeam-epoch"
165+version = "0.3.1"
166+source = "registry+https://github.com/rust-lang/crates.io-index"
167+dependencies = [
168+ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
169+ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
170+ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
171+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
172+ "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
173+ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
174+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
175+]
176+
177+[[package]]
178+name = "crossbeam-utils"
179+version = "0.2.2"
180+source = "registry+https://github.com/rust-lang/crates.io-index"
181+dependencies = [
182+ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
183+]
184+
185+[[package]]
186+name = "csv"
187+version = "1.0.5"
188+source = "registry+https://github.com/rust-lang/crates.io-index"
189+dependencies = [
190+ "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
191+ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
192+]
193+
194+[[package]]
195+name = "csv-core"
196+version = "0.1.5"
197+source = "registry+https://github.com/rust-lang/crates.io-index"
198+dependencies = [
81− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
199+ "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
82]
200]
83
201
84[[package]]
202[[package]]
@@ -88,15 +206,16 @@ dependencies = [
88 "bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
206 "bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
89 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
207 "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
90 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
208 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
209+ "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
91 "dodrio-js-api 0.1.0",
210 "dodrio-js-api 0.1.0",
92 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
211 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
93 "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
212 "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
94− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
213+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
95 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
214 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
96− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
215+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
97− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
216+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
98− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
217+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
99− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
218+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
100]
219]
101
220
102[[package]]
221[[package]]
@@ -107,9 +226,9 @@ dependencies = [
107 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
226 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
108 "dodrio 0.1.0",
227 "dodrio 0.1.0",
109 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
228 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
110− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
229+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
111− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
230+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
112− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
231+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
113]
232]
114
233
115[[package]]
234[[package]]
@@ -119,11 +238,11 @@ dependencies = [
119 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
238 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
120 "dodrio 0.1.0",
239 "dodrio 0.1.0",
121 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
240 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
122− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
241+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
123− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
242+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
124− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
243+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
125− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
244+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
126− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
245+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
127]
246]
128
247
129[[package]]
248[[package]]
@@ -132,9 +251,9 @@ version = "0.1.0"
132dependencies = [
251dependencies = [
133 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
252 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
134 "dodrio 0.1.0",
253 "dodrio 0.1.0",
135− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
254+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
136− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
255+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
137− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
256+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
138]
257]
139
258
140[[package]]
259[[package]]
@@ -143,9 +262,9 @@ version = "0.1.0"
143dependencies = [
262dependencies = [
144 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
263 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
145 "dodrio 0.1.0",
264 "dodrio 0.1.0",
146− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
265+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
147− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
266+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
148− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
267+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
149]
268]
150
269
151[[package]]
270[[package]]
@@ -154,10 +273,10 @@ version = "0.1.0"
154dependencies = [
273dependencies = [
155 "dodrio 0.1.0",
274 "dodrio 0.1.0",
156 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
275 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
157− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
276+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
158− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
277+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
159− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
278+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
160− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
279+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
161]
280]
162
281
163[[package]]
282[[package]]
@@ -167,9 +286,9 @@ dependencies = [
167 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
286 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
168 "dodrio 0.1.0",
287 "dodrio 0.1.0",
169 "dodrio-js-api 0.1.0",
288 "dodrio-js-api 0.1.0",
170− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
289+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
171− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
290+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
172− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
291+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
173]
292]
174
293
175[[package]]
294[[package]]
@@ -181,12 +300,12 @@ dependencies = [
181 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
300 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
182 "dodrio 0.1.0",
301 "dodrio 0.1.0",
183 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
302 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
184− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
303+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
185 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
304 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
186− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
305+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
187− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
306+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
188− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
307+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
189− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
308+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
190]
309]
191
310
192[[package]]
311[[package]]
@@ -196,11 +315,11 @@ dependencies = [
196 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
315 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
197 "dodrio 0.1.0",
316 "dodrio 0.1.0",
198 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
317 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
199− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
318+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
200− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
319+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
201− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
320+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
202− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
321+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
203− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
322+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
204]
323]
205
324
206[[package]]
325[[package]]
@@ -212,16 +331,21 @@ dependencies = [
212 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
331 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
213 "dodrio 0.1.0",
332 "dodrio 0.1.0",
214 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
333 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
215− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
334+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
216 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
335 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
217 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
336 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
218 "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
337 "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
219− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
338+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
220− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
339+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
221− "wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
340+ "wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
222− "web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
341+ "web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
223]
342]
224
343
344+[[package]]
345+name = "either"
346+version = "1.5.1"
347+source = "registry+https://github.com/rust-lang/crates.io-index"
348+
225[[package]]
349[[package]]
226name = "env_logger"
350name = "env_logger"
227version = "0.6.1"
351version = "0.6.1"
@@ -230,7 +354,7 @@ dependencies = [
230 "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
354 "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
231 "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
355 "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
232 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
356 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
233− "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
357+ "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
234 "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
358 "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
235]
359]
236
360
@@ -250,10 +374,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
250dependencies = [
374dependencies = [
251 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
375 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
252 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
376 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
253− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
377+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
254 "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
378 "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
255]
379]
256
380
381+[[package]]
382+name = "fuchsia-cprng"
383+version = "0.1.1"
384+source = "registry+https://github.com/rust-lang/crates.io-index"
385+
257[[package]]
386[[package]]
258name = "futures"
387name = "futures"
259version = "0.1.26"
388version = "0.1.26"
@@ -283,6 +412,14 @@ dependencies = [
283 "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
412 "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
284]
413]
285
414
415+[[package]]
416+name = "itertools"
417+version = "0.8.0"
418+source = "registry+https://github.com/rust-lang/crates.io-index"
419+dependencies = [
420+ "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
421+]
422+
286[[package]]
423[[package]]
287name = "itoa"
424name = "itoa"
288version = "0.4.3"
425version = "0.4.3"
@@ -290,10 +427,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
290
427
291[[package]]
428[[package]]
292name = "js-sys"
429name = "js-sys"
293−version = "0.3.16"
430+version = "0.3.17"
294source = "registry+https://github.com/rust-lang/crates.io-index"
431source = "registry+https://github.com/rust-lang/crates.io-index"
295dependencies = [
432dependencies = [
296− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
433+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
297]
434]
298
435
299[[package]]
436[[package]]
@@ -303,7 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
303
440
304[[package]]
441[[package]]
305name = "libc"
442name = "libc"
306−version = "0.2.50"
443+version = "0.2.51"
307source = "registry+https://github.com/rust-lang/crates.io-index"
444source = "registry+https://github.com/rust-lang/crates.io-index"
308
445
309[[package]]
446[[package]]
@@ -318,16 +455,42 @@ dependencies = [
318name = "memchr"
455name = "memchr"
319version = "2.2.0"
456version = "2.2.0"
320source = "registry+https://github.com/rust-lang/crates.io-index"
457source = "registry+https://github.com/rust-lang/crates.io-index"
458+dependencies = [
459+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
460+]
461+
462+[[package]]
463+name = "memoffset"
464+version = "0.2.1"
465+source = "registry+https://github.com/rust-lang/crates.io-index"
466+
467+[[package]]
468+name = "nodrop"
469+version = "0.1.13"
470+source = "registry+https://github.com/rust-lang/crates.io-index"
321
471
322[[package]]
472[[package]]
323name = "nom"
473name = "nom"
324−version = "4.2.2"
474+version = "4.2.3"
325source = "registry+https://github.com/rust-lang/crates.io-index"
475source = "registry+https://github.com/rust-lang/crates.io-index"
326dependencies = [
476dependencies = [
327 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
477 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
328 "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
478 "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
329]
479]
330
480
481+[[package]]
482+name = "num-traits"
483+version = "0.2.6"
484+source = "registry+https://github.com/rust-lang/crates.io-index"
485+
486+[[package]]
487+name = "num_cpus"
488+version = "1.10.0"
489+source = "registry+https://github.com/rust-lang/crates.io-index"
490+dependencies = [
491+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
492+]
493+
331[[package]]
494[[package]]
332name = "proc-macro2"
495name = "proc-macro2"
333version = "0.4.27"
496version = "0.4.27"
@@ -349,9 +512,73 @@ dependencies = [
349 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
512 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
350]
513]
351
514
515+[[package]]
516+name = "rand_core"
517+version = "0.3.1"
518+source = "registry+https://github.com/rust-lang/crates.io-index"
519+dependencies = [
520+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
521+]
522+
523+[[package]]
524+name = "rand_core"
525+version = "0.4.0"
526+source = "registry+https://github.com/rust-lang/crates.io-index"
527+
528+[[package]]
529+name = "rand_os"
530+version = "0.1.3"
531+source = "registry+https://github.com/rust-lang/crates.io-index"
532+dependencies = [
533+ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
534+ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
535+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
536+ "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
537+ "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
538+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
539+]
540+
541+[[package]]
542+name = "rand_xoshiro"
543+version = "0.1.0"
544+source = "registry+https://github.com/rust-lang/crates.io-index"
545+dependencies = [
546+ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
547+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
548+]
549+
550+[[package]]
551+name = "rayon"
552+version = "1.0.3"
553+source = "registry+https://github.com/rust-lang/crates.io-index"
554+dependencies = [
555+ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
556+ "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
557+ "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
558+]
559+
560+[[package]]
561+name = "rayon-core"
562+version = "1.4.1"
563+source = "registry+https://github.com/rust-lang/crates.io-index"
564+dependencies = [
565+ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
566+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
567+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
568+ "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
569+]
570+
571+[[package]]
572+name = "rdrand"
573+version = "0.4.0"
574+source = "registry+https://github.com/rust-lang/crates.io-index"
575+dependencies = [
576+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
577+]
578+
352[[package]]
579[[package]]
353name = "redox_syscall"
580name = "redox_syscall"
354−version = "0.1.51"
581+version = "0.1.52"
355source = "registry+https://github.com/rust-lang/crates.io-index"
582source = "registry+https://github.com/rust-lang/crates.io-index"
356
583
357[[package]]
584[[package]]
@@ -359,24 +586,24 @@ name = "redox_termios"
359version = "0.1.1"
586version = "0.1.1"
360source = "registry+https://github.com/rust-lang/crates.io-index"
587source = "registry+https://github.com/rust-lang/crates.io-index"
361dependencies = [
588dependencies = [
362− "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
589+ "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)",
363]
590]
364
591
365[[package]]
592[[package]]
366name = "regex"
593name = "regex"
367−version = "1.1.2"
594+version = "1.1.5"
368source = "registry+https://github.com/rust-lang/crates.io-index"
595source = "registry+https://github.com/rust-lang/crates.io-index"
369dependencies = [
596dependencies = [
370− "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
597+ "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
371 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
598 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
372− "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
599+ "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
373 "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
600 "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
374 "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
601 "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
375]
602]
376
603
377[[package]]
604[[package]]
378name = "regex-syntax"
605name = "regex-syntax"
379−version = "0.6.5"
606+version = "0.6.6"
380source = "registry+https://github.com/rust-lang/crates.io-index"
607source = "registry+https://github.com/rust-lang/crates.io-index"
381dependencies = [
608dependencies = [
382 "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
609 "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -392,11 +619,24 @@ name = "ryu"
392version = "0.2.7"
619version = "0.2.7"
393source = "registry+https://github.com/rust-lang/crates.io-index"
620source = "registry+https://github.com/rust-lang/crates.io-index"
394
621
622+[[package]]
623+name = "same-file"
624+version = "1.0.4"
625+source = "registry+https://github.com/rust-lang/crates.io-index"
626+dependencies = [
627+ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
628+]
629+
395[[package]]
630[[package]]
396name = "scoped-tls"
631name = "scoped-tls"
397version = "1.0.0"
632version = "1.0.0"
398source = "registry+https://github.com/rust-lang/crates.io-index"
633source = "registry+https://github.com/rust-lang/crates.io-index"
399
634
635+[[package]]
636+name = "scopeguard"
637+version = "0.3.3"
638+source = "registry+https://github.com/rust-lang/crates.io-index"
639+
400[[package]]
640[[package]]
401name = "serde"
641name = "serde"
402version = "1.0.89"
642version = "1.0.89"
@@ -412,7 +652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
412dependencies = [
652dependencies = [
413 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
653 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
414 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
654 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
415− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
655+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
416]
656]
417
657
418[[package]]
658[[package]]
@@ -432,7 +672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
432
672
433[[package]]
673[[package]]
434name = "syn"
674name = "syn"
435−version = "0.15.29"
675+version = "0.15.30"
436source = "registry+https://github.com/rust-lang/crates.io-index"
676source = "registry+https://github.com/rust-lang/crates.io-index"
437dependencies = [
677dependencies = [
438 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
678 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -447,7 +687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
447dependencies = [
687dependencies = [
448 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
688 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
449 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
689 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
450− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
690+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
451 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
691 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
452]
692]
453
693
@@ -464,11 +704,19 @@ name = "termion"
464version = "1.5.1"
704version = "1.5.1"
465source = "registry+https://github.com/rust-lang/crates.io-index"
705source = "registry+https://github.com/rust-lang/crates.io-index"
466dependencies = [
706dependencies = [
467− "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
707+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
468− "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
708+ "redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)",
469 "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
709 "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
470]
710]
471
711
712+[[package]]
713+name = "textwrap"
714+version = "0.10.0"
715+source = "registry+https://github.com/rust-lang/crates.io-index"
716+dependencies = [
717+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
718+]
719+
472[[package]]
720[[package]]
473name = "thread_local"
721name = "thread_local"
474version = "0.3.6"
722version = "0.3.6"
@@ -477,6 +725,15 @@ dependencies = [
477 "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
725 "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
478]
726]
479
727
728+[[package]]
729+name = "tinytemplate"
730+version = "1.0.1"
731+source = "registry+https://github.com/rust-lang/crates.io-index"
732+dependencies = [
733+ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
734+ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
735+]
736+
480[[package]]
737[[package]]
481name = "ucd-util"
738name = "ucd-util"
482version = "0.1.3"
739version = "0.1.3"
@@ -487,6 +744,11 @@ name = "unicode-segmentation"
487version = "1.2.1"
744version = "1.2.1"
488source = "registry+https://github.com/rust-lang/crates.io-index"
745source = "registry+https://github.com/rust-lang/crates.io-index"
489
746
747+[[package]]
748+name = "unicode-width"
749+version = "0.1.5"
750+source = "registry+https://github.com/rust-lang/crates.io-index"
751+
490[[package]]
752[[package]]
491name = "unicode-xid"
753name = "unicode-xid"
492version = "0.1.0"
754version = "0.1.0"
@@ -502,17 +764,27 @@ name = "version_check"
502version = "0.1.5"
764version = "0.1.5"
503source = "registry+https://github.com/rust-lang/crates.io-index"
765source = "registry+https://github.com/rust-lang/crates.io-index"
504
766
767+[[package]]
768+name = "walkdir"
769+version = "2.2.7"
770+source = "registry+https://github.com/rust-lang/crates.io-index"
771+dependencies = [
772+ "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
773+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
774+ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
775+]
776+
505[[package]]
777[[package]]
506name = "wasm-bindgen"
778name = "wasm-bindgen"
507−version = "0.2.39"
779+version = "0.2.40"
508source = "registry+https://github.com/rust-lang/crates.io-index"
780source = "registry+https://github.com/rust-lang/crates.io-index"
509dependencies = [
781dependencies = [
510− "wasm-bindgen-macro 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
782+ "wasm-bindgen-macro 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
511]
783]
512
784
513[[package]]
785[[package]]
514name = "wasm-bindgen-backend"
786name = "wasm-bindgen-backend"
515−version = "0.2.39"
787+version = "0.2.40"
516source = "registry+https://github.com/rust-lang/crates.io-index"
788source = "registry+https://github.com/rust-lang/crates.io-index"
517dependencies = [
789dependencies = [
518 "bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
790 "bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -520,63 +792,63 @@ dependencies = [
520 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
792 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
521 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
793 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
522 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
794 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
523− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
795+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
524− "wasm-bindgen-shared 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
796+ "wasm-bindgen-shared 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
525]
797]
526
798
527[[package]]
799[[package]]
528name = "wasm-bindgen-futures"
800name = "wasm-bindgen-futures"
529−version = "0.3.16"
801+version = "0.3.17"
530source = "registry+https://github.com/rust-lang/crates.io-index"
802source = "registry+https://github.com/rust-lang/crates.io-index"
531dependencies = [
803dependencies = [
532 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
804 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
533− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
805+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
534− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
806+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
535]
807]
536
808
537[[package]]
809[[package]]
538name = "wasm-bindgen-macro"
810name = "wasm-bindgen-macro"
539−version = "0.2.39"
811+version = "0.2.40"
540source = "registry+https://github.com/rust-lang/crates.io-index"
812source = "registry+https://github.com/rust-lang/crates.io-index"
541dependencies = [
813dependencies = [
542 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
814 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
543− "wasm-bindgen-macro-support 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
815+ "wasm-bindgen-macro-support 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
544]
816]
545
817
546[[package]]
818[[package]]
547name = "wasm-bindgen-macro-support"
819name = "wasm-bindgen-macro-support"
548−version = "0.2.39"
820+version = "0.2.40"
549source = "registry+https://github.com/rust-lang/crates.io-index"
821source = "registry+https://github.com/rust-lang/crates.io-index"
550dependencies = [
822dependencies = [
551 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
823 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
552 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
824 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
553− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
825+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
554− "wasm-bindgen-backend 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
826+ "wasm-bindgen-backend 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
555− "wasm-bindgen-shared 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
827+ "wasm-bindgen-shared 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
556]
828]
557
829
558[[package]]
830[[package]]
559name = "wasm-bindgen-shared"
831name = "wasm-bindgen-shared"
560−version = "0.2.39"
832+version = "0.2.40"
561source = "registry+https://github.com/rust-lang/crates.io-index"
833source = "registry+https://github.com/rust-lang/crates.io-index"
562
834
563[[package]]
835[[package]]
564name = "wasm-bindgen-test"
836name = "wasm-bindgen-test"
565−version = "0.2.39"
837+version = "0.2.40"
566source = "registry+https://github.com/rust-lang/crates.io-index"
838source = "registry+https://github.com/rust-lang/crates.io-index"
567dependencies = [
839dependencies = [
568 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
840 "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
569 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
841 "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
570− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
842+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
571 "scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
843 "scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
572− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
844+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
573− "wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
845+ "wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
574− "wasm-bindgen-test-macro 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
846+ "wasm-bindgen-test-macro 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
575]
847]
576
848
577[[package]]
849[[package]]
578name = "wasm-bindgen-test-macro"
850name = "wasm-bindgen-test-macro"
579−version = "0.2.39"
851+version = "0.2.40"
580source = "registry+https://github.com/rust-lang/crates.io-index"
852source = "registry+https://github.com/rust-lang/crates.io-index"
581dependencies = [
853dependencies = [
582 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
854 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -585,7 +857,7 @@ dependencies = [
585
857
586[[package]]
858[[package]]
587name = "wasm-bindgen-webidl"
859name = "wasm-bindgen-webidl"
588−version = "0.2.39"
860+version = "0.2.40"
589source = "registry+https://github.com/rust-lang/crates.io-index"
861source = "registry+https://github.com/rust-lang/crates.io-index"
590dependencies = [
862dependencies = [
591 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
863 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -593,22 +865,22 @@ dependencies = [
593 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
865 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
594 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
866 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
595 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
867 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
596− "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
868+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
597− "wasm-bindgen-backend 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
869+ "wasm-bindgen-backend 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
598 "weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
870 "weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
599]
871]
600
872
601[[package]]
873[[package]]
602name = "web-sys"
874name = "web-sys"
603−version = "0.3.16"
875+version = "0.3.17"
604source = "registry+https://github.com/rust-lang/crates.io-index"
876source = "registry+https://github.com/rust-lang/crates.io-index"
605dependencies = [
877dependencies = [
606 "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
878 "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
607 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
879 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
608− "js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
880+ "js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
609 "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
881 "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
610− "wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
882+ "wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
611− "wasm-bindgen-webidl 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
883+ "wasm-bindgen-webidl 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
612]
884]
613
885
614[[package]]
886[[package]]
@@ -616,7 +888,7 @@ name = "weedle"
616version = "0.8.0"
888version = "0.8.0"
617source = "registry+https://github.com/rust-lang/crates.io-index"
889source = "registry+https://github.com/rust-lang/crates.io-index"
618dependencies = [
890dependencies = [
619− "nom 4.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
891+ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
620]
892]
621
893
622[[package]]
894[[package]]
@@ -656,65 +928,97 @@ dependencies = [
656]
928]
657
929
658[metadata]
930[metadata]
659−"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
931+"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
932+"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
660"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
933"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
661"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
934"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
662"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"
935"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"
663"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
936"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
937+"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
664"checksum bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6a6bc2ba935b1e2f810787ceba8dfe86cbc164cee55925cae715541186673c4"
938"checksum bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6a6bc2ba935b1e2f810787ceba8dfe86cbc164cee55925cae715541186673c4"
665"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
939"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
666−"checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d"
940+"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
941+"checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2"
667"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
942"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
943+"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
944+"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
668"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
945"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
669"checksum console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48"
946"checksum console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48"
947+"checksum criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "1c6e5ee5b9652d4f851418c448af105642e1f99e9a2741a8ff45c0d2c911b1e0"
948+"checksum criterion-plot 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4107e4a5abb94267e0149922b8ff49dc70a87cc202820fdbfc0d3e1edbdc4b16"
949+"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
950+"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
951+"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
952+"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04"
953+"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65"
954+"checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac"
670"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
955"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
671"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
956"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
672"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
957"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
958+"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
673"checksum futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "62941eff9507c8177d448bd83a44d9b9760856e184081d8cd79ba9f03dd24981"
959"checksum futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "62941eff9507c8177d448bd83a44d9b9760856e184081d8cd79ba9f03dd24981"
674"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
960"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
675"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
961"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
676"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
962"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
963+"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
677"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
964"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
678−"checksum js-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3970f67ef4a716efeba501dbedcc01b4686301b661798a42b24dc8a59d2c6b01"
965+"checksum js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "d4bda84b98977341bb6ba1086ecbd9eab8bc929de0f2923c118baf76c21dd0c8"
679"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
966"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
680−"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1"
967+"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
681"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
968"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
682"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
969"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
683−"checksum nom 4.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22293d25d3f33a8567cc8a1dc20f40c7eeb761ce83d0fcca059858580790cac3"
970+"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
971+"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
972+"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
973+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
974+"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
684"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
975"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
685"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
976"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
686"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
977"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
978+"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
979+"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
980+"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
981+"checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929"
982+"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473"
983+"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
984+"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
687−"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85"
985+"checksum redox_syscall 0.1.52 (registry+https://github.com/rust-lang/crates.io-index)" = "d32b3053e5ced86e4bc0411fec997389532bf56b000e66cb4884eeeb41413d69"
688"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
986"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
689−"checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f"
987+"checksum regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "559008764a17de49a3146b234641644ed37d118d1ef641a0bb573d146edc6ce0"
690−"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861"
988+"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96"
691"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
989"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
692"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
990"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
991+"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
693"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
992"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
993+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
694"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
994"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
695"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
995"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
696"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
996"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
697"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
997"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
698−"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
998+"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
699"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
999"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
700"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
1000"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
701"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
1001"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
1002+"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
702"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
1003"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
1004+"checksum tinytemplate 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7655088894274afb52b807bd3c87072daa1fedd155068b8705cabfd628956115"
703"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
1005"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
704"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
1006"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
1007+"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
705"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
1008"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
706"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
1009"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
707"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
1010"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
1011+"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
708−"checksum wasm-bindgen 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "06e36714cc3947a9c80ddc9715edb319e445e5ab0d0b083debec0dd52558580e"
1012+"checksum wasm-bindgen 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "9742fc4860f47bede1090a5e4b0cfc33afcd70cfdf45dd28f2cfb02d4662b0dd"
709−"checksum wasm-bindgen-backend 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "6764493f6eeb3f8632ffd418bf7db6a7e7aca98417c115833e4663976cd60a5e"
1013+"checksum wasm-bindgen-backend 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d7f35ecbb4180513cdb9b298543321bcb278670730415cbb3205ff2c66a477"
710−"checksum wasm-bindgen-futures 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4327c859969477dbc2033bbbe3a6cc045cf155ef283ad9f3c2d804ba27d3fd9b"
1014+"checksum wasm-bindgen-futures 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "67bfbec85e48e8915b6acfa1c4d90b770ce033c96dc2067688f3bccd00d3b9e1"
711−"checksum wasm-bindgen-macro 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a473501c40a2a4da83ae7176fead61f10d003554b701071a704dd0667397027a"
1015+"checksum wasm-bindgen-macro 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "e3c86b06bcd28e92e87d2c2ad208889b2f69ea33f79810b91ef660cc3de65a4c"
712−"checksum wasm-bindgen-macro-support 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "72e5a249738c6315f65ce5527632e2a3a067cdf1765422d8ac1572dcee14f819"
1016+"checksum wasm-bindgen-macro-support 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "81d7338dd8c67e193d8ef18e5802dc03d8710456baa792c1c2e66847e57fd389"
713−"checksum wasm-bindgen-shared 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "70f57643dac7decc3b53f0e4ffd027e225b79281648842eb46015e58a2400870"
1017+"checksum wasm-bindgen-shared 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "0d57c3b66f2f3e4d96b50f49b7b7e2f4cfcddc88b15744433c98c5c105b26672"
714−"checksum wasm-bindgen-test 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "c864512cd0031acd354ba9252c27e426d95f8e7776e877104d54b2eb482999ac"
1018+"checksum wasm-bindgen-test 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "85bbc3ad78a8d59a63a02d59345e8da9a3e0c1617f79e77fd1c3da6e047db63e"
715−"checksum wasm-bindgen-test-macro 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ec739799351a564254f1f8a7b2b5fdb4117e11c5ee68082e47301722d8973e"
1019+"checksum wasm-bindgen-test-macro 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "114dfdf01bd3659dd79db05480fbd2751759d1c79cd9edc22f2fe611c866e932"
716−"checksum wasm-bindgen-webidl 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "ef834a1f96cdc811875c00f20680bf15e038e0d0d458a82772afec5fe31ec7c1"
1020+"checksum wasm-bindgen-webidl 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "fc7052a577eefce17a439edfce5c2eedc8432fe7457bbe949624cdf946233fa7"
717−"checksum web-sys 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "a8286eada98bf34db48fc89ca53428c529a906bf161c6bae4e022f826323eb38"
1021+"checksum web-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "72d02003924c77af871ad74216f828b6e32af776cbb3f5dab3dcde2f2713c012"
718"checksum weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26a4c67f132386d965390b8a734d5d10adbcd30eb5cc74bd9229af8b83f10044"
1022"checksum weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26a4c67f132386d965390b8a734d5d10adbcd30eb5cc74bd9229af8b83f10044"
719"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
1023"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
720"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1024"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
Cargo.toml+23
@@ -13,6 +13,26 @@ version = "0.1.0"
13[badges]
13[badges]
14travis-ci = { repository = "fitzgen/dodrio" }
14travis-ci = { repository = "fitzgen/dodrio" }
15
15
16+[lib]
17+path = "src/lib.rs"
18+bench = false
19+
20+[[test]]
21+name = "web"
22+path = "tests/web/main.rs"
23+required-features = ["xxx-unstable-internal-use-only"]
24+
25+[[bench]]
26+name = "benches"
27+path = "benches/benches.rs"
28+harness = false
29+required-features = ["xxx-unstable-internal-use-only"]
30+
31+[features]
32+# Don't use this feature! It only exists for exposing otherwise-internal APIs
33+# for testing and benchmarks.
34+xxx-unstable-internal-use-only = []
35+
16[dependencies]
36[dependencies]
17bumpalo = "2.3.0"
37bumpalo = "2.3.0"
18cfg-if = "0.1.7"
38cfg-if = "0.1.7"
@@ -50,6 +70,9 @@ features = [
50 "NodeList",
70 "NodeList",
51]
71]
52
72
73+[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
74+criterion = "0.2.10"
75+
53[profile.release]
76[profile.release]
54incremental = false
77incremental = false
55lto = true
78lto = true
README.md+4−6
@@ -31,12 +31,10 @@ struct Hello {
31}
31}
32
32
33impl Render for Hello {
33impl Render for Hello {
34− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
34+ fn render<'a>(&self, cx: &mut RenderContext<a>) -> Node<'a> {
35− where
35+ let who = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
36− 'a: 'bump,
36+ div(cx)
37− {
38− span(bump)
39− .children([text("Hello, "), text(&self.who), text("!")])
37+ .children([text(who.into_bump_str())])
40 .finish()
38 .finish()
41 }
39 }
42}
40}
benches/benches.rsnew file+129
@@ -0,0 +1,129 @@
1+#![cfg(all(
2+ feature = "xxx-unstable-internal-use-only",
3+ not(target_arch = "wasm32")
4+))]
5+
6+use criterion::{
7+ black_box, criterion_group, criterion_main, Benchmark, Criterion, ParameterizedBenchmark,
8+ Throughput,
9+};
10+use dodrio::{
11+ builder::*,
12+ bumpalo::{self, Bump},
13+ CachedSet, Node, Render, RenderContext, Vdom,
14+};
15+use std::cell::RefCell;
16+
17+/// The simplest thing we can render: `<div/>`.
18+struct Empty;
19+impl Render for Empty {
20+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
21+ div(&cx).finish()
22+ }
23+}
24+
25+/// Render a list that is `self.0` items long, has attributes and listeners.
26+struct SimpleList(usize);
27+impl Render for SimpleList {
28+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
29+ let mut children = bumpalo::collections::Vec::with_capacity_in(self.0, cx.bump);
30+ children.extend((0..self.0).map(|_| {
31+ li(&cx)
32+ .attr("class", "my-list-item")
33+ .on("click", |_root, _vdom, _event| {
34+ panic!("no one should call this")
35+ })
36+ .children([text("a list item")])
37+ .finish()
38+ }));
39+ ol(&cx).attr("id", "my-list").children(children).finish()
40+ }
41+}
42+
43+fn criterion_benchmark(c: &mut Criterion) {
44+ c.bench(
45+ "render",
46+ Benchmark::new("empty", {
47+ let mut bump = Bump::new();
48+ let cached_set = RefCell::new(CachedSet::default());
49+ move |b| {
50+ bump.reset();
51+ let mut cx = RenderContext::new(&bump, &cached_set);
52+ b.iter(|| {
53+ black_box(Empty.render(&mut cx));
54+ })
55+ }
56+ }),
57+ );
58+
59+ c.bench(
60+ "render",
61+ ParameterizedBenchmark::new(
62+ "list",
63+ {
64+ let mut bump = Bump::new();
65+ let cached_set = RefCell::new(CachedSet::default());
66+ move |b, n| {
67+ bump.reset();
68+ let mut cx = RenderContext::new(&bump, &cached_set);
69+ b.iter(|| {
70+ black_box(SimpleList(*n).render(&mut cx));
71+ })
72+ }
73+ },
74+ vec![
75+ // TODO: Only test one `n` value until
76+ // https://github.com/bheisler/criterion.rs/issues/269 is fixed.
77+ //
78+ // 100,
79+ // 1_000,
80+ 10_000,
81+ ],
82+ )
83+ .throughput(|n| Throughput::Elements(*n as u32)),
84+ );
85+
86+ c.bench(
87+ "render-and-diff",
88+ ParameterizedBenchmark::new(
89+ "same-list",
90+ |b, &n| {
91+ let vdom = Vdom::new(&(), SimpleList(n));
92+ b.iter(|| {
93+ vdom.immediately_render_and_diff(SimpleList(n));
94+ black_box(&vdom);
95+ })
96+ },
97+ vec![
98+ // TODO: Only test one `n` value until
99+ // https://github.com/bheisler/criterion.rs/issues/269 is fixed.
100+ //
101+ // 100,
102+ // 1_000,
103+ 10_000,
104+ ],
105+ )
106+ .with_function("empty-to-full-list-to-empty", |b, &n| {
107+ b.iter(|| {
108+ let vdom = Vdom::new(&(), Empty);
109+ vdom.immediately_render_and_diff(SimpleList(n));
110+ black_box(&vdom);
111+ vdom.immediately_render_and_diff(Empty);
112+ black_box(&vdom);
113+ });
114+ })
115+ .with_function("append-one-and-remove-one", |b, &n| {
116+ let vdom = Vdom::new(&(), SimpleList(n));
117+ b.iter(|| {
118+ vdom.immediately_render_and_diff(SimpleList(n + 1));
119+ black_box(&vdom);
120+ vdom.immediately_render_and_diff(SimpleList(n));
121+ black_box(&vdom);
122+ })
123+ })
124+ .throughput(|n| Throughput::Elements(*n as u32)),
125+ );
126+}
127+
128+criterion_group!(benches, criterion_benchmark);
129+criterion_main!(benches);
crates/js-api/src/lib.rs+19−30
@@ -66,8 +66,7 @@ And here is a Rust rendering component that internally uses the JS rendering
66component:
66component:
67
67
68```rust,no_run
68```rust,no_run
69−use dodrio::bumpalo::Bump;
70−use dodrio::{Node, Render, Vdom};
69+use dodrio::{Node, Render, RenderContext, Vdom};
71use dodrio_js_api::JsRender;
70use dodrio_js_api::JsRender;
72use js_sys::Object;
71use js_sys::Object;
73use wasm_bindgen::prelude::*;
72use wasm_bindgen::prelude::*;
@@ -101,15 +100,12 @@ impl GreetingViaJs {
101/// And finally the `Render` implementation! This adds a `<p>` element and some
100/// And finally the `Render` implementation! This adds a `<p>` element and some
102/// text around whatever the inner JS `Greeting` component renders.
101/// text around whatever the inner JS `Greeting` component renders.
103impl Render for GreetingViaJs {
102impl Render for GreetingViaJs {
104− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
103+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
105− where
106− 'a: 'bump,
107− {
108 use dodrio::builder::*;
104 use dodrio::builder::*;
109− p(bump)
105+ p(&cx)
110 .children([
106 .children([
111 text("JavaScript says: "),
107 text("JavaScript says: "),
112− self.js.render(bump),
108+ self.js.render(cx),
113 ])
109 ])
114 .finish()
110 .finish()
115 }
111 }
@@ -119,11 +115,7 @@ impl Render for GreetingViaJs {
119 */
115 */
120#![deny(missing_docs, missing_debug_implementations)]
116#![deny(missing_docs, missing_debug_implementations)]
121
117
122−use dodrio::{
118+use dodrio::{builder, bumpalo, Node, Render, RenderContext};
123− builder,
124− bumpalo::{self, Bump},
125− Node, Render,
126−};
127use futures::prelude::*;
119use futures::prelude::*;
128use js_sys::{Object, Promise, Reflect};
120use js_sys::{Object, Promise, Reflect};
129use wasm_bindgen::prelude::*;
121use wasm_bindgen::prelude::*;
@@ -222,11 +214,8 @@ impl JsRender {
222}
214}
223
215
224impl Render for JsRender {
216impl Render for JsRender {
225− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
217+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
226− where
227− 'a: 'bump,
228− {
229− create(bump, self.render())
218+ create(cx, self.render())
230 }
219 }
231}
220}
232
221
@@ -234,9 +223,9 @@ fn has_property(obj: &Object, property: &str) -> bool {
234 Reflect::has(obj, &property.into()).unwrap_or_default()
223 Reflect::has(obj, &property.into()).unwrap_or_default()
235}
224}
236
225
237−fn create(bump: &Bump, val: JsValue) -> Node {
226+fn create<'a>(cx: &mut RenderContext<'a>, val: JsValue) -> Node<'a> {
238 if let Some(txt) = val.as_string() {
227 if let Some(txt) = val.as_string() {
239− let text = bumpalo::collections::String::from_str_in(&txt, bump);
228+ let text = bumpalo::collections::String::from_str_in(&txt, cx.bump);
240 return builder::text(text.into_bump_str());
229 return builder::text(text.into_bump_str());
241 }
230 }
242
231
@@ -251,12 +240,12 @@ fn create(bump: &Bump, val: JsValue) -> Node {
251 );
240 );
252
241
253 let tag_name = elem.tag_name();
242 let tag_name = elem.tag_name();
254− let tag_name = bumpalo::collections::String::from_str_in(&tag_name, bump);
243+ let tag_name = bumpalo::collections::String::from_str_in(&tag_name, cx.bump);
255
244
256− builder::ElementBuilder::new(bump, tag_name.into_bump_str())
245+ builder::ElementBuilder::new(cx.bump, tag_name.into_bump_str())
257 .listeners({
246 .listeners({
258 let mut listeners =
247 let mut listeners =
259− bumpalo::collections::Vec::new_in(bump);
248+ bumpalo::collections::Vec::new_in(cx.bump);
260 if has_property(&elem, "listeners") {
249 if has_property(&elem, "listeners") {
261 let js_listeners = elem.listeners();
250 let js_listeners = elem.listeners();
262 listeners.reserve(js_listeners.length() as usize);
251 listeners.reserve(js_listeners.length() as usize);
@@ -275,10 +264,10 @@ fn create(bump: &Bump, val: JsValue) -> Node {
275 "listener objects returned by JS render methods must have an `callback` property"
264 "listener objects returned by JS render methods must have an `callback` property"
276 );
265 );
277 let on = listener.on();
266 let on = listener.on();
278− let on = bumpalo::collections::String::from_str_in(&on, bump);
267+ let on = bumpalo::collections::String::from_str_in(&on, cx.bump);
279 let callback = listener.callback();
268 let callback = listener.callback();
280 let elem = elem.clone();
269 let elem = elem.clone();
281− listeners.push(builder::on(bump, on.into_bump_str(), move |_root, vdom, event| {
270+ listeners.push(builder::on(cx.bump, on.into_bump_str(), move |_root, vdom, event| {
282 let vdom = VdomWeak::new(vdom);
271 let vdom = VdomWeak::new(vdom);
283 let vdom: JsValue = vdom.into();
272 let vdom: JsValue = vdom.into();
284 if let Err(e) = callback.call2(&elem, &vdom, &event) {
273 if let Err(e) = callback.call2(&elem, &vdom, &event) {
@@ -290,7 +279,7 @@ fn create(bump: &Bump, val: JsValue) -> Node {
290 listeners
279 listeners
291 })
280 })
292 .attributes({
281 .attributes({
293− let mut attributes = bumpalo::collections::Vec::new_in(bump);
282+ let mut attributes = bumpalo::collections::Vec::new_in(cx.bump);
294 if has_property(&elem, "attributes") {
283 if has_property(&elem, "attributes") {
295 let js_attributes = elem.attributes();
284 let js_attributes = elem.attributes();
296 attributes.reserve(js_attributes.length() as usize);
285 attributes.reserve(js_attributes.length() as usize);
@@ -309,21 +298,21 @@ fn create(bump: &Bump, val: JsValue) -> Node {
309 "attribute objects returned by JS render methods must have a `value` property"
298 "attribute objects returned by JS render methods must have a `value` property"
310 );
299 );
311 let name = attribute.name();
300 let name = attribute.name();
312− let name = bumpalo::collections::String::from_str_in(&name, bump);
301+ let name = bumpalo::collections::String::from_str_in(&name, cx.bump);
313 let value = attribute.value();
302 let value = attribute.value();
314− let value = bumpalo::collections::String::from_str_in(&value, bump);
303+ let value = bumpalo::collections::String::from_str_in(&value, cx.bump);
315 attributes.push(builder::attr(name.into_bump_str(), value.into_bump_str()));
304 attributes.push(builder::attr(name.into_bump_str(), value.into_bump_str()));
316 });
305 });
317 }
306 }
318 attributes
307 attributes
319 })
308 })
320 .children({
309 .children({
321− let mut children = bumpalo::collections::Vec::new_in(bump);
310+ let mut children = bumpalo::collections::Vec::new_in(cx.bump);
322 if has_property(&elem, "children") {
311 if has_property(&elem, "children") {
323 let js_children = elem.children();
312 let js_children = elem.children();
324 children.reserve(js_children.length() as usize);
313 children.reserve(js_children.length() as usize);
325 js_children.for_each(&mut |child, _index, _array| {
314 js_children.for_each(&mut |child, _index, _array| {
326− children.push(create(bump, child));
315+ children.push(create(cx, child));
327 });
316 });
328 }
317 }
329 children
318 children
examples/counter/src/lib.rs+6−10
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
1+use dodrio::{bumpalo, Render, RenderContext};
2−use dodrio::Render;
3use log::*;
2use log::*;
4use wasm_bindgen::prelude::*;
3use wasm_bindgen::prelude::*;
5
4
@@ -28,18 +27,15 @@ impl Counter {
28// The `Render` implementation for `Counter`s displays the current count and has
27// The `Render` implementation for `Counter`s displays the current count and has
29// buttons to increment and decrement the count.
28// buttons to increment and decrement the count.
30impl Render for Counter {
29impl Render for Counter {
31− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> dodrio::Node<'bump>
30+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> dodrio::Node<'a> {
32− where
33− 'a: 'bump,
34− {
35 use dodrio::builder::*;
31 use dodrio::builder::*;
36
32
37 // Stringify the count as a bump-allocated string.
33 // Stringify the count as a bump-allocated string.
38− let count = bumpalo::format!(in bump, "{}", self.count);
34+ let count = bumpalo::format!(in cx.bump, "{}", self.count);
39
35
40− div(bump)
36+ div(&cx)
41 .children([
37 .children([
42− button(bump)
38+ button(&cx)
43 .on("click", |root, vdom, _event| {
39 .on("click", |root, vdom, _event| {
44 // Cast the root render component to a `Counter`, since
40 // Cast the root render component to a `Counter`, since
45 // we know that's what it is.
41 // we know that's what it is.
@@ -55,7 +51,7 @@ impl Render for Counter {
55 .children([text("+")])
51 .children([text("+")])
56 .finish(),
52 .finish(),
57 text(count.into_bump_str()),
53 text(count.into_bump_str()),
58− button(bump)
54+ button(&cx)
59 .on("click", |root, vdom, _event| {
55 .on("click", |root, vdom, _event| {
60 // Same as above, but decrementing instead of incrementing.
56 // Same as above, but decrementing instead of incrementing.
61 root.unwrap_mut::<Counter>().decrement();
57 root.unwrap_mut::<Counter>().decrement();
examples/game-of-life/src/lib.rs+8−11
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
1+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
2−use dodrio::{Node, Render, Vdom};
3use futures::prelude::*;
2use futures::prelude::*;
4use std::cell::RefCell;
3use std::cell::RefCell;
5use std::rc::Rc;
4use std::rc::Rc;
@@ -129,20 +128,18 @@ impl Universe {
129
128
130/// The rendering implementation for our Game of Life.
129/// The rendering implementation for our Game of Life.
131impl Render for Universe {
130impl Render for Universe {
132− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
131+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
133− where
134− 'a: 'bump,
135− {
136 use dodrio::builder::*;
132 use dodrio::builder::*;
137
133
138− let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, bump);
134+ let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, cx.bump);
139
135
140 for row in self.cells.chunks(self.width as usize) {
136 for row in self.cells.chunks(self.width as usize) {
137+ let mut cells =
141− let mut cells = bumpalo::collections::Vec::with_capacity_in(self.width as usize, bump);
138+ bumpalo::collections::Vec::with_capacity_in(self.width as usize, cx.bump);
142
139
143 for cell in row {
140 for cell in row {
144 cells.push(
141 cells.push(
145− span(bump)
142+ span(&cx)
146 .attr("class", "cell")
143 .attr("class", "cell")
147 .attr(
144 .attr(
148 "style",
145 "style",
@@ -155,10 +152,10 @@ impl Render for Universe {
155 );
152 );
156 }
153 }
157
154
158− rows.push(div(bump).attr("class", "row").children(cells).finish());
155+ rows.push(div(&cx).attr("class", "row").children(cells).finish());
159 }
156 }
160
157
161− div(bump).attr("class", "universe").children(rows).finish()
158+ div(&cx).attr("class", "universe").children(rows).finish()
162 }
159 }
163}
160}
164
161
examples/hello-world/src/lib.rs+12−14
@@ -1,24 +1,20 @@
1−use dodrio::bumpalo::Bump;
1+use dodrio::{builder::*, bumpalo};
2−use dodrio::{Node, Render, Vdom};
2+use dodrio::{Node, Render, RenderContext, Vdom};
3use wasm_bindgen::prelude::*;
3use wasm_bindgen::prelude::*;
4
4
5/// A rendering component that displays a greeting.
5/// A rendering component that displays a greeting.
6−struct Hello<'who> {
6+struct Hello {
7 /// Who to greet.
7 /// Who to greet.
8− who: &'who str,
8+ who: String,
9}
9}
10
10
11// The `Render` implementation describes how to render a `Hello` component into
11// The `Render` implementation describes how to render a `Hello` component into
12// HTML.
12// HTML.
13−impl<'who> Render for Hello<'who> {
13+impl Render for Hello {
14− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
14+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
15− where
15+ let msg = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
16− 'a: 'bump,
16+ let msg = msg.into_bump_str();
17− {
18− use dodrio::builder::*;
19− p(bump)
20− .children([text("Hello, "), text(self.who), text("!")])
17+ p(&cx).children([text(msg)]).finish()
21− .finish()
22 }
18 }
23}
19}
24
20
@@ -33,7 +29,9 @@ pub fn run() {
33 let body = document.body().unwrap_throw();
29 let body = document.body().unwrap_throw();
34
30
35 // Create a new `Hello` render component.
31 // Create a new `Hello` render component.
36− let component = Hello { who: "World" };
32+ let component = Hello {
33+ who: String::from("World"),
34+ };
37
35
38 // Create a virtual DOM and mount it and the `Hello` render component to the
36 // Create a virtual DOM and mount it and the `Hello` render component to the
39 // `<body>`.
37 // `<body>`.
examples/input-form/src/lib.rs+9−10
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
1+use dodrio::{bumpalo, Node, Render, RenderContext};
2−use dodrio::{Node, Render};
3use wasm_bindgen::prelude::*;
2use wasm_bindgen::prelude::*;
4use wasm_bindgen::JsCast;
3use wasm_bindgen::JsCast;
5
4
@@ -25,17 +24,17 @@ impl SayHelloTo {
25// The `Render` implementation has a text `<input>` and a `<div>` that shows a
24// The `Render` implementation has a text `<input>` and a `<div>` that shows a
26// greeting to the `<input>`'s value.
25// greeting to the `<input>`'s value.
27impl Render for SayHelloTo {
26impl Render for SayHelloTo {
28− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
27+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
29− where
30− 'a: 'bump,
31− {
32 use dodrio::builder::*;
28 use dodrio::builder::*;
33
29
34− div(bump)
30+ div(&cx)
35 .children([
31 .children([
36− input(bump)
32+ input(&cx)
37 .attr("type", "text")
33 .attr("type", "text")
34+ .attr(
38− .attr("value", &self.who)
35+ "value",
36+ bumpalo::format!(in cx.bump, "{}", self.who).into_bump_str(),
37+ )
39 .on("input", |root, vdom, event| {
38 .on("input", |root, vdom, event| {
40 // If the event's target is our input...
39 // If the event's target is our input...
41 let input = match event
40 let input = match event
@@ -55,7 +54,7 @@ impl Render for SayHelloTo {
55 vdom.schedule_render();
54 vdom.schedule_render();
56 })
55 })
57 .finish(),
56 .finish(),
58− text(bumpalo::format!(in bump, "Hello, {}!", self.who).into_bump_str()),
57+ text(bumpalo::format!(in cx.bump, "Hello, {}!", self.who).into_bump_str()),
59 ])
58 ])
60 .finish()
59 .finish()
61 }
60 }
examples/js-component/src/lib.rs+4−8
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::Bump;
2−use dodrio::{Node, Render, Vdom};
1+use dodrio::{Node, Render, RenderContext, Vdom};
3use dodrio_js_api::JsRender;
2use dodrio_js_api::JsRender;
4use js_sys::Object;
3use js_sys::Object;
5use wasm_bindgen::prelude::*;
4use wasm_bindgen::prelude::*;
@@ -33,13 +32,10 @@ impl GreetingViaJs {
33/// Here's the `Render` implementation! This adds a `<p>` element and some text
32/// Here's the `Render` implementation! This adds a `<p>` element and some text
34/// around whatever the inner JS `Greeting` component renders.
33/// around whatever the inner JS `Greeting` component renders.
35impl Render for GreetingViaJs {
34impl Render for GreetingViaJs {
36− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
35+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
37− where
38− 'a: 'bump,
39− {
40 use dodrio::builder::*;
36 use dodrio::builder::*;
41− p(bump)
37+ p(&cx)
42− .children([text("JavaScript says: "), self.js.render(bump)])
38+ .children([text("JavaScript says: "), self.js.render(cx)])
43 .finish()
39 .finish()
44 }
40 }
45}
41}
examples/moire/src/lib.rs+33−58
@@ -1,8 +1,7 @@
1mod colors;
1mod colors;
2
2
3use cfg_if::cfg_if;
3use cfg_if::cfg_if;
4−use dodrio::bumpalo::{self, Bump};
4+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
5−use dodrio::{Node, Render, Vdom};
6use std::cell::RefCell;
5use std::cell::RefCell;
7use std::rc::Rc;
6use std::rc::Rc;
8use std::str;
7use std::str;
@@ -107,10 +106,7 @@ impl Moire {
107}
106}
108
107
109impl Render for Moire {
108impl Render for Moire {
110− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
109+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
111− where
112− 'a: 'bump,
113− {
114 use dodrio::builder::*;
110 use dodrio::builder::*;
115
111
116 let elapsed = web_sys::window()
112 let elapsed = web_sys::window()
@@ -120,18 +116,18 @@ impl Render for Moire {
120 .now();
116 .now();
121 let elapsed = elapsed / 1600.0;
117 let elapsed = elapsed / 1600.0;
122
118
123− main(bump)
119+ main(&cx)
124− .attr("style", self.moire_style(bump, elapsed))
120+ .attr("style", self.moire_style(cx, elapsed))
125 .children([
121 .children([
126 // The `<input>` that lets users control how many circles to
122 // The `<input>` that lets users control how many circles to
127 // render.
123 // render.
128− input(bump)
124+ input(&cx)
129 .attr("id", "circle-count")
125 .attr("id", "circle-count")
130 .attr("type", "range")
126 .attr("type", "range")
131 .attr("min", "30")
127 .attr("min", "30")
132 .attr("max", "500")
128 .attr("max", "500")
133 .attr("value", {
129 .attr("value", {
134− let count = bumpalo::format!(in bump, "{}", self.count);
130+ let count = bumpalo::format!(in cx.bump, "{}", self.count);
135 count.into_bump_str()
131 count.into_bump_str()
136 })
132 })
137 .on("change", |root, _vdom, event| {
133 .on("change", |root, _vdom, event| {
@@ -144,8 +140,8 @@ impl Render for Moire {
144 })
140 })
145 .finish(),
141 .finish(),
146 // And the actual concentric circles that form the Moire patterns.
142 // And the actual concentric circles that form the Moire patterns.
147− self.orbiting_objects(bump, elapsed),
143+ self.orbiting_objects(cx, elapsed),
148− self.lemniscate_objects(bump, elapsed),
144+ self.lemniscate_objects(cx, elapsed),
149 ])
145 ])
150 .finish()
146 .finish()
151 }
147 }
@@ -154,14 +150,11 @@ impl Render for Moire {
154/// Rendering helper methods.
150/// Rendering helper methods.
155impl Moire {
151impl Moire {
156 /// Generate the main Moire element's inline CSS styles.
152 /// Generate the main Moire element's inline CSS styles.
157− fn moire_style<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> &'bump str
153+ fn moire_style<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> &'a str {
158− where
159− 'a: 'bump,
160− {
161 let elapsed = elapsed / 3.0;
154 let elapsed = elapsed / 3.0;
162 let color = colors::get_interpolated_color(|(_, bg)| bg, elapsed % 1.0);
155 let color = colors::get_interpolated_color(|(_, bg)| bg, elapsed % 1.0);
163 let style = bumpalo::format!(
156 let style = bumpalo::format!(
164− in bump,
157+ in cx.bump,
165 "background-color: rgb({}, {}, {})",
158 "background-color: rgb({}, {}, {})",
166 color.r,
159 color.r,
167 color.g,
160 color.g,
@@ -171,93 +164,75 @@ impl Moire {
171 }
164 }
172
165
173 /// Generate the orbiting circle objects.
166 /// Generate the orbiting circle objects.
174− fn orbiting_objects<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> Node<'bump>
167+ fn orbiting_objects<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> Node<'a> {
175− where
176− 'a: 'bump,
177− {
178 const D: f64 = 200.0;
168 const D: f64 = 200.0;
179 let x = elapsed.sin() * D;
169 let x = elapsed.sin() * D;
180 let y = elapsed.cos() * D;
170 let y = elapsed.cos() * D;
181− self.moving_object(bump, elapsed, x, y)
171+ self.moving_object(cx, elapsed, x, y)
182 }
172 }
183
173
184 /// Generate the lemniscate circle objects that interact with the orbiting
174 /// Generate the lemniscate circle objects that interact with the orbiting
185 /// circle objects to form Moire patterns.
175 /// circle objects to form Moire patterns.
186− fn lemniscate_objects<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> Node<'bump>
176+ fn lemniscate_objects<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> Node<'a> {
187− where
188− 'a: 'bump,
189− {
190 const A: f64 = 150.0;
177 const A: f64 = 150.0;
191 let x = elapsed.sin() * A;
178 let x = elapsed.sin() * A;
192 let y = elapsed.sin() * elapsed.cos() * A;
179 let y = elapsed.sin() * elapsed.cos() * A;
193− self.moving_object(bump, elapsed, x, y)
180+ self.moving_object(cx, elapsed, x, y)
194 }
181 }
195
182
196− fn moving_object<'a, 'bump>(
183+ fn moving_object<'a>(
197− &'a self,
184+ &self,
198− bump: &'bump Bump,
185+ cx: &mut RenderContext<'a>,
199 elapsed: f64,
186 elapsed: f64,
200 x: f64,
187 x: f64,
201 y: f64,
188 y: f64,
202− ) -> Node<'bump>
189+ ) -> Node<'a> {
203− where
204− 'a: 'bump,
205− {
206 use dodrio::builder::*;
190 use dodrio::builder::*;
207− div(bump)
191+ div(&cx)
208 .attr("class", "object")
192 .attr("class", "object")
209− .attr("style", self.moving_object_style(bump, x, y))
193+ .attr("style", self.moving_object_style(cx, x, y))
210− .children([self.circle(bump, elapsed, self.count)])
194+ .children([self.circle(cx, elapsed, self.count)])
211 .finish()
195 .finish()
212 }
196 }
213
197
214 /// Generate inline CSS styles for a moving object.
198 /// Generate inline CSS styles for a moving object.
215− fn moving_object_style<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str
199+ fn moving_object_style<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64) -> &'a str {
216− where
217− 'a: 'bump,
218− {
219− bumpalo::format!(in bump, "left: {}px; top: {}px;", x, y).into_bump_str()
200+ bumpalo::format!(in cx.bump, "left: {}px; top: {}px;", x, y).into_bump_str()
220 }
201 }
221
202
222 /// Recursively generate `self.count` concentric circles.
203 /// Recursively generate `self.count` concentric circles.
223− fn circle<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64, n: u32) -> Node<'bump>
204+ fn circle<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64, n: u32) -> Node<'a> {
224− where
225− 'a: 'bump,
226− {
227 use dodrio::builder::*;
205 use dodrio::builder::*;
228
206
229 let r = n * 16;
207 let r = n * 16;
230
208
231− let mut circle = div(bump)
209+ let mut circle = div(&cx)
232 .attr("class", "circle")
210 .attr("class", "circle")
233 .attr("data-radius", {
211 .attr("data-radius", {
234− let r = bumpalo::format!(in bump, "{}", r);
212+ let r = bumpalo::format!(in cx.bump, "{}", r);
235 r.into_bump_str()
213 r.into_bump_str()
236 })
214 })
237− .attr("style", self.circle_style(bump, elapsed, n, r));
215+ .attr("style", self.circle_style(cx, elapsed, n, r));
238
216
239 if n > 0 {
217 if n > 0 {
240− circle = circle.child(self.circle(bump, elapsed, n - 1));
218+ circle = circle.child(self.circle(cx, elapsed, n - 1));
241 }
219 }
242
220
243 circle.finish()
221 circle.finish()
244 }
222 }
245
223
246 /// Generate inline CSS styles for a circle.
224 /// Generate inline CSS styles for a circle.
247− fn circle_style<'a, 'bump>(
225+ fn circle_style<'a>(
248− &'a self,
226+ &self,
249− bump: &'bump Bump,
227+ cx: &mut RenderContext<'a>,
250 elapsed: f64,
228 elapsed: f64,
251 n: u32,
229 n: u32,
252 r: u32,
230 r: u32,
253− ) -> &'bump str
231+ ) -> &'a str {
254− where
255− 'a: 'bump,
256− {
257 let (border, alpha) = self.circle_color(elapsed, n);
232 let (border, alpha) = self.circle_color(elapsed, n);
258 let margin = r / 2 + 3;
233 let margin = r / 2 + 3;
259 let style = bumpalo::format!(
234 let style = bumpalo::format!(
260− in bump,
235+ in cx.bump,
261 "border-color: rgba({}, {}, {}, {});\
236 "border-color: rgba({}, {}, {}, {});\
262 margin-left: -{}px;\
237 margin-left: -{}px;\
263 margin-top: -{}px;\
238 margin-top: -{}px;\
examples/sierpinski-triangle/src/lib.rs+23−38
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
1+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
2−use dodrio::{Node, Render, Vdom};
3use std::cell::RefCell;
2use std::cell::RefCell;
4use std::rc::Rc;
3use std::rc::Rc;
5use wasm_bindgen::prelude::*;
4use wasm_bindgen::prelude::*;
@@ -18,14 +17,11 @@ impl Container {
18 }
17 }
19
18
20 // Generate the container style to fluctuate triangle width.
19 // Generate the container style to fluctuate triangle width.
21− fn container_transform<'a, 'bump>(&'a self, bump: &'bump Bump, elapsed: f64) -> &'bump str
20+ fn container_transform<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> &'a str {
22− where
23− 'a: 'bump,
24− {
25 let t = elapsed % 10.0;
21 let t = elapsed % 10.0;
26 let scale = 0.45 + (if t > 5.0 { 10.0 - t } else { t }) / 40.0;
22 let scale = 0.45 + (if t > 5.0 { 10.0 - t } else { t }) / 40.0;
27 let transform = bumpalo::format!(
23 let transform = bumpalo::format!(
28− in bump,
24+ in cx.bump,
29 "transform: scaleX({}) scaleY(0.7) translateZ(0.1px)",
25 "transform: scaleX({}) scaleY(0.7) translateZ(0.1px)",
30 scale
26 scale
31 );
27 );
@@ -33,14 +29,11 @@ impl Container {
33 }
29 }
34
30
35 // Generate the dot's position on the grid.
31 // Generate the dot's position on the grid.
36− fn dot_style<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str
32+ fn dot_style<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64) -> &'a str {
37− where
38− 'a: 'bump,
39− {
40 let s = self.size * 1.3;
33 let s = self.size * 1.3;
41 let radius = s / 2.0;
34 let radius = s / 2.0;
42 let styles = bumpalo::format!(
35 let styles = bumpalo::format!(
43− in bump,
36+ in cx.bump,
44 r#"
37 r#"
45 width: {}px;
38 width: {}px;
46 height: {}px;
39 height: {}px;
@@ -55,54 +48,46 @@ impl Container {
55 }
48 }
56
49
57 // Create a dot node.
50 // Create a dot node.
58− fn dot<'a, 'bump>(&'a self, bump: &'bump Bump, x: f64, y: f64, content: u32) -> Node<'bump>
51+ fn dot<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64, content: u32) -> Node<'a> {
59− where
60− 'a: 'bump,
61− {
62 use dodrio::builder::{div, text};
52 use dodrio::builder::{div, text};
63
53
64− div(bump)
54+ div(&cx)
65 .attr("class", "dot")
55 .attr("class", "dot")
66− .attr("style", self.dot_style(bump, x, y))
56+ .attr("style", self.dot_style(cx, x, y))
67 .child(text(
57 .child(text(
68− bumpalo::format!(in bump, "{}", content).into_bump_str(),
58+ bumpalo::format!(in cx.bump, "{}", content).into_bump_str(),
69 ))
59 ))
70 .finish()
60 .finish()
71 }
61 }
72
62
73 // Create a flattened vector of dot nodes (arranged in recursive triangles)
63 // Create a flattened vector of dot nodes (arranged in recursive triangles)
74− fn triangle<'a, 'bump>(
64+ fn triangle<'a>(
75− &'a self,
65+ &self,
76− bump: &'bump Bump,
66+ cx: &mut RenderContext<'a>,
77 x: f64,
67 x: f64,
78 y: f64,
68 y: f64,
79 s: f64,
69 s: f64,
80 content: u32,
70 content: u32,
81− children: &mut bumpalo::collections::Vec<Node<'bump>>,
71+ children: &mut bumpalo::collections::Vec<Node<'a>>,
82− ) where
83− 'a: 'bump,
84− {
72+ ) {
85 if s <= self.size {
73 if s <= self.size {
86− children.push(self.dot(bump, x, y, content));
74+ children.push(self.dot(cx, x, y, content));
87 return;
75 return;
88 }
76 }
89
77
90 let s = s / 2.0;
78 let s = s / 2.0;
91
79
92 // Top of triangle
80 // Top of triangle
93− self.triangle(bump, x, y - (s / 2.0), s, content, children);
81+ self.triangle(cx, x, y - (s / 2.0), s, content, children);
94 // Bottom-left of triangle
82 // Bottom-left of triangle
95− self.triangle(bump, x - s, y + (s / 2.0), s, content, children);
83+ self.triangle(cx, x - s, y + (s / 2.0), s, content, children);
96 // Bottom-right of triangle
84 // Bottom-right of triangle
97− self.triangle(bump, x + s, y + (s / 2.0), s, content, children);
85+ self.triangle(cx, x + s, y + (s / 2.0), s, content, children);
98 }
86 }
99}
87}
100
88
101impl Render for Container {
89impl Render for Container {
102− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
90+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
103− where
104− 'a: 'bump,
105− {
106 use dodrio::builder::div;
91 use dodrio::builder::div;
107
92
108 let elapsed = web_sys::window()
93 let elapsed = web_sys::window()
@@ -114,12 +99,12 @@ impl Render for Container {
114
99
115 let modulus = elapsed as u32 % 10;
100 let modulus = elapsed as u32 % 10;
116
101
117− let mut children = bumpalo::collections::Vec::new_in(bump);
102+ let mut children = bumpalo::collections::Vec::new_in(cx.bump);
118− self.triangle(bump, 0.0, 0.0, 1000.0, modulus, &mut children);
103+ self.triangle(cx, 0.0, 0.0, 1000.0, modulus, &mut children);
119
104
120− div(bump)
105+ div(&cx)
121 .attr("class", "container")
106 .attr("class", "container")
122− .attr("style", self.container_transform(bump, elapsed))
107+ .attr("style", self.container_transform(cx, elapsed))
123 .children(children)
108 .children(children)
124 .finish()
109 .finish()
125 }
110 }
examples/todomvc/src/todo.rs+11−13
@@ -1,7 +1,7 @@
1//! Type definition and `dodrio::Render` implementation for a single todo item.
1//! Type definition and `dodrio::Render` implementation for a single todo item.
2
2
3use crate::keys;
3use crate::keys;
4−use dodrio::{bumpalo::Bump, Node, Render, RootRender, VdomWeak};
4+use dodrio::{Node, Render, RenderContext, RootRender, VdomWeak};
5use serde::{Deserialize, Serialize};
5use serde::{Deserialize, Serialize};
6use std::marker::PhantomData;
6use std::marker::PhantomData;
7use wasm_bindgen::{prelude::*, JsCast};
7use wasm_bindgen::{prelude::*, JsCast};
@@ -93,10 +93,7 @@ impl<C> Todo<C> {
93}
93}
94
94
95impl<C: TodoActions> Render for Todo<C> {
95impl<C: TodoActions> Render for Todo<C> {
96− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
96+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
97− where
98− 'a: 'bump,
99− {
100 use dodrio::{
97 use dodrio::{
101 builder::*,
98 builder::*,
102 bumpalo::{self, collections::String},
99 bumpalo::{self, collections::String},
@@ -104,10 +101,11 @@ impl<C: TodoActions> Render for Todo<C> {
104
101
105 let id = self.id;
102 let id = self.id;
106 let title = self.edits.as_ref().unwrap_or(&self.title);
103 let title = self.edits.as_ref().unwrap_or(&self.title);
104+ let title = bumpalo::format!(in cx.bump, "{}", title).into_bump_str();
107
105
108− li(bump)
106+ li(&cx)
109 .attr("class", {
107 .attr("class", {
110− let mut class = String::new_in(bump);
108+ let mut class = String::new_in(cx.bump);
111 if self.completed {
109 if self.completed {
112 class.push_str("completed ");
110 class.push_str("completed ");
113 }
111 }
@@ -117,10 +115,10 @@ impl<C: TodoActions> Render for Todo<C> {
117 class.into_bump_str()
115 class.into_bump_str()
118 })
116 })
119 .children([
117 .children([
120− div(bump)
118+ div(&cx)
121 .attr("class", "view")
119 .attr("class", "view")
122 .children([
120 .children([
123− input(bump)
121+ input(&cx)
124 .attr("class", "toggle")
122 .attr("class", "toggle")
125 .attr("type", "checkbox")
123 .attr("type", "checkbox")
126 .bool_attr("checked", self.completed)
124 .bool_attr("checked", self.completed)
@@ -128,13 +126,13 @@ impl<C: TodoActions> Render for Todo<C> {
128 C::toggle_completed(root, vdom, id);
126 C::toggle_completed(root, vdom, id);
129 })
127 })
130 .finish(),
128 .finish(),
131− label(bump)
129+ label(&cx)
132 .on("dblclick", move |root, vdom, _event| {
130 .on("dblclick", move |root, vdom, _event| {
133 C::begin_editing(root, vdom, id);
131 C::begin_editing(root, vdom, id);
134 })
132 })
135 .children([text(title)])
133 .children([text(title)])
136 .finish(),
134 .finish(),
137− button(bump)
135+ button(&cx)
138 .attr("class", "destroy")
136 .attr("class", "destroy")
139 .on("click", move |root, vdom, _event| {
137 .on("click", move |root, vdom, _event| {
140 C::delete(root, vdom, id);
138 C::delete(root, vdom, id);
@@ -142,13 +140,13 @@ impl<C: TodoActions> Render for Todo<C> {
142 .finish(),
140 .finish(),
143 ])
141 ])
144 .finish(),
142 .finish(),
145− input(bump)
143+ input(&cx)
146 .attr("class", "edit")
144 .attr("class", "edit")
147 .attr("value", title)
145 .attr("value", title)
148 .attr("name", "title")
146 .attr("name", "title")
149 .attr(
147 .attr(
150 "id",
148 "id",
151− bumpalo::format!(in bump, "todo-{}", id).into_bump_str(),
149+ bumpalo::format!(in cx.bump, "todo-{}", id).into_bump_str(),
152 )
150 )
153 .on("input", move |root, vdom, event| {
151 .on("input", move |root, vdom, event| {
154 let input = event
152 let input = event
examples/todomvc/src/todos.rs+36−51
@@ -5,10 +5,7 @@ use crate::controller::Controller;
5use crate::todo::{Todo, TodoActions};
5use crate::todo::{Todo, TodoActions};
6use crate::visibility::Visibility;
6use crate::visibility::Visibility;
7use crate::{keys, utils};
7use crate::{keys, utils};
8−use dodrio::{
9− bumpalo::{self, Bump},
10− Node, Render, RootRender, VdomWeak,
8+use dodrio::{bumpalo, Node, Render, RenderContext, RootRender, VdomWeak};
11−};
12use serde::{Deserialize, Serialize};
9use serde::{Deserialize, Serialize};
13use std::marker::PhantomData;
10use std::marker::PhantomData;
14use std::mem;
11use std::mem;
@@ -136,17 +133,14 @@ impl<C> Todos<C> {
136
133
137/// Rendering helpers.
134/// Rendering helpers.
138impl<C: TodosActions> Todos<C> {
135impl<C: TodosActions> Todos<C> {
139− fn header<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
136+ fn header<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
140− where
141− 'a: 'bump,
142− {
143 use dodrio::builder::*;
137 use dodrio::builder::*;
144
138
145− header(bump)
139+ header(&cx)
146 .attr("class", "header")
140 .attr("class", "header")
147 .children([
141 .children([
148− h1(bump).children([text("todos")]).finish(),
142+ h1(&cx).children([text("todos")]).finish(),
149− input(bump)
143+ input(&cx)
150 .on("input", |root, vdom, event| {
144 .on("input", |root, vdom, event| {
151 let input = event
145 let input = event
152 .target()
146 .target()
@@ -163,19 +157,19 @@ impl<C: TodosActions> Todos<C> {
163 .attr("class", "new-todo")
157 .attr("class", "new-todo")
164 .attr("placeholder", "What needs to be done?")
158 .attr("placeholder", "What needs to be done?")
165 .attr("autofocus", "")
159 .attr("autofocus", "")
160+ .attr(
166− .attr("value", &self.draft)
161+ "value",
162+ bumpalo::format!(in cx.bump, "{}", self.draft).into_bump_str(),
163+ )
167 .finish(),
164 .finish(),
168 ])
165 ])
169 .finish()
166 .finish()
170 }
167 }
171
168
172− fn todos_list<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
169+ fn todos_list<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
173− where
174− 'a: 'bump,
175− {
176 use dodrio::{builder::*, bumpalo::collections::Vec};
170 use dodrio::{builder::*, bumpalo::collections::Vec};
177
171
178− let mut todos = Vec::with_capacity_in(self.todos.len(), bump);
172+ let mut todos = Vec::with_capacity_in(self.todos.len(), cx.bump);
179 todos.extend(
173 todos.extend(
180 self.todos
174 self.todos
181 .iter()
175 .iter()
@@ -184,10 +178,10 @@ impl<C: TodosActions> Todos<C> {
184 Visibility::Active => !t.is_complete(),
178 Visibility::Active => !t.is_complete(),
185 Visibility::Completed => t.is_complete(),
179 Visibility::Completed => t.is_complete(),
186 })
180 })
187− .map(|t| t.render(bump)),
181+ .map(|t| t.render(cx)),
188 );
182 );
189
183
190− section(bump)
184+ section(&cx)
191 .attr("class", "main")
185 .attr("class", "main")
192 .attr(
186 .attr(
193 "visibility",
187 "visibility",
@@ -198,7 +192,7 @@ impl<C: TodosActions> Todos<C> {
198 },
192 },
199 )
193 )
200 .children([
194 .children([
201− input(bump)
195+ input(&cx)
202 .attr("class", "toggle-all")
196 .attr("class", "toggle-all")
203 .attr("id", "toggle-all")
197 .attr("id", "toggle-all")
204 .attr("type", "checkbox")
198 .attr("type", "checkbox")
@@ -208,19 +202,16 @@ impl<C: TodosActions> Todos<C> {
208 C::toggle_all(root, vdom);
202 C::toggle_all(root, vdom);
209 })
203 })
210 .finish(),
204 .finish(),
211− label(bump)
205+ label(&cx)
212 .attr("for", "toggle-all")
206 .attr("for", "toggle-all")
213 .children([text("Mark all as complete")])
207 .children([text("Mark all as complete")])
214 .finish(),
208 .finish(),
215− ul(bump).attr("class", "todo-list").children(todos).finish(),
209+ ul(&cx).attr("class", "todo-list").children(todos).finish(),
216 ])
210 ])
217 .finish()
211 .finish()
218 }
212 }
219
213
220− fn footer<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
214+ fn footer<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
221− where
222− 'a: 'bump,
223− {
224 use dodrio::builder::*;
215 use dodrio::builder::*;
225
216
226 let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
217 let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
@@ -230,36 +221,36 @@ impl<C: TodosActions> Todos<C> {
230 } else {
221 } else {
231 " items left"
222 " items left"
232 };
223 };
233− let incomplete_count = bumpalo::format!(in bump, "{}", incomplete_count);
224+ let incomplete_count = bumpalo::format!(in cx.bump, "{}", incomplete_count);
234
225
235 let clear_completed_text = bumpalo::format!(
226 let clear_completed_text = bumpalo::format!(
236− in bump,
227+ in cx.bump,
237 "Clear completed ({})",
228 "Clear completed ({})",
238 self.todos.iter().filter(|t| t.is_complete()).count()
229 self.todos.iter().filter(|t| t.is_complete()).count()
239 );
230 );
240
231
241− footer(bump)
232+ footer(&cx)
242 .attr("class", "footer")
233 .attr("class", "footer")
243 .bool_attr("hidden", self.todos.is_empty())
234 .bool_attr("hidden", self.todos.is_empty())
244 .children([
235 .children([
245− span(bump)
236+ span(&cx)
246 .attr("class", "todo-count")
237 .attr("class", "todo-count")
247 .children([
238 .children([
248− strong(bump)
239+ strong(&cx)
249 .children([text(incomplete_count.into_bump_str())])
240 .children([text(incomplete_count.into_bump_str())])
250 .finish(),
241 .finish(),
251 text(items_left),
242 text(items_left),
252 ])
243 ])
253 .finish(),
244 .finish(),
254− ul(bump)
245+ ul(&cx)
255 .attr("class", "filters")
246 .attr("class", "filters")
256 .children([
247 .children([
257− self.visibility_swap(bump, "#/", Visibility::All),
248+ self.visibility_swap(cx, "#/", Visibility::All),
258− self.visibility_swap(bump, "#/active", Visibility::Active),
249+ self.visibility_swap(cx, "#/active", Visibility::Active),
259− self.visibility_swap(bump, "#/completed", Visibility::Completed),
250+ self.visibility_swap(cx, "#/completed", Visibility::Completed),
260 ])
251 ])
261 .finish(),
252 .finish(),
262− button(bump)
253+ button(&cx)
263 .on("click", |root, vdom, _event| {
254 .on("click", |root, vdom, _event| {
264 C::delete_completed(root, vdom);
255 C::delete_completed(root, vdom);
265 })
256 })
@@ -271,22 +262,19 @@ impl<C: TodosActions> Todos<C> {
271 .finish()
262 .finish()
272 }
263 }
273
264
274− fn visibility_swap<'a, 'bump>(
265+ fn visibility_swap<'a>(
275− &'a self,
266+ &self,
276− bump: &'bump Bump,
267+ cx: &mut RenderContext<'a>,
277 url: &'static str,
268 url: &'static str,
278 target_vis: Visibility,
269 target_vis: Visibility,
279− ) -> Node<'bump>
270+ ) -> Node<'a> {
280− where
281− 'a: 'bump,
282− {
283 use dodrio::builder::*;
271 use dodrio::builder::*;
284
272
285− li(bump)
273+ li(&cx)
286 .on("click", move |root, vdom, _event| {
274 .on("click", move |root, vdom, _event| {
287 C::change_visibility(root, vdom, target_vis);
275 C::change_visibility(root, vdom, target_vis);
288 })
276 })
289− .children([a(bump)
277+ .children([a(&cx)
290 .attr("href", url)
278 .attr("href", url)
291 .attr(
279 .attr(
292 "class",
280 "class",
@@ -303,14 +291,11 @@ impl<C: TodosActions> Todos<C> {
303}
291}
304
292
305impl<C: TodosActions> Render for Todos<C> {
293impl<C: TodosActions> Render for Todos<C> {
306− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
294+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
307− where
308− 'a: 'bump,
309− {
310 use dodrio::builder::*;
295 use dodrio::builder::*;
311
296
312− div(bump)
297+ div(&cx)
313− .children([self.header(bump), self.todos_list(bump), self.footer(bump)])
298+ .children([self.header(cx), self.todos_list(cx), self.footer(cx)])
314 .finish()
299 .finish()
315 }
300 }
316}
301}
src/builder.rs+11−4
@@ -54,7 +54,11 @@ impl<'a>
54 /// let my_element_builder = ElementBuilder::new(&b, tag_name);
54 /// let my_element_builder = ElementBuilder::new(&b, tag_name);
55 /// # fn flip_coin() -> bool { true }
55 /// # fn flip_coin() -> bool { true }
56 /// ```
56 /// ```
57− pub fn new(bump: &'a Bump, tag_name: &'a str) -> Self {
57+ pub fn new<B>(bump: B, tag_name: &'a str) -> Self
58+ where
59+ B: Into<&'a Bump>,
60+ {
61+ let bump = bump.into();
58 ElementBuilder {
62 ElementBuilder {
59 bump,
63 bump,
60 tag_name,
64 tag_name,
@@ -378,14 +382,17 @@ macro_rules! builder_constructors {
378 $(
382 $(
379 $(#[$attr])*
383 $(#[$attr])*
380 #[inline]
384 #[inline]
381− pub fn $name<'a>(
385+ pub fn $name<'a, B>(
382− bump: &'a Bump,
386+ bump: B,
383 ) -> ElementBuilder<
387 ) -> ElementBuilder<
384 'a,
388 'a,
385 bumpalo::collections::Vec<'a, Listener<'a>>,
389 bumpalo::collections::Vec<'a, Listener<'a>>,
386 bumpalo::collections::Vec<'a, Attribute<'a>>,
390 bumpalo::collections::Vec<'a, Attribute<'a>>,
387 bumpalo::collections::Vec<'a, Node<'a>>,
391 bumpalo::collections::Vec<'a, Node<'a>>,
388− > {
392+ >
393+ where
394+ B: Into<&'a Bump>
395+ {
389 ElementBuilder::new(bump, stringify!($name))
396 ElementBuilder::new(bump, stringify!($name))
390 }
397 }
391 )*
398 )*
src/cached.rs+67−70
@@ -1,26 +1,16 @@
1−use crate::{Node, Render};
1+use crate::{
2−use bumpalo::Bump;
2+ cached_set::{CacheId, CachedSet},
3+ Node, Render, RenderContext,
4+};
3−use std::cell::RefCell;
5+use std::cell::Cell;
4−use std::mem;
5use std::ops::{Deref, DerefMut};
6use std::ops::{Deref, DerefMut};
6
7
7−// TODO: This implementation will only cache the rendering (generation of the
8−// virtual DOM) but not the diffing of the cached subtree. We could skip diffing
9−// for cached tree by adding `fn is_cached(&self) -> bool` to `Node` that we can
10−// check during diffing. This comes at the cost of bloating `Node`'s size (since
11−// we don't have any padding to sneak an extra `bool` field into). We should
12−// investigate whether it is worth adding this or not.
13−
14/// A renderable that supports caching for when rendering is expensive but can
8/// A renderable that supports caching for when rendering is expensive but can
15/// generate the same DOM tree.
9/// generate the same DOM tree.
16−#[derive(Debug)]
10+#[derive(Clone, Debug)]
17pub struct Cached<R> {
11pub struct Cached<R> {
18 inner: R,
12 inner: R,
19− bump: bumpalo::Bump,
20− // Actually a self-reference into `self.bump`. Safe because we ensure that
21− // whenever we hand out a cached node, we use a lifetime that cannot outlive
22− // its owning `Cached<R>`.
23− cached: RefCell<Option<Node<'static>>>,
13+ cached: Cell<Option<CacheId>>,
24}
14}
25
15
26impl<R> Cached<R> {
16impl<R> Cached<R> {
@@ -29,18 +19,14 @@ impl<R> Cached<R> {
29 /// # Example
19 /// # Example
30 ///
20 ///
31 /// ```
21 /// ```
32− /// use bumpalo::Bump;
33− /// use dodrio::{Cached, Node, Render};
22+ /// use dodrio::{Cached, Node, Render, RenderContext};
34 ///
23 ///
35 /// pub struct Counter {
24 /// pub struct Counter {
36 /// count: u32,
25 /// count: u32,
37 /// }
26 /// }
38 ///
27 ///
39 /// impl Render for Counter {
28 /// impl Render for Counter {
40− /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
29+ /// fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
41− /// where
42− /// 'a: 'bump
43− /// {
44 /// // ...
30 /// // ...
45 /// # unimplemented!()
31 /// # unimplemented!()
46 /// }
32 /// }
@@ -54,13 +40,8 @@ impl<R> Cached<R> {
54 /// ```
40 /// ```
55 #[inline]
41 #[inline]
56 pub fn new(inner: R) -> Cached<R> {
42 pub fn new(inner: R) -> Cached<R> {
57− let bump = Bump::new();
58− let cached = RefCell::new(None);
43+ let cached = Cell::new(None);
59− Cached {
44+ Cached { inner, cached }
60− inner,
61− bump,
62− cached,
63− }
64 }
45 }
65
46
66 /// Invalidate the cached rendering.
47 /// Invalidate the cached rendering.
@@ -76,8 +57,7 @@ impl<R> Cached<R> {
76 /// displaying greetings to old `who`s.
57 /// displaying greetings to old `who`s.
77 ///
58 ///
78 /// ```
59 /// ```
79− /// use bumpalo::Bump;
80− /// use dodrio::{Cached, Node, Render};
60+ /// use dodrio::{bumpalo, Cached, Node, Render, RenderContext};
81 ///
61 ///
82 /// /// A component that renders to "<p>Hello, {who}!</p>"
62 /// /// A component that renders to "<p>Hello, {who}!</p>"
83 /// pub struct Hello {
63 /// pub struct Hello {
@@ -85,13 +65,11 @@ impl<R> Cached<R> {
85 /// }
65 /// }
86 ///
66 ///
87 /// impl Render for Hello {
67 /// impl Render for Hello {
88− /// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
68+ /// fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
89− /// where
90− /// 'a: 'bump,
91− /// {
92 /// use dodrio::builder::*;
69 /// use dodrio::builder::*;
93− /// p(bump)
70+ /// let greeting = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
94− /// .children([text("Hello, "), text(&self.who), text("!")])
71+ /// p(&cx)
72+ /// .children([text(greeting.into_bump_str())])
95 /// .finish()
73 /// .finish()
96 /// }
74 /// }
97 /// }
75 /// }
@@ -104,13 +82,13 @@ impl<R> Cached<R> {
104 /// }
82 /// }
105 /// ```
83 /// ```
106 #[inline]
84 #[inline]
107− pub fn invalidate(cached: &mut Self) {
85+ pub fn invalidate(cached: &Self) {
108− *cached.cached.borrow_mut() = None;
86+ cached.cached.set(None);
109 }
87 }
110
88
111 /// Convert a `Cached<R>` back into a plain `R`.
89 /// Convert a `Cached<R>` back into a plain `R`.
112 #[inline]
90 #[inline]
113− pub fn into_inner(cached: Cached<R>) -> R {
91+ pub fn into_inner(cached: Self) -> R {
114 cached.inner
92 cached.inner
115 }
93 }
116}
94}
@@ -129,35 +107,54 @@ impl<R> DerefMut for Cached<R> {
129 }
107 }
130}
108}
131
109
132−unsafe fn extend_node_lifetime<'a>(node: Node<'a>) -> Node<'static> {
133− mem::transmute(node)
134−}
135−
136−impl<R: Render> Render for Cached<R> {
110+impl<R> Render for Cached<R>
137− fn render<'a, 'bump>(&'a self, _: &'bump Bump) -> Node<'bump>
138− where
111+where
139− 'a: 'bump,
112+ R: Render,
140− {
113+{
141− let mut cached = self.cached.borrow_mut();
114+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
142−
115+ let id = match self.cached.get() {
143− if let Some(cached) = cached.as_ref() {
116+ // This does-the-cache-contain-this-id check is necessary because
144− // The cached node is actually a self-reference, so it has the `'a`
117+ // the same `Cached<R>` instance can be rendered into vdom A, which
145− // lifetime.
118+ // will save the results into A's cached set and yield id X. Then,
146− let cached: Node<'a> = cached.clone();
119+ // it can be rendered *again* into a second vdom B, and B's cached
147−
120+ // set does not have the saved results for id X. If we didn't have
148− // But the `'a` lifetime outlives `'bump`, so we can safely convert
121+ // this check, instead of a cache miss, we would have a panic.
149− // it to the narrower `'bump` lifetime.
122+ //
150− let cached: Node<'bump> = cached;
123+ // This scenario should basically never happen in the real world,
151−
124+ // however. If we ever find that it is worth sharing a cached render
152− // Return the cached rendering!
125+ // component between multiple vdoms, and want to avoid these
153− return cached;
126+ // "unnecessary" cache misses, we can do this:
127+ //
128+ // * Make each `Cached<R>` have an instance id and generation
129+ // counter.
130+ //
131+ // * On invalidation, bump the generation counter.
132+ //
133+ // * Add a generation member to the `CacheEntry`.
134+ //
135+ // * Each vdom maintains a map from `Cached<R>` instance id to
136+ // `CacheEntry`s.
137+ //
138+ // * We only re-use the cached results if the `Cached<R>`'s
139+ // generation counter matches the entry in the vdom's cached set.
140+ //
141+ // This is all do-able but is a bit more book keeping than we really
142+ // want to do unless it is well motivated.
143+ Some(id)
144+ if {
145+ let cached_set = cx.cached_set.borrow();
146+ cached_set.contains(id)
147+ } =>
148+ {
149+ id
150+ }
151+ _ => {
152+ let id = CachedSet::insert(cx, |nested_cx| self.inner.render(nested_cx));
153+ self.cached.set(Some(id));
154+ id
155+ }
154− }
156+ };
155
157
156− // We don't have a cached node. Render into our own `Bump`, cache it for
158+ Node::cached(id)
157− // future renders, and return it. Same lifetimes as above.
158− let node: Node<'a> = self.inner.render(&self.bump);
159− *cached = Some(unsafe { extend_node_lifetime(node.clone()) });
160− let node: Node<'bump> = node;
161− node
162 }
159 }
163}
160}
src/cached_set.rsnew file+155
@@ -0,0 +1,155 @@
1+use crate::{
2+ events::EventsRegistry,
3+ node::{Node, NodeKind},
4+ render_context::RenderContext,
5+};
6+use bumpalo::Bump;
7+use fxhash::{FxHashMap, FxHashSet};
8+use std::sync::atomic::{AtomicUsize, Ordering};
9+use std::u32;
10+use wasm_bindgen::prelude::*;
11+
12+static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
13+
14+pub_unstable_internal! {
15+ #[derive(Debug, Default)]
16+ pub(crate) struct CachedSet {
17+ items: FxHashMap<CacheId, CacheEntry>,
18+ }
19+}
20+
21+pub_unstable_internal! {
22+ #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
23+ pub(crate) struct CacheId(u32);
24+}
25+
26+#[derive(Debug)]
27+pub(crate) struct CacheEntry {
28+ bump: Bump,
29+
30+ // Actually a reference into `bump`.
31+ node: *const Node<'static>,
32+
33+ // All the other cached entries that `node` references (including transitive
34+ // edges). By eagerly following transitive edges and recording them the
35+ // once, we don't have to repeat the tracing every time we want to garbage
36+ // collect cache entries.
37+ edges: FxHashSet<CacheId>,
38+}
39+
40+impl CachedSet {
41+ pub(crate) fn gc(&mut self, registry: &mut EventsRegistry, roots: &[CacheId]) {
42+ // TODO: We need to port a hash set over in `bumpalo::collections` so
43+ // that we can use a temporary bump arena for this set.
44+ let mut mark_bits = FxHashSet::default();
45+ mark_bits.reserve(self.items.len());
46+
47+ for root in roots {
48+ if mark_bits.insert(*root) {
49+ let entry = self
50+ .items
51+ .get(root)
52+ .expect_throw("CachedSet::gc: should have root in cached set");
53+
54+ // NB: no need to recurse here because `edges` already contains
55+ // transitive edges!
56+ mark_bits.extend(entry.edges.iter().cloned());
57+ }
58+ }
59+
60+ self.items.retain(|id, entry| {
61+ let keep = mark_bits.contains(id);
62+ if !keep {
63+ debug!("CachedSet::gc: removing {:?}", id);
64+ let node: &Node = unsafe { &*entry.node };
65+ registry.remove_subtree(node);
66+ }
67+ keep
68+ });
69+ }
70+
71+ // Trace all the transitive edges to other cached entries that the given
72+ // node has.
73+ fn trace(&self, node: &Node) -> FxHashSet<CacheId> {
74+ let mut edges = FxHashSet::default();
75+
76+ let bump = Bump::new();
77+ let mut stack = bumpalo::collections::Vec::with_capacity_in(64, &bump);
78+ stack.push(node);
79+
80+ while let Some(node) = stack.pop() {
81+ match &node.kind {
82+ NodeKind::Text(_) => continue,
83+ NodeKind::Cached(c) => {
84+ debug_assert!(self.items.contains_key(&c.id));
85+ edges.insert(c.id);
86+ edges.extend(
87+ self.items
88+ .get(&c.id)
89+ .expect_throw("CachedSet::trace: should have c.id in cached set")
90+ .edges
91+ .iter()
92+ .cloned(),
93+ );
94+ }
95+ NodeKind::Element(el) => {
96+ stack.extend(el.children);
97+ }
98+ }
99+ }
100+
101+ edges
102+ }
103+
104+ fn next_id(&mut self) -> CacheId {
105+ let next = ID_COUNTER.fetch_add(1, Ordering::AcqRel) as u32;
106+ let next = if next == u32::MAX { None } else { Some(next) };
107+ CacheId(next.expect_throw("ID_COUNTER overflowed"))
108+ }
109+
110+ pub(crate) fn insert<F>(cx: &mut RenderContext, f: F) -> CacheId
111+ where
112+ F: for<'a> FnOnce(&mut RenderContext<'a>) -> Node<'a>,
113+ {
114+ let bump = Bump::new();
115+ let set = cx.cached_set;
116+ let mut nested_cx = RenderContext::new(&bump, set);
117+ let node = f(&mut nested_cx);
118+ let node = bump.alloc(node);
119+ let edges = {
120+ let set = set.borrow();
121+ set.trace(node)
122+ };
123+ let node = node as *mut Node<'_> as usize as *const Node<'static>;
124+ let entry = CacheEntry { bump, node, edges };
125+
126+ let mut set = set.borrow_mut();
127+ let id = set.next_id();
128+ debug!("CachedSet::insert: id = {:?}; entry = {:?}", id, entry);
129+ set.items.insert(id, entry);
130+ id
131+ }
132+
133+ /// Does the cached set contain a cached node with the given id?
134+ pub fn contains(&self, id: CacheId) -> bool {
135+ self.items.contains_key(&id)
136+ }
137+
138+ /// Get the node for the given cache id.
139+ pub fn get(&self, mut id: CacheId) -> Node {
140+ debug!("CachedSet::get: id = {:?}", id);
141+ loop {
142+ let entry = self
143+ .items
144+ .get(&id)
145+ .expect_throw("CachedSet::get: should have id in set");
146+ let node: &Node = unsafe { &*entry.node };
147+ if let NodeKind::Cached(ref c) = node.kind {
148+ id = c.id;
149+ continue;
150+ } else {
151+ return node.clone();
152+ }
153+ }
154+ }
155+}
src/change_list.rs+76−48
@@ -2,34 +2,63 @@ use crate::Listener;
2use bumpalo::Bump;
2use bumpalo::Bump;
3use fxhash::FxHashMap;
3use fxhash::FxHashMap;
4use std::fmt;
4use std::fmt;
5−use std::sync::Once;
6−use wasm_bindgen::prelude::*;
7
5
8pub mod js {
6pub mod js {
9− use wasm_bindgen::prelude::*;
7+ cfg_if::cfg_if! {
8+ if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
9+ #[derive(Clone, Debug)]
10+ pub struct ChangeList {}
11+ impl ChangeList {
12+ pub fn new(_container: &crate::Element) -> ChangeList {
13+ ChangeList {}
14+ }
15+ pub fn unmount(&self) {}
16+ pub fn add_change_list_range(&self, _start: usize, _len: usize) {}
17+ pub fn init_events_trampoline(&self, _trampoline: &crate::EventsTrampoline) {}
18+ }
10
19
11− #[wasm_bindgen]
20+ pub fn eval_change_list() {}
12− extern "C" {
21+ } else {
13− #[derive(Clone, Debug)]
22+ use wasm_bindgen::prelude::*;
14− pub type ChangeList;
15
23
16− #[wasm_bindgen(constructor)]
24+ #[wasm_bindgen]
25+ extern "C" {
26+ #[derive(Clone, Debug)]
17− pub fn new(container: &web_sys::Node) -> ChangeList;
27+ pub type ChangeList;
18
28
19− #[wasm_bindgen(structural, method)]
29+ #[wasm_bindgen(constructor)]
20− pub fn unmount(this: &ChangeList);
30+ pub fn new(container: &web_sys::Element) -> ChangeList;
21
31
22− #[wasm_bindgen(structural, method, js_name = addChangeListRange)]
32+ #[wasm_bindgen(structural, method)]
23− pub fn add_change_list_range(this: &ChangeList, start: usize, len: usize);
33+ pub fn unmount(this: &ChangeList);
24
34
25− #[wasm_bindgen(structural, method, js_name = applyChanges)]
35+ #[wasm_bindgen(structural, method, js_name = addChangeListRange)]
26− pub fn apply_changes(this: &ChangeList, memory: JsValue);
36+ pub fn add_change_list_range(this: &ChangeList, start: usize, len: usize);
27
37
38+ #[wasm_bindgen(structural, method, js_name = applyChanges)]
39+ pub fn apply_changes(this: &ChangeList, memory: JsValue);
40+
28− #[wasm_bindgen(structural, method, js_name = initEventsTrampoline)]
41+ #[wasm_bindgen(structural, method, js_name = initEventsTrampoline)]
29− pub fn init_events_trampoline(
42+ pub fn init_events_trampoline(
30− this: &ChangeList,
43+ this: &ChangeList,
31− trampoline: &Closure<Fn(web_sys::Event, u32, u32)>,
44+ trampoline: &crate::EventsTrampoline,
45+ );
46+ }
47+
48+ pub fn eval_change_list() {
49+ use std::sync::Once;
50+ // XXX: Because wasm-bindgen-test doesn't support third party JS
51+ // dependencies, we can't use `wasm_bindgen(module = "...")` for our
52+ // `ChangeList` JS import. Instead, this *should* be a local JS snippet,
53+ // but that isn't implemented yet:
54+ // https://github.com/rustwasm/rfcs/pull/6
55+ static EVAL: Once = Once::new();
56+ EVAL.call_once(|| {
57+ js_sys::eval(include_str!("../js/change-list.js"))
58+ .expect_throw("should eval change-list.js OK");
32− );
59+ });
60+ }
61+ }
33 }
62 }
34}
63}
35
64
@@ -43,7 +72,7 @@ pub(crate) struct ChangeList {
43 strings_cache: FxHashMap<String, StringsCacheEntry>,
72 strings_cache: FxHashMap<String, StringsCacheEntry>,
44 next_string_key: u32,
73 next_string_key: u32,
45 js: js::ChangeList,
74 js: js::ChangeList,
46− events_trampoline: Option<Closure<Fn(web_sys::Event, u32, u32)>>,
75+ events_trampoline: Option<crate::EventsTrampoline>,
47}
76}
48
77
49impl Drop for ChangeList {
78impl Drop for ChangeList {
@@ -64,18 +93,8 @@ impl fmt::Debug for ChangeList {
64}
93}
65
94
66impl ChangeList {
95impl ChangeList {
67− pub(crate) fn new(container: &web_sys::Node) -> ChangeList {
96+ pub(crate) fn new(container: &crate::Element) -> ChangeList {
68− // XXX: Because wasm-bindgen-test doesn't support third party JS
97+ js::eval_change_list();
69− // dependencies, we can't use `wasm_bindgen(module = "...")` for our
70− // `ChangeList` JS import. Instead, this *should* be a local JS snippet,
71− // but that isn't implemented yet:
72− // https://github.com/rustwasm/rfcs/pull/6
73− static EVAL: Once = Once::new();
74− EVAL.call_once(|| {
75− js_sys::eval(include_str!("../js/change-list.js"))
76− .expect("should eval change-list.js OK");
77− });
78−
79 let bump = Bump::new();
98 let bump = Bump::new();
80 let strings_cache = FxHashMap::default();
99 let strings_cache = FxHashMap::default();
81 let js = js::ChangeList::new(container);
100 let js = js::ChangeList::new(container);
@@ -88,27 +107,36 @@ impl ChangeList {
88 }
107 }
89 }
108 }
90
109
91− pub(crate) fn apply_changes(&mut self) {
92− let js = &self.js;
93− unsafe {
94− self.bump.each_allocated_chunk(|ch| {
95− js.add_change_list_range(ch.as_ptr() as usize, ch.len());
96− });
97− }
98− js.apply_changes(wasm_bindgen::memory());
99− self.bump.reset();
100− }
101−
102− pub(crate) fn init_events_trampoline(
110+ pub(crate) fn init_events_trampoline(&mut self, trampoline: crate::EventsTrampoline) {
103− &mut self,
104− trampoline: Closure<Fn(web_sys::Event, u32, u32)>,
105− ) {
106 debug_assert!(self.events_trampoline.is_none());
111 debug_assert!(self.events_trampoline.is_none());
107 self.js.init_events_trampoline(&trampoline);
112 self.js.init_events_trampoline(&trampoline);
108 self.events_trampoline = Some(trampoline);
113 self.events_trampoline = Some(trampoline);
109 }
114 }
110}
115}
111
116
117+cfg_if::cfg_if! {
118+ if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
119+ impl ChangeList {
120+ pub(crate) fn apply_changes(&mut self) {
121+ // Do nothing...
122+ }
123+ }
124+ } else {
125+ impl ChangeList {
126+ pub(crate) fn apply_changes(&mut self) {
127+ let js = &self.js;
128+ unsafe {
129+ self.bump.each_allocated_chunk(|ch| {
130+ js.add_change_list_range(ch.as_ptr() as usize, ch.len());
131+ });
132+ }
133+ js.apply_changes(wasm_bindgen::memory());
134+ self.bump.reset();
135+ }
136+ }
137+ }
138+}
139+
112#[repr(u32)]
140#[repr(u32)]
113#[derive(Clone, Copy, Debug)]
141#[derive(Clone, Copy, Debug)]
114enum ChangeDiscriminant {
142enum ChangeDiscriminant {
src/diff.rsnew file+310
@@ -0,0 +1,310 @@
1+use crate::{
2+ cached_set::{CacheId, CachedSet},
3+ change_list::ChangeList,
4+ events::EventsRegistry,
5+ node::{Attribute, ElementNode, Listener, Node, NodeKind, TextNode},
6+};
7+use std::cmp;
8+
9+pub(crate) fn diff(
10+ cached_set: &CachedSet,
11+ change_list: &mut ChangeList,
12+ registry: &mut EventsRegistry,
13+ old: Node,
14+ new: Node,
15+ cached_roots: &mut bumpalo::collections::Vec<CacheId>,
16+) {
17+ match (&new.kind, &old.kind) {
18+ (
19+ &NodeKind::Text(TextNode { text: new_text }),
20+ &NodeKind::Text(TextNode { text: old_text }),
21+ ) => {
22+ debug!(" both are text nodes");
23+ if new_text != old_text {
24+ debug!(" text needs updating");
25+ change_list.emit_set_text(new_text);
26+ }
27+ }
28+
29+ (&NodeKind::Text(_), &NodeKind::Element(_)) => {
30+ debug!(" replacing a text node with an element");
31+ create(cached_set, change_list, registry, new, cached_roots);
32+ registry.remove_subtree(&old);
33+ change_list.emit_replace_with();
34+ }
35+
36+ (&NodeKind::Element(_), &NodeKind::Text(_)) => {
37+ debug!(" replacing an element with a text node");
38+ create(cached_set, change_list, registry, new, cached_roots);
39+ // Note: text nodes cannot have event listeners, so we don't need to
40+ // remove the old node's listeners from our registry her.
41+ change_list.emit_replace_with();
42+ }
43+
44+ (
45+ &NodeKind::Element(ElementNode {
46+ tag_name: new_tag_name,
47+ listeners: new_listeners,
48+ attributes: new_attributes,
49+ children: new_children,
50+ namespace: new_namespace,
51+ }),
52+ &NodeKind::Element(ElementNode {
53+ tag_name: old_tag_name,
54+ listeners: old_listeners,
55+ attributes: old_attributes,
56+ children: old_children,
57+ namespace: old_namespace,
58+ }),
59+ ) => {
60+ debug!(" updating an element");
61+ if new_tag_name != old_tag_name || new_namespace != old_namespace {
62+ debug!(" different tag names or namespaces; creating new element and replacing old element");
63+ create(cached_set, change_list, registry, new, cached_roots);
64+ registry.remove_subtree(&old);
65+ change_list.emit_replace_with();
66+ return;
67+ }
68+ diff_listeners(change_list, registry, old_listeners, new_listeners);
69+ diff_attributes(change_list, old_attributes, new_attributes);
70+ diff_children(
71+ cached_set,
72+ change_list,
73+ registry,
74+ old_children,
75+ new_children,
76+ cached_roots,
77+ );
78+ }
79+
80+ // Both the new and old nodes are cached.
81+ (&NodeKind::Cached(ref new), &NodeKind::Cached(ref old)) => {
82+ cached_roots.push(new.id);
83+
84+ if new.id == old.id {
85+ // This is the same cached node, so nothing has changed!
86+ return;
87+ }
88+
89+ let new = cached_set.get(new.id);
90+ let old = cached_set.get(old.id);
91+ diff(cached_set, change_list, registry, old, new, cached_roots);
92+ }
93+
94+ // New cached node when the old node was not cached. In this scenario,
95+ // we assume that they are pretty different, and it isn't worth diffing
96+ // the subtrees, so we just create the new cached node afresh.
97+ (&NodeKind::Cached(ref c), _) => {
98+ cached_roots.push(c.id);
99+ let new = cached_set.get(c.id);
100+ create(cached_set, change_list, registry, new, cached_roots);
101+ registry.remove_subtree(&old);
102+ change_list.emit_replace_with();
103+ }
104+
105+ // Old cached node and new non-cached node. Again, assume that they are
106+ // probably pretty different and create the new non-cached node afresh.
107+ (_, &NodeKind::Cached(_)) => {
108+ create(cached_set, change_list, registry, new, cached_roots);
109+ registry.remove_subtree(&old);
110+ change_list.emit_replace_with();
111+ }
112+ }
113+}
114+
115+fn diff_listeners(
116+ change_list: &mut ChangeList,
117+ registry: &mut EventsRegistry,
118+ old: &[Listener],
119+ new: &[Listener],
120+) {
121+ debug!(" updating event listeners");
122+
123+ 'outer1: for new_l in new {
124+ unsafe {
125+ // Safety relies on removing `new_l` from the registry manually at
126+ // the end of its lifetime. This happens below in the `'outer2`
127+ // loop, and elsewhere in diffing when removing old dom trees.
128+ registry.add(new_l);
129+ }
130+
131+ for old_l in old {
132+ if new_l.event == old_l.event {
133+ change_list.emit_update_event_listener(new_l);
134+ continue 'outer1;
135+ }
136+ }
137+
138+ change_list.emit_new_event_listener(new_l);
139+ }
140+
141+ 'outer2: for old_l in old {
142+ registry.remove(old_l);
143+
144+ for new_l in new {
145+ if new_l.event == old_l.event {
146+ continue 'outer2;
147+ }
148+ }
149+ change_list.emit_remove_event_listener(old_l.event);
150+ }
151+}
152+
153+fn diff_attributes(change_list: &mut ChangeList, old: &[Attribute], new: &[Attribute]) {
154+ debug!(" updating attributes");
155+
156+ // Do O(n^2) passes to add/update and remove attributes, since
157+ // there are almost always very few attributes.
158+ 'outer: for new_attr in new {
159+ if new_attr.is_volatile() {
160+ change_list.emit_set_attribute(new_attr.name, new_attr.value);
161+ } else {
162+ for old_attr in old {
163+ if old_attr.name == new_attr.name {
164+ if old_attr.value != new_attr.value {
165+ change_list.emit_set_attribute(new_attr.name, new_attr.value);
166+ }
167+ continue 'outer;
168+ }
169+ }
170+ change_list.emit_set_attribute(new_attr.name, new_attr.value);
171+ }
172+ }
173+
174+ 'outer2: for old_attr in old {
175+ for new_attr in new {
176+ if old_attr.name == new_attr.name {
177+ continue 'outer2;
178+ }
179+ }
180+ change_list.emit_remove_attribute(old_attr.name);
181+ }
182+}
183+
184+fn diff_children(
185+ cached_set: &CachedSet,
186+ change_list: &mut ChangeList,
187+ registry: &mut EventsRegistry,
188+ old: &[Node],
189+ new: &[Node],
190+ cached_roots: &mut bumpalo::collections::Vec<CacheId>,
191+) {
192+ debug!(" updating children shared by old and new");
193+
194+ let num_children_to_diff = cmp::min(new.len(), old.len());
195+ let mut new_children = new.iter();
196+ let mut old_children = old.iter();
197+ let mut pushed = false;
198+
199+ for (i, (new_child, old_child)) in new_children
200+ .by_ref()
201+ .zip(old_children.by_ref())
202+ .take(num_children_to_diff)
203+ .enumerate()
204+ {
205+ if i == 0 {
206+ change_list.emit_push_first_child();
207+ pushed = true;
208+ } else {
209+ debug_assert!(pushed);
210+ change_list.emit_pop_push_next_sibling();
211+ }
212+
213+ diff(
214+ cached_set,
215+ change_list,
216+ registry,
217+ old_child.clone(),
218+ new_child.clone(),
219+ cached_roots,
220+ );
221+ }
222+
223+ if old_children.next().is_some() {
224+ debug!(" removing extra old children");
225+ debug_assert!(new_children.next().is_none());
226+ if !pushed {
227+ change_list.emit_push_first_child();
228+ } else {
229+ change_list.emit_pop_push_next_sibling();
230+ }
231+ change_list.emit_remove_self_and_next_siblings();
232+ pushed = false;
233+ } else {
234+ debug!(" creating new children");
235+ for (i, new_child) in new_children.enumerate() {
236+ if i == 0 && pushed {
237+ change_list.emit_pop();
238+ pushed = false;
239+ }
240+ create(
241+ cached_set,
242+ change_list,
243+ registry,
244+ new_child.clone(),
245+ cached_roots,
246+ );
247+ change_list.emit_append_child();
248+ }
249+ }
250+
251+ debug!(" done updating children");
252+ if pushed {
253+ change_list.emit_pop();
254+ }
255+}
256+
257+fn create(
258+ cached_set: &CachedSet,
259+ change_list: &mut ChangeList,
260+ registry: &mut EventsRegistry,
261+ node: Node,
262+ cached_roots: &mut bumpalo::collections::Vec<CacheId>,
263+) {
264+ match node.kind {
265+ NodeKind::Text(TextNode { text }) => {
266+ change_list.emit_create_text_node(text);
267+ }
268+ NodeKind::Element(ElementNode {
269+ tag_name,
270+ listeners,
271+ attributes,
272+ children,
273+ namespace,
274+ }) => {
275+ if let Some(namespace) = namespace {
276+ change_list.emit_create_element_ns(tag_name, namespace);
277+ } else {
278+ change_list.emit_create_element(tag_name);
279+ }
280+ for l in listeners {
281+ unsafe {
282+ registry.add(l);
283+ }
284+ change_list.emit_new_event_listener(l);
285+ }
286+ for attr in attributes {
287+ if namespace.is_none() || attr.name.starts_with("xmlns") {
288+ change_list.emit_set_attribute(&attr.name, &attr.value);
289+ } else {
290+ change_list.emit_set_attribute_ns(&attr.name, &attr.value);
291+ }
292+ }
293+ for child in children {
294+ create(
295+ cached_set,
296+ change_list,
297+ registry,
298+ child.clone(),
299+ cached_roots,
300+ );
301+ change_list.emit_append_child();
302+ }
303+ }
304+ NodeKind::Cached(c) => {
305+ cached_roots.push(c.id);
306+ let node = cached_set.get(c.id);
307+ create(cached_set, change_list, registry, node, cached_roots)
308+ }
309+ }
310+}
src/events.rs+132−83
@@ -1,98 +1,147 @@
1−use crate::vdom::{VdomInner, VdomWeak};
1+use crate::{node::Node, vdom::VdomInner, Listener};
2−use crate::{Listener, ListenerCallback};
3use std::cell::RefCell;
2use std::cell::RefCell;
4−use std::collections::HashMap;
5−use std::fmt;
6−use std::mem;
7use std::rc::{Rc, Weak};
3use std::rc::{Rc, Weak};
8−use wasm_bindgen::closure::Closure;
9−use wasm_bindgen::prelude::*;
10
4
11−/// The events registry manages event listeners for a virtual DOM.
5+cfg_if::cfg_if! {
12−///
6+ if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
13−/// The events registry is persistent across virtual DOM rendering and double
7+ #[derive(Debug)]
14−/// buffering.
15−pub(crate) struct EventsRegistry {
8+ pub(crate) struct EventsRegistry {}
9+ impl EventsRegistry {
16− vdom: Weak<VdomInner>,
10+ pub(crate) fn new(_vdom: Weak<VdomInner>) -> (
17− active: HashMap<(u32, u32), ListenerCallback<'static>>,
11+ Rc<RefCell<EventsRegistry>>,
12+ crate::EventsTrampoline,
13+ ) {
14+ (Rc::new(RefCell::new(EventsRegistry {})), ())
15+ }
16+ pub(crate) fn remove(&mut self, _listener: &Listener) {}
17+ pub(crate) fn remove_subtree(&mut self, _node: &Node) {}
18+ pub(crate) unsafe fn add<'a>(&mut self, _listener: &'a Listener<'a>) {}
19+ pub(crate) fn clear_active_listeners(&mut self) {}
20+ }
21+ } else {
22+ use bumpalo::Bump;
23+ use crate::{
24+ node::{ElementNode, ListenerCallback, NodeKind},
25+ vdom::VdomWeak,
18−}
26+ };
27+ use fxhash::FxHashMap;
28+ use std::fmt;
29+ use std::mem;
30+ use wasm_bindgen::closure::Closure;
31+ use wasm_bindgen::prelude::*;
19
32
20−impl fmt::Debug for EventsRegistry {
33+ /// The events registry manages event listeners for a virtual DOM.
21− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34+ ///
35+ /// The events registry is persistent across virtual DOM rendering and double
36+ /// buffering.
22− f.debug_struct("EventsRegistry")
37+ pub(crate) struct EventsRegistry {
23− .field("active", &self.active.keys().collect::<Vec<_>>())
38+ vdom: Weak<VdomInner>,
24− .finish()
39+ active: FxHashMap<(u32, u32), ListenerCallback<'static>>,
25− }
26−}
40+ }
27
41
28−impl EventsRegistry {
42+ impl fmt::Debug for EventsRegistry {
29− /// Construct a new events registry and JS function trampoline that weakly
43+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30− /// holds the new registry and can be used by JS to invoke listeners on the
44+ f.debug_struct("EventsRegistry")
31− /// Rust side.
45+ .field("active", &self.active.keys().collect::<Vec<_>>())
32− pub(crate) fn new(
46+ .finish()
33− vdom: Weak<VdomInner>,
47+ }
34− ) -> (
35− Rc<RefCell<EventsRegistry>>,
36− Closure<Fn(web_sys::Event, u32, u32)>,
37− ) {
38− let registry = Rc::new(RefCell::new(EventsRegistry {
39− vdom,
40− active: HashMap::new(),
41− }));
48+ }
42
49
43− let weak_registry = Rc::downgrade(&registry);
50+ impl EventsRegistry {
44− let closure = Closure::wrap(Box::new(move |event, a, b| {
51+ /// Construct a new events registry and JS function trampoline that weakly
45− debug!("Events trampoline invoked with (0x{:x}, 0x{:x})", a, b);
52+ /// holds the new registry and can be used by JS to invoke listeners on the
46− debug_assert!(a != 0);
53+ /// Rust side.
54+ pub(crate) fn new(
55+ vdom: Weak<VdomInner>,
56+ ) -> (
57+ Rc<RefCell<EventsRegistry>>,
58+ crate::EventsTrampoline,
59+ ) {
60+ let registry = Rc::new(RefCell::new(EventsRegistry {
61+ vdom,
62+ active: FxHashMap::default(),
63+ }));
47
64
48− // if the VdomInnerExclusive is keeping this closure alive, then the
49− // VdomInnerExclusive should also be keeping the registry alive
50− let registry = weak_registry.upgrade().unwrap_throw();
65+ let weak_registry = Rc::downgrade(&registry);
51− let registry = registry.borrow();
66+ let closure = Closure::wrap(Box::new(move |event, a, b| {
67+ debug!("Events trampoline invoked with (0x{:x}, 0x{:x})", a, b);
68+ debug_assert!(a != 0);
52
69
70+ // if the VdomInnerExclusive is keeping this closure alive, then the
71+ // VdomInnerExclusive should also be keeping the registry alive
72+ let registry = weak_registry.upgrade().unwrap_throw();
73+ let registry = registry.borrow();
74+
53− match registry.active.get(&(a, b)) {
75+ match registry.active.get(&(a, b)) {
54− None => warn!(
76+ None => warn!(
55− "EventsRegistry closure invoked with unknown listener parts: \
77+ "EventsRegistry closure invoked with unknown listener parts: \
56− (0x{:x}, 0x{:x})",
78+ (0x{:x}, 0x{:x})",
57− a, b
79+ a, b
58− ),
80+ ),
59− Some(callback) => {
81+ Some(callback) => {
60− let vdom = registry.vdom.upgrade().expect_throw(
82+ let vdom = registry.vdom.upgrade().expect_throw(
61− "if the registry is still around, then the vdom should still be around",
83+ "if the registry is still around, then the vdom should still be around",
62− );
84+ );
63− let vdom_weak = VdomWeak::new(&vdom);
85+ let vdom_weak = VdomWeak::new(&vdom);
64− let mut vdom = vdom.exclusive.borrow_mut();
86+ let mut vdom = vdom.exclusive.borrow_mut();
65− let component = vdom.component_raw_mut();
87+ let component = vdom.component_raw_mut();
66− callback(component, vdom_weak, event);
88+ callback(component, vdom_weak, event);
67− }
89+ }
90+ }
91+ }) as Box<Fn(web_sys::Event, u32, u32)>);
92+
93+ (registry, closure)
68 }
94 }
69− }) as Box<Fn(web_sys::Event, u32, u32)>);
70
95
71− (registry, closure)
96+ pub(crate) fn remove(&mut self, listener: &Listener) {
97+ let id = listener.get_callback_parts();
98+ debug!("EventsRegistry::remove(0x{:x}, 0x{:x})", id.0, id.1);
99+ debug_assert!(id.0 != 0);
100+ self.active.remove(&id);
72− }
101+ }
73
102
74− /// Add an event listener to the registry, exposing to JS.
75− ///
76− /// # Unsafety
77− ///
78− /// The listener's lifetime is extended to `'static` and it is the caller's
79− /// responsibility to ensure that the listener is not kept in the registry
80− /// after it is dropped. This is typically done via clearing the whole
81− /// registry on each render and repopulating it during diffing.
82− pub(crate) unsafe fn add<'a>(&mut self, listener: &'a Listener<'a>) {
103+ pub(crate) fn remove_subtree(&mut self, node: &Node) {
83− let id = listener.get_callback_parts();
104+ let bump = Bump::new();
84− debug!("EventsRegistry::add(0x{:x}, 0x{:x})", id.0, id.1);
105+ let mut stack = bumpalo::collections::Vec::with_capacity_in(64, &bump);
85− debug_assert!(id.0 != 0);
106+ stack.push(node);
86
107
87− let callback =
108+ while let Some(node) = stack.pop() {
88− mem::transmute::<ListenerCallback<'a>, ListenerCallback<'static>>(listener.callback);
109+ match node.kind {
89− let old = self.active.insert(id, callback);
110+ NodeKind::Cached(_) | NodeKind::Text(_) => continue,
90− debug_assert!(old.is_none());
111+ NodeKind::Element(ElementNode {listeners, children, ..}) => {
112+ for l in listeners {
113+ self.remove(l);
114+ }
115+ stack.extend(children);
116+ }
117+ }
118+ }
91− }
119+ }
92
120
121+ /// Add an event listener to the registry, exposing to JS.
122+ ///
123+ /// # Unsafety
124+ ///
125+ /// The listener's lifetime is extended to `'static` and it is the
126+ /// caller's responsibility to ensure that the listener is not kept
127+ /// in the registry after it is dropped. This is maintained during
128+ /// diffing.
129+ pub(crate) unsafe fn add<'a>(&mut self, listener: &'a Listener<'a>) {
130+ let id = listener.get_callback_parts();
131+ debug!("EventsRegistry::add(0x{:x}, 0x{:x})", id.0, id.1);
132+ debug_assert!(id.0 != 0);
133+
134+ let callback =
135+ mem::transmute::<ListenerCallback<'a>, ListenerCallback<'static>>(listener.callback);
136+ let old = self.active.insert(id, callback);
137+ debug_assert!(old.is_none());
138+ }
139+
93− /// Clear all event listeners from the registry.
140+ /// Clear all event listeners from the registry.
94− pub(crate) fn clear_active_listeners(&mut self) {
141+ pub(crate) fn clear_active_listeners(&mut self) {
95− debug!("EventsRegistry::clear_active_listeners()");
142+ debug!("EventsRegistry::clear_active_listeners()");
96− self.active.clear();
143+ self.active.clear();
144+ }
145+ }
97 }
146 }
98}
147}
src/lib.rs+62−10
@@ -3,7 +3,7 @@
3//! ## Example
3//! ## Example
4//!
4//!
5//! ```no_run
5//! ```no_run
6−//! use dodrio::{bumpalo::Bump, Attribute, Node, Render};
6+//! use dodrio::{bumpalo, Attribute, Node, Render, RenderContext};
7//! use wasm_bindgen::UnwrapThrowExt;
7//! use wasm_bindgen::UnwrapThrowExt;
8//!
8//!
9//! /// A component that greets someone.
9//! /// A component that greets someone.
@@ -19,15 +19,13 @@
19//! }
19//! }
20//!
20//!
21//! impl<'who> Render for Hello<'who> {
21//! impl<'who> Render for Hello<'who> {
22−//! fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
22+//! fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
23−//! where
24−//! 'a: 'bump,
25−//! {
26//! use dodrio::builder::*;
23//! use dodrio::builder::*;
27//!
24//!
28−//! let id = bumpalo::format!(in bump, "hello-{}", self.who);
25+//! let id = bumpalo::format!(in cx.bump, "hello-{}", self.who);
26+//! let who = bumpalo::collections::String::from_str_in(self.who, cx.bump).into_bump_str();
29//!
27//!
30−//! div(bump)
28+//! div(&cx)
31//! .attr("id", id.into_bump_str())
29//! .attr("id", id.into_bump_str())
32//! .on("click", |root, _vdom, _event| {
30//! .on("click", |root, _vdom, _event| {
33//! let hello = root.unwrap_mut::<Hello>();
31//! let hello = root.unwrap_mut::<Hello>();
@@ -37,9 +35,9 @@
37//! })
35//! })
38//! .children([
36//! .children([
39//! text("Hello, "),
37//! text("Hello, "),
40−//! strong(bump)
38+//! strong(&cx)
41//! .children([
39//! .children([
42−//! text(self.who),
40+//! text(who),
43//! text("!"),
41//! text("!"),
44//! ])
42//! ])
45//! .finish(),
43//! .finish(),
@@ -63,20 +61,74 @@ cfg_if::cfg_if! {
63 }
61 }
64}
62}
65
63
64+// A macro to expose items unstably, only for internal/testing usage.
65+cfg_if::cfg_if! {
66+ if #[cfg(feature = "xxx-unstable-internal-use-only")] {
67+ macro_rules! pub_unstable_internal {
68+ ( $(#[$attr:meta])* pub(crate) $( $thing:tt )* ) => {
69+ #[doc(hidden)]
70+ $( #[$attr] )*
71+ pub $( $thing )*
72+ }
73+ }
74+ } else {
75+ macro_rules! pub_unstable_internal {
76+ ( $(#[$attr:meta])* pub(crate) $( $thing:tt )* ) => {
77+ $( #[$attr] )*
78+ pub(crate) $( $thing )*
79+ }
80+ }
81+ }
82+}
83+
66// Only `pub` so that the wasm-bindgen bindings work.
84// Only `pub` so that the wasm-bindgen bindings work.
67#[doc(hidden)]
85#[doc(hidden)]
68pub mod change_list;
86pub mod change_list;
69
87
70mod cached;
88mod cached;
89+mod cached_set;
90+mod diff;
71mod events;
91mod events;
72mod node;
92mod node;
73mod render;
93mod render;
94+mod render_context;
74mod vdom;
95mod vdom;
75
96
76pub mod builder;
97pub mod builder;
77
98
78// Re-export items at the top level.
99// Re-export items at the top level.
79pub use self::cached::Cached;
100pub use self::cached::Cached;
80−pub use self::node::{Attribute, ElementNode, Listener, ListenerCallback, Node, TextNode};
101+pub use self::node::{Attribute, Listener, Node};
81pub use self::render::{Render, RootRender};
102pub use self::render::{Render, RootRender};
103+pub use self::render_context::RenderContext;
82pub use self::vdom::{Vdom, VdomWeak};
104pub use self::vdom::{Vdom, VdomWeak};
105+
106+cfg_if::cfg_if! {
107+ if #[cfg(all(target_arch = "wasm32", not(feature = "xxx-unstable-internal-use-only")))] {
108+ use wasm_bindgen::__rt::WasmRefCell as RefCell;
109+ } else {
110+ use std::cell::RefCell;
111+ }
112+}
113+
114+// Polyfill some Web stuff for benchmarking...
115+cfg_if::cfg_if! {
116+ if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
117+ /// An element node in the physical DOM.
118+ pub type Element = ();
119+
120+ pub(crate) type EventsTrampoline = ();
121+ } else {
122+ /// An element node in the physical DOM.
123+ pub type Element = web_sys::Element;
124+
125+ pub(crate) type EventsTrampoline = wasm_bindgen::closure::Closure<Fn(web_sys::Event, u32, u32)>;
126+ }
127+}
128+
129+cfg_if::cfg_if! {
130+ if #[cfg(feature = "xxx-unstable-internal-use-only")] {
131+ pub use self::cached_set::{CachedSet};
132+ pub use self::node::{ElementNode, NodeKind, TextNode};
133+ }
134+}
src/node.rs+77−73
@@ -1,35 +1,69 @@
1−use crate::{RootRender, VdomWeak};
1+use crate::{cached_set::CacheId, RootRender, VdomWeak};
2use bumpalo::Bump;
2use bumpalo::Bump;
3use std::fmt;
3use std::fmt;
4use std::iter;
4use std::iter;
5+use std::marker::PhantomData;
5use std::mem;
6use std::mem;
6
7
7−/// A node is either a text node or an element.
8+/// A virtual DOM node.
8#[derive(Debug, Clone)]
9#[derive(Debug, Clone)]
9−pub enum Node<'a> {
10+pub struct Node<'a> {
10− /// A text node.
11+ #[cfg(feature = "xxx-unstable-internal-use-only")]
11− Text(TextNode<'a>),
12+ #[doc(hidden)]
13+ pub kind: NodeKind<'a>,
12
14
13− /// An element potentially with attributes and children.
15+ #[cfg(not(feature = "xxx-unstable-internal-use-only"))]
14− Element(ElementNode<'a>),
16+ pub(crate) kind: NodeKind<'a>,
15}
17}
16
18
17−/// Text nodes are just a string of text. They cannot have attributes or
19+pub_unstable_internal! {
18−/// children.
20+ /// A node is either a text node or an element.
19−#[derive(Debug, Clone)]
21+ #[derive(Debug, Clone)]
20−pub struct TextNode<'a> {
21− pub(crate) text: &'a str,
22+ pub(crate) enum NodeKind<'a> {
23+ /// A text node.
24+ Text(TextNode<'a>),
25+
26+ /// An element potentially with attributes and children.
27+ Element(ElementNode<'a>),
28+
29+ /// A node in the vdom's `CachedSet`. This allows us to avoid
30+ /// re-rendering and re-diffing subtrees.
31+ Cached(CachedNode<'a>),
32+ }
22}
33}
23
34
35+pub_unstable_internal! {
36+ /// Text nodes are just a string of text. They cannot have attributes or
37+ /// children.
38+ #[derive(Debug, Clone)]
39+ pub(crate) struct TextNode<'a> {
40+ pub text: &'a str,
41+ }
42+}
43+
44+pub_unstable_internal! {
24−/// Elements have a tag name, zero or more attributes, and zero or more
45+ /// Elements have a tag name, zero or more attributes, and zero or more
25−/// children.
46+ /// children.
26−#[derive(Debug, Clone)]
47+ #[derive(Debug, Clone)]
27−pub struct ElementNode<'a> {
48+ pub(crate) struct ElementNode<'a> {
28− pub(crate) tag_name: &'a str,
49+ pub tag_name: &'a str,
29− pub(crate) listeners: &'a [Listener<'a>],
50+ pub listeners: &'a [Listener<'a>],
30− pub(crate) attributes: &'a [Attribute<'a>],
51+ pub attributes: &'a [Attribute<'a>],
31− pub(crate) children: &'a [Node<'a>],
52+ pub children: &'a [Node<'a>],
32− pub(crate) namespace: Option<&'a str>,
53+ pub namespace: Option<&'a str>,
54+ }
55+}
56+
57+pub_unstable_internal! {
58+ /// A cached node exists in an arena that is internal to the `Vdom`. It
59+ /// allows us to avoid both re-rendering a sub-tree and re-diffing
60+ /// it. `CachedNode`s cannot contain cycles, since they can only contain
61+ /// other `CachedNode`s that already exist when they are being constructed.
62+ #[derive(Debug, Clone)]
63+ pub(crate) struct CachedNode<'a> {
64+ pub id: CacheId,
65+ pub _phantom: PhantomData<&'a Node<'a>>,
66+ }
33}
67}
34
68
35/// An event listener callback function.
69/// An event listener callback function.
@@ -39,7 +73,7 @@ pub struct ElementNode<'a> {
39/// 1. The virtual DOM's root rendering component.
73/// 1. The virtual DOM's root rendering component.
40/// 2. A capability to scheduler virtual DOM re-rendering.
74/// 2. A capability to scheduler virtual DOM re-rendering.
41/// 3. The event that occurred.
75/// 3. The event that occurred.
42−pub type ListenerCallback<'a> =
76+pub(crate) type ListenerCallback<'a> =
43 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
77 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
44
78
45/// An event listener.
79/// An event listener.
@@ -121,35 +155,34 @@ impl<'a> Node<'a> {
121 let attributes: &'a Attributes = bump.alloc(attributes);
155 let attributes: &'a Attributes = bump.alloc(attributes);
122 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
156 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
123
157
158+ Node {
124− Node::Element(ElementNode {
159+ kind: NodeKind::Element(ElementNode {
125− tag_name,
160+ tag_name,
126− listeners,
161+ listeners,
127− attributes,
162+ attributes,
128− children,
163+ children,
129− namespace,
164+ namespace,
130− })
131− }
165+ }),
132−
133− /// Is this node a text node?
134− pub fn is_text(&self) -> bool {
135− match self {
136− Node::Text(_) => true,
137− _ => false,
138 }
166 }
139 }
167 }
140
168
141− /// Is this node an element?
169+ /// Construct a new text node with the given text.
142− pub fn is_element(&self) -> bool {
170+ #[inline]
143− match self {
171+ pub(crate) fn text(text: &'a str) -> Node<'a> {
144− Node::Element { .. } => true,
172+ Node {
145− _ => false,
173+ kind: NodeKind::Text(TextNode { text }),
146 }
174 }
147 }
175 }
148
176
149− /// Construct a new text node with the given text.
177+ /// Construct a new cached node with the given id.
150 #[inline]
178 #[inline]
151− pub(crate) fn text(text: &'a str) -> Node<'a> {
179+ pub(crate) fn cached(id: CacheId) -> Node<'a> {
152− Node::Text(TextNode { text })
180+ Node {
181+ kind: NodeKind::Cached(CachedNode {
182+ id,
183+ _phantom: PhantomData,
184+ }),
185+ }
153 }
186 }
154}
187}
155
188
@@ -169,35 +202,6 @@ impl<'a> IntoIterator for Node<'a> {
169 }
202 }
170}
203}
171
204
172−impl<'a> TextNode<'a> {
173− /// Get this text node's text content.
174− pub fn text(&self) -> &'a str {
175− self.text
176− }
177−}
178−
179−impl<'a> ElementNode<'a> {
180− /// Get this element's tag name.
181− pub fn tag_name(&self) -> &'a str {
182− self.tag_name
183− }
184−
185− /// Get this element's attributes.
186− pub fn attributes(&self) -> &'a [Attribute<'a>] {
187− self.attributes
188− }
189−
190− /// Get this element's attributes.
191− pub fn children(&self) -> &'a [Node<'a>] {
192− self.children
193− }
194−
195− /// Get this element's namespace.
196− pub fn namespace(&self) -> Option<&'a str> {
197− self.namespace
198− }
199−}
200−
201union CallbackFatPtr<'a> {
205union CallbackFatPtr<'a> {
202 callback: ListenerCallback<'a>,
206 callback: ListenerCallback<'a>,
203 parts: (u32, u32),
207 parts: (u32, u32),
src/render.rs+27−39
@@ -1,5 +1,4 @@
1−use crate::Node;
1+use crate::{Node, RenderContext};
2−use bumpalo::Bump;
3use std::any::Any;
2use std::any::Any;
4use std::rc::Rc;
3use std::rc::Rc;
5use wasm_bindgen::UnwrapThrowExt;
4use wasm_bindgen::UnwrapThrowExt;
@@ -11,37 +10,25 @@ use wasm_bindgen::UnwrapThrowExt;
11///
10///
12/// ## `Bump` Allocation
11/// ## `Bump` Allocation
13///
12///
14−/// `Render` implementations can use the provided `Bump` for very fast
13+/// `Render` implementations can use the `Bump` inside the provided
15−/// allocation for anything that needs to be allocated during rendering.
14+/// `RenderContext` for very fast allocation for anything that needs to be
16−///
15+/// temporarily allocated during rendering.
17−/// ## The `'a: 'bump` Lifetime Bound
18−///
19−/// The `'a: 'bump` bounds enforce that `self` outlives the given bump
20−/// allocator. This means that if `self` contains a string, the string does not
21−/// need to be copied into the output `Node` and can be used by reference
22−/// instead (i.e. it prevents accidentally using the string after its been
23−/// freed). The `'a: 'bump` bound also enables abstractions like
24−/// `dodrio::Cached` that can re-use cached `Node`s across `render`s without
25−/// copying them.
26///
16///
27/// ## Example
17/// ## Example
28///
18///
29/// ```no_run
19/// ```no_run
30−/// use dodrio::{bumpalo::Bump, Node, Render};
20+/// use dodrio::{Node, Render, RenderContext};
31///
21///
32/// pub struct MyComponent;
22/// pub struct MyComponent;
33///
23///
34/// impl Render for MyComponent {
24/// impl Render for MyComponent {
35−/// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
25+/// fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
36−/// where
37−/// 'a: 'bump
38−/// {
39/// use dodrio::builder::*;
26/// use dodrio::builder::*;
40///
27///
41−/// p(bump)
28+/// p(&cx)
42/// .children([
29/// .children([
43/// text("This is "),
30/// text("This is "),
44−/// strong(bump).children([text("my component")]).finish(),
31+/// strong(&cx).children([text("my component")]).finish(),
45/// text(" rendered!"),
32/// text(" rendered!"),
46/// ])
33/// ])
47/// .finish()
34/// .finish()
@@ -49,22 +36,17 @@ use wasm_bindgen::UnwrapThrowExt;
49/// }
36/// }
50/// ```
37/// ```
51pub trait Render {
38pub trait Render {
52− /// Render `self` as a virtual DOM. Use the given `Bump` for temporary
39+ /// Render `self` as a virtual DOM. Use the given context's `Bump` for
53− /// allocations.
40+ /// temporary allocations.
54− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
41+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a>;
55− where
56− 'a: 'bump;
57}
42}
58
43
59impl<'r, R> Render for &'r R
44impl<'r, R> Render for &'r R
60where
45where
61 R: Render,
46 R: Render,
62{
47{
63− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
48+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
64− where
65− 'a: 'bump,
66− {
67− (**self).render(bump)
49+ (**self).render(cx)
68 }
50 }
69}
51}
70
52
@@ -72,11 +54,8 @@ impl<R> Render for Rc<R>
72where
54where
73 R: Render,
55 R: Render,
74{
56{
75− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
57+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
76− where
77− 'a: 'bump,
78− {
79− (**self).render(bump)
58+ (**self).render(cx)
80 }
59 }
81}
60}
82
61
@@ -99,7 +78,10 @@ pub trait RootRender: Any + Render {
99 fn as_any_mut(&mut self) -> &mut Any;
78 fn as_any_mut(&mut self) -> &mut Any;
100}
79}
101
80
81+impl<T> RootRender for T
82+where
102−impl<T: Any + Render> RootRender for T {
83+ T: Any + Render,
84+{
103 fn as_any(&self) -> &Any {
85 fn as_any(&self) -> &Any {
104 self
86 self
105 }
87 }
@@ -117,7 +99,10 @@ impl dyn RootRender {
117 ///
99 ///
118 /// Panics if this virtual DOM's root rendering component is not an `R`
100 /// Panics if this virtual DOM's root rendering component is not an `R`
119 /// instance.
101 /// instance.
120− pub fn unwrap_ref<R: RootRender>(&self) -> &R {
102+ pub fn unwrap_ref<R>(&self) -> &R
103+ where
104+ R: RootRender,
105+ {
121 self.as_any()
106 self.as_any()
122 .downcast_ref::<R>()
107 .downcast_ref::<R>()
123 .expect_throw("bad `RootRender::unwrap_ref` call")
108 .expect_throw("bad `RootRender::unwrap_ref` call")
@@ -130,7 +115,10 @@ impl dyn RootRender {
130 ///
115 ///
131 /// Panics if this virtual DOM's root rendering component is not an `R`
116 /// Panics if this virtual DOM's root rendering component is not an `R`
132 /// instance.
117 /// instance.
133− pub fn unwrap_mut<R: RootRender>(&mut self) -> &mut R {
118+ pub fn unwrap_mut<R>(&mut self) -> &mut R
119+ where
120+ R: RootRender,
121+ {
134 self.as_any_mut()
122 self.as_any_mut()
135 .downcast_mut::<R>()
123 .downcast_mut::<R>()
136 .expect_throw("bad `RootRender::unwrap_ref` call")
124 .expect_throw("bad `RootRender::unwrap_ref` call")
src/render_context.rsnew file+63
@@ -0,0 +1,63 @@
1+use crate::cached_set::CachedSet;
2+use bumpalo::Bump;
3+use std::fmt;
4+
5+/// Common context available to all `Render` implementations.
6+///
7+/// Notably, the `RenderContext` gives access to the bump arena that the virtual
8+/// DOM should be allocated within. This is available via the `bump` field.
9+pub struct RenderContext<'a> {
10+ /// The underlying bump arena that virtual DOMs are rendered into.
11+ ///
12+ /// ## Example
13+ ///
14+ /// ```
15+ /// use dodrio::RenderContext;
16+ ///
17+ /// // Given a rendering context, allocate an i32 inside its bump arena.
18+ /// fn foo<'a>(cx: &mut RenderContext<'a>) -> &'a mut i32 {
19+ /// cx.bump.alloc(42)
20+ /// }
21+ /// ```
22+ pub bump: &'a Bump,
23+
24+ pub(crate) cached_set: &'a crate::RefCell<CachedSet>,
25+
26+ // Prevent exhaustive matching on the rendering context, so we can always
27+ // add more members in a semver-compatible way.
28+ _non_exhaustive: (),
29+}
30+
31+impl fmt::Debug for RenderContext<'_> {
32+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33+ f.debug_struct("RenderContext")
34+ .field("bump", &self.bump)
35+ .finish()
36+ }
37+}
38+
39+impl<'a> RenderContext<'a> {
40+ pub_unstable_internal! {
41+ pub(crate) fn new(bump: &'a Bump, cached_set: &'a crate::RefCell<CachedSet>) -> Self {
42+ RenderContext {
43+ bump,
44+ cached_set,
45+ _non_exhaustive: (),
46+ }
47+ }
48+ }
49+}
50+
51+impl<'a, 'b> From<&'b RenderContext<'a>> for &'a Bump {
52+ #[inline]
53+ fn from(cx: &'b RenderContext<'a>) -> &'a Bump {
54+ cx.bump
55+ }
56+}
57+
58+impl<'a, 'b, 'c> From<&'c &'b mut RenderContext<'a>> for &'a Bump {
59+ #[inline]
60+ fn from(cx: &'c &'b mut RenderContext<'a>) -> &'a Bump {
61+ cx.bump
62+ }
63+}
src/vdom.rs+81−264
@@ -1,12 +1,13 @@
1use super::change_list::ChangeList;
1use super::change_list::ChangeList;
2−use super::node::{Attribute, ElementNode, Listener, Node, TextNode};
3use super::RootRender;
2use super::RootRender;
3+use crate::cached_set::CachedSet;
4use crate::events::EventsRegistry;
4use crate::events::EventsRegistry;
5+use crate::node::Node;
6+use crate::RenderContext;
5use bumpalo::Bump;
7use bumpalo::Bump;
6use futures::future::Future;
8use futures::future::Future;
7use std::cell::Cell;
9use std::cell::Cell;
8use std::cell::RefCell;
10use std::cell::RefCell;
9−use std::cmp;
10use std::fmt;
11use std::fmt;
11use std::mem;
12use std::mem;
12use std::mem::ManuallyDrop;
13use std::mem::ManuallyDrop;
@@ -54,10 +55,11 @@ pub(crate) struct VdomInnerExclusive {
54 // implementation.
55 // implementation.
55 component: Option<Box<RootRender>>,
56 component: Option<Box<RootRender>>,
56
57
57− dom_buffers: [Bump; 2],
58+ dom_buffers: Option<[Bump; 2]>,
58 change_list: ManuallyDrop<ChangeList>,
59 change_list: ManuallyDrop<ChangeList>,
59− container: web_sys::Element,
60+ container: crate::Element,
60 events_registry: Option<Rc<RefCell<EventsRegistry>>>,
61 events_registry: Option<Rc<RefCell<EventsRegistry>>>,
62+ cached_set: crate::RefCell<CachedSet>,
61
63
62 // Actually a reference into `self.dom_buffers[0]` or if `self.component` is
64 // Actually a reference into `self.dom_buffers[0]` or if `self.component` is
63 // caching renders, into `self.component`'s bump.
65 // caching renders, into `self.component`'s bump.
@@ -109,7 +111,36 @@ impl Drop for VdomInnerExclusive {
109 let mut registry = registry.borrow_mut();
111 let mut registry = registry.borrow_mut();
110 registry.clear_active_listeners();
112 registry.clear_active_listeners();
111
113
114+ empty_container(&self.container);
115+ }
116+}
117+
118+cfg_if::cfg_if! {
119+ if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
120+ fn empty_container(_container: &crate::Element) {}
121+ fn initialize_container(_container: &crate::Element) {}
122+ } else {
123+ fn empty_container(container: &crate::Element) {
112− self.container.set_inner_html("");
124+ container.set_inner_html("");
125+ }
126+
127+ fn initialize_container(container: &crate::Element) {
128+ empty_container(container);
129+
130+ // Create the dummy `<div/>` child in the container.
131+ let window = web_sys::window().expect_throw("should have access to the Window");
132+ let document = window
133+ .document()
134+ .expect("should have access to the Document");
135+ container
136+ .append_child(
137+ document
138+ .create_element("div")
139+ .expect("should create element OK")
140+ .as_ref(),
141+ )
142+ .expect("should append child OK");
143+ }
113 }
144 }
114}
145}
115
146
@@ -118,7 +149,7 @@ impl Vdom {
118 /// rendering component.
149 /// rendering component.
119 ///
150 ///
120 /// This will box the given component into trait object.
151 /// This will box the given component into trait object.
121− pub fn new<R>(container: &web_sys::Element, component: R) -> Vdom
152+ pub fn new<R>(container: &crate::Element, component: R) -> Vdom
122 where
153 where
123 R: RootRender,
154 R: RootRender,
124 {
155 {
@@ -127,31 +158,14 @@ impl Vdom {
127
158
128 /// Construct a `Vdom` with the already-boxed-as-a-trait-object root
159 /// Construct a `Vdom` with the already-boxed-as-a-trait-object root
129 /// rendering component.
160 /// rendering component.
130− pub fn with_boxed_root_render(
161+ pub fn with_boxed_root_render(container: &crate::Element, component: Box<RootRender>) -> Vdom {
131− container: &web_sys::Element,
132− component: Box<RootRender>,
133− ) -> Vdom {
134 let dom_buffers = [Bump::new(), Bump::new()];
162 let dom_buffers = [Bump::new(), Bump::new()];
135 let change_list = ManuallyDrop::new(ChangeList::new(container));
163 let change_list = ManuallyDrop::new(ChangeList::new(container));
136
164
137− // Ensure that the container is empty.
138− container.set_inner_html("");
139−
140 // Create a dummy `<div/>` in our container.
165 // Create a dummy `<div/>` in our container.
166+ initialize_container(container);
141 let current_root = Node::element(&dom_buffers[0], "div", [], [], [], None);
167 let current_root = Node::element(&dom_buffers[0], "div", [], [], [], None);
142 let current_root = Some(unsafe { extend_node_lifetime(current_root) });
168 let current_root = Some(unsafe { extend_node_lifetime(current_root) });
143− let window = web_sys::window().expect("should have access to the Window");
144− let document = window
145− .document()
146− .expect("should have access to the Document");
147− container
148− .append_child(
149− document
150− .create_element("div")
151− .expect("should create element OK")
152− .as_ref(),
153− )
154− .expect("should append child OK");
155
169
156 let container = container.clone();
170 let container = container.clone();
157 let inner = Rc::new(VdomInner {
171 let inner = Rc::new(VdomInner {
@@ -160,11 +174,12 @@ impl Vdom {
160 },
174 },
161 exclusive: RefCell::new(VdomInnerExclusive {
175 exclusive: RefCell::new(VdomInnerExclusive {
162 component: Some(component),
176 component: Some(component),
163− dom_buffers,
177+ dom_buffers: Some(dom_buffers),
164 change_list,
178 change_list,
165 container,
179 container,
166 current_root,
180 current_root,
167 events_registry: None,
181 events_registry: None,
182+ cached_set: crate::RefCell::new(Default::default()),
168 }),
183 }),
169 });
184 });
170
185
@@ -182,6 +197,19 @@ impl Vdom {
182 Vdom { inner }
197 Vdom { inner }
183 }
198 }
184
199
200+ /// Immediately re-render and diff. Only for internal testing and
201+ /// benchmarking purposes.
202+ #[cfg(feature = "xxx-unstable-internal-use-only")]
203+ pub fn immediately_render_and_diff<R>(&self, component: R)
204+ where
205+ R: RootRender,
206+ {
207+ let mut exclusive = self.inner.exclusive.borrow_mut();
208+ let component = Box::new(component) as Box<RootRender>;
209+ exclusive.component = Some(component);
210+ exclusive.render();
211+ }
212+
185 /// Run this virtual DOM and its listeners forever and never unmount it.
213 /// Run this virtual DOM and its listeners forever and never unmount it.
186 #[inline]
214 #[inline]
187 pub fn forget(self) {
215 pub fn forget(self) {
@@ -221,36 +249,42 @@ impl VdomInnerExclusive {
221 pub(crate) fn render(&mut self) {
249 pub(crate) fn render(&mut self) {
222 unsafe {
250 unsafe {
223 let events_registry = self.events_registry.take().unwrap();
251 let events_registry = self.events_registry.take().unwrap();
224−
225 {
252 {
226− // All the old listeners are no longer active. We will build a new
227− // set of active listeners when diffing.
228− //
229− // NB: if we end up avoiding diffing cached renders (instead of just
230− // avoiding re-rendering them) then we will need to maintain cached
231− // active listeners, and can't just clear all active listeners and
232− // rebuild them here.
233 let mut registry = events_registry.borrow_mut();
253 let mut registry = events_registry.borrow_mut();
234− registry.clear_active_listeners();
235
254
236 // Reset the inactive bump arena's pointer.
255 // Reset the inactive bump arena's pointer.
237− self.dom_buffers[1].reset();
256+ let mut dom_buffers = self.dom_buffers.take().unwrap_throw();
257+ dom_buffers[1].reset();
238
258
239 // Render the new current contents into the inactive bump arena.
259 // Render the new current contents into the inactive bump arena.
260+ let mut cx = RenderContext::new(&dom_buffers[1], &self.cached_set);
240− let new_contents = self
261+ let new_contents = self.component.as_ref().unwrap_throw().render(&mut cx);
241− .component
242− .as_ref()
243− .unwrap_throw()
244− .render(&self.dom_buffers[1]);
245 let new_contents = extend_node_lifetime(new_contents);
262 let new_contents = extend_node_lifetime(new_contents);
246
263
247 // Diff the old contents with the new contents.
264 // Diff the old contents with the new contents.
248 let old_contents = self.current_root.take().unwrap();
265 let old_contents = self.current_root.take().unwrap();
266+ let mut cache_roots = bumpalo::collections::Vec::new_in(&dom_buffers[1]);
267+ {
268+ let cached_set = self.cached_set.borrow();
269+ crate::diff::diff(
270+ &cached_set,
271+ &mut self.change_list,
272+ &mut registry,
273+ old_contents,
249− self.diff(&mut registry, old_contents, new_contents.clone());
274+ new_contents.clone(),
275+ &mut cache_roots,
276+ );
277+ }
278+
279+ {
280+ // Clean up unused cached renders.
281+ let mut cached_set = self.cached_set.borrow_mut();
282+ cached_set.gc(&mut registry, &cache_roots);
283+ }
250
284
251 // Swap the buffers to make the bump arena with the new contents the
285 // Swap the buffers to make the bump arena with the new contents the
252 // active arena, and the old one into the inactive arena.
286 // active arena, and the old one into the inactive arena.
253− self.swap_buffers();
287+ self.swap_buffers(dom_buffers);
254 self.set_current_root(new_contents);
288 self.set_current_root(new_contents);
255 }
289 }
256
290
@@ -264,234 +298,17 @@ impl VdomInnerExclusive {
264 }
298 }
265 }
299 }
266
300
267− fn swap_buffers(&mut self) {
301+ fn swap_buffers(&mut self, mut dom_buffers: [Bump; 2]) {
302+ debug_assert!(self.dom_buffers.is_none());
268− let (first, second) = self.dom_buffers.as_mut().split_at_mut(1);
303+ let (first, second) = dom_buffers.as_mut().split_at_mut(1);
269 mem::swap(&mut first[0], &mut second[0]);
304 mem::swap(&mut first[0], &mut second[0]);
305+ self.dom_buffers = Some(dom_buffers);
270 }
306 }
271
307
272 unsafe fn set_current_root(&mut self, current: Node<'static>) {
308 unsafe fn set_current_root(&mut self, current: Node<'static>) {
273 debug_assert!(self.current_root.is_none());
309 debug_assert!(self.current_root.is_none());
274 self.current_root = Some(current);
310 self.current_root = Some(current);
275 }
311 }
276−
277− fn diff<'a>(&mut self, registry: &mut EventsRegistry, old: Node<'a>, new: Node<'a>) {
278− // debug!("---------------------------------------------------------");
279− // debug!("dodrio::Vdom::diff");
280− // debug!(" old = {:#?}", old);
281− // debug!(" new = {:#?}", new);
282− match (&new, old) {
283− (&Node::Text(TextNode { text: new_text }), Node::Text(TextNode { text: old_text })) => {
284− debug!(" both are text nodes");
285− if new_text != old_text {
286− debug!(" text needs updating");
287− self.change_list.emit_set_text(new_text);
288− }
289− }
290− (&Node::Text(_), Node::Element(_)) => {
291− debug!(" replacing a text node with an element");
292− self.create(registry, new);
293− self.change_list.emit_replace_with();
294− }
295− (&Node::Element(_), Node::Text(_)) => {
296− debug!(" replacing an element with a text node");
297− self.create(registry, new);
298− self.change_list.emit_replace_with();
299− }
300− (
301− &Node::Element(ElementNode {
302− tag_name: new_tag_name,
303− listeners: new_listeners,
304− attributes: new_attributes,
305− children: new_children,
306− namespace: new_namespace,
307− }),
308− Node::Element(ElementNode {
309− tag_name: old_tag_name,
310− listeners: old_listeners,
311− attributes: old_attributes,
312− children: old_children,
313− namespace: old_namespace,
314− }),
315− ) => {
316− debug!(" updating an element");
317− if new_tag_name != old_tag_name || new_namespace != old_namespace {
318− debug!(" different tag names or namespaces; creating new element and replacing old element");
319− self.create(registry, new);
320− self.change_list.emit_replace_with();
321− return;
322− }
323− self.diff_listeners(registry, old_listeners, new_listeners);
324− self.diff_attributes(old_attributes, new_attributes);
325− self.diff_children(registry, old_children, new_children);
326− }
327− }
328− }
329−
330− fn diff_listeners<'a>(
331− &mut self,
332− registry: &mut EventsRegistry,
333− old: &'a [Listener<'a>],
334− new: &'a [Listener<'a>],
335− ) {
336− debug!(" updating event listeners");
337−
338− 'outer1: for new_l in new {
339− unsafe {
340− // Safety relies on removing `new_l` from the registry manually
341− // at the end of its lifetime. This happens when we invoke
342− // `clear_active_listeners` at the start of a new rendering
343− // phase.
344− registry.add(new_l);
345− }
346− for old_l in old {
347− if new_l.event == old_l.event {
348− self.change_list.emit_update_event_listener(new_l);
349− continue 'outer1;
350− }
351− }
352− self.change_list.emit_new_event_listener(new_l);
353− }
354−
355− 'outer2: for old_l in old {
356− for new_l in new {
357− if new_l.event == old_l.event {
358− continue 'outer2;
359− }
360− }
361− self.change_list.emit_remove_event_listener(old_l.event);
362− }
363− }
364−
365− fn diff_attributes(&mut self, old: &[Attribute], new: &[Attribute]) {
366− debug!(" updating attributes");
367−
368− // Do O(n^2) passes to add/update and remove attributes, since
369− // there are almost always very few attributes.
370− 'outer: for new_attr in new {
371− if new_attr.is_volatile() {
372− self.change_list
373− .emit_set_attribute(new_attr.name, new_attr.value);
374− } else {
375− for old_attr in old {
376− if old_attr.name == new_attr.name {
377− if old_attr.value != new_attr.value {
378− self.change_list
379− .emit_set_attribute(new_attr.name, new_attr.value);
380− }
381− continue 'outer;
382− }
383− }
384− self.change_list
385− .emit_set_attribute(new_attr.name, new_attr.value);
386− }
387− }
388−
389− 'outer2: for old_attr in old {
390− for new_attr in new {
391− if old_attr.name == new_attr.name {
392− continue 'outer2;
393− }
394− }
395− self.change_list.emit_remove_attribute(old_attr.name);
396− }
397− }
398−
399− fn diff_children<'a>(
400− &mut self,
401− registry: &mut EventsRegistry,
402− old: &'a [Node<'a>],
403− new: &'a [Node<'a>],
404− ) {
405− debug!(" updating children shared by old and new");
406−
407− let num_children_to_diff = cmp::min(new.len(), old.len());
408− let mut new_children = new.iter();
409− let mut old_children = old.iter();
410− let mut pushed = false;
411−
412− for (i, (new_child, old_child)) in new_children
413− .by_ref()
414− .zip(old_children.by_ref())
415− .take(num_children_to_diff)
416− .enumerate()
417− {
418− if i == 0 {
419− self.change_list.emit_push_first_child();
420− pushed = true;
421− } else {
422− debug_assert!(pushed);
423− self.change_list.emit_pop_push_next_sibling();
424− }
425−
426− self.diff(registry, old_child.clone(), new_child.clone());
427− }
428−
429− if old_children.next().is_some() {
430− debug!(" removing extra old children");
431− debug_assert!(new_children.next().is_none());
432− if !pushed {
433− self.change_list.emit_push_first_child();
434− } else {
435− self.change_list.emit_pop_push_next_sibling();
436− }
437− self.change_list.emit_remove_self_and_next_siblings();
438− pushed = false;
439− } else {
440− debug!(" creating new children");
441− for (i, new_child) in new_children.enumerate() {
442− if i == 0 && pushed {
443− self.change_list.emit_pop();
444− pushed = false;
445− }
446− self.create(registry, new_child.clone());
447− self.change_list.emit_append_child();
448− }
449− }
450−
451− debug!(" done updating children");
452− if pushed {
453− self.change_list.emit_pop();
454− }
455− }
456−
457− fn create<'a>(&mut self, registry: &mut EventsRegistry, node: Node<'a>) {
458− match node {
459− Node::Text(TextNode { text }) => {
460− self.change_list.emit_create_text_node(text);
461− }
462− Node::Element(ElementNode {
463− tag_name,
464− listeners,
465− attributes,
466− children,
467− namespace,
468− }) => {
469− if let Some(namespace) = namespace {
470− self.change_list.emit_create_element_ns(tag_name, namespace);
471− } else {
472− self.change_list.emit_create_element(tag_name);
473− }
474− for l in listeners {
475− unsafe {
476− registry.add(l);
477− }
478− self.change_list.emit_new_event_listener(l);
479− }
480− for attr in attributes {
481− if namespace.is_none() || attr.name.starts_with("xmlns") {
482− self.change_list.emit_set_attribute(&attr.name, &attr.value);
483− } else {
484− self.change_list
485− .emit_set_attribute_ns(&attr.name, &attr.value);
486− }
487− }
488− for child in children {
489− self.create(registry, child.clone());
490− self.change_list.emit_append_child();
491− }
492− }
493− }
494− }
495}
312}
496
313
497fn request_animation_frame(f: &Closure<FnMut()>) {
314fn request_animation_frame(f: &Closure<FnMut()>) {
tests/web/cached.rs+140−7
@@ -1,5 +1,5 @@
1−use super::{assert_rendered, create_element, RenderFn};
1+use super::{assert_rendered, before_after, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo::Bump, Cached, Node, Render, Vdom};
2+use dodrio::{builder::*, bumpalo, Cached, Node, Render, RenderContext, Vdom};
3use futures::prelude::*;
3use futures::prelude::*;
4use std::cell::Cell;
4use std::cell::Cell;
5use std::rc::Rc;
5use std::rc::Rc;
@@ -19,14 +19,11 @@ impl CountRenders {
19}
19}
20
20
21impl Render for CountRenders {
21impl Render for CountRenders {
22− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
22+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
23− where
24− 'a: 'bump,
25− {
26 let count = self.render_count.get() + 1;
23 let count = self.render_count.get() + 1;
27 self.render_count.set(count);
24 self.render_count.set(count);
28
25
29− let s = bumpalo::format!(in bump, "{}", count);
26+ let s = bumpalo::format!(in cx.bump, "{}", count);
30 text(s.into_bump_str())
27 text(s.into_bump_str())
31 }
28 }
32}
29}
@@ -99,3 +96,139 @@ fn uses_cached_render() -> impl Future<Item = (), Error = JsValue> {
99 })
96 })
100 .map_err(|e| JsValue::from(e.to_string()))
97 .map_err(|e| JsValue::from(e.to_string()))
101}
98}
99+
100+struct Id(&'static str);
101+impl Render for Id {
102+ fn render<'a>(&self, _cx: &mut RenderContext<'a>) -> Node<'a> {
103+ text(self.0)
104+ }
105+}
106+
107+thread_local! {
108+ static WARM_CHEESE: Rc<Cached<Id>> = Rc::new(Cached::new(Id("cheese")));
109+ static WARM_CHEESIER: Rc<Cached<Id>> = Rc::new(Cached::new(Id("cheesier")));
110+}
111+
112+fn warm_cheese<'a>(cx: &mut RenderContext<'a>) -> Node<'a> {
113+ WARM_CHEESE.with(|c| {
114+ let _ = c.render(cx);
115+ c.render(cx)
116+ })
117+}
118+
119+fn warm_cheesier<'a>(cx: &mut RenderContext<'a>) -> Node<'a> {
120+ WARM_CHEESIER.with(|c| {
121+ let _ = c.render(cx);
122+ c.render(cx)
123+ })
124+}
125+
126+before_after! {
127+ cold_cache_and_not_cached {
128+ before(cx) {
129+ Cached::new(Id("cheese")).render(cx)
130+ }
131+ after(cx) {
132+ Id("ravioli").render(cx)
133+ }
134+ }
135+
136+ not_cached_and_cold_cache {
137+ before(cx) {
138+ Id("ravioli").render(cx)
139+ }
140+ after(cx) {
141+ Cached::new(Id("cheese")).render(cx)
142+ }
143+ }
144+
145+ cold_cache_and_cold_cache_same {
146+ before(cx) {
147+ Cached::new(Id("cheese")).render(cx)
148+ }
149+ after(cx) {
150+ Cached::new(Id("cheese")).render(cx)
151+ }
152+ }
153+
154+ cold_cache_and_cold_cache_different {
155+ before(cx) {
156+ Cached::new(Id("cheese")).render(cx)
157+ }
158+ after(cx) {
159+ Cached::new(Id("ravioli")).render(cx)
160+ }
161+ }
162+
163+ warm_cache_and_not_cached {
164+ before(cx) {
165+ warm_cheese(cx)
166+ }
167+ after(cx) {
168+ Id("ravioli").render(cx)
169+ }
170+ }
171+
172+ not_cached_and_warm_cache {
173+ before(cx) {
174+ Id("ravioli").render(cx)
175+ }
176+ after(cx) {
177+ warm_cheese(cx)
178+ }
179+ }
180+
181+ warm_cache_and_warm_cache_same {
182+ before(cx) {
183+ warm_cheese(cx)
184+ }
185+ after(cx) {
186+ warm_cheese(cx)
187+ }
188+ }
189+
190+ warm_cache_and_warm_cache_different {
191+ before(cx) {
192+ warm_cheese(cx)
193+ }
194+ after(cx) {
195+ warm_cheesier(cx)
196+ }
197+ }
198+
199+ cold_cache_and_warm_cache_same {
200+ before(cx) {
201+ Cached::new(Id("cheese")).render(cx)
202+ }
203+ after(cx) {
204+ warm_cheese(cx)
205+ }
206+ }
207+
208+ cold_cache_and_warm_cache_different {
209+ before(cx) {
210+ Cached::new(Id("cheese")).render(cx)
211+ }
212+ after(cx) {
213+ warm_cheesier(cx)
214+ }
215+ }
216+
217+ warm_cache_and_cold_cache_same {
218+ before(cx) {
219+ warm_cheese(cx)
220+ }
221+ after(cx) {
222+ Cached::new(Id("cheese")).render(cx)
223+ }
224+ }
225+
226+ warm_cache_and_cold_cache_different {
227+ before(cx) {
228+ warm_cheesier(cx)
229+ }
230+ after(cx) {
231+ Cached::new(Id("cheese")).render(cx)
232+ }
233+ }
234+}
tests/web/events.rs+5−86
@@ -1,6 +1,5 @@
1use super::create_element;
1use super::create_element;
2−use bumpalo::Bump;
3−use dodrio::{Node, Render, Vdom};
2+use dodrio::{Node, Render, RenderContext, Vdom};
4use futures::Future;
3use futures::Future;
5use std::cell::{Cell, RefCell};
4use std::cell::{Cell, RefCell};
6use std::rc::Rc;
5use std::rc::Rc;
@@ -26,12 +25,9 @@ impl EventContainer {
26}
25}
27
26
28impl Render for EventContainer {
27impl Render for EventContainer {
29− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
28+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
30− where
31− 'a: 'bump,
32− {
33 use dodrio::builder::*;
29 use dodrio::builder::*;
34− div(bump)
30+ div(&cx)
35 .attr("id", "target")
31 .attr("id", "target")
36 .on(self.event, |root, _scheduler, _event| {
32 .on(self.event, |root, _scheduler, _event| {
37 (root.unwrap_mut::<EventContainer>().on_event)();
33 (root.unwrap_mut::<EventContainer>().on_event)();
@@ -138,16 +134,13 @@ impl ListensOnlyOnFirstRender {
138}
134}
139
135
140impl Render for ListensOnlyOnFirstRender {
136impl Render for ListensOnlyOnFirstRender {
141− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
137+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
142− where
143− 'a: 'bump,
144− {
145 use dodrio::builder::*;
138 use dodrio::builder::*;
146
139
147 let count = self.count.get();
140 let count = self.count.get();
148 self.count.set(count + 1);
141 self.count.set(count + 1);
149
142
150− let mut elem = div(bump).attr("id", "target");
143+ let mut elem = div(&cx).attr("id", "target");
151 if count == 0 {
144 if count == 0 {
152 elem = elem.on("click", |root, _scheduler, _event| {
145 elem = elem.on("click", |root, _scheduler, _event| {
153 (root.unwrap_mut::<ListensOnlyOnFirstRender>().callback)();
146 (root.unwrap_mut::<ListensOnlyOnFirstRender>().callback)();
@@ -272,77 +265,3 @@ fn event_does_not_fire_after_unmounting() -> impl Future<Item = (), Error = JsVa
272 })
265 })
273 .map_err(|e| JsValue::from(e.to_string()))
266 .map_err(|e| JsValue::from(e.to_string()))
274}
267}
275−
276−struct CountRendersAndListen {
277− count: Cell<usize>,
278− callback: Box<FnMut(usize)>,
279−}
280−
281−impl CountRendersAndListen {
282− fn new<F>(callback: F) -> CountRendersAndListen
283− where
284− F: 'static + FnMut(usize),
285− {
286− CountRendersAndListen {
287− count: Cell::new(0),
288− callback: Box::new(callback),
289− }
290− }
291−}
292−
293−impl Render for CountRendersAndListen {
294− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
295− where
296− 'a: 'bump,
297− {
298− use dodrio::builder::*;
299−
300− let count = self.count.get() + 1;
301− self.count.set(count);
302−
303− button(bump)
304− .attr("id", "target")
305− .on("click", |comp, _vdom, _event| {
306− let comp: &mut _ = comp.unwrap_mut::<dodrio::Cached<CountRendersAndListen>>();
307− let comp: &mut CountRendersAndListen = comp;
308− let n = comp.count.get();
309− (comp.callback)(n);
310− })
311− .finish()
312− }
313−}
314−
315−#[wasm_bindgen_test(async)]
316−fn cached_listeners_work_correctly() -> impl Future<Item = (), Error = JsValue> {
317− let container = create_element("div");
318−
319− let (sender, receiver) = futures::sync::oneshot::channel();
320− let mut sender = Some(sender);
321− let vdom = Vdom::new(
322− &container,
323− dodrio::Cached::new(CountRendersAndListen::new(move |n| {
324− sender.take().unwrap().send(n).unwrap();
325− })),
326− );
327−
328− let weak0 = vdom.weak();
329− let weak1 = vdom.weak();
330− let weak2 = vdom.weak();
331− vdom.weak()
332− // Re-render a few times, so we are using the cached version.
333− .render()
334− .and_then(move |_| weak0.render())
335− .and_then(move |_| weak1.render())
336− .and_then(move |_| weak2.render())
337− .map_err(|e| JsValue::from(e.to_string()))
338− .and_then(move |_| {
339− target(&container).click();
340−
341− receiver
342− .map(|n| {
343− assert_eq!(n, 1);
344− drop(vdom);
345− })
346− .map_err(|e| JsValue::from(e.to_string()))
347− })
348−}
tests/web/js_api.rs+12−15
@@ -1,5 +1,5 @@
1use super::{assert_rendered, create_element, RenderFn};
1use super::{assert_rendered, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo::Bump, Node, Render, Vdom};
2+use dodrio::{builder::*, Node, Render, RenderContext, Vdom};
3use dodrio_js_api::JsRender;
3use dodrio_js_api::JsRender;
4use futures::prelude::*;
4use futures::prelude::*;
5use futures::sync::oneshot;
5use futures::sync::oneshot;
@@ -37,13 +37,10 @@ impl WrapJs {
37}
37}
38
38
39impl Render for WrapJs {
39impl Render for WrapJs {
40− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
40+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
41− where
42− 'a: 'bump,
43− {
44− div(bump)
41+ div(&cx)
45 .attr("class", "wrap-js")
42 .attr("class", "wrap-js")
46− .children([self.inner.render(bump)])
43+ .children([self.inner.render(cx)])
47 .finish()
44 .finish()
48 }
45 }
49}
46}
@@ -109,14 +106,14 @@ fn can_use_js_rendering_components() -> impl Future<Item = (), Error = JsValue>
109 let vdom = Rc::new(Vdom::new(&container, component));
106 let vdom = Rc::new(Vdom::new(&container, component));
110 assert_rendered(
107 assert_rendered(
111 &container,
108 &container,
112− &RenderFn(|bump| {
109+ &RenderFn(|cx| {
113− div(bump)
110+ div(&cx)
114 .attr("class", "wrap-js")
111 .attr("class", "wrap-js")
115− .children([span(bump)
112+ .children([span(&cx)
116 .attr("class", "js-component")
113 .attr("class", "js-component")
117 .children([
114 .children([
118 text("Here is some plain text"),
115 text("Here is some plain text"),
119− b(bump)
116+ b(&cx)
120 .children([text("...and here is some bold text")])
117 .children([text("...and here is some bold text")])
121 .finish(),
118 .finish(),
122 text("0"),
119 text("0"),
@@ -138,14 +135,14 @@ fn can_use_js_rendering_components() -> impl Future<Item = (), Error = JsValue>
138 .map(move |_| {
135 .map(move |_| {
139 assert_rendered(
136 assert_rendered(
140 &container,
137 &container,
141− &RenderFn(|bump| {
138+ &RenderFn(|cx| {
142− div(bump)
139+ div(&cx)
143 .attr("class", "wrap-js")
140 .attr("class", "wrap-js")
144− .children([span(bump)
141+ .children([span(&cx)
145 .attr("class", "js-component")
142 .attr("class", "js-component")
146 .children([
143 .children([
147 text("Here is some plain text"),
144 text("Here is some plain text"),
148− b(bump)
145+ b(&cx)
149 .children([text("...and here is some bold text")])
146 .children([text("...and here is some bold text")])
150 .finish(),
147 .finish(),
151 text("1"),
148 text("1"),
tests/web/main.rs+36−22
@@ -1,11 +1,14 @@
1//! Test suite for the Web and headless browsers.
1//! Test suite for the Web and headless browsers.
2
2
3−#![cfg(target_arch = "wasm32")]
3+#![cfg(all(feature = "xxx-unstable-internal-use-only", target_arch = "wasm32"))]
4
4
5use bumpalo::Bump;
5use bumpalo::Bump;
6+use dodrio::{
6−use dodrio::{Attribute, Node, Render, Vdom};
7+ Attribute, CachedSet, ElementNode, Node, NodeKind, Render, RenderContext, TextNode, Vdom,
8+};
7use futures::prelude::*;
9use futures::prelude::*;
8use log::*;
10use log::*;
11+use std::cell::RefCell;
9use std::rc::Rc;
12use std::rc::Rc;
10use wasm_bindgen::prelude::*;
13use wasm_bindgen::prelude::*;
11use wasm_bindgen::JsCast;
14use wasm_bindgen::JsCast;
@@ -49,12 +52,16 @@ pub fn init_logging() {
49pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
52pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
50 init_logging();
53 init_logging();
51
54
55+ let cached_set = &RefCell::new(CachedSet::default());
52 let bump = &Bump::new();
56 let bump = &Bump::new();
57+ let cx = &mut RenderContext::new(bump, cached_set);
53− let node = r.render(bump);
58+ let node = r.render(cx);
54 let child = container
59 let child = container
55 .first_child()
60 .first_child()
56 .expect("container does not have anything rendered into it?");
61 .expect("container does not have anything rendered into it?");
62+
63+ let cached_set = cached_set.borrow();
57− check_node(&child, &node);
64+ check_node(&cached_set, &child, &node);
58
65
59 fn stringify_actual_node(n: &web_sys::Node) -> String {
66 fn stringify_actual_node(n: &web_sys::Node) -> String {
60 if let Some(el) = n.dyn_ref::<web_sys::Element>() {
67 if let Some(el) = n.dyn_ref::<web_sys::Element>() {
@@ -64,12 +71,12 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
64 }
71 }
65 }
72 }
66
73
67− fn check_node(actual: &web_sys::Node, expected: &Node) {
74+ fn check_node(cached_set: &CachedSet, actual: &web_sys::Node, expected: &Node) {
68 debug!("check_render:");
75 debug!("check_render:");
69 debug!(" actual = {}", stringify_actual_node(&actual));
76 debug!(" actual = {}", stringify_actual_node(&actual));
70 debug!(" expected = {:#?}", expected);
77 debug!(" expected = {:#?}", expected);
71− match expected {
78+ match expected.kind {
72− Node::Text(text_node) => {
79+ NodeKind::Text(TextNode { text }) => {
73 assert_eq!(
80 assert_eq!(
74 actual.node_name().to_uppercase(),
81 actual.node_name().to_uppercase(),
75 "#TEXT",
82 "#TEXT",
@@ -77,25 +84,35 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
77 );
84 );
78 assert_eq!(
85 assert_eq!(
79 actual.text_content().unwrap_or_default(),
86 actual.text_content().unwrap_or_default(),
80− text_node.text(),
87+ text,
81 "actual.text_content() == expected.text()"
88 "actual.text_content() == expected.text()"
82 );
89 );
83 }
90 }
84− Node::Element(elem) => {
91+ NodeKind::Element(ElementNode {
92+ tag_name,
93+ attributes,
94+ children,
95+ namespace,
96+ ..
97+ }) => {
85 assert_eq!(
98 assert_eq!(
86 actual.node_name().to_uppercase(),
99 actual.node_name().to_uppercase(),
87− elem.tag_name().to_uppercase(),
100+ tag_name.to_uppercase(),
88 "actual.node_name() == expected.tag_name()"
101 "actual.node_name() == expected.tag_name()"
89 );
102 );
90 let actual = actual
103 let actual = actual
91 .dyn_ref::<web_sys::Element>()
104 .dyn_ref::<web_sys::Element>()
92 .expect("`actual` should be an `Element`");
105 .expect("`actual` should be an `Element`");
93− check_attributes(actual.attributes(), elem.attributes());
106+ check_attributes(actual.attributes(), attributes);
94− check_children(actual.child_nodes(), elem.children());
107+ check_children(cached_set, actual.child_nodes(), children);
95− if let Some(namespace) = elem.namespace() {
108+ if let Some(namespace) = namespace {
96 assert_eq!(actual.namespace_uri(), Some(namespace.into()))
109 assert_eq!(actual.namespace_uri(), Some(namespace.into()))
97 }
110 }
98 }
111 }
112+ NodeKind::Cached(ref c) => {
113+ let expected = cached_set.get(c.id);
114+ check_node(cached_set, actual, &expected);
115+ }
99 }
116 }
100 }
117 }
101
118
@@ -118,7 +135,7 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
118 }
135 }
119 }
136 }
120
137
121− fn check_children(actual: web_sys::NodeList, expected: &[Node]) {
138+ fn check_children(cached_set: &CachedSet, actual: web_sys::NodeList, expected: &[Node]) {
122 assert_eq!(
139 assert_eq!(
123 actual.length(),
140 actual.length(),
124 expected.len() as u32,
141 expected.len() as u32,
@@ -126,7 +143,7 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
126 );
143 );
127 for (i, child) in expected.iter().enumerate() {
144 for (i, child) in expected.iter().enumerate() {
128 let actual_child = actual.item(i as u32).unwrap();
145 let actual_child = actual.item(i as u32).unwrap();
129− check_node(&actual_child, child);
146+ check_node(cached_set, &actual_child, child);
130 }
147 }
131 }
148 }
132}
149}
@@ -134,17 +151,14 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
134/// Use the function `F` to render.
151/// Use the function `F` to render.
135pub struct RenderFn<F>(F)
152pub struct RenderFn<F>(F)
136where
153where
137− F: for<'bump> Fn(&'bump Bump) -> Node<'bump>;
154+ F: for<'a> Fn(&mut RenderContext<'a>) -> Node<'a>;
138
155
139impl<F> Render for RenderFn<F>
156impl<F> Render for RenderFn<F>
140where
157where
141− F: for<'bump> Fn(&'bump Bump) -> Node<'bump>,
158+ F: for<'a> Fn(&mut RenderContext<'a>) -> Node<'a>,
142{
159{
143− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
160+ fn render<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
144− where
145− 'a: 'bump,
146− {
147− (self.0)(bump)
161+ (self.0)(cx)
148 }
162 }
149}
163}
150
164
tests/web/render.rs+85−85
@@ -6,7 +6,7 @@ use wasm_bindgen_test::*;
6
6
7#[wasm_bindgen_test]
7#[wasm_bindgen_test]
8fn render_initial_text() {
8fn render_initial_text() {
9− let hello = Rc::new(RenderFn(|_bump| text("hello")));
9+ let hello = Rc::new(RenderFn(|_cx| text("hello")));
10
10
11 let container = create_element("div");
11 let container = create_element("div");
12 let _vdom = Vdom::new(&container, hello.clone());
12 let _vdom = Vdom::new(&container, hello.clone());
@@ -15,10 +15,10 @@ fn render_initial_text() {
15
15
16#[wasm_bindgen_test]
16#[wasm_bindgen_test]
17fn render_initial_node() {
17fn render_initial_node() {
18− let hello = Rc::new(RenderFn(|bump| {
18+ let hello = Rc::new(RenderFn(|cx| {
19− div(bump)
19+ div(&cx)
20 .attr("id", "hello-world")
20 .attr("id", "hello-world")
21− .children([text("Hello "), span(bump).child(text("World!")).finish()])
21+ .children([text("Hello "), span(&cx).child(text("World!")).finish()])
22 .finish()
22 .finish()
23 }));
23 }));
24
24
@@ -30,96 +30,96 @@ fn render_initial_node() {
30#[wasm_bindgen_test]
30#[wasm_bindgen_test]
31fn container_is_emptied_upon_drop() {
31fn container_is_emptied_upon_drop() {
32 let container = create_element("div");
32 let container = create_element("div");
33− let vdom = Vdom::new(&container, RenderFn(|_bump| text("blah")));
33+ let vdom = Vdom::new(&container, RenderFn(|_cx| text("blah")));
34 drop(vdom);
34 drop(vdom);
35 assert!(container.first_child().is_none());
35 assert!(container.first_child().is_none());
36}
36}
37
37
38before_after! {
38before_after! {
39 same_text {
39 same_text {
40− before(_bump) {
40+ before(_cx) {
41 text("hello")
41 text("hello")
42 }
42 }
43− after(_bump) {
43+ after(_cx) {
44 text("hello")
44 text("hello")
45 }
45 }
46 }
46 }
47
47
48 update_text {
48 update_text {
49− before(_bump) {
49+ before(_cx) {
50 text("before")
50 text("before")
51 }
51 }
52− after(_bump) {
52+ after(_cx) {
53 text("after")
53 text("after")
54 }
54 }
55 }
55 }
56
56
57 replace_text_with_elem {
57 replace_text_with_elem {
58− before(_bump) {
58+ before(_cx) {
59 text("before")
59 text("before")
60 }
60 }
61− after(bump) {
61+ after(cx) {
62− div(bump).finish()
62+ div(&cx).finish()
63 }
63 }
64 }
64 }
65
65
66 replace_elem_with_text {
66 replace_elem_with_text {
67− before(bump) {
67+ before(cx) {
68− div(bump).finish()
68+ div(&cx).finish()
69 }
69 }
70− after(_bump) {
70+ after(_cx) {
71 text("before")
71 text("before")
72 }
72 }
73 }
73 }
74
74
75 same_elem {
75 same_elem {
76− before(bump) {
76+ before(cx) {
77− div(bump).finish()
77+ div(&cx).finish()
78 }
78 }
79− after(bump) {
79+ after(cx) {
80− div(bump).finish()
80+ div(&cx).finish()
81 }
81 }
82 }
82 }
83
83
84 elems_with_different_tag_names {
84 elems_with_different_tag_names {
85− before(bump) {
85+ before(cx) {
86− span(bump).finish()
86+ span(&cx).finish()
87 }
87 }
88− after(bump) {
88+ after(cx) {
89− div(bump).finish()
89+ div(&cx).finish()
90 }
90 }
91 }
91 }
92
92
93 same_tag_name_update_attribute {
93 same_tag_name_update_attribute {
94− before(bump) {
94+ before(cx) {
95− div(bump).attr("value", "1").finish()
95+ div(&cx).attr("value", "1").finish()
96 }
96 }
97− after(bump) {
97+ after(cx) {
98− div(bump).attr("value", "2").finish()
98+ div(&cx).attr("value", "2").finish()
99 }
99 }
100 }
100 }
101
101
102 same_tag_name_remove_attribute {
102 same_tag_name_remove_attribute {
103− before(bump) {
103+ before(cx) {
104− div(bump).attr("value", "1").finish()
104+ div(&cx).attr("value", "1").finish()
105 }
105 }
106− after(bump) {
106+ after(cx) {
107− div(bump).finish()
107+ div(&cx).finish()
108 }
108 }
109 }
109 }
110
110
111 same_tag_name_add_attribute {
111 same_tag_name_add_attribute {
112− before(bump) {
112+ before(cx) {
113− div(bump).finish()
113+ div(&cx).finish()
114 }
114 }
115− after(bump) {
115+ after(cx) {
116− div(bump).attr("value", "2").finish()
116+ div(&cx).attr("value", "2").finish()
117 }
117 }
118 }
118 }
119
119
120 same_tag_name_many_attributes {
120 same_tag_name_many_attributes {
121− before(bump) {
121+ before(cx) {
122− div(bump)
122+ div(&cx)
123 .attr("before-1", "1")
123 .attr("before-1", "1")
124 .attr("shared-1", "1")
124 .attr("shared-1", "1")
125 .attr("modified-1", "1")
125 .attr("modified-1", "1")
@@ -131,8 +131,8 @@ before_after! {
131 .attr("modified-3", "3")
131 .attr("modified-3", "3")
132 .finish()
132 .finish()
133 }
133 }
134− after(bump) {
134+ after(cx) {
135− div(bump)
135+ div(&cx)
136 .attr("after-1", "1")
136 .attr("after-1", "1")
137 .attr("shared-1", "1")
137 .attr("shared-1", "1")
138 .attr("modified-1", "100")
138 .attr("modified-1", "100")
@@ -147,108 +147,108 @@ before_after! {
147 }
147 }
148
148
149 same_tag_same_children {
149 same_tag_same_children {
150− before(bump) {
150+ before(cx) {
151− div(bump).child(text("child")).finish()
151+ div(&cx).child(text("child")).finish()
152 }
152 }
153− after(bump) {
153+ after(cx) {
154− div(bump).child(text("child")).finish()
154+ div(&cx).child(text("child")).finish()
155 }
155 }
156 }
156 }
157
157
158 same_tag_update_child {
158 same_tag_update_child {
159− before(bump) {
159+ before(cx) {
160− div(bump).child(text("before")).finish()
160+ div(&cx).child(text("before")).finish()
161 }
161 }
162− after(bump) {
162+ after(cx) {
163− div(bump).child(text("after")).finish()
163+ div(&cx).child(text("after")).finish()
164 }
164 }
165 }
165 }
166
166
167 same_tag_add_child {
167 same_tag_add_child {
168− before(bump) {
168+ before(cx) {
169− div(bump).finish()
169+ div(&cx).finish()
170 }
170 }
171− after(bump) {
171+ after(cx) {
172− div(bump).child(text("child")).finish()
172+ div(&cx).child(text("child")).finish()
173 }
173 }
174 }
174 }
175
175
176 same_tag_remove_child {
176 same_tag_remove_child {
177− before(bump) {
177+ before(cx) {
178− div(bump).child(text("child")).finish()
178+ div(&cx).child(text("child")).finish()
179 }
179 }
180− after(bump) {
180+ after(cx) {
181− div(bump).finish()
181+ div(&cx).finish()
182 }
182 }
183 }
183 }
184
184
185 same_tag_update_many_children {
185 same_tag_update_many_children {
186− before(bump) {
186+ before(cx) {
187− div(bump)
187+ div(&cx)
188 .children([
188 .children([
189− div(bump).finish(),
189+ div(&cx).finish(),
190− span(bump).finish(),
190+ span(&cx).finish(),
191− p(bump).finish(),
191+ p(&cx).finish(),
192 ])
192 ])
193 .finish()
193 .finish()
194 }
194 }
195− after(bump) {
195+ after(cx) {
196− div(bump)
196+ div(&cx)
197 .children([
197 .children([
198− span(bump).finish(),
198+ span(&cx).finish(),
199− p(bump).finish(),
199+ p(&cx).finish(),
200− div(bump).finish(),
200+ div(&cx).finish(),
201 ])
201 ])
202 .finish()
202 .finish()
203 }
203 }
204 }
204 }
205
205
206 same_tag_remove_many_children {
206 same_tag_remove_many_children {
207− before(bump) {
207+ before(cx) {
208− div(bump)
208+ div(&cx)
209 .children([
209 .children([
210− div(bump).finish(),
210+ div(&cx).finish(),
211− span(bump).finish(),
211+ span(&cx).finish(),
212− p(bump).finish(),
212+ p(&cx).finish(),
213 ])
213 ])
214 .finish()
214 .finish()
215 }
215 }
216− after(bump) {
216+ after(cx) {
217− div(bump)
217+ div(&cx)
218 .children([
218 .children([
219− div(bump).finish(),
219+ div(&cx).finish(),
220 ])
220 ])
221 .finish()
221 .finish()
222 }
222 }
223 }
223 }
224
224
225 same_tag_add_many_children {
225 same_tag_add_many_children {
226− before(bump) {
226+ before(cx) {
227− div(bump)
227+ div(&cx)
228 .children([
228 .children([
229− div(bump).finish(),
229+ div(&cx).finish(),
230 ])
230 ])
231 .finish()
231 .finish()
232 }
232 }
233− after(bump) {
233+ after(cx) {
234− div(bump)
234+ div(&cx)
235 .children([
235 .children([
236− div(bump).finish(),
236+ div(&cx).finish(),
237− span(bump).finish(),
237+ span(&cx).finish(),
238− p(bump).finish(),
238+ p(&cx).finish(),
239 ])
239 ])
240 .finish()
240 .finish()
241 }
241 }
242 }
242 }
243
243
244 same_tag_different_namespace {
244 same_tag_different_namespace {
245− before(bump) {
245+ before(cx) {
246− div(bump)
246+ div(&cx)
247 .namespace(Some("http://example.com"))
247 .namespace(Some("http://example.com"))
248 .finish()
248 .finish()
249 }
249 }
250− after(bump) {
250+ after(cx) {
251− div(bump)
251+ div(&cx)
252 .namespace(Some("http://example.net"))
252 .namespace(Some("http://example.net"))
253 .finish()
253 .finish()
254 }
254 }