1use alloc::collections::{BTreeMap, BTreeSet};
7use alloc::vec::Vec;
8use core::any::Any;
9use core::cell::RefCell;
10use std::boxed::Box;
11
12use crate::encode::{BatchableResult, BinaryDecode};
13use crate::id_allocator::{BorrowIds, HeapIds, IdSlab, InstallIdBatch};
14use crate::ipc::DecodedData;
15use crate::ipc::{EncodedData, MessageType, OutboundIPCMessage};
16use crate::lazy::ThreadLocalKey;
17use crate::object_store::ObjectHandle;
18use crate::runtime::WryIPC;
19use crate::type_cache::TypeCache;
20use crate::value::JSIDX_RESERVED;
21
22#[derive(Default)]
26pub(crate) struct OperationFreeFrame {
27 heap_ids: Vec<u64>,
29 object_handles: Vec<u32>,
31}
32
33pub struct Runtime {
38 encoder: EncodedData,
40 heap_ids: HeapIds,
42 borrow_ids: BorrowIds,
44 object_handles: IdSlab<u32>,
47 is_batching: bool,
49 type_cache: TypeCache,
51 objects: BTreeMap<u32, Box<dyn Any>>,
53 pending_object_drops: BTreeSet<u32>,
57 op_free_stack: Vec<OperationFreeFrame>,
61 ipc: WryIPC,
63 webview_id: u64,
65 thread_locals: BTreeMap<ThreadLocalKey<'static>, Box<dyn Any>>,
67 inbound_evaluate_depth: u32,
72}
73
74impl Runtime {
75 pub(crate) fn new(ipc: WryIPC, webview_id: u64) -> Self {
76 let encoder = Self::new_encoder_for_evaluate();
77 Self {
78 encoder,
79 heap_ids: HeapIds::new(),
80 borrow_ids: BorrowIds::new(),
81 object_handles: IdSlab::new(1),
82 is_batching: false,
83 type_cache: TypeCache::new(),
84 objects: BTreeMap::new(),
86 pending_object_drops: BTreeSet::new(),
87 op_free_stack: Vec::new(),
88 ipc,
89 webview_id,
90 thread_locals: BTreeMap::new(),
91 inbound_evaluate_depth: 0,
92 }
93 }
94
95 pub(crate) fn enter_inbound_evaluate(&mut self) {
97 self.inbound_evaluate_depth += 1;
98 }
99
100 pub(crate) fn leave_inbound_evaluate(&mut self) {
102 self.inbound_evaluate_depth -= 1;
103 }
104
105 fn in_inbound_evaluate(&self) -> bool {
109 self.inbound_evaluate_depth > 0
110 }
111
112 fn new_encoder_for_evaluate() -> EncodedData {
113 let mut encoder = EncodedData::new();
114 encoder.push_u8(MessageType::Evaluate as u8);
115 encoder
116 }
117
118 pub fn observe_js_heap_id(&mut self, id: u64) {
120 self.heap_ids.observe_js_heap_id(id);
121 }
122
123 pub fn get_next_placeholder_id(&mut self) -> u64 {
125 self.heap_ids.next_placeholder_id()
126 }
127
128 pub fn get_next_inbound_js_heap_id(&mut self) -> u64 {
131 self.heap_ids.next_inbound_js_heap_id()
132 }
133
134 pub fn get_next_borrow_id(&mut self) -> u64 {
138 self.borrow_ids.next_borrow_id()
139 }
140
141 pub fn push_borrow_frame(&mut self) {
144 self.borrow_ids.push_frame();
145 }
146
147 pub fn pop_borrow_frame(&mut self) {
150 self.borrow_ids.pop_frame();
151 }
152
153 pub fn release_heap_id(&mut self, id: u64) -> Option<u64> {
157 self.heap_ids.release_heap_slot(id);
158 match self.op_free_stack.last_mut() {
159 Some(frame) => {
160 frame.heap_ids.push(id);
161 None
162 }
163 None => Some(id),
164 }
165 }
166
167 pub fn recycle_heap_id(&mut self, id: u64) {
168 self.heap_ids.recycle_heap_id(id);
169 }
170
171 pub fn recycle_heap_id_if_released(&mut self, id: u64) -> bool {
172 self.heap_ids.recycle_heap_id_if_released(id)
173 }
174
175 pub fn defer_heap_id_recycle_until_flush(&mut self, id: u64) {
176 self.encoder.defer_heap_id_recycle_until_flush(id);
177 }
178
179 pub(crate) fn take_message(&mut self) -> (OutboundIPCMessage, Vec<u64>) {
182 let reserved_ids = self.take_reserved_placeholder_ids();
183 let mut encoder = self.take_encoder();
184 let heap_ids_to_recycle_after_flush = encoder.take_heap_ids_to_recycle_after_flush();
185 (
186 self.finish_rust_to_js_message(encoder, Some(&reserved_ids)),
187 heap_ids_to_recycle_after_flush,
188 )
189 }
190
191 pub(crate) fn finish_respond_message(&mut self, encoder: EncodedData) -> OutboundIPCMessage {
193 self.finish_rust_to_js_message(encoder, None)
194 }
195
196 fn finish_rust_to_js_message(
197 &mut self,
198 mut encoder: EncodedData,
199 reserved_ids: Option<&[u64]>,
200 ) -> OutboundIPCMessage {
201 let install_ids = self.take_pending_install_ids();
202 prepend_rust_to_js_prelude(&mut encoder, &install_ids, reserved_ids);
203 let pending_type_ids = encoder.take_pending_type_ids();
204 if reserved_ids.is_some() {
211 self.type_cache.push_pending_frame(pending_type_ids);
212 } else {
213 self.type_cache.ack_type_ids(&pending_type_ids);
214 }
215 let top_level = reserved_ids.is_some() && !self.in_inbound_evaluate();
218 OutboundIPCMessage::new(crate::ipc::IPCMessage::new(encoder.to_bytes()), top_level)
219 }
220
221 pub(crate) fn is_empty(&self) -> bool {
222 self.encoder.byte_len() <= 17
224 }
225
226 pub(crate) fn push_operation_frame(&mut self) {
227 self.op_free_stack.push(OperationFreeFrame::default());
228 }
229
230 pub(crate) fn release_object_handle(&mut self, handle: ObjectHandle) -> Option<Box<dyn Any>> {
231 match self.op_free_stack.last_mut() {
232 Some(frame) => {
233 frame.object_handles.push(handle.raw());
234 None
235 }
236 None => self.remove_object_untyped(handle.raw()),
237 }
238 }
239
240 pub(crate) fn pop_operation_frame(&mut self) -> OperationFreeFrame {
245 let frame = self
246 .op_free_stack
247 .pop()
248 .expect("pop_operation_frame called with empty frame stack");
249
250 if let Some(parent) = self.op_free_stack.last_mut() {
251 parent.heap_ids.extend(frame.heap_ids);
252 parent.object_handles.extend(frame.object_handles);
253 OperationFreeFrame::default()
254 } else {
255 frame
256 }
257 }
258
259 pub(crate) fn set_batching(&mut self, batching: bool) {
260 self.is_batching = batching;
261 }
262
263 pub(crate) fn is_batching(&self) -> bool {
264 self.is_batching
265 }
266
267 pub(crate) fn take_pending_install_ids(&mut self) -> InstallIdBatch {
269 self.heap_ids.take_pending_install_ids()
270 }
271
272 pub(crate) fn take_reserved_placeholder_ids(&mut self) -> Vec<u64> {
274 self.heap_ids.take_reserved_placeholder_ids()
275 }
276
277 pub(crate) fn take_encoder(&mut self) -> EncodedData {
278 let next = Self::new_encoder_for_evaluate();
279 core::mem::replace(&mut self.encoder, next)
280 }
281
282 pub(crate) fn extend_encoder(&mut self, other: &EncodedData) {
283 self.encoder.u8_buf.extend_from_slice(&other.u8_buf[1..]);
286 self.encoder.u32_buf.extend_from_slice(&other.u32_buf);
287 self.encoder.u16_buf.extend_from_slice(&other.u16_buf);
288 self.encoder.str_buf.extend_from_slice(&other.str_buf);
289 self.encoder
290 .heap_ids_to_recycle_after_flush
291 .extend_from_slice(&other.heap_ids_to_recycle_after_flush);
292 self.encoder
293 .pending_type_ids
294 .extend_from_slice(&other.pending_type_ids);
295 self.encoder.needs_flush |= other.needs_flush;
296 }
297
298 pub(crate) fn get_or_create_type_id(&mut self, type_bytes: &[u8]) -> (u32, bool) {
301 self.type_cache.get_or_create_type_id(type_bytes)
302 }
303
304 pub(crate) fn pop_and_ack_type_cache_frame(&mut self) {
307 self.type_cache.pop_and_ack_pending_frame();
308 }
309
310 pub(crate) fn insert_object<T: 'static>(&mut self, obj: T) -> u32 {
312 let handle = self.object_handles.alloc();
313 self.objects.insert(handle, Box::new(obj));
314 handle
315 }
316
317 pub(crate) fn take_thread_local<T: 'static>(&mut self, key: ThreadLocalKey<'static>) -> T {
319 *self
320 .thread_locals
321 .remove(&key)
322 .expect("thread local not found")
323 .downcast::<T>()
324 .expect("type mismatch")
325 }
326
327 pub(crate) fn insert_thread_local<T: 'static>(
329 &mut self,
330 key: ThreadLocalKey<'static>,
331 value: T,
332 ) {
333 self.thread_locals.insert(key, Box::new(value));
334 }
335
336 pub(crate) fn has_thread_local(&self, key: ThreadLocalKey<'static>) -> bool {
338 self.thread_locals.contains_key(&key)
339 }
340
341 pub(crate) fn get_object<T: 'static>(&self, handle: u32) -> &T {
342 let boxed = self.objects.get(&handle).expect("invalid handle");
343 boxed.downcast_ref::<T>().expect("type mismatch")
344 }
345
346 pub(crate) fn take_object<T: 'static>(&mut self, handle: u32) -> T {
347 let boxed = self.objects.remove(&handle).expect("invalid handle");
348 *boxed.downcast::<T>().expect("type mismatch")
349 }
350
351 pub(crate) fn reinsert_object<T: 'static>(&mut self, handle: u32, obj: T) {
352 if self.pending_object_drops.remove(&handle) {
356 self.object_handles.free(handle);
357 drop(obj);
358 return;
359 }
360 assert!(
361 self.objects.insert(handle, Box::new(obj)).is_none(),
362 "object handle {handle} was reinserted while occupied"
363 );
364 }
365
366 pub(crate) fn remove_object<T: 'static>(&mut self, handle: u32) -> T {
368 let boxed = self.objects.remove(&handle).expect("invalid handle");
369 self.object_handles.free(handle);
370 *boxed.downcast::<T>().expect("type mismatch")
371 }
372
373 pub(crate) fn remove_object_untyped(&mut self, handle: u32) -> Option<Box<dyn Any>> {
374 let object = self.objects.remove(&handle);
375 if object.is_some() {
376 self.object_handles.free(handle);
377 } else if self.object_handles.contains(handle) {
378 self.pending_object_drops.insert(handle);
384 }
385 object
386 }
387
388 pub(crate) fn ipc(&self) -> &WryIPC {
390 &self.ipc
391 }
392
393 pub(crate) fn webview_id(&self) -> u64 {
395 self.webview_id
396 }
397}
398
399fn push_id_list(buf: &mut Vec<u32>, ids: &[u64]) {
400 buf.push(ids.len() as u32);
401 for &id in ids {
402 buf.push((id & 0xFFFF_FFFF) as u32);
403 buf.push((id >> 32) as u32);
404 }
405}
406
407fn prepend_rust_to_js_prelude(
408 encoder: &mut EncodedData,
409 install_ids: &[u64],
410 reserved_ids: Option<&[u64]>,
411) {
412 let mut prelude = Vec::new();
413 push_id_list(&mut prelude, install_ids);
416 if let Some(reserved_ids) = reserved_ids {
417 push_id_list(&mut prelude, reserved_ids);
418 }
419 encoder.insert_u32s(0, &prelude);
422}
423
424thread_local! {
425 pub(crate) static RUNTIME: RefCell<Vec<Runtime>> = const { RefCell::new(Vec::new()) };
427}
428
429fn push_runtime(runtime: Runtime) {
430 RUNTIME.with(|state| {
431 state.borrow_mut().push(runtime);
432 });
433}
434
435fn pop_runtime() -> Runtime {
436 RUNTIME.with(|state| {
437 state
438 .borrow_mut()
439 .pop()
440 .expect("No runtime available to pop")
441 })
442}
443
444pub(crate) fn in_runtime<O>(runtime: Runtime, run: impl FnOnce() -> O) -> (Runtime, O) {
445 push_runtime(runtime);
446 let out = run();
447 let runtime = pop_runtime();
448 (runtime, out)
449}
450
451pub(crate) fn with_runtime<R>(f: impl FnOnce(&mut Runtime) -> R) -> R {
452 RUNTIME.with(|state| {
453 let mut state = state.borrow_mut();
454 f(state.last_mut().expect("No runtime available"))
455 })
456}
457
458pub fn is_batching() -> bool {
460 with_runtime(|state| state.is_batching())
461}
462
463fn runtime_already_dropped() -> bool {
466 match RUNTIME.try_with(|state| {
467 state
468 .try_borrow()
469 .map(|runtime_stack| runtime_stack.is_empty())
470 }) {
471 Ok(Ok(value)) => value,
472 Ok(Err(_)) | Err(_) => true,
473 }
474}
475
476pub(crate) fn queue_js_drop(id: u64) {
479 debug_assert!(
480 id >= JSIDX_RESERVED,
481 "Attempted to drop reserved JS heap ID {id}"
482 );
483
484 if runtime_already_dropped() {
485 return;
486 }
487
488 let id = match RUNTIME.try_with(|state| {
489 state.try_borrow_mut().ok().and_then(|mut runtime_stack| {
490 runtime_stack
491 .last_mut()
492 .map(|runtime| runtime.release_heap_id(id))
493 })
494 }) {
495 Ok(Some(id)) => id,
496 Ok(None) | Err(_) => return,
497 };
498 if let Some(id) = id {
499 crate::js_helpers::js_drop_heap_ref(id);
500 recycle_heap_id_after_js_drop(id);
501 }
502}
503
504pub(crate) fn queue_js_dispose_rust_function(id: u64) {
508 debug_assert!(
509 id >= JSIDX_RESERVED,
510 "Attempted to dispose reserved JS heap ID {id}"
511 );
512
513 if runtime_already_dropped() {
514 return;
515 }
516
517 crate::js_helpers::js_dispose_rust_function(id);
518}
519
520fn recycle_heap_id_after_js_drop(id: u64) {
521 let _ = RUNTIME.try_with(|state| {
522 let Ok(mut runtime_stack) = state.try_borrow_mut() else {
523 return;
524 };
525 let Some(runtime) = runtime_stack.last_mut() else {
526 return;
527 };
528
529 if runtime.is_batching() {
530 runtime.defer_heap_id_recycle_until_flush(id);
531 } else {
532 runtime.recycle_heap_id(id);
533 }
534 });
535}
536
537pub(crate) fn queue_rust_object_drop(handle: ObjectHandle) {
540 let object = RUNTIME
541 .try_with(|state| {
542 state.try_borrow_mut().ok().and_then(|mut runtime_stack| {
543 runtime_stack
544 .last_mut()
545 .and_then(|runtime| runtime.release_object_handle(handle))
546 })
547 })
548 .unwrap_or_default();
549 drop(object);
550}
551
552pub(crate) fn add_operation(
554 encoder: &mut EncodedData,
555 fn_id: u32,
556 add_args: impl FnOnce(&mut EncodedData),
557) {
558 encoder.push_u32(fn_id);
559 add_args(encoder);
560}
561
562pub(crate) fn run_js_sync<R: BatchableResult>(
569 fn_id: u32,
570 add_args: impl FnOnce(&mut EncodedData),
571) -> R {
572 let mut batch = with_runtime(|state| {
577 state.push_operation_frame();
579 state.take_encoder()
580 });
581 add_operation(&mut batch, fn_id, add_args);
582
583 let needs_flush = batch.needs_flush;
585
586 with_runtime(|state| {
587 let encoded_during_op = core::mem::replace(&mut state.encoder, batch);
588 state.extend_encoder(&encoded_during_op);
589 });
590
591 let mut placeholder = with_runtime(|state| R::try_placeholder(state));
593
594 let result = if !is_batching() || needs_flush {
597 flush_and_then(move |mut data| {
598 let response = placeholder
599 .take()
600 .unwrap_or_else(|| R::decode(&mut data).expect("Failed to decode return value"));
601 assert!(
602 data.is_empty(),
603 "Extra data remaining after decoding response"
604 );
605 response
606 })
607 } else {
608 placeholder.unwrap_or_else(|| flush_and_return::<R>())
609 };
610
611 let frame = with_runtime(|state| state.pop_operation_frame());
613 for id in frame.heap_ids {
614 crate::js_helpers::js_drop_heap_ref(id);
615 recycle_heap_id_after_js_drop(id);
616 }
617 for handle in frame.object_handles {
618 let object = with_runtime(|state| state.remove_object_untyped(handle));
619 drop(object);
620 }
621
622 result
623}
624
625pub(crate) fn flush_and_return<R: BinaryDecode>() -> R {
627 flush_and_then(|mut data| {
628 let response = R::decode(&mut data).expect("Failed to decode return value");
629 assert!(
630 data.is_empty(),
631 "Extra data remaining after decoding response"
632 );
633 response
634 })
635}
636
637pub(crate) fn flush_and_then<R>(mut then: impl for<'a> FnMut(DecodedData<'a>) -> R) -> R {
638 use crate::runtime::WryBindgenEvent;
639
640 let (batch_msg, heap_ids_to_recycle_after_flush) = with_runtime(|state| state.take_message());
641
642 with_runtime(|runtime| {
645 (runtime.ipc().proxy)(WryBindgenEvent::ipc(runtime.webview_id(), batch_msg))
646 });
647 let mut heap_ids_to_recycle_after_flush = Some(heap_ids_to_recycle_after_flush);
648 loop {
649 if let Some(result) = crate::runtime::progress_js_with(&mut then) {
650 recycle_heap_ids_after_flush(
651 heap_ids_to_recycle_after_flush
652 .take()
653 .expect("heap IDs should only be recycled once per flush"),
654 );
655 return result;
656 }
657 }
658}
659
660fn recycle_heap_ids_after_flush(ids: Vec<u64>) {
661 for id in ids {
662 with_runtime(|state| {
663 state.recycle_heap_id_if_released(id);
664 });
665 }
666}
667
668pub fn batch<R, F: FnOnce() -> R>(f: F) -> R {
672 let currently_batching = is_batching();
673 with_runtime(|state| state.set_batching(true));
675
676 let result = f();
678
679 if !currently_batching {
680 force_flush();
682 }
683
684 with_runtime(|state| state.set_batching(currently_batching));
686
687 result
688}
689
690pub fn batch_async<'a, R, F: core::future::Future<Output = R> + 'a>(
692 f: F,
693) -> impl core::future::Future<Output = R> + 'a {
694 let mut f = Box::pin(f);
695 std::future::poll_fn(move |ctx| batch(|| f.as_mut().poll(ctx)))
696}
697
698pub fn force_flush() {
699 let has_pending = with_runtime(|state| !state.is_empty());
700 if has_pending {
701 flush_and_return::<()>();
702 }
703}
704
705#[cfg(test)]
706mod take_encoder_tests {
707 use std::sync::Arc;
708
709 use super::*;
710 use crate::ipc::IPCMessage;
711 use crate::runtime::WryIPC;
712
713 fn test_runtime() -> Runtime {
714 let (ipc, _senders) = WryIPC::new(Arc::new(|_| {}));
715 Runtime::new(ipc, 0)
716 }
717
718 #[test]
719 fn take_encoder_yields_an_evaluate_message_with_no_request_id() {
720 let mut runtime = test_runtime();
721 assert!(runtime.is_empty());
722
723 let first = runtime.take_encoder();
724 let bytes = IPCMessage::new(first.to_bytes());
725 assert_eq!(bytes.ty().unwrap(), MessageType::Evaluate);
726 assert!(first.u32_buf.is_empty());
729 }
730
731 #[test]
732 fn object_drop_during_checkout_is_deferred_then_honored() {
733 use std::cell::Cell;
734 use std::rc::Rc;
735
736 struct DropFlag(Rc<Cell<bool>>);
737 impl Drop for DropFlag {
738 fn drop(&mut self) {
739 self.0.set(true);
740 }
741 }
742
743 let mut runtime = test_runtime();
744 let dropped = Rc::new(Cell::new(false));
745 let handle = runtime.insert_object(DropFlag(dropped.clone()));
746
747 let checked_out = runtime.take_object::<DropFlag>(handle);
750
751 assert!(runtime.remove_object_untyped(handle).is_none());
754 assert!(
755 !dropped.get(),
756 "object must not be dropped while it is checked out"
757 );
758
759 runtime.reinsert_object(handle, checked_out);
762 assert!(
763 dropped.get(),
764 "deferred drop must run once the checkout completes"
765 );
766
767 let reused = runtime.insert_object(DropFlag(Rc::new(Cell::new(false))));
769 assert_eq!(reused, handle);
770 }
771}