rquickjs_core/loader/
compile.rs

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