Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/refactor-weakref-webcore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/web-core": patch
---

refactor: with WeakRef in element APIs and WASM bindings to improve memory management.
8 changes: 5 additions & 3 deletions packages/web-platform/web-core/binary/client/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ export class MainThreadWasmContext {
add_dataset(unique_id: number, key: any, value: any): void;
add_run_worklet_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: any | null): void;
common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string, is_bubble: boolean): void;
create_element_common(parent_component_unique_id: number, dom: HTMLElement, css_id?: number | null, component_id?: string | null): number;
create_element_common(parent_component_unique_id: number, dom: HTMLElement, dom_ref: WeakRef<object>, css_id?: number | null, component_id?: string | null): number;
dispatch_event_by_path(bubble_unique_id_path: Uint32Array, event_name: string, is_capture: boolean, serialized_event: any): boolean;
dispatch_global_bind_event(bubble_unique_id_path: Uint32Array, event_name: string, serialized_event: any): void;
gc(): void;
get_component_id(unique_id: number): string | undefined;
get_config(unique_id: number): object;
get_css_id_by_unique_id(unique_id: number): number | undefined;
get_data_by_key(unique_id: number, key: string): any;
get_dataset(unique_id: number): object;
get_dom_by_unique_id(unique_id: number): HTMLElement | undefined;
get_dom_by_unique_id(unique_id: number): WeakRef<object> | undefined;
get_element_config(unique_id: number): object | undefined;
get_event(unique_id: number, event_name: string, event_type: string): any;
get_events(unique_id: number): EventInfo[];
Expand Down Expand Up @@ -201,9 +202,10 @@ export interface InitOutput {
readonly mainthreadwasmcontext_add_dataset: (a: number, b: number, c: any, d: any) => [number, number];
readonly mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
readonly mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number, g: number) => void;
readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: number, e: number, f: number) => number;
readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: any, e: number, f: number, g: number) => number;
readonly mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => number;
readonly mainthreadwasmcontext_dispatch_global_bind_event: (a: number, b: number, c: number, d: number, e: number, f: any) => void;
readonly mainthreadwasmcontext_gc: (a: number) => void;
readonly mainthreadwasmcontext_get_component_id: (a: number, b: number) => [number, number, number, number];
readonly mainthreadwasmcontext_get_config: (a: number, b: number) => [number, number, number];
readonly mainthreadwasmcontext_get_css_id_by_unique_id: (a: number, b: number) => number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number
export const mainthreadwasmcontext_add_dataset: (a: number, b: number, c: any, d: any) => [number, number];
export const mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const mainthreadwasmcontext_common_event_handler: (a: number, b: any, c: number, d: number, e: number, f: number, g: number) => void;
export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: number, e: number, f: number) => number;
export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: any, d: any, e: number, f: number, g: number) => number;
export const mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => number;
export const mainthreadwasmcontext_dispatch_global_bind_event: (a: number, b: number, c: number, d: number, e: number, f: any) => void;
export const mainthreadwasmcontext_gc: (a: number) => void;
export const mainthreadwasmcontext_get_component_id: (a: number, b: number) => [number, number, number, number];
export const mainthreadwasmcontext_get_config: (a: number, b: number) => [number, number, number];
export const mainthreadwasmcontext_get_css_id_by_unique_id: (a: number, b: number) => number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ export class MainThreadWasmContext {
add_dataset(unique_id: number, key: any, value: any): void;
add_run_worklet_event(unique_id: number, event_type: string, event_name: string, event_handler_identifier?: any | null): void;
common_event_handler(event: any, bubble_unique_id_path: Uint32Array, event_name: string, is_bubble: boolean): void;
create_element_common(parent_component_unique_id: number, dom: HTMLElement, css_id?: number | null, component_id?: string | null): number;
create_element_common(parent_component_unique_id: number, dom: HTMLElement, dom_ref: WeakRef<object>, css_id?: number | null, component_id?: string | null): number;
dispatch_event_by_path(bubble_unique_id_path: Uint32Array, event_name: string, is_capture: boolean, serialized_event: any): boolean;
dispatch_global_bind_event(bubble_unique_id_path: Uint32Array, event_name: string, serialized_event: any): void;
gc(): void;
get_component_id(unique_id: number): string | undefined;
get_config(unique_id: number): object;
get_css_id_by_unique_id(unique_id: number): number | undefined;
get_data_by_key(unique_id: number, key: string): any;
get_dataset(unique_id: number): object;
get_dom_by_unique_id(unique_id: number): HTMLElement | undefined;
get_dom_by_unique_id(unique_id: number): WeakRef<object> | undefined;
get_element_config(unique_id: number): object | undefined;
get_event(unique_id: number, event_name: string, event_type: string): any;
get_events(unique_id: number): EventInfo[];
Expand Down Expand Up @@ -201,9 +202,10 @@ export interface InitOutput {
readonly mainthreadwasmcontext_add_dataset: (a: number, b: number, c: number, d: number, e: number) => void;
readonly mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
readonly mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
readonly mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
readonly mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
readonly mainthreadwasmcontext_dispatch_global_bind_event: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly mainthreadwasmcontext_gc: (a: number) => void;
readonly mainthreadwasmcontext_get_component_id: (a: number, b: number, c: number) => void;
readonly mainthreadwasmcontext_get_config: (a: number, b: number, c: number) => void;
readonly mainthreadwasmcontext_get_css_id_by_unique_id: (a: number, b: number) => number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number
export const mainthreadwasmcontext_add_dataset: (a: number, b: number, c: number, d: number, e: number) => void;
export const mainthreadwasmcontext_add_run_worklet_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const mainthreadwasmcontext_common_event_handler: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
export const mainthreadwasmcontext_create_element_common: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
export const mainthreadwasmcontext_dispatch_event_by_path: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
export const mainthreadwasmcontext_dispatch_global_bind_event: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const mainthreadwasmcontext_gc: (a: number) => void;
export const mainthreadwasmcontext_get_component_id: (a: number, b: number, c: number) => void;
export const mainthreadwasmcontext_get_config: (a: number, b: number, c: number) => void;
export const mainthreadwasmcontext_get_css_id_by_unique_id: (a: number, b: number) => number;
Expand Down
33 changes: 24 additions & 9 deletions packages/web-platform/web-core/src/js_binding/mts_js_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,39 @@ extern "C" {
#[wasm_bindgen(method, js_name = "addEventListener")]
pub fn add_event_listener(this: &RustMainthreadContextBinding, event_name: &str);

#[wasm_bindgen(method, js_name = "enableElementEvent")]
#[wasm_bindgen(method, catch, js_name = "enableElementEvent")]
pub fn enable_element_event(
this: &RustMainthreadContextBinding,
element: &web_sys::HtmlElement,
element: &js_sys::WeakRef,
event_name: &str,
);
) -> Result<(), JsValue>;

#[wasm_bindgen(method, js_name = "disableElementEvent")]
#[wasm_bindgen(method, catch, js_name = "disableElementEvent")]
pub fn disable_element_event(
this: &RustMainthreadContextBinding,
element: &web_sys::HtmlElement,
element: &js_sys::WeakRef,
event_name: &str,
);
) -> Result<(), JsValue>;

#[wasm_bindgen(method, js_name = "getClassList")]
#[wasm_bindgen(method, catch, js_name = "getClassList")]
pub fn get_class_name_list(
this: &RustMainthreadContextBinding,
element: &web_sys::HtmlElement,
) -> Vec<String>;
element: &js_sys::WeakRef,
) -> Result<Vec<String>, JsValue>;

#[wasm_bindgen(method, catch, js_name = "setAttribute")]
pub fn set_attribute(
this: &RustMainthreadContextBinding,
element: &js_sys::WeakRef,
name: &str,
value: &str,
) -> Result<(), JsValue>;

#[wasm_bindgen(method, catch, js_name = "removeAttribute")]
pub fn remove_attribute(
this: &RustMainthreadContextBinding,
element: &js_sys::WeakRef,
name: &str,
) -> Result<(), JsValue>;

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ impl MainThreadWasmContext {

if should_enable {
if let Some(element) = self.unique_id_to_dom_map.get(&unique_id) {
self
let _ = self
.mts_binding
.enable_element_event(element, event_name_str);
}
} else if should_disable {
if let Some(element) = self.unique_id_to_dom_map.get(&unique_id) {
self
let _ = self
.mts_binding
.disable_element_event(element, event_name_str);
}
Expand Down Expand Up @@ -122,13 +122,13 @@ impl MainThreadWasmContext {

if should_enable {
if let Some(element) = self.unique_id_to_dom_map.get(&unique_id) {
self
let _ = self
.mts_binding
.enable_element_event(element, event_name_str);
}
} else if should_disable {
if let Some(element) = self.unique_id_to_dom_map.get(&unique_id) {
self
let _ = self
.mts_binding
.disable_element_event(element, event_name_str);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,22 @@ impl MainThreadWasmContext {
{
let element = self.unique_id_to_dom_map.get(&unique_id).expect_throw("El");
if let Some(entry_name) = &entry_name {
let _ = element.set_attribute(constants::LYNX_ENTRY_NAME_ATTRIBUTE, entry_name);
let _ = self.mts_binding.set_attribute(
element,
constants::LYNX_ENTRY_NAME_ATTRIBUTE,
entry_name,
);
}
if css_id != 0 {
let _ = element.set_attribute(constants::CSS_ID_ATTRIBUTE, &css_id.to_string());
let _ = self.mts_binding.set_attribute(
element,
constants::CSS_ID_ATTRIBUTE,
&css_id.to_string(),
);
} else {
let _ = element.remove_attribute(constants::CSS_ID_ATTRIBUTE);
let _ = self
.mts_binding
.remove_attribute(element, constants::CSS_ID_ATTRIBUTE);
}
{
let element_data_cell = self.get_element_data_by_unique_id(unique_id).unwrap_throw();
Expand All @@ -54,7 +64,10 @@ impl MainThreadWasmContext {
self.style_manager.update_css_og_style(
unique_id,
element_data.css_id,
self.mts_binding.get_class_name_list(element),
self
.mts_binding
.get_class_name_list(element)
.unwrap_or_default(),
entry_name,
)?;
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct MainThreadWasmContext {
pub(super) unique_id_to_element_map: Vec<Option<Rc<RefCell<Box<LynxElementData>>>>>,
pub(super) unique_id_to_dom_map: FnvHashMap<usize, web_sys::HtmlElement>,
pub(super) unique_id_to_dom_map: FnvHashMap<usize, js_sys::WeakRef>,
pub(super) timing_flags: Vec<String>,

pub(super) enabled_events: FnvHashSet<String>,
Expand Down Expand Up @@ -78,6 +78,7 @@ impl MainThreadWasmContext {
self: &mut MainThreadWasmContext,
parent_component_unique_id: usize,
dom: web_sys::HtmlElement,
dom_ref: js_sys::WeakRef,
css_id: Option<i32>,
component_id: Option<String>,
) -> usize {
Expand Down Expand Up @@ -111,11 +112,11 @@ impl MainThreadWasmContext {
self
.unique_id_to_element_map
.push(Some(Rc::new(RefCell::new(element_data))));
self.unique_id_to_dom_map.insert(unique_id, dom.clone());
self.unique_id_to_dom_map.insert(unique_id, dom_ref);
unique_id
}

pub fn get_dom_by_unique_id(&self, unique_id: usize) -> Option<web_sys::HtmlElement> {
pub fn get_dom_by_unique_id(&self, unique_id: usize) -> Option<js_sys::WeakRef> {
self.unique_id_to_dom_map.get(&unique_id).cloned()
}

Expand Down Expand Up @@ -145,10 +146,18 @@ impl MainThreadWasmContext {
.map(|element_data_cell| element_data_cell.borrow().css_id)
}

// pub fn gc(&mut self) {
// self.unique_id_to_element_map.retain(|_, value| {
// let dom = value.get_dom();
// dom.is_connected()
// });
// }
pub fn gc(&mut self) {
let mut ids_to_remove = Vec::new();
for (unique_id, weak_ref) in self.unique_id_to_dom_map.iter() {
if weak_ref.deref().is_none() {
ids_to_remove.push(*unique_id);
}
}
for id in ids_to_remove {
self.unique_id_to_dom_map.remove(&id);
if let Some(element_data) = self.unique_id_to_element_map.get_mut(id) {
*element_data = None;
}
}
}
}
4 changes: 2 additions & 2 deletions packages/web-platform/web-core/tests/element-apis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1576,9 +1576,9 @@ describe('Element APIs', () => {
mtsGlobalThis.__AppendElement(root, element);

const spy = vi.spyOn(mtsBinding, 'getClassList');
const classes = mtsBinding.getClassList(element);
const classes = mtsBinding.getClassList(new WeakRef(element));

expect(spy).toHaveBeenCalledWith(element);
expect(spy).toHaveBeenCalledWith(expect.any(WeakRef));
expect(classes).toEqual(expect.arrayContaining(['foo', 'bar']));
expect(classes.length).toBe(2);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,19 @@ export class WASMJSBinding implements RustMainthreadContextBinding {
}

getClassList(
element: HTMLElement,
elementRef: WeakRef<HTMLElement>,
): string[] {
return [...(element.classList as unknown as string[])];
const element = elementRef.deref();
if (element) {
return [...(element.classList as unknown as string[])];
}
return [];
}

getElementByUniqueId(uniqueId: number): HTMLElement | undefined {
return this.wasmContext?.get_dom_by_unique_id(uniqueId);
return this.wasmContext?.get_dom_by_unique_id(uniqueId)?.deref() as
| HTMLElement
| undefined;
}

getElementByComponentId(
Expand Down Expand Up @@ -203,17 +209,33 @@ export class WASMJSBinding implements RustMainthreadContextBinding {
);
}

enableElementEvent(element: HTMLElement, eventName: string) {
enableElementEvent(elementRef: WeakRef<HTMLElement>, eventName: string) {
const element = elementRef.deref();
if (element) {
// @ts-expect-error
element.enableEvent?.(LynxEventNameToW3cCommon[eventName] ?? eventName);
}
}

disableElementEvent(element: HTMLElement, eventName: string) {
disableElementEvent(elementRef: WeakRef<HTMLElement>, eventName: string) {
const element = elementRef.deref();
if (element) {
// @ts-expect-error
element.disableEvent?.(LynxEventNameToW3cCommon[eventName] ?? eventName);
}
}

setAttribute(elementRef: WeakRef<HTMLElement>, name: string, value: string) {
const element = elementRef.deref();
if (element) {
element.setAttribute(name, value);
}
}

removeAttribute(elementRef: WeakRef<HTMLElement>, name: string) {
const element = elementRef.deref();
if (element) {
element.removeAttribute(name);
}
}
}
Loading
Loading