Skip to main content

jstime_core/
isolate_state.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4pub(crate) struct FetchRequest {
5    pub(crate) url: String,
6    pub(crate) method: String,
7    pub(crate) headers: Vec<(String, String)>,
8    pub(crate) body: Option<String>,
9    pub(crate) resolver: v8::Global<v8::PromiseResolver>,
10}
11
12/// Represents an active dgram socket that should be polled for incoming messages
13#[allow(dead_code)]
14pub(crate) struct ActiveDgramSocket {
15    pub(crate) socket_id: u64,
16    pub(crate) socket_ptr: *mut std::net::UdpSocket,
17    pub(crate) callback: v8::Global<v8::Function>,
18    pub(crate) is_ref: bool, // If true, keeps the event loop alive
19}
20
21/// Stores response body data for streaming
22#[allow(dead_code)]
23pub(crate) struct StreamingFetch {
24    pub(crate) stream_id: u64,
25    pub(crate) body_data: Vec<u8>,
26    pub(crate) offset: usize,
27}
28
29/// Cache for frequently used V8 strings to avoid repeated UTF-8 ↔ V8 conversions.
30/// This addresses a key performance bottleneck by caching commonly used property names
31/// and string literals throughout the runtime.
32///
33/// Note: Some fields are not yet used but are included for comprehensive caching
34/// opportunities across all builtins. The unused fields are intentionally kept
35/// to support future optimizations.
36#[allow(dead_code)]
37pub(crate) struct StringCache {
38    // Fetch-related strings
39    pub(crate) status: Option<v8::Global<v8::String>>,
40    pub(crate) status_text: Option<v8::Global<v8::String>>,
41    pub(crate) headers: Option<v8::Global<v8::String>>,
42
43    // Common property names
44    pub(crate) name: Option<v8::Global<v8::String>>,
45    pub(crate) type_: Option<v8::Global<v8::String>>,
46    pub(crate) value: Option<v8::Global<v8::String>>,
47    pub(crate) length: Option<v8::Global<v8::String>>,
48    pub(crate) done: Option<v8::Global<v8::String>>,
49    pub(crate) message: Option<v8::Global<v8::String>>,
50    pub(crate) stack: Option<v8::Global<v8::String>>,
51
52    // Crypto-related strings
53    pub(crate) algorithm: Option<v8::Global<v8::String>>,
54    pub(crate) hash: Option<v8::Global<v8::String>>,
55    pub(crate) key_data: Option<v8::Global<v8::String>>,
56    pub(crate) extractable: Option<v8::Global<v8::String>>,
57    pub(crate) usages: Option<v8::Global<v8::String>>,
58    pub(crate) secret: Option<v8::Global<v8::String>>,
59    pub(crate) iv: Option<v8::Global<v8::String>>,
60    pub(crate) additional_data: Option<v8::Global<v8::String>>,
61
62    // Event-related strings
63    pub(crate) listeners: Option<v8::Global<v8::String>>,
64    pub(crate) stop_propagation: Option<v8::Global<v8::String>>,
65    pub(crate) stop_immediate_propagation: Option<v8::Global<v8::String>>,
66    pub(crate) default_prevented: Option<v8::Global<v8::String>>,
67    pub(crate) current_target: Option<v8::Global<v8::String>>,
68    pub(crate) target: Option<v8::Global<v8::String>>,
69    pub(crate) cancelable: Option<v8::Global<v8::String>>,
70
71    // File system strings
72    pub(crate) is_file: Option<v8::Global<v8::String>>,
73    pub(crate) is_directory: Option<v8::Global<v8::String>>,
74    pub(crate) is_symbolic_link: Option<v8::Global<v8::String>>,
75    pub(crate) size: Option<v8::Global<v8::String>>,
76    pub(crate) mtime_ms: Option<v8::Global<v8::String>>,
77    pub(crate) recursive: Option<v8::Global<v8::String>>,
78
79    // Module/import strings
80    pub(crate) url: Option<v8::Global<v8::String>>,
81
82    // Process-related strings
83    pub(crate) encoding: Option<v8::Global<v8::String>>,
84    pub(crate) path: Option<v8::Global<v8::String>>,
85    pub(crate) data: Option<v8::Global<v8::String>>,
86}
87
88impl StringCache {
89    pub(crate) fn new() -> Self {
90        Self {
91            // Fetch-related
92            status: None,
93            status_text: None,
94            headers: None,
95
96            // Common property names
97            name: None,
98            type_: None,
99            value: None,
100            length: None,
101            done: None,
102            message: None,
103            stack: None,
104
105            // Crypto-related
106            algorithm: None,
107            hash: None,
108            key_data: None,
109            extractable: None,
110            usages: None,
111            secret: None,
112            iv: None,
113            additional_data: None,
114
115            // Event-related
116            listeners: None,
117            stop_propagation: None,
118            stop_immediate_propagation: None,
119            default_prevented: None,
120            current_target: None,
121            target: None,
122            cancelable: None,
123
124            // File system
125            is_file: None,
126            is_directory: None,
127            is_symbolic_link: None,
128            size: None,
129            mtime_ms: None,
130            recursive: None,
131
132            // Module/import
133            url: None,
134
135            // Process-related
136            encoding: None,
137            path: None,
138            data: None,
139        }
140    }
141}
142
143/// Macro to get or create a cached string.
144/// This simplifies the pattern of checking for cached strings and creating them if needed.
145///
146/// Usage: get_or_create_cached_string!(scope, cache, field_name, "literal")
147#[macro_export]
148macro_rules! get_or_create_cached_string {
149    ($scope:expr, $cache:expr, $field:ident, $literal:expr) => {{
150        if let Some(ref cached) = $cache.$field {
151            v8::Local::new($scope, cached)
152        } else {
153            let key = v8::String::new($scope, $literal).unwrap();
154            let isolate: &v8::Isolate = $scope;
155            $cache.$field = Some(v8::Global::new(isolate, key));
156            key
157        }
158    }};
159}
160
161pub(crate) struct IsolateState {
162    pub(crate) context: Option<v8::Global<v8::Context>>,
163    pub(crate) module_map: crate::module::ModuleMap,
164    pub(crate) event_loop: Rc<RefCell<crate::event_loop::EventLoop>>,
165    pub(crate) timers_to_clear: Rc<RefCell<Vec<crate::event_loop::TimerId>>>,
166    pub(crate) timers_to_add: Rc<RefCell<Vec<crate::event_loop::PendingTimer>>>,
167    pub(crate) next_timer_id: Rc<RefCell<u64>>,
168    pub(crate) pending_fetches: Rc<RefCell<Vec<FetchRequest>>>,
169    pub(crate) string_cache: Rc<RefCell<StringCache>>,
170    pub(crate) http_agent: ureq::Agent,
171    pub(crate) process_argv: Vec<String>,
172    pub(crate) next_stream_id: Rc<RefCell<u64>>,
173    pub(crate) streaming_fetches: Rc<RefCell<rustc_hash::FxHashMap<u64, StreamingFetch>>>,
174    // Object pool for frequently allocated header vectors
175    pub(crate) header_vec_pool: Rc<crate::pool::Pool<Vec<(String, String)>>>,
176    // Buffered random number generator for crypto operations
177    pub(crate) buffered_random: RefCell<crate::buffered_random::BufferedRandom>,
178    // Active dgram sockets that should be polled for incoming messages
179    pub(crate) active_dgram_sockets: Rc<RefCell<rustc_hash::FxHashMap<u64, ActiveDgramSocket>>>,
180    // Next dgram socket ID
181    pub(crate) next_dgram_socket_id: Rc<RefCell<u64>>,
182}
183
184impl IsolateState {
185    pub(crate) fn new(
186        context: v8::Global<v8::Context>,
187        process_argv: Vec<String>,
188    ) -> Rc<RefCell<IsolateState>> {
189        let timers_to_clear = Rc::new(RefCell::new(Vec::new()));
190        let timers_to_add = Rc::new(RefCell::new(Vec::new()));
191        let next_timer_id = Rc::new(RefCell::new(1u64));
192        let pending_fetches = Rc::new(RefCell::new(Vec::new()));
193        let string_cache = Rc::new(RefCell::new(StringCache::new()));
194        let next_stream_id = Rc::new(RefCell::new(1u64));
195        let streaming_fetches = Rc::new(RefCell::new(rustc_hash::FxHashMap::default()));
196        let active_dgram_sockets = Rc::new(RefCell::new(rustc_hash::FxHashMap::default()));
197        let next_dgram_socket_id = Rc::new(RefCell::new(1u64));
198
199        // Create object pool for header vectors with reasonable capacity limit
200        let header_vec_pool = Rc::new(crate::pool::Pool::new(200));
201
202        // Create an HTTP agent for connection pooling
203        // Configure to not treat HTTP status codes as errors (fetch API expects this)
204        let config = ureq::config::Config::builder()
205            .http_status_as_error(false)
206            .build();
207        let http_agent = ureq::Agent::new_with_config(config);
208
209        Rc::new(RefCell::new(IsolateState {
210            context: Some(context),
211            module_map: crate::module::ModuleMap::new(),
212            event_loop: Rc::new(RefCell::new(crate::event_loop::EventLoop::new(
213                timers_to_clear.clone(),
214                timers_to_add.clone(),
215                next_timer_id.clone(),
216                pending_fetches.clone(),
217                active_dgram_sockets.clone(),
218            ))),
219            timers_to_clear,
220            timers_to_add,
221            next_timer_id,
222            pending_fetches,
223            string_cache,
224            http_agent,
225            process_argv,
226            next_stream_id,
227            streaming_fetches,
228            header_vec_pool,
229            buffered_random: RefCell::new(crate::buffered_random::BufferedRandom::new()),
230            active_dgram_sockets,
231            next_dgram_socket_id,
232        }))
233    }
234
235    pub(crate) fn get(scope: &mut v8::Isolate) -> Rc<RefCell<Self>> {
236        scope
237            .get_slot::<Rc<RefCell<IsolateState>>>()
238            .unwrap()
239            .clone()
240    }
241
242    pub(crate) fn context(&self) -> v8::Global<v8::Context> {
243        match &self.context {
244            Some(c) => c.clone(),
245            None => unsafe { std::hint::unreachable_unchecked() },
246        }
247    }
248
249    pub(crate) fn drop_context(&mut self) {
250        self.context.take();
251    }
252}