1mod buffered_random;
2mod builtins;
3mod error;
4mod event_loop;
5mod isolate_state;
6mod js_loading;
7mod module;
8mod pool;
9mod script;
10mod sourcemap;
11
12pub(crate) use isolate_state::IsolateState;
13
14pub fn init(v8_flags: Option<Vec<String>>) {
15 static ICU_INIT: std::sync::Once = std::sync::Once::new();
18 ICU_INIT.call_once(|| {
19 let icu_data =
20 align_data::include_aligned!(align_data::Align16, "../third_party/icu/icudtl.dat");
21 let _ = v8::icu::set_common_data_74(icu_data);
23 v8::icu::set_default_locale("en_US");
25 });
26
27 let mut flags = v8_flags.unwrap_or_default();
28
29 let perf_flags = [
32 "--turbofan", "--opt", ];
35
36 for flag in &perf_flags {
37 if !flags
38 .iter()
39 .any(|f| f.starts_with(flag) || f.starts_with(&format!("--no-{}", &flag[2..])))
40 {
41 flags.push(flag.to_string());
42 }
43 }
44
45 flags.push("jstime".to_owned());
46 flags.rotate_right(1);
47
48 v8::V8::set_flags_from_command_line(flags);
49
50 static V8_INIT: std::sync::Once = std::sync::Once::new();
51 V8_INIT.call_once(|| {
52 let platform = v8::new_default_platform(0, false).make_shared();
53 v8::V8::initialize_platform(platform);
54 v8::V8::initialize();
55 });
56}
57
58#[derive(Default)]
60pub struct Options {
61 pub snapshot: Option<&'static [u8]>,
62 taking_snapshot: bool,
63 pub process_argv: Vec<String>,
64 pub warmup_iterations: usize,
68}
69
70impl Options {
71 pub fn new(snapshot: Option<&'static [u8]>) -> Options {
72 Options {
73 snapshot,
74 taking_snapshot: false,
75 process_argv: Vec::new(),
76 warmup_iterations: 0,
77 }
78 }
79
80 pub fn with_process_argv(mut self, argv: Vec<String>) -> Self {
81 self.process_argv = argv;
82 self
83 }
84
85 pub fn with_warmup(mut self, iterations: usize) -> Self {
86 self.warmup_iterations = iterations;
87 self
88 }
89}
90
91#[allow(clippy::all)]
93pub struct JSTime {
94 isolate: Option<v8::OwnedIsolate>,
95 taking_snapshot: bool,
96 warmup_iterations: usize,
97}
98
99impl JSTime {
100 pub fn new(options: Options) -> JSTime {
102 let mut create_params = v8::Isolate::create_params()
103 .external_references(builtins::get_external_references().into_vec().into())
104 .heap_limits(0, 1024 * 1024 * 1024); if let Some(snapshot) = options.snapshot {
106 create_params = create_params.snapshot_blob(snapshot.into());
107 }
108 let isolate = v8::Isolate::new(create_params);
109 JSTime::create(options, isolate)
110 }
111
112 pub fn create_snapshot(mut options: Options) -> Vec<u8> {
113 assert!(
114 options.snapshot.is_none(),
115 "Cannot pass snapshot data while creating snapshot"
116 );
117 options.taking_snapshot = true;
118
119 let external_refs = builtins::get_external_references();
120 let external_refs_cow: std::borrow::Cow<'static, [v8::ExternalReference]> =
121 std::borrow::Cow::Owned(external_refs.into_vec());
122 let mut isolate = v8::Isolate::snapshot_creator(Some(external_refs_cow), None);
123
124 isolate.set_host_initialize_import_meta_object_callback(
126 module::host_initialize_import_meta_object_callback,
127 );
128
129 isolate.set_host_import_module_dynamically_callback(
131 module::host_import_module_dynamically_callback,
132 );
133
134 let global_context = {
135 v8::scope!(let scope, &mut isolate);
136 let context = v8::Context::new(scope, Default::default());
137 let isolate_ref: &v8::Isolate = scope;
138 v8::Global::new(isolate_ref, context)
139 };
140
141 isolate.set_slot(IsolateState::new(global_context, options.process_argv));
142
143 {
145 let context = IsolateState::get(&mut isolate).borrow().context();
146 v8::scope!(let scope, &mut isolate);
147 let context_local = v8::Local::new(scope, context);
148 let scope = &mut v8::ContextScope::new(scope, context_local);
149
150 builtins::Builtins::create(scope);
152
153 scope.set_default_context(context_local);
155 }
156
157 IsolateState::get(&mut isolate).borrow_mut().drop_context();
159
160 match isolate.create_blob(v8::FunctionCodeHandling::Keep) {
161 Some(data) => data.to_vec(),
162 None => {
163 panic!("Unable to create snapshot");
164 }
165 }
166 }
167
168 fn create(options: Options, mut isolate: v8::OwnedIsolate) -> JSTime {
169 isolate.set_host_initialize_import_meta_object_callback(
171 module::host_initialize_import_meta_object_callback,
172 );
173
174 isolate.set_host_import_module_dynamically_callback(
176 module::host_import_module_dynamically_callback,
177 );
178
179 let global_context = {
180 v8::scope!(let scope, &mut isolate);
181 let context = v8::Context::new(scope, Default::default());
182 let isolate_ref: &v8::Isolate = scope;
183 v8::Global::new(isolate_ref, context)
184 };
185
186 isolate.set_slot(IsolateState::new(global_context, options.process_argv));
187
188 if options.snapshot.is_none() {
190 let context = IsolateState::get(&mut isolate).borrow().context();
191 v8::scope!(let scope, &mut isolate);
192 let context_local = v8::Local::new(scope, context);
193 let mut scope = v8::ContextScope::new(scope, context_local);
194 builtins::Builtins::create(&mut scope);
195 }
196
197 JSTime {
198 isolate: Some(isolate),
199 taking_snapshot: options.taking_snapshot,
200 warmup_iterations: options.warmup_iterations,
201 }
202 }
203
204 fn isolate(&mut self) -> &mut v8::Isolate {
205 match self.isolate.as_mut() {
206 Some(i) => i,
207 None => unsafe {
208 std::hint::unreachable_unchecked();
209 },
210 }
211 }
212
213 pub fn import(&mut self, filename: &str) -> Result<(), String> {
215 if self.warmup_iterations > 0 {
217 self.warmup_import(filename)?;
218 }
219
220 let result = {
221 let context = IsolateState::get(self.isolate()).borrow().context();
222 v8::scope!(let scope, self.isolate());
223 let context_local = v8::Local::new(scope, context);
224 let mut scope = v8::ContextScope::new(scope, context_local);
225
226 v8::tc_scope!(let tc, &mut scope);
228 let loader = module::Loader::new();
229
230 let mut cwd = std::env::current_dir().unwrap();
231 cwd.push("jstime");
232 let cwd = cwd.into_os_string().into_string().unwrap();
233 match loader.import(tc, &cwd, filename) {
234 Ok(_) => Ok(()),
235 Err(exception) => {
236 if tc.has_caught() {
238 Err(crate::error::format_exception(tc))
239 } else {
240 Err(crate::error::format_exception_value(tc, exception))
242 }
243 }
244 }
245 };
246
247 self.run_event_loop();
249
250 result
251 }
252
253 fn warmup_import(&mut self, filename: &str) -> Result<(), String> {
256 for _ in 0..self.warmup_iterations {
257 let context = IsolateState::get(self.isolate()).borrow().context();
258 v8::scope!(let scope, self.isolate());
259 let context_local = v8::Local::new(scope, context);
260 let mut scope = v8::ContextScope::new(scope, context_local);
261 v8::tc_scope!(let tc, &mut scope);
262 let loader = module::Loader::new();
263 let mut cwd = std::env::current_dir().unwrap();
264 cwd.push("jstime");
265 let cwd = cwd.into_os_string().into_string().unwrap();
266 match loader.import(tc, &cwd, filename) {
268 Ok(_) => {}
269 Err(exception) => {
270 if tc.has_caught() {
271 return Err(crate::error::format_exception(tc));
272 } else {
273 return Err(crate::error::format_exception_value(tc, exception));
274 }
275 }
276 }
277 }
278 Ok(())
279 }
280
281 pub fn run_script(&mut self, source: &str, filename: &str) -> Result<String, String> {
285 if self.warmup_iterations > 0 {
287 self.warmup_script(source, filename)?;
288 }
289
290 let result = self.run_script_no_event_loop(source, filename);
291
292 self.run_event_loop();
294
295 result
296 }
297
298 fn warmup_script(&mut self, source: &str, filename: &str) -> Result<(), String> {
301 for _ in 0..self.warmup_iterations {
302 let context = IsolateState::get(self.isolate()).borrow().context();
303 v8::scope!(let scope, self.isolate());
304 let context_local = v8::Local::new(scope, context);
305 let mut scope = v8::ContextScope::new(scope, context_local);
306 script::run(&mut scope, source, filename)?;
308 }
309 Ok(())
310 }
311
312 pub fn run_script_no_event_loop(
315 &mut self,
316 source: &str,
317 filename: &str,
318 ) -> Result<String, String> {
319 let context = IsolateState::get(self.isolate()).borrow().context();
320 v8::scope!(let scope, self.isolate());
321 let context_local = v8::Local::new(scope, context);
322 let mut scope = v8::ContextScope::new(scope, context_local);
323 match script::run(&mut scope, source, filename) {
324 Ok(v) => {
325 let isolate: &v8::Isolate = &scope;
326 Ok(v.to_string(&scope).unwrap().to_rust_string_lossy(isolate))
327 }
328 Err(e) => Err(e),
329 }
330 }
331
332 pub fn tick_event_loop(&mut self) {
335 let context = IsolateState::get(self.isolate()).borrow().context();
336 v8::scope!(let scope, self.isolate());
337 let context_local = v8::Local::new(scope, context);
338 let mut scope = v8::ContextScope::new(scope, context_local);
339 let event_loop = event_loop::get_event_loop(&mut scope);
340 event_loop.borrow_mut().tick(&mut scope);
341 }
342
343 fn run_event_loop(&mut self) {
345 let context = IsolateState::get(self.isolate()).borrow().context();
346 v8::scope!(let scope, self.isolate());
347 let context_local = v8::Local::new(scope, context);
348 let mut scope = v8::ContextScope::new(scope, context_local);
349 let event_loop = event_loop::get_event_loop(&mut scope);
350 event_loop.borrow_mut().run(&mut scope);
351 }
352}
353
354impl Drop for JSTime {
355 fn drop(&mut self) {
356 if self.taking_snapshot {
357 std::mem::forget(self.isolate.take().unwrap())
360 }
361 }
362}