rquickjs_core/loader/
compile.rs

1use crate::{
2    loader::{util::resolve_simple, Loader, Resolver},
3    Ctx, Lock, Module, Mut, Ref, Result, WriteOptions,
4};
5use alloc::{string::String, vec::Vec};
6use core::{
7    iter::FusedIterator,
8    ops::{Deref, DerefMut},
9};
10#[cfg(not(feature = "std"))]
11use hashbrown::{hash_map::Iter as HashMapIter, HashMap};
12#[cfg(feature = "std")]
13use std::collections::{hash_map::Iter as HashMapIter, HashMap};
14
15/// Modules compiling data
16#[derive(Default, Clone)]
17pub struct Compile<T = ()> {
18    data: Ref<Mut<CompileData>>,
19    inner: T,
20}
21
22impl<T> Deref for Compile<T> {
23    type Target = T;
24
25    fn deref(&self) -> &Self::Target {
26        &self.inner
27    }
28}
29
30impl<T> DerefMut for Compile<T> {
31    fn deref_mut(&mut self) -> &mut Self::Target {
32        &mut self.inner
33    }
34}
35
36impl Compile {
37    /// Create new compiling scope
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Create compiling resolver by wrapping other resolver
43    pub fn resolver<R: Resolver>(&self, resolver: R) -> Compile<R> {
44        Compile {
45            data: self.data.clone(),
46            inner: resolver,
47        }
48    }
49
50    /// Create compiling loader by wrapping other script loader
51    pub fn loader<L: Loader>(&self, loader: L) -> Compile<L> {
52        Compile {
53            data: self.data.clone(),
54            inner: loader,
55        }
56    }
57
58    /// Get resolved modules with paths
59    ///
60    /// You can use [`IntoIterator::into_iter()`] to get an iterator over tuples which includes module _name_ (`&str`) and _path_ (`&str`).
61    pub fn modules(&self) -> ResolvedModules {
62        ResolvedModules(self.data.lock())
63    }
64
65    /// Get loaded modules with bytecodes
66    ///
67    /// You can use [`IntoIterator::into_iter()`] to get an iterator over tuples which includes module _path_ (`&str`) and _bytecode_ (`&[u8]`).
68    pub fn bytecodes(&self) -> CompiledBytecodes {
69        CompiledBytecodes(self.data.lock())
70    }
71}
72
73/// A list of resolved modules
74///
75/// It can be converted into iterator over resolved modules.
76pub struct ResolvedModules<'i>(Lock<'i, CompileData>);
77
78impl<'i, 'r: 'i> IntoIterator for &'r ResolvedModules<'i> {
79    type IntoIter = ResolvedModulesIter<'i>;
80    type Item = (&'i str, &'i str);
81    fn into_iter(self) -> Self::IntoIter {
82        ResolvedModulesIter(self.0.modules.iter())
83    }
84}
85
86/// An iterator over resolved modules
87///
88/// Each item is a tuple consists of module name and path.
89pub struct ResolvedModulesIter<'r>(HashMapIter<'r, String, String>);
90
91impl<'i> Iterator for ResolvedModulesIter<'i> {
92    type Item = (&'i str, &'i str);
93
94    fn next(&mut self) -> Option<Self::Item> {
95        self.0
96            .next()
97            .map(|(path, name)| (name.as_str(), path.as_str()))
98    }
99
100    fn size_hint(&self) -> (usize, Option<usize>) {
101        self.0.size_hint()
102    }
103}
104
105impl<'i> ExactSizeIterator for ResolvedModulesIter<'i> {
106    fn len(&self) -> usize {
107        self.0.len()
108    }
109}
110
111impl<'i> FusedIterator for ResolvedModulesIter<'i> {}
112
113/// A list of compiled bytecodes of loaded modules
114///
115/// It can be converted into iterator of loaded modules with bytecodes.
116pub struct CompiledBytecodes<'i>(Lock<'i, CompileData>);
117
118impl<'i, 'r: 'i> IntoIterator for &'r CompiledBytecodes<'i> {
119    type IntoIter = CompiledBytecodesIter<'i>;
120    type Item = (&'i str, &'i [u8]);
121    fn into_iter(self) -> Self::IntoIter {
122        CompiledBytecodesIter {
123            data: &self.0,
124            index: 0,
125        }
126    }
127}
128
129/// An iterator over loaded bytecodes of modules
130///
131/// Each item is a tuple of module path and bytecode.
132pub struct CompiledBytecodesIter<'r> {
133    data: &'r CompileData,
134    index: usize,
135}
136
137impl<'i> Iterator for CompiledBytecodesIter<'i> {
138    type Item = (&'i str, &'i [u8]);
139
140    fn next(&mut self) -> Option<Self::Item> {
141        let CompileData { modules, bytecodes } = &self.data;
142        if self.index < bytecodes.len() {
143            let (path, data) = &bytecodes[self.index];
144            self.index += 1;
145            modules
146                .get(path.as_str())
147                .map(|name| (name.as_str(), data.as_ref()))
148        } else {
149            None
150        }
151    }
152
153    fn size_hint(&self) -> (usize, Option<usize>) {
154        let len = self.len();
155        (len, Some(len))
156    }
157}
158
159impl<'i> ExactSizeIterator for CompiledBytecodesIter<'i> {
160    fn len(&self) -> usize {
161        self.data.bytecodes.len() - self.index
162    }
163}
164
165impl<'i> FusedIterator for CompiledBytecodesIter<'i> {}
166
167#[derive(Debug, Default)]
168struct CompileData {
169    // { module_path: internal_name }
170    modules: HashMap<String, String>,
171    // [ (module_path, module_bytecode) ]
172    bytecodes: Vec<(String, Vec<u8>)>,
173}
174
175impl<R> Resolver for Compile<R>
176where
177    R: Resolver,
178{
179    fn resolve<'js>(&mut self, ctx: &Ctx<'js>, base: &str, name: &str) -> Result<String> {
180        self.inner.resolve(ctx, base, name).inspect(|path| {
181            let name = resolve_simple(base, name);
182            self.data.lock().modules.insert(path.clone(), name);
183        })
184    }
185}
186
187impl<L> Loader for Compile<L>
188where
189    L: Loader,
190{
191    fn load<'js>(&mut self, ctx: &Ctx<'js>, path: &str) -> Result<Module<'js>> {
192        let module = self.inner.load(ctx, path)?;
193        let data = module.write(WriteOptions::default())?;
194        self.data.lock().bytecodes.push((path.into(), data));
195        Ok(module)
196    }
197}