1use crate::object::JSObject;
2use crate::runtime::atom::AtomTable;
3use crate::runtime::context::JSContext;
4use crate::util::FxHashMap;
5use crate::value::JSValue;
6use std::cell::RefCell;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ModuleState {
10 Unlinked,
11 Linking,
12 Linked,
13 Evaluating,
14 Evaluated,
15 Errored,
16}
17
18#[derive(Clone)]
19pub struct ModuleExport {
20 pub name: String,
21 pub value: JSValue,
22 pub mutable: bool,
23}
24
25#[derive(Clone)]
26pub struct ModuleImport {
27 pub module_specifier: String,
28 pub import_name: String,
29 pub local_name: String,
30}
31
32pub struct Module {
33 pub specifier: String,
34 pub state: ModuleState,
35 pub exports: FxHashMap<String, ModuleExport>,
36 pub imports: Vec<ModuleImport>,
37 pub namespace_object: Option<usize>,
38 pub source: String,
39 pub import_meta_object: Option<usize>,
40 pub error: Option<String>,
41}
42
43impl Module {
44 pub fn new(specifier: String, source: String) -> Self {
45 Module {
46 specifier,
47 state: ModuleState::Unlinked,
48 exports: FxHashMap::default(),
49 imports: Vec::new(),
50 namespace_object: None,
51 source,
52 import_meta_object: None,
53 error: None,
54 }
55 }
56
57 pub fn add_export(&mut self, name: String, value: JSValue, mutable: bool) {
58 self.exports.insert(
59 name.clone(),
60 ModuleExport {
61 name,
62 value,
63 mutable,
64 },
65 );
66 }
67
68 pub fn add_import(
69 &mut self,
70 module_specifier: String,
71 import_name: String,
72 local_name: String,
73 ) {
74 self.imports.push(ModuleImport {
75 module_specifier,
76 import_name,
77 local_name,
78 });
79 }
80
81 pub fn get_export(&self, name: &str) -> Option<&ModuleExport> {
82 self.exports.get(name)
83 }
84
85 pub fn get_export_mut(&mut self, name: &str) -> Option<&mut ModuleExport> {
86 self.exports.get_mut(name)
87 }
88
89 pub fn get_export_value(&self, name: &str) -> JSValue {
90 self.exports
91 .get(name)
92 .map(|e| e.value.clone())
93 .unwrap_or(JSValue::undefined())
94 }
95
96 pub fn set_export_value(&mut self, name: &str, value: JSValue) {
97 if let Some(export) = self.exports.get_mut(name) {
98 export.value = value;
99 }
100 }
101
102 pub fn create_namespace_object(&mut self, atom_table: &mut AtomTable) -> usize {
103 let mut ns_obj = JSObject::new();
104 for (name, export) in &self.exports {
105 let atom = atom_table.intern(name);
106 ns_obj.set(atom, export.value.clone());
107 }
108 let ptr = Box::into_raw(Box::new(ns_obj)) as usize;
109 self.namespace_object = Some(ptr);
110 ptr
111 }
112
113 pub fn get_or_create_namespace_object(&mut self, atom_table: &mut AtomTable) -> usize {
114 if let Some(ptr) = self.namespace_object {
115 ptr
116 } else {
117 self.create_namespace_object(atom_table)
118 }
119 }
120
121 pub fn has_dependencies(&self) -> bool {
122 !self.imports.is_empty()
123 }
124
125 pub fn get_dependencies(&self) -> Vec<String> {
126 self.imports
127 .iter()
128 .map(|imp| imp.module_specifier.clone())
129 .filter(|s| !s.is_empty())
130 .collect()
131 }
132}
133
134pub struct ModuleRegistry {
135 modules: FxHashMap<String, RefCell<Module>>,
136 resolving: Vec<String>,
137}
138
139impl ModuleRegistry {
140 pub fn new() -> Self {
141 ModuleRegistry {
142 modules: FxHashMap::default(),
143 resolving: Vec::new(),
144 }
145 }
146
147 pub fn register(&mut self, module: Module) {
148 self.modules
149 .insert(module.specifier.clone(), RefCell::new(module));
150 }
151
152 pub fn get(&self, specifier: &str) -> Option<&RefCell<Module>> {
153 self.modules.get(specifier)
154 }
155
156 pub fn get_mut(&mut self, specifier: &str) -> Option<&mut RefCell<Module>> {
157 self.modules.get_mut(specifier)
158 }
159
160 pub fn has(&self, specifier: &str) -> bool {
161 self.modules.contains_key(specifier)
162 }
163
164 pub fn begin_resolve(&mut self, specifier: &str) -> bool {
165 if self.resolving.contains(&specifier.to_string()) {
166 return false;
167 }
168 self.resolving.push(specifier.to_string());
169 true
170 }
171
172 pub fn end_resolve(&mut self, specifier: &str) {
173 self.resolving.retain(|s| s != specifier);
174 }
175
176 pub fn is_resolving(&self, specifier: &str) -> bool {
177 self.resolving.contains(&specifier.to_string())
178 }
179
180 pub fn is_state(&self, specifier: &str, state: ModuleState) -> bool {
181 self.modules
182 .get(specifier)
183 .map(|m| m.borrow().state == state)
184 .unwrap_or(false)
185 }
186
187 pub fn set_state(&mut self, specifier: &str, state: ModuleState) {
188 if let Some(m) = self.modules.get(specifier) {
189 m.borrow_mut().state = state;
190 }
191 }
192
193 pub fn get_all_specifiers(&self) -> Vec<String> {
194 self.modules.keys().cloned().collect()
195 }
196}
197
198impl Default for ModuleRegistry {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204pub fn resolve_specifier(specifier: &str, base_path: &str) -> String {
205 if specifier.starts_with("./") || specifier.starts_with("../") {
206 let base = std::path::Path::new(base_path);
207 let base_dir = base.parent().unwrap_or(std::path::Path::new("."));
208 let resolved = base_dir.join(specifier);
209 let canonical = std::fs::canonicalize(&resolved).unwrap_or(resolved);
210 canonical.to_string_lossy().to_string()
211 } else if specifier.starts_with('/') {
212 specifier.to_string()
213 } else {
214 let path = std::path::Path::new(".").join(specifier);
215 let canonical = std::fs::canonicalize(&path).unwrap_or(path);
216 canonical.to_string_lossy().to_string()
217 }
218}
219
220pub fn load_module_source(specifier: &str) -> Result<String, String> {
221 let path = if specifier.ends_with(".js") || specifier.ends_with(".mjs") {
222 specifier.to_string()
223 } else {
224 format!("{}.js", specifier)
225 };
226
227 std::fs::read_to_string(&path).map_err(|e| format!("Failed to load module '{}': {}", path, e))
228}
229
230pub fn load_and_evaluate_module(ctx: &mut JSContext, specifier: &str) -> Result<usize, String> {
231 use crate::compiler::ast::BlockStatement;
232 use crate::compiler::codegen::CodeGenerator;
233 use crate::compiler::parser::Parser;
234 use crate::compiler::peephole;
235 use crate::runtime::vm::VM;
236
237 let resolved = if specifier.starts_with("./")
238 || specifier.starts_with("../")
239 || specifier.starts_with('/')
240 {
241 if let Some(current) = ctx.get_current_module() {
242 resolve_specifier(specifier, current)
243 } else {
244 let cwd = std::env::current_dir()
245 .map(|p| p.to_string_lossy().to_string())
246 .unwrap_or_else(|_| ".".to_string());
247 resolve_specifier(specifier, &cwd)
248 }
249 } else {
250 specifier.to_string()
251 };
252
253 let already_evaluated = {
254 let registry = ctx.runtime().module_registry();
255 registry
256 .get(&resolved)
257 .map(|m| m.borrow().state == ModuleState::Evaluated)
258 .unwrap_or(false)
259 };
260
261 if already_evaluated {
262 let atom_table_ptr = ctx.atom_table_mut() as *mut _;
263 let ns_ptr = unsafe {
264 let registry = ctx.runtime_mut().module_registry_mut();
265 let module_cell = registry.get_mut(&resolved);
266 if let Some(module_cell) = module_cell {
267 module_cell
268 .borrow_mut()
269 .get_or_create_namespace_object(&mut *atom_table_ptr)
270 } else {
271 return Err(format!("Module {} not found", resolved));
272 }
273 };
274 return Ok(ns_ptr);
275 }
276
277 let is_manually_registered = {
278 let registry = ctx.runtime().module_registry();
279 registry
280 .get(&resolved)
281 .map(|m| {
282 let module = m.borrow();
283 !module.exports.is_empty()
284 })
285 .unwrap_or(false)
286 };
287
288 if is_manually_registered {
289 {
290 let registry = ctx.runtime_mut().module_registry_mut();
291 if let Some(module_cell) = registry.get_mut(&resolved) {
292 module_cell.borrow_mut().state = ModuleState::Evaluated;
293 }
294 }
295 let atom_table_ptr = ctx.atom_table_mut() as *mut _;
296 let ns_ptr = unsafe {
297 let registry = ctx.runtime_mut().module_registry_mut();
298 let module_cell = registry.get_mut(&resolved);
299 if let Some(module_cell) = module_cell {
300 module_cell
301 .borrow_mut()
302 .get_or_create_namespace_object(&mut *atom_table_ptr)
303 } else {
304 return Err(format!("Module {} not found", resolved));
305 }
306 };
307 return Ok(ns_ptr);
308 }
309
310 {
311 let registry = ctx.runtime().module_registry();
312 if !registry.has(&resolved) {
313 let source = load_module_source(&resolved)?;
314 let module = Module::new(resolved.clone(), source);
315 ctx.runtime_mut().module_registry_mut().register(module);
316 }
317 }
318
319 let source = {
320 let registry = ctx.runtime().module_registry();
321 if let Some(module_cell) = registry.get(&resolved) {
322 module_cell.borrow().source.clone()
323 } else {
324 return Err(format!("Module {} not found after registration", resolved));
325 }
326 };
327
328 let dependencies: Vec<String> = {
329 let registry = ctx.runtime().module_registry();
330 if let Some(module_cell) = registry.get(&resolved) {
331 module_cell.borrow().get_dependencies()
332 } else {
333 Vec::new()
334 }
335 };
336
337 {
338 let registry = ctx.runtime_mut().module_registry_mut();
339 if let Some(module_cell) = registry.get_mut(&resolved) {
340 module_cell.borrow_mut().state = ModuleState::Evaluating;
341 }
342 }
343
344 for dep_specifier in &dependencies {
345 let resolved_dep = if dep_specifier.starts_with("./") || dep_specifier.starts_with("../") {
346 resolve_specifier(dep_specifier, &resolved)
347 } else {
348 dep_specifier.clone()
349 };
350 load_and_evaluate_module(ctx, &resolved_dep)?;
351 }
352
353 let old_module = ctx.get_current_module().map(|s| s.to_string());
354 ctx.set_current_module(Some(resolved.clone()));
355
356 let rb = {
357 let ast = Parser::new(&source).parse()?;
358 let opt_level = ctx.get_compiler_opt_level();
359 let mut codegen = CodeGenerator::with_opt_level(opt_level);
360 let block = BlockStatement {
361 body: ast.body,
362 lines: ast.lines,
363 };
364 let (mut rb, _) = codegen.compile_script(&block, ctx)?;
365 peephole::optimize_with_level(&mut rb, opt_level);
366 rb
367 };
368
369 let result = {
370 let mut vm = VM::new();
371 vm.execute(ctx, &rb)
372 };
373
374 ctx.set_current_module(old_module);
375
376 match result {
377 Ok(_) => {
378 {
379 let registry = ctx.runtime_mut().module_registry_mut();
380 if let Some(module_cell) = registry.get_mut(&resolved) {
381 module_cell.borrow_mut().state = ModuleState::Evaluated;
382 }
383 }
384 let ns_ptr = {
385 let atom_table_ptr = ctx.atom_table_mut() as *mut _;
386 let registry = ctx.runtime_mut().module_registry_mut();
387 if let Some(module_cell) = registry.get_mut(&resolved) {
388 unsafe {
389 module_cell
390 .borrow_mut()
391 .get_or_create_namespace_object(&mut *atom_table_ptr)
392 }
393 } else {
394 return Err(format!("Module {} not found after evaluation", resolved));
395 }
396 };
397 Ok(ns_ptr)
398 }
399 Err(e) => {
400 {
401 let registry = ctx.runtime_mut().module_registry_mut();
402 if let Some(module_cell) = registry.get_mut(&resolved) {
403 let mut module = module_cell.borrow_mut();
404 module.state = ModuleState::Errored;
405 module.error = Some(e.clone());
406 }
407 }
408 Err(e)
409 }
410 }
411}