@@ -62,12 +62,18 @@ pub(super) struct DfsInfo {
6262/// but with a state machine-like design for better correctness.
6363///
6464/// [cyclic]: https://tc39.es/ecma262/#table-cyclic-module-fields
65- #[ derive( Debug , Trace , Finalize , Default ) ]
65+ #[ derive( Debug , Trace , Finalize ) ]
6666#[ boa_gc( unsafe_no_drop) ]
6767enum ModuleStatus {
68- #[ default]
69- Unlinked ,
68+ Unlinked {
69+ #[ unsafe_ignore_trace]
70+ source : boa_ast:: Module ,
71+ source_text : SourceText ,
72+ } ,
7073 Linking {
74+ #[ unsafe_ignore_trace]
75+ source : boa_ast:: Module ,
76+ source_text : SourceText ,
7177 info : DfsInfo ,
7278 } ,
7379 PreLinked {
@@ -111,15 +117,21 @@ impl ModuleStatus {
111117 where
112118 F : FnOnce ( Self ) -> Self ,
113119 {
114- * self = f ( std:: mem:: take ( self ) ) ;
120+ * self = f ( std:: mem:: replace (
121+ self ,
122+ ModuleStatus :: Unlinked {
123+ source : boa_ast:: Module :: default ( ) ,
124+ source_text : SourceText :: default ( ) ,
125+ } ,
126+ ) ) ;
115127 }
116128
117129 /// Gets the current index info of the module within the dependency graph, or `None` if the
118130 /// module is not in a state executing the dfs algorithm.
119131 const fn dfs_info ( & self ) -> Option < & DfsInfo > {
120132 match self {
121- Self :: Unlinked | Self :: EvaluatingAsync { .. } | Self :: Evaluated { .. } => None ,
122- Self :: Linking { info }
133+ Self :: Unlinked { .. } | Self :: EvaluatingAsync { .. } | Self :: Evaluated { .. } => None ,
134+ Self :: Linking { info, .. }
123135 | Self :: PreLinked { info, .. }
124136 | Self :: Linked { info, .. }
125137 | Self :: Evaluating { info, .. } => Some ( info) ,
@@ -130,8 +142,8 @@ impl ModuleStatus {
130142 /// or `None` if the module is not in a state executing the dfs algorithm.
131143 fn dfs_info_mut ( & mut self ) -> Option < & mut DfsInfo > {
132144 match self {
133- Self :: Unlinked | Self :: EvaluatingAsync { .. } | Self :: Evaluated { .. } => None ,
134- Self :: Linking { info }
145+ Self :: Unlinked { .. } | Self :: EvaluatingAsync { .. } | Self :: Evaluated { .. } => None ,
146+ Self :: Linking { info, .. }
135147 | Self :: PreLinked { info, .. }
136148 | Self :: Linked { info, .. }
137149 | Self :: Evaluating { info, .. } => Some ( info) ,
@@ -142,7 +154,7 @@ impl ModuleStatus {
142154 /// level capability.
143155 const fn top_level_capability ( & self ) -> Option < & PromiseCapability > {
144156 match & self {
145- Self :: Unlinked
157+ Self :: Unlinked { .. }
146158 | Self :: Linking { .. }
147159 | Self :: PreLinked { .. }
148160 | Self :: Linked { .. } => None ,
@@ -182,14 +194,30 @@ impl ModuleStatus {
182194 /// Gets the declarative environment from the module status.
183195 fn environment ( & self ) -> Option < Gc < DeclarativeEnvironment > > {
184196 match self {
185- ModuleStatus :: Unlinked | ModuleStatus :: Linking { .. } => None ,
197+ ModuleStatus :: Unlinked { .. } | ModuleStatus :: Linking { .. } => None ,
186198 ModuleStatus :: PreLinked { environment, .. }
187199 | ModuleStatus :: Linked { environment, .. }
188200 | ModuleStatus :: Evaluating { environment, .. }
189201 | ModuleStatus :: EvaluatingAsync { environment, .. }
190202 | ModuleStatus :: Evaluated { environment, .. } => Some ( environment. clone ( ) ) ,
191203 }
192204 }
205+
206+ /// If this module is in the unlinked or linking states, gets its source.
207+ fn source ( & self ) -> Option < ( & boa_ast:: Module , & SourceText ) > {
208+ match self {
209+ ModuleStatus :: Unlinked {
210+ source,
211+ source_text,
212+ }
213+ | ModuleStatus :: Linking {
214+ source,
215+ source_text,
216+ ..
217+ } => Some ( ( source, source_text) ) ,
218+ _ => None ,
219+ }
220+ }
193221}
194222
195223/// The execution context of a [`SourceTextModule`].
@@ -242,8 +270,6 @@ impl std::fmt::Debug for SourceTextModule {
242270struct ModuleCode {
243271 has_tla : bool ,
244272 requested_modules : IndexSet < super :: ModuleRequest , BuildHasherDefault < FxHasher > > ,
245- source : RefCell < Option < boa_ast:: Module > > ,
246- source_text : RefCell < Option < SourceText > > ,
247273 path : Option < PathBuf > ,
248274 import_entries : Vec < ImportEntry > ,
249275 local_export_entries : Vec < LocalExportEntry > ,
@@ -307,7 +333,7 @@ impl SourceTextModule {
307333 ///
308334 /// [parse]: https://tc39.es/ecma262/#sec-parsemodule
309335 pub ( super ) fn new (
310- code : boa_ast:: Module ,
336+ source : boa_ast:: Module ,
311337 interner : & Interner ,
312338 source_text : SourceText ,
313339 path : Option < PathBuf > ,
@@ -320,11 +346,11 @@ impl SourceTextModule {
320346 interner,
321347 requests : IndexSet :: default ( ) ,
322348 } ;
323- let _ = visitor. visit_module ( & code ) ;
349+ let _ = visitor. visit_module ( & source ) ;
324350 visitor. requests
325351 } ;
326352 // 4. Let importEntries be ImportEntries of body.
327- let import_entries = code . items ( ) . import_entries ( ) ;
353+ let import_entries = source . items ( ) . import_entries ( ) ;
328354
329355 // 5. Let importedBoundNames be ImportedLocalNames(importEntries).
330356 // Can be ignored because this is just a simple `Iter::map`
@@ -337,7 +363,7 @@ impl SourceTextModule {
337363 let mut star_export_entries = Vec :: new ( ) ;
338364
339365 // 10. For each ExportEntry Record ee of exportEntries, do
340- for ee in code . items ( ) . export_entries ( ) {
366+ for ee in source . items ( ) . export_entries ( ) {
341367 match ee {
342368 // a. If ee.[[ModuleRequest]] is null, then
343369 ExportEntry :: Ordinary ( entry) => {
@@ -393,7 +419,7 @@ impl SourceTextModule {
393419 }
394420
395421 // 11. Let async be body Contains await.
396- let has_tla = contains ( & code , ContainsSymbol :: AwaitExpression ) ;
422+ let has_tla = contains ( & source , ContainsSymbol :: AwaitExpression ) ;
397423
398424 // 12. Return Source Text Module Record {
399425 // [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[CycleRoot]]: empty,
@@ -409,16 +435,17 @@ impl SourceTextModule {
409435 // }.
410436 // Most of this can be ignored, since `Status` takes care of the remaining state.
411437 Self {
412- status : GcRefCell :: default ( ) ,
438+ status : GcRefCell :: new ( ModuleStatus :: Unlinked {
439+ source,
440+ source_text,
441+ } ) ,
413442 loaded_modules : GcRefCell :: default ( ) ,
414443 async_parent_modules : GcRefCell :: default ( ) ,
415444 import_meta : GcRefCell :: default ( ) ,
416445 code : ModuleCode {
417- source : RefCell :: new ( Some ( code) ) ,
418- source_text : RefCell :: new ( Some ( source_text) ) ,
419- path,
420- requested_modules,
421446 has_tla,
447+ requested_modules,
448+ path,
422449 import_entries,
423450 local_export_entries,
424451 indirect_export_entries,
@@ -523,7 +550,7 @@ impl SourceTextModule {
523550 // 2. If module is a Cyclic Module Record, module.[[Status]] is new, and state.[[Visited]] does not contain
524551 // module, then
525552 // a. Append module to state.[[Visited]].
526- if matches ! ( & * self . status. borrow( ) , ModuleStatus :: Unlinked )
553+ if matches ! ( & * self . status. borrow( ) , ModuleStatus :: Unlinked { .. } )
527554 && state. visited . borrow_mut ( ) . insert ( module_self. clone ( ) )
528555 {
529556 // b. Let requestedModulesCount be the number of elements in module.[[RequestedModules]].
@@ -765,7 +792,7 @@ impl SourceTextModule {
765792 // 1. Assert: module.[[Status]] is one of unlinked, linked, evaluating-async, or evaluated.
766793 debug_assert ! ( matches!(
767794 & * self . status. borrow( ) ,
768- ModuleStatus :: Unlinked
795+ ModuleStatus :: Unlinked { .. }
769796 | ModuleStatus :: Linked { .. }
770797 | ModuleStatus :: EvaluatingAsync { .. }
771798 | ModuleStatus :: Evaluated { .. }
@@ -780,12 +807,24 @@ impl SourceTextModule {
780807 // a. For each Cyclic Module Record m of stack, do
781808 for m in stack. iter ( ) . filter_map ( |cmr| cmr. kind ( ) . as_source_text ( ) ) {
782809 // i. Assert: m.[[Status]] is linking.
783- debug_assert ! ( matches!( & * m. status. borrow( ) , ModuleStatus :: Linking { .. } ) ) ;
784810 // ii. Set m.[[Status]] to unlinked.
785- * m. status . borrow_mut ( ) = ModuleStatus :: Unlinked ;
811+ m. status . borrow_mut ( ) . transition ( |status| match status {
812+ ModuleStatus :: Linking {
813+ source,
814+ source_text,
815+ ..
816+ } => ModuleStatus :: Unlinked {
817+ source,
818+ source_text,
819+ } ,
820+ _ => unreachable ! ( "i. Assert: m.[[Status]] is linking." ) ,
821+ } ) ;
786822 }
787823 // b. Assert: module.[[Status]] is unlinked.
788- debug_assert ! ( matches!( & * self . status. borrow( ) , ModuleStatus :: Unlinked ) ) ;
824+ debug_assert ! ( matches!(
825+ & * self . status. borrow( ) ,
826+ ModuleStatus :: Unlinked { .. }
827+ ) ) ;
789828 // c. Return ? result.
790829 return Err ( err) ;
791830 }
@@ -827,18 +866,23 @@ impl SourceTextModule {
827866 return Ok ( index) ;
828867 }
829868
830- // 3. Assert: module.[[Status]] is unlinked.
831- debug_assert ! ( matches!( & * self . status. borrow( ) , ModuleStatus :: Unlinked ) ) ;
832-
833869 // 4. Set module.[[Status]] to linking.
834870 // 5. Set module.[[DFSIndex]] to index.
835871 // 6. Set module.[[DFSAncestorIndex]] to index.
836- * self . status . borrow_mut ( ) = ModuleStatus :: Linking {
837- info : DfsInfo {
838- dfs_index : index,
839- dfs_ancestor_index : index,
872+ self . status . borrow_mut ( ) . transition ( |status| match status {
873+ ModuleStatus :: Unlinked {
874+ source,
875+ source_text,
876+ } => ModuleStatus :: Linking {
877+ source,
878+ source_text,
879+ info : DfsInfo {
880+ dfs_index : index,
881+ dfs_ancestor_index : index,
882+ } ,
840883 } ,
841- } ;
884+ _ => unreachable ! ( "3. Assert: module.[[Status]] is unlinked." ) ,
885+ } ) ;
842886
843887 // 7. Set index to index + 1.
844888 index += 1 ;
@@ -873,10 +917,11 @@ impl SourceTextModule {
873917 DfsInfo {
874918 dfs_ancestor_index, ..
875919 } ,
920+ ..
876921 } = & * required_module_src. status . borrow ( )
877922 {
878- // 1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]],
879- // requiredModule.[[DFSAncestorIndex]]).
923+ // 1. Set module.[[DFSAncestorIndex]] to
924+ // min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).
880925
881926 Some ( * dfs_ancestor_index)
882927 } else {
@@ -976,7 +1021,7 @@ impl SourceTextModule {
9761021 // 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.
9771022 let ( module, promise) = {
9781023 match & * self . status . borrow ( ) {
979- ModuleStatus :: Unlinked
1024+ ModuleStatus :: Unlinked { .. }
9801025 | ModuleStatus :: Linking { .. }
9811026 | ModuleStatus :: PreLinked { .. }
9821027 | ModuleStatus :: Evaluating { .. } => {
@@ -1559,24 +1604,16 @@ impl SourceTextModule {
15591604 // 4. Assert: realm is not undefined.
15601605 let realm = module_self. realm ( ) . clone ( ) ;
15611606
1562- // Take the AST and source text — they are only needed during compilation.
1563- // Dropping them here frees the parse tree after linking is complete.
1564- let source = self
1565- . code
1566- . source
1567- . take ( )
1568- . expect ( "module source consumed before initialize_environment" ) ;
1569- let source_text = self
1570- . code
1571- . source_text
1572- . take ( )
1573- . expect ( "module source_text consumed before initialize_environment" ) ;
1607+ let status = self . status . borrow ( ) ;
1608+ let ( source, source_text) = status
1609+ . source ( )
1610+ . expect ( "module can only initialize its environment in the linking phase" ) ;
15741611
15751612 // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
15761613 // 6. Set module.[[Environment]] to env.
15771614 let env = source. scope ( ) . clone ( ) ;
15781615
1579- let spanned_source_text = SpannedSourceText :: new_source_only ( source_text) ;
1616+ let spanned_source_text = SpannedSourceText :: new_source_only ( source_text. clone ( ) ) ;
15801617 let mut compiler = ByteCompiler :: new (
15811618 js_string ! ( "<main>" ) ,
15821619 true ,
@@ -1664,7 +1701,7 @@ impl SourceTextModule {
16641701
16651702 // 18. Let code be module.[[ECMAScriptCode]].
16661703 // 19. Let varDeclarations be the VarScopedDeclarations of code.
1667- let var_declarations = var_scoped_declarations ( & source) ;
1704+ let var_declarations = var_scoped_declarations ( source) ;
16681705 // 20. Let declaredVarNames be a new empty List.
16691706 let mut declared_var_names = Vec :: new ( ) ;
16701707 // 21. For each element d of varDeclarations, do
@@ -1695,7 +1732,7 @@ impl SourceTextModule {
16951732
16961733 // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code.
16971734 // 23. Let privateEnv be null.
1698- let lex_declarations = lexically_scoped_declarations ( & source) ;
1735+ let lex_declarations = lexically_scoped_declarations ( source) ;
16991736 let mut functions = Vec :: new ( ) ;
17001737 // 24. For each element d of lexDeclarations, do
17011738 for declaration in lex_declarations {
@@ -1759,6 +1796,7 @@ impl SourceTextModule {
17591796 // 8. Let moduleContext be a new ECMAScript code execution context.
17601797 let mut envs = EnvironmentStack :: new ( ) ;
17611798 envs. push_module ( source. scope ( ) . clone ( ) ) ;
1799+ drop ( status) ;
17621800
17631801 // 9. Set the Function of moduleContext to null.
17641802 // 10. Assert: module.[[Realm]] is not undefined.
@@ -1862,7 +1900,7 @@ impl SourceTextModule {
18621900
18631901 // 16. Set module.[[Context]] to moduleContext.
18641902 self . status . borrow_mut ( ) . transition ( |state| match state {
1865- ModuleStatus :: Linking { info } => ModuleStatus :: PreLinked {
1903+ ModuleStatus :: Linking { info, .. } => ModuleStatus :: PreLinked {
18661904 environment : env,
18671905 info,
18681906 context : SourceTextContext {
0 commit comments