@@ -1,5 +1,6 @@
11use crate::Listener;
22use bumpalo::Bump;
3+use fxhash::FxHashMap;
34use std::fmt;
45use std::sync::Once;
56use wasm_bindgen::prelude::*;
@@ -32,8 +33,15 @@ pub mod js {
3233 }
3334}
3435
36+struct StringsCacheEntry {
37+ key: u32,
38+ used: bool,
39+}
40+
3541pub(crate) struct ChangeList {
3642 bump: Bump,
43+ strings_cache: FxHashMap<String, StringsCacheEntry>,
44+ next_string_key: u32,
3745 js: js::ChangeList,
3846 events_trampoline: Option<Closure<Fn(web_sys::Event, u32, u32)>>,
3947}
@@ -69,9 +77,12 @@ impl ChangeList {
6977 });
7078
7179 let bump = Bump::new();
80+ let strings_cache = FxHashMap::default();
7281 let js = js::ChangeList::new(container);
7382 ChangeList {
7483 bump,
84+ strings_cache,
85+ next_string_key: 0,
7586 js,
7687 events_trampoline: None,
7788 }
@@ -238,6 +249,24 @@ enum ChangeDiscriminant {
238249 /// stack.top().removeEventListener(String(pointer, length));
239250 /// ```
240251 RemoveEventListener = 13,
252+
253+ /// Immediates: `(pointer, length, id)`
254+ ///
255+ /// Stack: `[...] -> [...]`
256+ ///
257+ /// ```text
258+ /// addString(String(pointer, length), id);
259+ /// ```
260+ AddString = 14,
261+
262+ /// Immediates: `(id)`
263+ ///
264+ /// Stack: `[...] -> [...]`
265+ ///
266+ /// ```text
267+ /// dropString(id);
268+ /// ```
269+ DropString = 15,
241270}
242271
243272// Allocation utilities to ensure that we only allocation sequences of `u32`s
@@ -249,22 +278,63 @@ impl ChangeList {
249278 self.bump.alloc(discriminant as u32);
250279 }
251280
252− // Note: no 1-immediate opcodes at this time.
281+ // Allocate an opcode with one immediate.
282+ fn op1(&self, discriminant: ChangeDiscriminant, a: u32) {
283+ self.bump.alloc([discriminant as u32, a]);
284+ }
253285
254286 // Allocate an opcode with two immediates.
255287 fn op2(&self, discriminant: ChangeDiscriminant, a: u32, b: u32) {
256288 self.bump.alloc([discriminant as u32, a, b]);
257289 }
258290
259− // Note: no 3-immediate opcodes at this time.
260−
261− // Allocate an opcode with four immediates.
262− fn op4(&self, discriminant: ChangeDiscriminant, a: u32, b: u32, c: u32, d: u32) {
263− self.bump.alloc([discriminant as u32, a, b, c, d]);
291+ // Allocate an opcode with three immediates.
292+ fn op3(&self, discriminant: ChangeDiscriminant, a: u32, b: u32, c: u32) {
293+ self.bump.alloc([discriminant as u32, a, b, c]);
264294 }
295+
296+ // Note: no 4-immediate opcodes at this time.
265297}
266298
267299impl ChangeList {
300+ fn ensure_string(&mut self, string: &str) -> u32 {
301+ if let Some(entry) = self.strings_cache.get_mut(string) {
302+ entry.used = true;
303+ entry.key
304+ } else {
305+ let key = self.next_string_key;
306+ self.next_string_key += 1;
307+ let entry = StringsCacheEntry { key, used: true };
308+ self.strings_cache.insert(string.to_string(), entry);
309+ self.op3(
310+ ChangeDiscriminant::AddString,
311+ string.as_ptr() as u32,
312+ string.len() as u32,
313+ key,
314+ );
315+ key
316+ }
317+ }
318+
319+ pub(crate) fn drop_unused_strings(&mut self) {
320+ debug!("drop_unused_strings()");
321+ let mut new_cache = FxHashMap::default();
322+ for (key, val) in self.strings_cache.iter() {
323+ if val.used {
324+ new_cache.insert(
325+ key.clone(),
326+ StringsCacheEntry {
327+ key: val.key,
328+ used: false,
329+ },
330+ );
331+ } else {
332+ self.op1(ChangeDiscriminant::DropString, val.key);
333+ }
334+ }
335+ self.strings_cache = new_cache;
336+ }
337+
268338 pub(crate) fn emit_set_text(&self, text: &str) {
269339 debug!("emit_set_text({:?})", text);
270340 self.op2(
@@ -284,24 +354,17 @@ impl ChangeList {
284354 self.op0(ChangeDiscriminant::ReplaceWith);
285355 }
286356
287− pub(crate) fn emit_set_attribute(&self, name: &str, value: &str) {
357+ pub(crate) fn emit_set_attribute(&mut self, name: &str, value: &str) {
288358 debug!("emit_set_attribute({:?}, {:?})", name, value);
289− self.op4(
290− ChangeDiscriminant::SetAttribute,
291− name.as_ptr() as u32,
292− name.len() as u32,
293− value.as_ptr() as u32,
294− value.len() as u32,
295− );
359+ let name_id = self.ensure_string(name);
360+ let value_id = self.ensure_string(value);
361+ self.op2(ChangeDiscriminant::SetAttribute, name_id, value_id);
296362 }
297363
298− pub(crate) fn emit_remove_attribute(&self, name: &str) {
364+ pub(crate) fn emit_remove_attribute(&mut self, name: &str) {
299365 debug!("emit_remove_attribute({:?})", name);
300− self.op2(
301− ChangeDiscriminant::RemoveAttribute,
302− name.as_ptr() as u32,
303− name.len() as u32,
304− );
366+ let name_id = self.ensure_string(name);
367+ self.op1(ChangeDiscriminant::RemoveAttribute, name_id);
305368 }
306369
307370 pub(crate) fn emit_push_first_child(&self) {
@@ -333,47 +396,31 @@ impl ChangeList {
333396 );
334397 }
335398
336− pub(crate) fn emit_create_element(&self, tag_name: &str) {
399+ pub(crate) fn emit_create_element(&mut self, tag_name: &str) {
337400 debug!("emit_create_element({:?})", tag_name);
338− self.op2(
339− ChangeDiscriminant::CreateElement,
340− tag_name.as_ptr() as u32,
341− tag_name.len() as u32,
342− );
401+ let tag_name_id = self.ensure_string(tag_name);
402+ self.op1(ChangeDiscriminant::CreateElement, tag_name_id);
343403 }
344404
345− pub(crate) fn emit_new_event_listener(&self, listener: &Listener) {
405+ pub(crate) fn emit_new_event_listener(&mut self, listener: &Listener) {
346406 debug!("emit_new_event_listener({:?})", listener);
347407 let (a, b) = listener.get_callback_parts();
348408 debug_assert!(a != 0);
349− self.op4(
350− ChangeDiscriminant::NewEventListener,
351− listener.event.as_ptr() as u32,
352− listener.event.len() as u32,
353− a,
354− b,
355− );
409+ let event_id = self.ensure_string(listener.event);
410+ self.op3(ChangeDiscriminant::NewEventListener, event_id, a, b);
356411 }
357412
358− pub(crate) fn emit_update_event_listener(&self, listener: &Listener) {
413+ pub(crate) fn emit_update_event_listener(&mut self, listener: &Listener) {
359414 debug!("emit_update_event_listener({:?})", listener);
360415 let (a, b) = listener.get_callback_parts();
361416 debug_assert!(a != 0);
362− self.op4(
363− ChangeDiscriminant::UpdateEventListener,
364− listener.event.as_ptr() as u32,
365− listener.event.len() as u32,
366− a,
367− b,
368− );
417+ let event_id = self.ensure_string(listener.event);
418+ self.op3(ChangeDiscriminant::UpdateEventListener, event_id, a, b);
369419 }
370420
371− pub(crate) fn emit_remove_event_listener(&self, event: &str) {
421+ pub(crate) fn emit_remove_event_listener(&mut self, event: &str) {
372422 debug!("emit_remove_event_listener({:?})", event);
373− self.op2(
374− ChangeDiscriminant::RemoveEventListener,
375− event.as_ptr() as u32,
376− event.len() as u32,
377− );
423+ let event_id = self.ensure_string(event);
424+ self.op1(ChangeDiscriminant::RemoveEventListener, event_id);
378425 }
379426}