1use anyhow::{Context, Result, bail};
2use tracing::Instrument;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{FxIndexMap, ResolvedVc, TryJoinIterExt, Upcast, ValueToString, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbo_tasks_hash::HashAlgorithm;
7use turbopack_core::{
8 asset::{Asset, AssetContent},
9 chunk::{
10 AssetSuffix, Chunk, ChunkGroupResult, ChunkItem, ChunkType, ChunkableModule,
11 ChunkingConfig, ChunkingConfigs, ChunkingContext, ContentHashing, EntryChunkGroupResult,
12 EvaluatableAsset, MinifyType, SourceMapSourceType, SourceMapsType, UnusedReferences,
13 UrlBehavior,
14 availability_info::AvailabilityInfo,
15 chunk_group::{MakeChunkGroupResult, make_chunk_group},
16 chunk_id_strategy::ModuleIdStrategy,
17 },
18 environment::Environment,
19 ident::AssetIdent,
20 module::Module,
21 module_graph::{
22 ModuleGraph,
23 binding_usage_info::{BindingUsageInfo, ModuleExportUsage},
24 chunk_group_info::ChunkGroup,
25 },
26 output::{OutputAsset, OutputAssets},
27};
28use turbopack_ecmascript::{
29 async_chunk::module::AsyncLoaderModule,
30 chunk::EcmascriptChunk,
31 manifest::{chunk_asset::ManifestAsyncModule, loader_module::ManifestLoaderModule},
32};
33use turbopack_ecmascript_runtime::RuntimeType;
34
35use crate::ecmascript::node::{
36 chunk::EcmascriptBuildNodeChunk, entry::chunk::EcmascriptBuildNodeEntryChunk,
37};
38
39pub struct NodeJsChunkingContextBuilder {
41 chunking_context: NodeJsChunkingContext,
42}
43
44impl NodeJsChunkingContextBuilder {
45 pub fn asset_prefix(mut self, asset_prefix: Option<RcStr>) -> Self {
46 self.chunking_context.asset_prefix = asset_prefix;
47 self
48 }
49
50 pub fn asset_prefix_override(mut self, tag: RcStr, prefix: RcStr) -> Self {
51 self.chunking_context.asset_prefixes.insert(tag, prefix);
52 self
53 }
54
55 pub fn asset_root_path_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
56 self.chunking_context.asset_root_paths.insert(tag, path);
57 self
58 }
59
60 pub fn client_roots_override(mut self, tag: RcStr, path: FileSystemPath) -> Self {
61 self.chunking_context.client_roots.insert(tag, path);
62 self
63 }
64
65 pub fn url_behavior_override(mut self, tag: RcStr, behavior: UrlBehavior) -> Self {
66 self.chunking_context.url_behaviors.insert(tag, behavior);
67 self
68 }
69
70 pub fn default_url_behavior(mut self, behavior: UrlBehavior) -> Self {
71 self.chunking_context.default_url_behavior = Some(behavior);
72 self
73 }
74
75 pub fn minify_type(mut self, minify_type: MinifyType) -> Self {
76 self.chunking_context.minify_type = minify_type;
77 self
78 }
79
80 pub fn source_maps(mut self, source_maps: SourceMapsType) -> Self {
81 self.chunking_context.source_maps_type = source_maps;
82 self
83 }
84
85 pub fn file_tracing(mut self, enable_tracing: bool) -> Self {
86 self.chunking_context.enable_file_tracing = enable_tracing;
87 self
88 }
89
90 pub fn nested_async_availability(mut self, enable_nested_async_availability: bool) -> Self {
91 self.chunking_context.enable_nested_async_availability = enable_nested_async_availability;
92 self
93 }
94
95 pub fn module_merging(mut self, enable_module_merging: bool) -> Self {
96 self.chunking_context.enable_module_merging = enable_module_merging;
97 self
98 }
99
100 pub fn dynamic_chunk_content_loading(
101 mut self,
102 enable_dynamic_chunk_content_loading: bool,
103 ) -> Self {
104 self.chunking_context.enable_dynamic_chunk_content_loading =
105 enable_dynamic_chunk_content_loading;
106 self
107 }
108
109 pub fn runtime_type(mut self, runtime_type: RuntimeType) -> Self {
110 self.chunking_context.runtime_type = runtime_type;
111 self
112 }
113
114 pub fn manifest_chunks(mut self, manifest_chunks: bool) -> Self {
115 self.chunking_context.manifest_chunks = manifest_chunks;
116 self
117 }
118
119 pub fn source_map_source_type(mut self, source_map_source_type: SourceMapSourceType) -> Self {
120 self.chunking_context.source_map_source_type = source_map_source_type;
121 self
122 }
123
124 pub fn module_id_strategy(mut self, module_id_strategy: ResolvedVc<ModuleIdStrategy>) -> Self {
125 self.chunking_context.module_id_strategy = Some(module_id_strategy);
126 self
127 }
128
129 pub fn export_usage(mut self, export_usage: Option<ResolvedVc<BindingUsageInfo>>) -> Self {
130 self.chunking_context.export_usage = export_usage;
131 self
132 }
133
134 pub fn unused_references(mut self, unused_references: ResolvedVc<UnusedReferences>) -> Self {
135 self.chunking_context.unused_references = Some(unused_references);
136 self
137 }
138
139 pub fn chunking_config<T>(mut self, ty: ResolvedVc<T>, chunking_config: ChunkingConfig) -> Self
140 where
141 T: Upcast<Box<dyn ChunkType>>,
142 {
143 self.chunking_context
144 .chunking_configs
145 .push((ResolvedVc::upcast_non_strict(ty), chunking_config));
146 self
147 }
148
149 pub fn debug_ids(mut self, debug_ids: bool) -> Self {
150 self.chunking_context.debug_ids = debug_ids;
151 self
152 }
153
154 pub fn worker_forwarded_globals(mut self, globals: Vec<RcStr>) -> Self {
155 self.chunking_context
156 .worker_forwarded_globals
157 .extend(globals);
158 self
159 }
160
161 pub fn asset_content_hashing(mut self, content_hashing: ContentHashing) -> Self {
162 self.chunking_context.asset_content_hashing = content_hashing;
163 self
164 }
165
166 pub fn hash_salt(mut self, salt: ResolvedVc<RcStr>) -> Self {
167 self.chunking_context.hash_salt = salt;
168 self
169 }
170
171 pub fn build(self) -> Vc<NodeJsChunkingContext> {
173 NodeJsChunkingContext::cell(self.chunking_context)
174 }
175}
176
177#[turbo_tasks::value]
179#[derive(Debug, Clone)]
180pub struct NodeJsChunkingContext {
181 root_path: FileSystemPath,
183 output_root: FileSystemPath,
185 output_root_to_root_path: RcStr,
187 client_root: FileSystemPath,
189 #[bincode(with = "turbo_bincode::indexmap")]
191 client_roots: FxIndexMap<RcStr, FileSystemPath>,
192 chunk_root_path: FileSystemPath,
194 asset_root_path: FileSystemPath,
196 #[bincode(with = "turbo_bincode::indexmap")]
198 asset_root_paths: FxIndexMap<RcStr, FileSystemPath>,
199 asset_prefix: Option<RcStr>,
201 #[bincode(with = "turbo_bincode::indexmap")]
203 asset_prefixes: FxIndexMap<RcStr, RcStr>,
204 #[bincode(with = "turbo_bincode::indexmap")]
206 url_behaviors: FxIndexMap<RcStr, UrlBehavior>,
207 default_url_behavior: Option<UrlBehavior>,
209 environment: ResolvedVc<Environment>,
211 runtime_type: RuntimeType,
213 enable_file_tracing: bool,
215 enable_nested_async_availability: bool,
217 enable_module_merging: bool,
219 enable_dynamic_chunk_content_loading: bool,
221 minify_type: MinifyType,
223 source_maps_type: SourceMapsType,
225 manifest_chunks: bool,
227 module_id_strategy: Option<ResolvedVc<ModuleIdStrategy>>,
229 export_usage: Option<ResolvedVc<BindingUsageInfo>>,
231 unused_references: Option<ResolvedVc<UnusedReferences>>,
233 source_map_source_type: SourceMapSourceType,
235 chunking_configs: Vec<(ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig)>,
237 debug_ids: bool,
239 worker_forwarded_globals: Vec<RcStr>,
241 asset_content_hashing: ContentHashing,
243 hash_salt: ResolvedVc<RcStr>,
245}
246
247impl NodeJsChunkingContext {
248 pub fn builder(
250 root_path: FileSystemPath,
251 output_root: FileSystemPath,
252 output_root_to_root_path: RcStr,
253 client_root: FileSystemPath,
254 chunk_root_path: FileSystemPath,
255 asset_root_path: FileSystemPath,
256 environment: ResolvedVc<Environment>,
257 runtime_type: RuntimeType,
258 ) -> NodeJsChunkingContextBuilder {
259 NodeJsChunkingContextBuilder {
260 chunking_context: NodeJsChunkingContext {
261 root_path,
262 output_root,
263 output_root_to_root_path,
264 client_root,
265 client_roots: Default::default(),
266 chunk_root_path,
267 asset_root_path,
268 asset_root_paths: Default::default(),
269 asset_prefix: None,
270 asset_prefixes: Default::default(),
271 url_behaviors: Default::default(),
272 default_url_behavior: None,
273 enable_file_tracing: false,
274 enable_nested_async_availability: false,
275 enable_module_merging: false,
276 enable_dynamic_chunk_content_loading: false,
277 environment,
278 runtime_type,
279 minify_type: MinifyType::NoMinify,
280 source_maps_type: SourceMapsType::Full,
281 manifest_chunks: false,
282 source_map_source_type: SourceMapSourceType::TurbopackUri,
283 module_id_strategy: None,
284 export_usage: None,
285 unused_references: None,
286 chunking_configs: Default::default(),
287 debug_ids: false,
288 worker_forwarded_globals: vec![],
289 asset_content_hashing: ContentHashing::Direct { length: 13 },
290 hash_salt: ResolvedVc::cell(RcStr::default()),
291 },
292 }
293 }
294}
295
296#[turbo_tasks::value_impl]
297impl NodeJsChunkingContext {
298 #[turbo_tasks::function]
303 pub fn runtime_type(&self) -> Vc<RuntimeType> {
304 self.runtime_type.cell()
305 }
306
307 #[turbo_tasks::function]
309 pub fn minify_type(&self) -> Vc<MinifyType> {
310 self.minify_type.cell()
311 }
312
313 #[turbo_tasks::function]
314 pub fn hash_salt(&self) -> Vc<RcStr> {
315 *self.hash_salt
316 }
317
318 #[turbo_tasks::function]
319 pub fn asset_prefix(&self) -> Vc<Option<RcStr>> {
320 Vc::cell(self.asset_prefix.clone())
321 }
322}
323
324impl NodeJsChunkingContext {
325 async fn generate_chunk(
326 self: Vc<Self>,
327 chunk: ResolvedVc<Box<dyn Chunk>>,
328 ) -> Result<ResolvedVc<Box<dyn OutputAsset>>> {
329 Ok(
330 if let Some(ecmascript_chunk) = ResolvedVc::try_downcast_type::<EcmascriptChunk>(chunk)
331 {
332 ResolvedVc::upcast(
333 EcmascriptBuildNodeChunk::new(self, *ecmascript_chunk)
334 .to_resolved()
335 .await?,
336 )
337 } else if let Some(output_asset) =
338 ResolvedVc::try_sidecast::<Box<dyn OutputAsset>>(chunk)
339 {
340 output_asset
341 } else {
342 bail!("Unable to generate output asset for chunk");
343 },
344 )
345 }
346}
347
348#[turbo_tasks::value_impl]
349impl ChunkingContext for NodeJsChunkingContext {
350 #[turbo_tasks::function]
351 fn name(&self) -> Vc<RcStr> {
352 Vc::cell(rcstr!("unknown"))
353 }
354
355 #[turbo_tasks::function]
356 fn root_path(&self) -> Vc<FileSystemPath> {
357 self.root_path.clone().cell()
358 }
359
360 #[turbo_tasks::function]
361 fn output_root(&self) -> Vc<FileSystemPath> {
362 self.output_root.clone().cell()
363 }
364
365 #[turbo_tasks::function]
366 fn output_root_to_root_path(&self) -> Vc<RcStr> {
367 Vc::cell(self.output_root_to_root_path.clone())
368 }
369
370 #[turbo_tasks::function]
371 fn environment(&self) -> Vc<Environment> {
372 *self.environment
373 }
374
375 #[turbo_tasks::function]
376 fn is_tracing_enabled(&self) -> Vc<bool> {
377 Vc::cell(self.enable_file_tracing)
378 }
379
380 #[turbo_tasks::function]
381 fn is_nested_async_availability_enabled(&self) -> Vc<bool> {
382 Vc::cell(self.enable_nested_async_availability)
383 }
384
385 #[turbo_tasks::function]
386 fn is_module_merging_enabled(&self) -> Vc<bool> {
387 Vc::cell(self.enable_module_merging)
388 }
389
390 #[turbo_tasks::function]
391 fn is_dynamic_chunk_content_loading_enabled(&self) -> Vc<bool> {
392 Vc::cell(self.enable_dynamic_chunk_content_loading)
393 }
394
395 #[turbo_tasks::function]
396 pub fn minify_type(&self) -> Vc<MinifyType> {
397 self.minify_type.cell()
398 }
399
400 #[turbo_tasks::function]
401 async fn asset_url(&self, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>> {
402 let asset_path = ident.to_string();
403
404 let client_root = tag
405 .as_ref()
406 .and_then(|tag| self.client_roots.get(tag))
407 .unwrap_or(&self.client_root);
408
409 let asset_prefix = tag
410 .as_ref()
411 .and_then(|tag| self.asset_prefixes.get(tag))
412 .or(self.asset_prefix.as_ref());
413
414 let asset_path = asset_path
415 .strip_prefix(&format!("{}/", client_root.path))
416 .context("expected client root to contain asset path")?;
417
418 Ok(Vc::cell(
419 format!(
420 "{}{}",
421 asset_prefix.map(|s| s.as_str()).unwrap_or("/"),
422 asset_path
423 )
424 .into(),
425 ))
426 }
427
428 #[turbo_tasks::function]
429 fn chunk_root_path(&self) -> Vc<FileSystemPath> {
430 self.chunk_root_path.clone().cell()
431 }
432
433 #[turbo_tasks::function]
434 async fn chunk_path(
435 &self,
436 _asset: Option<Vc<Box<dyn Asset>>>,
437 ident: Vc<AssetIdent>,
438 prefix: Option<RcStr>,
439 extension: RcStr,
440 ) -> Result<Vc<FileSystemPath>> {
441 let root_path = self.chunk_root_path.clone();
442 let name = ident
443 .output_name(self.root_path.clone(), prefix, extension)
444 .owned()
445 .await?;
446 Ok(root_path.join(&name)?.cell())
447 }
448
449 #[turbo_tasks::function]
450 fn reference_chunk_source_maps(&self, _chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool> {
451 Vc::cell(match self.source_maps_type {
452 SourceMapsType::Full => true,
453 SourceMapsType::Partial => true,
454 SourceMapsType::None => false,
455 })
456 }
457
458 #[turbo_tasks::function]
459 fn reference_module_source_maps(&self, _module: Vc<Box<dyn Module>>) -> Vc<bool> {
460 Vc::cell(match self.source_maps_type {
461 SourceMapsType::Full => true,
462 SourceMapsType::Partial => true,
463 SourceMapsType::None => false,
464 })
465 }
466
467 #[turbo_tasks::function]
468 fn source_map_source_type(&self) -> Vc<SourceMapSourceType> {
469 self.source_map_source_type.cell()
470 }
471
472 #[turbo_tasks::function]
473 fn chunking_configs(&self) -> Result<Vc<ChunkingConfigs>> {
474 Ok(Vc::cell(self.chunking_configs.iter().cloned().collect()))
475 }
476
477 #[turbo_tasks::function]
478 async fn asset_path(
479 self: Vc<Self>,
480 content: Vc<AssetContent>,
481 original_asset_ident: Vc<AssetIdent>,
482 tag: Option<RcStr>,
483 ) -> Result<Vc<FileSystemPath>> {
484 let this = self.await?;
485 let source_path = original_asset_ident.path().await?;
486 let basename = source_path.file_name();
487 let ContentHashing::Direct { length } = this.asset_content_hashing;
488 let hash = content
489 .content_hash(self.hash_salt(), HashAlgorithm::Xxh3Hash128Base38)
490 .await?;
491 let hash = hash
492 .as_ref()
493 .context("Missing content when trying to generate the content hash for static asset")?;
494 let short_hash = &hash[..length as usize];
495 let asset_path = match source_path.extension_ref() {
496 Some(ext) => format!(
497 "{basename}.{short_hash}.{ext}",
498 basename = &basename[..basename.len() - ext.len() - 1],
499 ),
500 None => format!("{basename}.{short_hash}"),
501 };
502
503 let asset_root_path = tag
504 .as_ref()
505 .and_then(|tag| this.asset_root_paths.get(tag))
506 .unwrap_or(&this.asset_root_path);
507
508 Ok(asset_root_path.join(&asset_path)?.cell())
509 }
510
511 #[turbo_tasks::function]
512 fn url_behavior(&self, tag: Option<RcStr>) -> Vc<UrlBehavior> {
513 tag.as_ref()
514 .and_then(|tag| self.url_behaviors.get(tag))
515 .cloned()
516 .or_else(|| self.default_url_behavior.clone())
517 .unwrap_or(UrlBehavior {
518 suffix: AssetSuffix::Inferred,
519 static_suffix: ResolvedVc::cell(None),
520 })
521 .cell()
522 }
523
524 #[turbo_tasks::function]
525 async fn chunk_group(
526 self: ResolvedVc<Self>,
527 ident: Vc<AssetIdent>,
528 chunk_group: ChunkGroup,
529 module_graph: Vc<ModuleGraph>,
530 availability_info: AvailabilityInfo,
531 ) -> Result<Vc<ChunkGroupResult>> {
532 let span = tracing::info_span!("chunking", name = display(ident.to_string().await?));
533 async move {
534 let modules = chunk_group.entries();
535 let MakeChunkGroupResult {
536 chunks,
537 referenced_output_assets,
538 references,
539 availability_info,
540 } = make_chunk_group(
541 modules,
542 module_graph,
543 ResolvedVc::upcast(self),
544 availability_info,
545 )
546 .await?;
547
548 let chunks = chunks.await?;
549
550 let assets = chunks
551 .iter()
552 .map(|chunk| self.generate_chunk(*chunk))
553 .try_join()
554 .await?;
555
556 Ok(ChunkGroupResult {
557 assets: ResolvedVc::cell(assets),
558 referenced_assets: ResolvedVc::cell(referenced_output_assets),
559 references: ResolvedVc::cell(references),
560 availability_info,
561 }
562 .cell())
563 }
564 .instrument(span)
565 .await
566 }
567
568 #[turbo_tasks::function]
569 pub async fn entry_chunk_group(
570 self: ResolvedVc<Self>,
571 path: FileSystemPath,
572 chunk_group: ChunkGroup,
573 module_graph: Vc<ModuleGraph>,
574 extra_chunks: Vc<OutputAssets>,
575 extra_referenced_assets: Vc<OutputAssets>,
576 availability_info: AvailabilityInfo,
577 ) -> Result<Vc<EntryChunkGroupResult>> {
578 let span = tracing::info_span!(
579 "chunking",
580 name = display(path.value_to_string().await?),
581 chunking_type = "entry",
582 );
583 async move {
584 let entries = chunk_group.entries();
585 let MakeChunkGroupResult {
586 chunks,
587 mut referenced_output_assets,
588 references,
589 availability_info,
590 } = make_chunk_group(
591 entries,
592 module_graph,
593 ResolvedVc::upcast(self),
594 availability_info,
595 )
596 .await?;
597
598 let chunks = chunks.await?;
599
600 let extra_chunks = extra_chunks.await?;
601 let mut other_chunks = chunks
602 .iter()
603 .map(|chunk| self.generate_chunk(*chunk))
604 .try_join()
605 .await?;
606 other_chunks.extend(extra_chunks.iter().copied());
607
608 referenced_output_assets.extend(extra_referenced_assets.await?.iter().copied());
609
610 let Some(module) = ResolvedVc::try_sidecast(chunk_group.entries().last().unwrap())
611 else {
612 bail!("module must be placeable in an ecmascript chunk");
613 };
614
615 let evaluatable_assets = chunk_group
616 .entries()
617 .map(|entry| {
618 ResolvedVc::try_sidecast::<Box<dyn EvaluatableAsset>>(entry)
619 .context("entry_chunk_group entries must be evaluatable")
620 })
621 .collect::<Result<Vec<_>>>()?;
622
623 let asset = ResolvedVc::upcast(
624 EcmascriptBuildNodeEntryChunk::new(
625 path,
626 Vc::cell(other_chunks),
627 Vc::cell(evaluatable_assets),
628 *module,
629 Vc::cell(referenced_output_assets),
630 Vc::cell(references),
631 module_graph,
632 *self,
633 )
634 .to_resolved()
635 .await?,
636 );
637
638 Ok(EntryChunkGroupResult {
639 asset,
640 availability_info,
641 }
642 .cell())
643 }
644 .instrument(span)
645 .await
646 }
647
648 #[turbo_tasks::function]
649 fn evaluated_chunk_group(
650 self: Vc<Self>,
651 _ident: Vc<AssetIdent>,
652 _chunk_group: ChunkGroup,
653 _module_graph: Vc<ModuleGraph>,
654 _availability_info: AvailabilityInfo,
655 ) -> Result<Vc<ChunkGroupResult>> {
656 bail!("the Node.js chunking context does not support evaluated chunk groups")
657 }
658
659 #[turbo_tasks::function]
660 fn chunk_item_id_strategy(&self) -> Vc<ModuleIdStrategy> {
661 *self
662 .module_id_strategy
663 .unwrap_or_else(|| ModuleIdStrategy::default().resolved_cell())
664 }
665
666 #[turbo_tasks::function]
667 async fn async_loader_chunk_item(
668 self: Vc<Self>,
669 module: Vc<Box<dyn ChunkableModule>>,
670 module_graph: Vc<ModuleGraph>,
671 availability_info: AvailabilityInfo,
672 ) -> Result<Vc<Box<dyn ChunkItem>>> {
673 let chunking_context: ResolvedVc<Box<dyn ChunkingContext>> =
674 Vc::upcast::<Box<dyn ChunkingContext>>(self)
675 .to_resolved()
676 .await?;
677 Ok(if self.await?.manifest_chunks {
678 let manifest_asset = ManifestAsyncModule::new(
679 module,
680 module_graph,
681 *chunking_context,
682 availability_info,
683 )
684 .to_resolved()
685 .await?;
686 let loader_module = ManifestLoaderModule::new(*manifest_asset);
687 loader_module.as_chunk_item(module_graph, *chunking_context)
688 } else {
689 let module = AsyncLoaderModule::new(module, *chunking_context, availability_info);
690 module.as_chunk_item(module_graph, *chunking_context)
691 })
692 }
693
694 #[turbo_tasks::function]
695 async fn async_loader_chunk_item_ident(
696 self: Vc<Self>,
697 module: Vc<Box<dyn ChunkableModule>>,
698 ) -> Result<Vc<AssetIdent>> {
699 Ok(if self.await?.manifest_chunks {
700 ManifestLoaderModule::asset_ident_for(module)
701 } else {
702 AsyncLoaderModule::asset_ident_for(module)
703 })
704 }
705
706 #[turbo_tasks::function]
707 async fn module_export_usage(
708 &self,
709 module: ResolvedVc<Box<dyn Module>>,
710 ) -> Result<Vc<ModuleExportUsage>> {
711 if let Some(export_usage) = self.export_usage {
712 Ok(export_usage.await?.used_exports(module).await?)
713 } else {
714 Ok(ModuleExportUsage::all())
715 }
716 }
717
718 #[turbo_tasks::function]
719 fn unused_references(&self) -> Vc<UnusedReferences> {
720 if let Some(unused_references) = self.unused_references {
721 *unused_references
722 } else {
723 Vc::cell(Default::default())
724 }
725 }
726
727 #[turbo_tasks::function]
728 fn debug_ids_enabled(&self) -> Vc<bool> {
729 Vc::cell(self.debug_ids)
730 }
731
732 #[turbo_tasks::function]
733 fn worker_forwarded_globals(&self) -> Vc<Vec<RcStr>> {
734 Vc::cell(self.worker_forwarded_globals.clone())
735 }
736}