|
1 | 1 | use js_sys::{Array, Object, Uint8Array}; |
2 | 2 | use std::borrow::Cow; |
3 | | -use std::cell::RefCell; |
| 3 | +use std::cell::UnsafeCell; |
4 | 4 | use std::mem; |
5 | 5 | use std::rc::Rc; |
6 | 6 | use wacore_binary::{ |
@@ -120,16 +120,29 @@ pub struct InternalBinaryNode { |
120 | 120 | _owned_data: Rc<[u8]>, |
121 | 121 | node_ref: NodeRef<'static>, |
122 | 122 | #[wasm_bindgen(skip)] |
123 | | - cached_attrs: RefCell<Option<Attrs>>, |
| 123 | + cached_attrs: UnsafeCell<Option<Attrs>>, |
124 | 124 | #[wasm_bindgen(skip)] |
125 | | - cached_content: RefCell<Option<Content>>, |
| 125 | + cached_content: UnsafeCell<Option<Content>>, |
126 | 126 | } |
127 | 127 |
|
128 | 128 | impl InternalBinaryNode { |
129 | 129 | #[inline(always)] |
130 | 130 | fn node_ref(&self) -> &NodeRef<'static> { |
131 | 131 | &self.node_ref |
132 | 132 | } |
| 133 | + |
| 134 | + #[inline] |
| 135 | + fn convert_attrs(attrs: &[(Cow<'_, str>, ValueRef<'_>)]) -> Attrs { |
| 136 | + let obj = Object::new(); |
| 137 | + for (k, v) in attrs.iter() { |
| 138 | + let js_value = match v.as_str() { |
| 139 | + Some(s) => JsValue::from_str(s), |
| 140 | + None => JsValue::from_str(&v.to_string()), |
| 141 | + }; |
| 142 | + let _ = js_sys::Reflect::set(&obj, &JsValue::from_str(k), &js_value); |
| 143 | + } |
| 144 | + obj.unchecked_into() |
| 145 | + } |
133 | 146 | } |
134 | 147 |
|
135 | 148 | #[wasm_bindgen] |
@@ -183,72 +196,63 @@ impl InternalBinaryNode { |
183 | 196 |
|
184 | 197 | #[wasm_bindgen(getter)] |
185 | 198 | pub fn attrs(&self) -> Attrs { |
186 | | - let mut cached = self.cached_attrs.borrow_mut(); |
187 | | - if cached.is_none() { |
188 | | - let attrs = &self.node_ref().attrs; |
189 | | - |
190 | | - let obj = Object::new(); |
191 | | - for (k, v) in attrs.iter() { |
192 | | - let js_value = match v.as_str() { |
193 | | - Some(s) => JsValue::from_str(s), |
194 | | - None => JsValue::from_str(&v.to_string()), |
195 | | - }; |
196 | | - let _ = js_sys::Reflect::set(&obj, &JsValue::from_str(k), &js_value); |
197 | | - } |
198 | | - |
199 | | - *cached = Some(obj.unchecked_into()); |
| 199 | + // SAFETY: WASM is single-threaded |
| 200 | + let cached = unsafe { &mut *self.cached_attrs.get() }; |
| 201 | + if let Some(attrs) = cached.as_ref() { |
| 202 | + return attrs.clone(); |
200 | 203 | } |
201 | 204 |
|
202 | | - cached |
203 | | - .as_ref() |
204 | | - .expect("Cached attributes should be populated before access") |
205 | | - .clone() |
206 | | - .unchecked_into() |
| 205 | + let attrs = Self::convert_attrs(&self.node_ref().attrs); |
| 206 | + *cached = Some(attrs.clone()); |
| 207 | + attrs |
207 | 208 | } |
208 | 209 |
|
209 | 210 | #[wasm_bindgen(setter)] |
210 | 211 | pub fn set_attrs(&self, new_attrs: Attrs) { |
211 | | - *self.cached_attrs.borrow_mut() = Some(new_attrs); |
| 212 | + // SAFETY: WASM is single-threaded |
| 213 | + unsafe { *self.cached_attrs.get() = Some(new_attrs) }; |
212 | 214 | } |
213 | 215 |
|
214 | 216 | #[wasm_bindgen(getter)] |
215 | 217 | pub fn content(&self) -> Option<Content> { |
216 | | - let mut cached = self.cached_content.borrow_mut(); |
217 | | - if cached.is_none() { |
218 | | - match self.node_ref().content.as_deref() { |
219 | | - Some(NodeContentRef::Bytes(bytes)) => { |
220 | | - let bytes_ref = bytes.as_ref(); |
221 | | - let u8arr = Uint8Array::new_with_length(bytes_ref.len() as u32); |
222 | | - u8arr.copy_from(bytes_ref); |
223 | | - *cached = Some(u8arr.unchecked_into()); |
224 | | - } |
225 | | - Some(NodeContentRef::String(s)) => { |
226 | | - *cached = Some(JsValue::from_str(s).unchecked_into()); |
227 | | - } |
228 | | - Some(NodeContentRef::Nodes(nodes)) => { |
229 | | - let arr = Array::new_with_length(nodes.len() as u32); |
230 | | - for (i, node_ref) in nodes.iter().enumerate() { |
231 | | - let child = InternalBinaryNode { |
232 | | - _owned_data: Rc::clone(&self._owned_data), |
233 | | - node_ref: node_ref.clone(), |
234 | | - cached_attrs: RefCell::new(None), |
235 | | - cached_content: RefCell::new(None), |
236 | | - }; |
237 | | - arr.set(i as u32, child.into()); |
238 | | - } |
239 | | - *cached = Some(arr.unchecked_into()); |
| 218 | + // SAFETY: WASM is single-threaded |
| 219 | + let cached = unsafe { &mut *self.cached_content.get() }; |
| 220 | + if let Some(content) = cached.as_ref() { |
| 221 | + return Some(content.clone()); |
| 222 | + } |
| 223 | + |
| 224 | + let result: Option<Content> = match self.node_ref().content.as_deref() { |
| 225 | + Some(NodeContentRef::Bytes(bytes)) => { |
| 226 | + let bytes_ref = bytes.as_ref(); |
| 227 | + let u8arr = Uint8Array::new_with_length(bytes_ref.len() as u32); |
| 228 | + u8arr.copy_from(bytes_ref); |
| 229 | + Some(u8arr.unchecked_into()) |
| 230 | + } |
| 231 | + Some(NodeContentRef::String(s)) => Some(JsValue::from_str(s).unchecked_into()), |
| 232 | + Some(NodeContentRef::Nodes(nodes)) => { |
| 233 | + let arr = Array::new_with_length(nodes.len() as u32); |
| 234 | + for (i, node_ref) in nodes.iter().enumerate() { |
| 235 | + let child = InternalBinaryNode { |
| 236 | + _owned_data: Rc::clone(&self._owned_data), |
| 237 | + node_ref: node_ref.clone(), |
| 238 | + cached_attrs: UnsafeCell::new(None), |
| 239 | + cached_content: UnsafeCell::new(None), |
| 240 | + }; |
| 241 | + arr.set(i as u32, child.into()); |
240 | 242 | } |
241 | | - None => *cached = Some(JsValue::undefined().unchecked_into()), |
| 243 | + Some(arr.unchecked_into()) |
242 | 244 | } |
243 | | - } |
244 | | - cached |
245 | | - .as_ref() |
246 | | - .map(|v| v.clone().unchecked_into::<Content>()) |
| 245 | + None => None, |
| 246 | + }; |
| 247 | + |
| 248 | + *cached = result.clone(); |
| 249 | + result |
247 | 250 | } |
248 | 251 |
|
249 | 252 | #[wasm_bindgen(setter)] |
250 | 253 | pub fn set_content(&self, new_content: Content) { |
251 | | - *self.cached_content.borrow_mut() = Some(new_content); |
| 254 | + // SAFETY: WASM is single-threaded |
| 255 | + unsafe { *self.cached_content.get() = Some(new_content) }; |
252 | 256 | } |
253 | 257 | } |
254 | 258 |
|
@@ -281,7 +285,7 @@ pub fn decode_node(data: Vec<u8>) -> Result<InternalBinaryNode, JsValue> { |
281 | 285 | Ok(InternalBinaryNode { |
282 | 286 | _owned_data: owned_data, |
283 | 287 | node_ref, |
284 | | - cached_attrs: RefCell::new(None), |
285 | | - cached_content: RefCell::new(None), |
| 288 | + cached_attrs: UnsafeCell::new(None), |
| 289 | + cached_content: UnsafeCell::new(None), |
286 | 290 | }) |
287 | 291 | } |
0 commit comments