1use crate::glue::CompileGlobalScriptToStencil_C;
6use crate::jsapi::JS::{
7 DestroyFrontendContext, FrontendContext as RawFrontendContext,
8 InstantiationStorage as RawInstantiationStorage, NewFrontendContext, PrepareForInstantiate,
9 ReadOnlyCompileOptions,
10};
11use crate::jsapi::{SetNativeStackQuota, ThreadStackQuotaForSize};
12use crate::rust::{transform_str_to_source_text, OwningCompileOptionsWrapper, Stencil};
13
14use std::mem::MaybeUninit;
15use std::ops::Deref;
16use std::sync::Arc;
17use std::thread::{self, JoinHandle};
18
19const STACK_SIZE: usize = 2048 * 1024 - 2 * 4096;
28
29pub struct FrontendContext(*mut RawFrontendContext);
30
31unsafe impl Send for FrontendContext {}
32
33impl FrontendContext {
34 pub fn new() -> Self {
35 Self(unsafe { NewFrontendContext() })
36 }
37
38 pub fn set_stack_quota(&self, size: usize) {
39 unsafe {
40 SetNativeStackQuota(self.0, ThreadStackQuotaForSize(size));
41 }
42 }
43}
44
45impl Deref for FrontendContext {
46 type Target = *mut RawFrontendContext;
47 fn deref(&self) -> &Self::Target {
48 &self.0
49 }
50}
51
52impl Drop for FrontendContext {
53 fn drop(&mut self) {
54 unsafe { DestroyFrontendContext(self.0) }
55 }
56}
57
58pub struct InstantiationStorage(RawInstantiationStorage);
59
60impl InstantiationStorage {
61 pub fn as_mut_ptr(&mut self) -> *mut RawInstantiationStorage {
62 &mut self.0
63 }
64}
65
66unsafe impl Send for InstantiationStorage {}
67
68impl Default for InstantiationStorage {
69 fn default() -> InstantiationStorage {
70 unsafe { std::mem::zeroed() }
71 }
72}
73
74pub struct CompilationResult {
75 pub stencil: Option<Stencil>,
76 pub storage: InstantiationStorage,
77 pub fc: FrontendContext,
78 pub compile_options: OwningCompileOptionsWrapper,
79}
80
81pub struct OffThreadToken(JoinHandle<Option<CompilationResult>>);
82
83impl OffThreadToken {
84 pub fn finish(self) -> Option<CompilationResult> {
88 self.0.join().ok().flatten()
89 }
90}
91
92pub fn compile_to_stencil_offthread<F>(
96 options: *const ReadOnlyCompileOptions,
97 source: Arc<String>,
98 callback: F,
99) -> OffThreadToken
100where
101 F: FnOnce(CompilationResult) -> Option<CompilationResult> + Send + 'static,
102{
103 let fc = FrontendContext::new();
104 let options = OwningCompileOptionsWrapper::new_for_fc(&fc, options);
105 OffThreadToken(
106 thread::Builder::new()
107 .name("OffThread Compile".to_string())
108 .stack_size(STACK_SIZE)
109 .spawn(move || {
110 fc.set_stack_quota(STACK_SIZE);
111
112 let mut source = transform_str_to_source_text(&source);
113 let mut uninit_stencil = MaybeUninit::uninit();
114
115 unsafe {
116 CompileGlobalScriptToStencil_C(
117 *fc,
118 options.read_only(),
119 &mut source,
120 uninit_stencil.as_mut_ptr(),
121 )
122 }
123
124 let raw_stencil = unsafe { uninit_stencil.assume_init() };
125 let stencil = unsafe { Stencil::from_raw(raw_stencil) };
126
127 let mut storage = InstantiationStorage::default();
128
129 let ok = !stencil.is_null()
130 && unsafe { PrepareForInstantiate(*fc, *stencil, storage.as_mut_ptr()) };
131
132 let stencil = if ok { Some(stencil) } else { None };
133
134 callback(CompilationResult {
135 stencil,
136 storage,
137 fc,
138 compile_options: options,
139 })
140 })
141 .unwrap(),
142 )
143}