@@ -1,5 +1,7 @@
11use crate::Listener;
22use bumpalo::Bump;
3+use std::cell::Cell;
4+use std::collections::HashMap;
35use std::fmt;
46use std::sync::Once;
57use wasm_bindgen::prelude::*;
@@ -32,8 +34,15 @@ pub mod js {
3234 }
3335}
3436
37+struct StringsCacheEntry {
38+ key: u32,
39+ used: bool,
40+}
41+
3542pub(crate) struct ChangeList {
3643 bump: Bump,
44+ strings_cache: HashMap<String, StringsCacheEntry>,
45+ next_string_key: Cell<u32>,
3746 js: js::ChangeList,
3847 events_trampoline: Option<Closure<Fn(web_sys::Event, u32, u32)>>,
3948}
@@ -69,9 +78,12 @@ impl ChangeList {
6978 });
7079
7180 let bump = Bump::new();
81+ let strings_cache = HashMap::new();
7282 let js = js::ChangeList::new(container);
7383 ChangeList {
7484 bump,
85+ strings_cache,
86+ next_string_key: Cell::new(0),
7587 js,
7688 events_trampoline: None,
7789 }
@@ -238,6 +250,24 @@ enum ChangeDiscriminant {
238250 /// stack.top().removeEventListener(String(pointer, length));
239251 /// ```
240252 RemoveEventListener = 13,
253+
254+ /// Immediates: `(pointer, length, id)`
255+ ///
256+ /// Stack: `[...] -> [...]`
257+ ///
258+ /// ```text
259+ /// addString(String(pointer, length), id);
260+ /// ```
261+ AddString = 14,
262+
263+ /// Immediates: `(id)`
264+ ///
265+ /// Stack: `[...] -> [...]`
266+ ///
267+ /// ```text
268+ /// dropString(id);
269+ /// ```
270+ DropString = 15,
241271}
242272
243273// Allocation utilities to ensure that we only allocation sequences of `u32`s
@@ -249,22 +279,62 @@ impl ChangeList {
249279 self.bump.alloc(discriminant as u32);
250280 }
251281
252− // Note: no 1-immediate opcodes at this time.
282+ // Allocate an opcode with one immediate.
283+ fn op1(&self, discriminant: ChangeDiscriminant, a: u32) {
284+ self.bump.alloc([discriminant as u32, a]);
285+ }
253286
254287 // Allocate an opcode with two immediates.
255288 fn op2(&self, discriminant: ChangeDiscriminant, a: u32, b: u32) {
256289 self.bump.alloc([discriminant as u32, a, b]);
257290 }
258291
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]);
292+ // Allocate an opcode with three immediates.
293+ fn op3(&self, discriminant: ChangeDiscriminant, a: u32, b: u32, c: u32) {
294+ self.bump.alloc([discriminant as u32, a, b, c]);
264295 }
296+
297+ // Note: no 4-immediate opcodes at this time.
265298}
266299
267300impl ChangeList {
301+ fn ensure_string(&mut self, string: &str) -> u32 {
302+ let entry = self.strings_cache.get_mut(string);
303+ if entry.is_some() {
304+ let entry = entry.unwrap();
305+ entry.used = true;
306+ entry.key
307+ } else {
308+ let key = self.next_string_key.get();
309+ self.next_string_key.set(key + 1);
310+ let entry = StringsCacheEntry { key, used: true };
311+ self.strings_cache.insert(string.to_string(), entry);
312+ self.op3(
313+ ChangeDiscriminant::AddString,
314+ string.as_ptr() as u32,
315+ string.len() as u32,
316+ key
317+ );
318+ key
319+ }
320+ }
321+
322+ pub(crate) fn drop_unused_strings(&mut self) {
323+ debug!("drop_unused_strings()");
324+ let mut new_cache = HashMap::new();
325+ for (key, val) in self.strings_cache.iter() {
326+ if val.used {
327+ new_cache.insert(key.clone(), StringsCacheEntry { key: val.key, used: false });
328+ } else {
329+ self.op1(
330+ ChangeDiscriminant::DropString,
331+ val.key,
332+ );
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,23 +354,23 @@ 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(
359+ let name_id = self.ensure_string(name);
360+ let value_id = self.ensure_string(value);
361+ self.op2(
290362 ChangeDiscriminant::SetAttribute,
291− name.as_ptr() as u32,
292− name.len() as u32,
293− value.as_ptr() as u32,
294− value.len() as u32,
363+ name_id,
364+ value_id,
295365 );
296366 }
297367
298− pub(crate) fn emit_remove_attribute(&self, name: &str) {
368+ pub(crate) fn emit_remove_attribute(&mut self, name: &str) {
299369 debug!("emit_remove_attribute({:?})", name);
300− self.op2(
370+ let name_id = self.ensure_string(name);
371+ self.op1(
301372 ChangeDiscriminant::RemoveAttribute,
302− name.as_ptr() as u32,
303− name.len() as u32,
373+ name_id,
304374 );
305375 }
306376
@@ -333,47 +403,47 @@ impl ChangeList {
333403 );
334404 }
335405
336− pub(crate) fn emit_create_element(&self, tag_name: &str) {
406+ pub(crate) fn emit_create_element(&mut self, tag_name: &str) {
337407 debug!("emit_create_element({:?})", tag_name);
338− self.op2(
408+ let tag_name_id = self.ensure_string(tag_name);
409+ self.op1(
339410 ChangeDiscriminant::CreateElement,
340− tag_name.as_ptr() as u32,
341− tag_name.len() as u32,
411+ tag_name_id,
342412 );
343413 }
344414
345− pub(crate) fn emit_new_event_listener(&self, listener: &Listener) {
415+ pub(crate) fn emit_new_event_listener(&mut self, listener: &Listener) {
346416 debug!("emit_new_event_listener({:?})", listener);
347417 let (a, b) = listener.get_callback_parts();
348418 debug_assert!(a != 0);
349− self.op4(
419+ let event_id = self.ensure_string(listener.event);
420+ self.op3(
350421 ChangeDiscriminant::NewEventListener,
351− listener.event.as_ptr() as u32,
352− listener.event.len() as u32,
422+ event_id,
353423 a,
354424 b,
355425 );
356426 }
357427
358− pub(crate) fn emit_update_event_listener(&self, listener: &Listener) {
428+ pub(crate) fn emit_update_event_listener(&mut self, listener: &Listener) {
359429 debug!("emit_update_event_listener({:?})", listener);
360430 let (a, b) = listener.get_callback_parts();
361431 debug_assert!(a != 0);
362− self.op4(
432+ let event_id = self.ensure_string(listener.event);
433+ self.op3(
363434 ChangeDiscriminant::UpdateEventListener,
364− listener.event.as_ptr() as u32,
365− listener.event.len() as u32,
435+ event_id,
366436 a,
367437 b,
368438 );
369439 }
370440
371− pub(crate) fn emit_remove_event_listener(&self, event: &str) {
441+ pub(crate) fn emit_remove_event_listener(&mut self, event: &str) {
372442 debug!("emit_remove_event_listener({:?})", event);
373− self.op2(
443+ let event_id = self.ensure_string(event);
444+ self.op1(
374445 ChangeDiscriminant::RemoveEventListener,
375− event.as_ptr() as u32,
376− event.len() as u32,
446+ event_id,
377447 );
378448 }
379449}