@@ -1,5 +1,7 @@
1use crate::Listener;
1use crate::Listener;
2use bumpalo::Bump;
2use bumpalo::Bump;
4+use std::collections::HashMap;
3use std::fmt;
5use std::fmt;
4use std::sync::Once;
6use std::sync::Once;
5use wasm_bindgen::prelude::*;
7use wasm_bindgen::prelude::*;
@@ -32,8 +34,15 @@ pub mod js {
37+struct StringsCacheEntry {
35pub(crate) struct ChangeList {
42pub(crate) struct ChangeList {
36 bump: Bump,
43 bump: Bump,
44+ strings_cache: HashMap<String, StringsCacheEntry>,
45+ next_string_key: Cell<u32>,
37 js: js::ChangeList,
46 js: js::ChangeList,
38 events_trampoline: Option<Closure<Fn(web_sys::Event, u32, u32)>>,
47 events_trampoline: Option<Closure<Fn(web_sys::Event, u32, u32)>>,
@@ -69,9 +78,12 @@ impl ChangeList {
71 let bump = Bump::new();
80 let bump = Bump::new();
81+ let strings_cache = HashMap::new();
72 let js = js::ChangeList::new(container);
82 let js = js::ChangeList::new(container);
73 ChangeList {
83 ChangeList {
86+ next_string_key: Cell::new(0),
76 events_trampoline: None,
88 events_trampoline: None,
@@ -238,6 +250,24 @@ enum ChangeDiscriminant {
238 /// stack.top().removeEventListener(String(pointer, length));
250 /// stack.top().removeEventListener(String(pointer, length));
240 RemoveEventListener = 13,
252 RemoveEventListener = 13,
254+ /// Immediates: `(pointer, length, id)`
256+ /// Stack: `[...] -> [...]`
259+ /// addString(String(pointer, length), id);
263+ /// Immediates: `(id)`
265+ /// Stack: `[...] -> [...]`
243// Allocation utilities to ensure that we only allocation sequences of `u32`s
273// Allocation utilities to ensure that we only allocation sequences of `u32`s
@@ -249,22 +279,62 @@ impl ChangeList {
249 self.bump.alloc(discriminant as u32);
279 self.bump.alloc(discriminant as u32);
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]);
254 // Allocate an opcode with two immediates.
287 // Allocate an opcode with two immediates.
255 fn op2(&self, discriminant: ChangeDiscriminant, a: u32, b: u32) {
288 fn op2(&self, discriminant: ChangeDiscriminant, a: u32, b: u32) {
256 self.bump.alloc([discriminant as u32, a, b]);
289 self.bump.alloc([discriminant as u32, a, b]);
259− // Note: no 3-immediate opcodes at this time.
261− // Allocate an opcode with four immediates.
292+ // Allocate an opcode with three immediates.
262− fn op4(&self, discriminant: ChangeDiscriminant, a: u32, b: u32, c: u32, d: u32) {
293+ fn op3(&self, discriminant: ChangeDiscriminant, a: u32, b: u32, c: u32) {
263− self.bump.alloc([discriminant as u32, a, b, c, d]);
294+ self.bump.alloc([discriminant as u32, a, b, c]);
297+ // Note: no 4-immediate opcodes at this time.
267impl ChangeList {
300impl 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();
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);
313+ ChangeDiscriminant::AddString,
314+ string.as_ptr() as u32,
315+ string.len() as u32,
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() {
327+ new_cache.insert(key.clone(), StringsCacheEntry { key: val.key, used: false });
330+ ChangeDiscriminant::DropString,
335+ self.strings_cache = new_cache;
268 pub(crate) fn emit_set_text(&self, text: &str) {
338 pub(crate) fn emit_set_text(&self, text: &str) {
269 debug!("emit_set_text({:?})", text);
339 debug!("emit_set_text({:?})", text);
270 self.op2(
340 self.op2(
@@ -284,23 +354,23 @@ impl ChangeList {
284 self.op0(ChangeDiscriminant::ReplaceWith);
354 self.op0(ChangeDiscriminant::ReplaceWith);
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) {
288 debug!("emit_set_attribute({:?}, {:?})", name, value);
358 debug!("emit_set_attribute({:?}, {:?})", name, value);
359+ let name_id = self.ensure_string(name);
360+ let value_id = self.ensure_string(value);
289− self.op4(
361+ self.op2(
290 ChangeDiscriminant::SetAttribute,
362 ChangeDiscriminant::SetAttribute,
291− name.as_ptr() as u32,
363+ name_id,
292− name.len() as u32,
364+ value_id,
293− value.as_ptr() as u32,
298− pub(crate) fn emit_remove_attribute(&self, name: &str) {
368+ pub(crate) fn emit_remove_attribute(&mut self, name: &str) {
299 debug!("emit_remove_attribute({:?})", name);
369 debug!("emit_remove_attribute({:?})", name);
300− self.op2(
370+ let name_id = self.ensure_string(name);
301 ChangeDiscriminant::RemoveAttribute,
372 ChangeDiscriminant::RemoveAttribute,
302− name.as_ptr() as u32,
373+ name_id,
@@ -333,47 +403,47 @@ impl ChangeList {
336− pub(crate) fn emit_create_element(&self, tag_name: &str) {
406+ pub(crate) fn emit_create_element(&mut self, tag_name: &str) {
337 debug!("emit_create_element({:?})", tag_name);
407 debug!("emit_create_element({:?})", tag_name);
338− self.op2(
408+ let tag_name_id = self.ensure_string(tag_name);
339 ChangeDiscriminant::CreateElement,
410 ChangeDiscriminant::CreateElement,
340− tag_name.as_ptr() as u32,
411+ tag_name_id,
341− tag_name.len() as u32,
345− pub(crate) fn emit_new_event_listener(&self, listener: &Listener) {
415+ pub(crate) fn emit_new_event_listener(&mut self, listener: &Listener) {
346 debug!("emit_new_event_listener({:?})", listener);
416 debug!("emit_new_event_listener({:?})", listener);
347 let (a, b) = listener.get_callback_parts();
417 let (a, b) = listener.get_callback_parts();
348 debug_assert!(a != 0);
418 debug_assert!(a != 0);
349− self.op4(
419+ let event_id = self.ensure_string(listener.event);
350 ChangeDiscriminant::NewEventListener,
421 ChangeDiscriminant::NewEventListener,
351− listener.event.as_ptr() as u32,
422+ event_id,
352− listener.event.len() as u32,
358− pub(crate) fn emit_update_event_listener(&self, listener: &Listener) {
428+ pub(crate) fn emit_update_event_listener(&mut self, listener: &Listener) {
359 debug!("emit_update_event_listener({:?})", listener);
429 debug!("emit_update_event_listener({:?})", listener);
360 let (a, b) = listener.get_callback_parts();
430 let (a, b) = listener.get_callback_parts();
361 debug_assert!(a != 0);
431 debug_assert!(a != 0);
362− self.op4(
432+ let event_id = self.ensure_string(listener.event);
363 ChangeDiscriminant::UpdateEventListener,
434 ChangeDiscriminant::UpdateEventListener,
364− listener.event.as_ptr() as u32,
435+ event_id,
365− listener.event.len() as u32,
371− pub(crate) fn emit_remove_event_listener(&self, event: &str) {
441+ pub(crate) fn emit_remove_event_listener(&mut self, event: &str) {
372 debug!("emit_remove_event_listener({:?})", event);
442 debug!("emit_remove_event_listener({:?})", event);
373− self.op2(
443+ let event_id = self.ensure_string(event);
374 ChangeDiscriminant::RemoveEventListener,
445 ChangeDiscriminant::RemoveEventListener,
375− event.as_ptr() as u32,
446+ event_id,