1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::structured_dict::{
7 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
8};
9use crate::bedrock::use_dictionary_router::UseDictionaryRouter;
10use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
11use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
12use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
13use crate::{DictExt, LazyGet, WithPorcelain};
14use async_trait::async_trait;
15use capability_source::{
16 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
17 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
18 InternalCapability, InternalEventStreamCapability, VoidSource,
19};
20use cm_rust::offer::{OfferDecl, OfferDeclCommon};
21use cm_rust::{
22 CapabilityTypeName, DictionaryValue, ExposeDecl, ExposeDeclCommon, FidlIntoNative, SourceName,
23 SourcePath, UseDeclCommon,
24};
25use cm_types::{Availability, BorrowedSeparatedPath, IterablePath, Name, SeparatedPath};
26use fidl::endpoints::DiscoverableProtocolMarker;
27use fidl_fuchsia_component as fcomponent;
28use fidl_fuchsia_component_decl as fdecl;
29use fidl_fuchsia_component_runtime::RouteRequest;
30use fidl_fuchsia_io as fio;
31use fidl_fuchsia_sys2 as fsys;
32use fuchsia_sync::Mutex;
33use futures::FutureExt;
34use itertools::Itertools;
35use log::warn;
36use moniker::{ChildName, Moniker};
37use router_error::RouterError;
38use runtime_capabilities::{
39 Capability, CapabilityBound, Connector, Data, Dictionary, DirConnector, Routable, Router,
40 WeakInstanceToken,
41};
42use std::collections::{BTreeMap, HashMap};
43use std::fmt::Debug;
44use std::sync::{Arc, LazyLock};
45
46pub type EventStreamFilter = Option<BTreeMap<String, DictionaryValue>>;
48
49#[derive(Clone)]
51pub struct EventStreamSourceRouter {
52 pub router: Arc<Router<Dictionary>>,
55 pub filter: EventStreamFilter,
58}
59pub type EventStreamUseRouterFn<C> =
60 dyn Fn(&Arc<C>, Vec<EventStreamSourceRouter>) -> Arc<Router<Connector>>;
61
62static NAMESPACE: LazyLock<Name> = LazyLock::new(|| "namespace".parse().unwrap());
63static NUMBERED_HANDLES: LazyLock<Name> = LazyLock::new(|| "numbered_handles".parse().unwrap());
64static RUNNER: LazyLock<Name> = LazyLock::new(|| "runner".parse().unwrap());
65static CONFIG: LazyLock<Name> = LazyLock::new(|| "config".parse().unwrap());
66
67#[derive(Debug, Clone)]
69pub struct ProgramInput {
70 inner: Arc<Dictionary>,
76}
77
78impl Default for ProgramInput {
79 fn default() -> Self {
80 Self::new(Dictionary::new(), None, Dictionary::new())
81 }
82}
83
84impl From<ProgramInput> for Arc<Dictionary> {
85 fn from(program_input: ProgramInput) -> Self {
86 program_input.inner
87 }
88}
89
90impl ProgramInput {
91 pub fn new(
92 namespace: Arc<Dictionary>,
93 runner: Option<Arc<Router<Connector>>>,
94 config: Arc<Dictionary>,
95 ) -> Self {
96 let inner = Dictionary::new();
97 inner.insert(NAMESPACE.clone(), Capability::Dictionary(namespace));
98 if let Some(runner) = runner {
99 inner.insert(RUNNER.clone(), Capability::ConnectorRouter(runner));
100 }
101 inner.insert(NUMBERED_HANDLES.clone(), Capability::Dictionary(Dictionary::new()));
102 inner.insert(CONFIG.clone(), Capability::Dictionary(config));
103 ProgramInput { inner }
104 }
105
106 pub fn namespace(&self) -> Arc<Dictionary> {
108 let cap = self.inner.get(&*NAMESPACE).unwrap();
109 let Capability::Dictionary(dict) = cap else {
110 unreachable!("namespace entry must be a dictionary: {cap:?}");
111 };
112 dict
113 }
114
115 pub fn numbered_handles(&self) -> Arc<Dictionary> {
117 let cap = self.inner.get(&*NUMBERED_HANDLES).unwrap();
118 let Capability::Dictionary(dict) = cap else {
119 unreachable!("numbered_handles entry must be a dictionary: {cap:?}");
120 };
121 dict
122 }
123
124 pub fn runner(&self) -> Option<Arc<Router<Connector>>> {
126 let cap = self.inner.get(&*RUNNER);
127 match cap {
128 None => None,
129 Some(Capability::ConnectorRouter(r)) => Some(r),
130 cap => unreachable!("runner entry must be a router: {cap:?}"),
131 }
132 }
133
134 fn set_runner(&self, capability: Capability) {
135 let _ = self.inner.insert(RUNNER.clone(), capability);
136 }
137
138 pub fn config(&self) -> Arc<Dictionary> {
140 let cap = self.inner.get(&*CONFIG).unwrap();
141 let Capability::Dictionary(dict) = cap else {
142 unreachable!("config entry must be a dictionary: {cap:?}");
143 };
144 dict
145 }
146}
147
148#[derive(Debug)]
151pub struct ComponentSandbox {
152 pub component_input: ComponentInput,
154
155 pub component_output: ComponentOutput,
157
158 pub program_input: ProgramInput,
160
161 pub program_output_dict: Arc<Dictionary>,
163
164 framework_router: Mutex<Arc<Router<Dictionary>>>,
174
175 pub capability_sourced_capabilities_dict: Arc<Dictionary>,
178
179 pub declared_dictionaries: Arc<Dictionary>,
181
182 pub child_inputs: StructuredDictMap<ComponentInput>,
185
186 pub collection_inputs: StructuredDictMap<ComponentInput>,
190}
191
192impl Default for ComponentSandbox {
193 fn default() -> Self {
194 static NULL_ROUTER: LazyLock<Arc<Router<Dictionary>>> =
195 LazyLock::new(|| Router::new(NullRouter {}));
196 struct NullRouter;
197 #[async_trait]
198 impl Routable<Dictionary> for NullRouter {
199 async fn route(
200 &self,
201 _request: RouteRequest,
202 _target: Arc<WeakInstanceToken>,
203 ) -> Result<Option<Arc<Dictionary>>, RouterError> {
204 panic!("null router invoked");
205 }
206 async fn route_debug(
207 &self,
208 _request: RouteRequest,
209 _target: Arc<WeakInstanceToken>,
210 ) -> Result<CapabilitySource, RouterError> {
211 panic!("null router invoked");
212 }
213 }
214 let framework_router = Mutex::new(NULL_ROUTER.clone());
215 Self {
216 framework_router,
217 component_input: Default::default(),
218 component_output: Default::default(),
219 program_input: Default::default(),
220 program_output_dict: Default::default(),
221 capability_sourced_capabilities_dict: Default::default(),
222 declared_dictionaries: Default::default(),
223 child_inputs: Default::default(),
224 collection_inputs: Default::default(),
225 }
226 }
227}
228
229impl From<ComponentSandbox> for Arc<Dictionary> {
230 fn from(sandbox: ComponentSandbox) -> Arc<Dictionary> {
231 let sandbox_dictionary = Dictionary::new();
232 sandbox_dictionary.insert(
233 Name::new("framework").unwrap(),
234 Capability::DictionaryRouter(sandbox.framework_router.lock().clone()),
235 );
236 sandbox_dictionary.insert(
237 Name::new("component_input").unwrap(),
238 Capability::Dictionary(sandbox.component_input.into()),
239 );
240 sandbox_dictionary.insert(
241 Name::new("component_output").unwrap(),
242 Capability::Dictionary(sandbox.component_output.into()),
243 );
244 sandbox_dictionary.insert(
245 Name::new("program_input").unwrap(),
246 Capability::Dictionary(sandbox.program_input.into()),
247 );
248 sandbox_dictionary.insert(
249 Name::new("program_output").unwrap(),
250 Capability::Dictionary(sandbox.program_output_dict),
251 );
252 sandbox_dictionary.insert(
253 Name::new("capability_sourced").unwrap(),
254 Capability::Dictionary(sandbox.capability_sourced_capabilities_dict),
255 );
256 sandbox_dictionary.insert(
257 Name::new("declared_dictionaries").unwrap(),
258 Capability::Dictionary(sandbox.declared_dictionaries),
259 );
260 sandbox_dictionary.insert(
261 Name::new("child_inputs").unwrap(),
262 Capability::Dictionary(sandbox.child_inputs.into()),
263 );
264 sandbox_dictionary.insert(
265 Name::new("collection_inputs").unwrap(),
266 Capability::Dictionary(sandbox.collection_inputs.into()),
267 );
268 sandbox_dictionary
269 }
270}
271
272impl Clone for ComponentSandbox {
273 fn clone(&self) -> Self {
274 let Self {
275 component_input,
276 component_output,
277 program_input,
278 program_output_dict,
279 framework_router,
280 capability_sourced_capabilities_dict,
281 declared_dictionaries,
282 child_inputs,
283 collection_inputs,
284 } = self;
285 Self {
286 component_input: component_input.clone(),
287 component_output: component_output.clone(),
288 program_input: program_input.clone(),
289 program_output_dict: program_output_dict.clone(),
290 framework_router: Mutex::new(framework_router.lock().clone()),
291 capability_sourced_capabilities_dict: capability_sourced_capabilities_dict.clone(),
292 declared_dictionaries: declared_dictionaries.clone(),
293 child_inputs: child_inputs.clone(),
294 collection_inputs: collection_inputs.clone(),
295 }
296 }
297}
298
299impl ComponentSandbox {
300 pub fn append(&self, sandbox: &ComponentSandbox) {
303 let ComponentSandbox {
306 component_input,
307 component_output,
308 program_input,
309 program_output_dict,
310 framework_router,
311 capability_sourced_capabilities_dict,
312 declared_dictionaries,
313 child_inputs,
314 collection_inputs,
315 } = sandbox;
316 for (copy_from, copy_to) in [
317 (&component_input.capabilities(), &self.component_input.capabilities()),
318 (&component_input.environment().debug(), &self.component_input.environment().debug()),
319 (
320 &component_input.environment().runners(),
321 &self.component_input.environment().runners(),
322 ),
323 (
324 &component_input.environment().resolvers(),
325 &self.component_input.environment().resolvers(),
326 ),
327 (&component_output.capabilities(), &self.component_output.capabilities()),
328 (&component_output.framework(), &self.component_output.framework()),
329 (&program_input.namespace(), &self.program_input.namespace()),
330 (&program_input.numbered_handles(), &self.program_input.numbered_handles()),
331 (&program_input.config(), &self.program_input.config()),
332 (&program_output_dict, &self.program_output_dict),
333 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
334 (&declared_dictionaries, &self.declared_dictionaries),
335 ] {
336 copy_to.append(copy_from).expect("conflicting entry found");
337 }
338 if let Some(timeout) = component_input.environment().stop_timeout() {
339 self.component_input.environment().set_stop_timeout(timeout as i64);
340 }
341 *self.framework_router.lock() = framework_router.lock().clone();
342 if let Some(runner_router) = program_input.runner() {
343 self.program_input.set_runner(runner_router.into());
344 }
345 self.child_inputs.append(child_inputs).unwrap();
346 self.collection_inputs.append(collection_inputs).unwrap();
347 }
348
349 pub fn framework_router(&self) -> Arc<Router<Dictionary>> {
350 self.framework_router.lock().clone()
351 }
352}
353
354pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
357 component: &Arc<C>,
358 child_component_output_dictionary_routers: HashMap<ChildName, Arc<Router<Dictionary>>>,
359 decl: &cm_rust::ComponentDecl,
360 component_input: ComponentInput,
361 program_output_dict: Arc<Dictionary>,
362 framework_router: Arc<Router<Dictionary>>,
363 capability_sourced_capabilities_dict: Arc<Dictionary>,
364 declared_dictionaries: Arc<Dictionary>,
365 error_reporter: impl ErrorReporter,
366 aggregate_router_fn: &AggregateRouterFn<C>,
367 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
368) -> ComponentSandbox {
369 let component_output = ComponentOutput::new();
370 let program_input = ProgramInput::default();
371 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
372 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
373 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
374
375 for environment_decl in &decl.environments {
376 let _ = environments.insert(
377 environment_decl.name.clone(),
378 build_environment(
379 component,
380 &child_component_output_dictionary_routers,
381 &component_input,
382 environment_decl,
383 &program_output_dict,
384 &error_reporter,
385 ),
386 );
387 }
388
389 for child in &decl.children {
390 let environment;
391 if let Some(environment_name) = child.environment.as_ref() {
392 environment = environments.get(environment_name).expect(
393 "child references nonexistent environment, \
394 this should be prevented in manifest validation",
395 );
396 } else {
397 environment = component_input.environment();
398 }
399 let input = ComponentInput::new(environment);
400 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
401 let _ = child_inputs.insert(name, input);
402 }
403
404 for collection in &decl.collections {
405 let environment;
406 if let Some(environment_name) = collection.environment.as_ref() {
407 environment = environments.get(environment_name).expect(
408 "collection references nonexistent environment, \
409 this should be prevented in manifest validation",
410 )
411 } else {
412 environment = component_input.environment();
413 }
414 let input = ComponentInput::new(environment);
415 let _ = collection_inputs.insert(collection.name.clone(), input);
416 }
417
418 let mut dictionary_use_bundles = Vec::with_capacity(decl.uses.len());
419 for use_bundle in group_use_aggregates(&decl.uses).into_iter() {
420 let first_use = *use_bundle.first().unwrap();
421 match first_use {
422 cm_rust::UseDecl::Service(_)
423 if matches!(first_use.source(), cm_rust::UseSource::Collection(_)) =>
424 {
425 let cm_rust::UseSource::Collection(collection_name) = first_use.source() else {
426 unreachable!();
427 };
428 let availability = *first_use.availability();
429 let aggregate = (aggregate_router_fn)(
430 component.clone(),
431 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
432 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
433 capability: AggregateCapability::Service(first_use.source_name().clone()),
434 moniker: component.moniker().clone(),
435 members: vec![AggregateMember::try_from(first_use).unwrap()],
436 instances: vec![],
437 }),
438 )
439 .with_porcelain_with_default(CapabilityTypeName::Service)
440 .availability(availability)
441 .target(component)
442 .error_info(first_use)
443 .error_reporter(error_reporter.clone())
444 .log_errors()
445 .build();
446 let prev = program_input
447 .namespace()
448 .insert_capability(first_use.path().unwrap(), aggregate.into());
449 assert!(
450 prev.is_none(),
451 "failed to insert {}: preexisting value",
452 first_use.path().unwrap()
453 );
454 }
455 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirConnector, _>(
456 component,
457 &child_component_output_dictionary_routers,
458 &component_input,
459 &program_input,
460 &program_output_dict,
461 &framework_router,
462 &capability_sourced_capabilities_dict,
463 first_use,
464 error_reporter.clone(),
465 ),
466 cm_rust::UseDecl::Directory(_) | cm_rust::UseDecl::Storage(_) => {
467 extend_dict_with_use::<DirConnector, C>(
468 component,
469 &child_component_output_dictionary_routers,
470 &component_input,
471 &program_input,
472 &program_output_dict,
473 &framework_router,
474 &capability_sourced_capabilities_dict,
475 first_use,
476 error_reporter.clone(),
477 )
478 }
479 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
480 extend_dict_with_use::<Connector, _>(
481 component,
482 &child_component_output_dictionary_routers,
483 &component_input,
484 &program_input,
485 &program_output_dict,
486 &framework_router,
487 &capability_sourced_capabilities_dict,
488 first_use,
489 error_reporter.clone(),
490 )
491 }
492 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
493 component,
494 &child_component_output_dictionary_routers,
495 &component_input,
496 &program_input,
497 &program_output_dict,
498 config,
499 error_reporter.clone(),
500 ),
501 cm_rust::UseDecl::EventStream(_) => extend_dict_with_event_stream_uses(
502 component,
503 &component_input,
504 &program_input,
505 use_bundle,
506 error_reporter.clone(),
507 event_stream_use_router_fn,
508 ),
509 cm_rust::UseDecl::Dictionary(_) => {
510 dictionary_use_bundles.push(use_bundle);
511 }
512 }
513 }
514
515 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
519 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
520 extend_dict_with_use::<Connector, _>(
521 component,
522 &child_component_output_dictionary_routers,
523 &component_input,
524 &program_input,
525 &program_output_dict,
526 &framework_router,
527 &capability_sourced_capabilities_dict,
528 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
529 source: cm_rust::UseSource::Environment,
530 source_name: runner_name.clone(),
531 source_dictionary: Default::default(),
532 }),
533 error_reporter.clone(),
534 )
535 }
536 }
537
538 for dictionary_use_bundle in dictionary_use_bundles {
545 extend_dict_with_dictionary_use(
546 component,
547 &child_component_output_dictionary_routers,
548 &component_input,
549 &program_input,
550 &program_output_dict,
551 &framework_router,
552 &capability_sourced_capabilities_dict,
553 dictionary_use_bundle,
554 error_reporter.clone(),
555 )
556 }
557
558 for offer_bundle in group_offer_aggregates(&decl.offers) {
559 let first_offer = offer_bundle.first().unwrap();
560 let get_target_dict = || match first_offer.target() {
561 cm_rust::offer::OfferTarget::Child(child_ref) => {
562 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
563 let child_name = Name::new(child_ref.name.as_str())
564 .expect("child is static so name is not long");
565 if child_inputs.get(&child_name).is_none() {
566 let _ = child_inputs.insert(child_name.clone(), Default::default());
567 }
568 child_inputs
569 .get(&child_name)
570 .expect("component input was just added")
571 .capabilities()
572 }
573 cm_rust::offer::OfferTarget::Collection(name) => {
574 if collection_inputs.get(&name).is_none() {
575 let _ = collection_inputs.insert(name.clone(), Default::default());
576 }
577 collection_inputs
578 .get(&name)
579 .expect("collection input was just added")
580 .capabilities()
581 }
582 cm_rust::offer::OfferTarget::Capability(name) => {
583 let dict = match declared_dictionaries.get(name) {
584 Some(dict) => dict,
585 None => {
586 let dict = Dictionary::new();
587 let _ = declared_dictionaries
588 .insert(name.clone(), Capability::Dictionary(dict.clone()));
589 Capability::Dictionary(dict)
590 }
591 };
592 let Capability::Dictionary(dict) = dict else {
593 panic!("wrong type in dictionary");
594 };
595 dict
596 }
597 };
598 match first_offer {
599 cm_rust::offer::OfferDecl::Service(_)
600 if offer_bundle.len() == 1
601 && !matches!(
602 first_offer.source(),
603 cm_rust::offer::OfferSource::Collection(_)
604 ) =>
605 {
606 extend_dict_with_offer::<DirConnector, _>(
607 component,
608 &child_component_output_dictionary_routers,
609 &component_input,
610 &program_output_dict,
611 &framework_router,
612 &capability_sourced_capabilities_dict,
613 first_offer,
614 &(get_target_dict)(),
615 error_reporter.clone(),
616 )
617 }
618 cm_rust::offer::OfferDecl::Service(_) => {
619 let aggregate_router = new_aggregate_router_from_service_offers(
620 &offer_bundle,
621 component,
622 &child_component_output_dictionary_routers,
623 &component_input,
624 &program_output_dict,
625 &framework_router,
626 &capability_sourced_capabilities_dict,
627 error_reporter.clone(),
628 aggregate_router_fn,
629 );
630 (get_target_dict)()
631 .insert(first_offer.target_name().clone(), aggregate_router.into());
632 }
633 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
634 component,
635 &child_component_output_dictionary_routers,
636 &component_input,
637 &program_output_dict,
638 &framework_router,
639 &capability_sourced_capabilities_dict,
640 first_offer,
641 &(get_target_dict)(),
642 error_reporter.clone(),
643 ),
644 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
645 extend_dict_with_offer::<DirConnector, _>(
646 component,
647 &child_component_output_dictionary_routers,
648 &component_input,
649 &program_output_dict,
650 &framework_router,
651 &capability_sourced_capabilities_dict,
652 first_offer,
653 &(get_target_dict)(),
654 error_reporter.clone(),
655 )
656 }
657 cm_rust::offer::OfferDecl::Dictionary(_)
658 | cm_rust::offer::OfferDecl::EventStream(_) => extend_dict_with_offer::<Dictionary, _>(
659 component,
660 &child_component_output_dictionary_routers,
661 &component_input,
662 &program_output_dict,
663 &framework_router,
664 &capability_sourced_capabilities_dict,
665 first_offer,
666 &(get_target_dict)(),
667 error_reporter.clone(),
668 ),
669 cm_rust::offer::OfferDecl::Protocol(_)
670 | cm_rust::offer::OfferDecl::Runner(_)
671 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
672 component,
673 &child_component_output_dictionary_routers,
674 &component_input,
675 &program_output_dict,
676 &framework_router,
677 &capability_sourced_capabilities_dict,
678 first_offer,
679 &(get_target_dict)(),
680 error_reporter.clone(),
681 ),
682 }
683 }
684
685 for expose_bundle in group_expose_aggregates(&decl.exposes) {
686 let first_expose = expose_bundle.first().unwrap();
687 match first_expose {
688 cm_rust::ExposeDecl::Service(_)
689 if expose_bundle.len() == 1
690 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
691 {
692 extend_dict_with_expose::<DirConnector, _>(
693 component,
694 &child_component_output_dictionary_routers,
695 &program_output_dict,
696 &framework_router,
697 &capability_sourced_capabilities_dict,
698 first_expose,
699 &component_output,
700 error_reporter.clone(),
701 )
702 }
703 cm_rust::ExposeDecl::Service(_) => {
704 let mut aggregate_sources = vec![];
705 let temp_component_output = ComponentOutput::new();
706 for expose in expose_bundle.iter() {
707 extend_dict_with_expose::<DirConnector, _>(
708 component,
709 &child_component_output_dictionary_routers,
710 &program_output_dict,
711 &framework_router,
712 &capability_sourced_capabilities_dict,
713 expose,
714 &temp_component_output,
715 error_reporter.clone(),
716 );
717 match temp_component_output.capabilities().remove(first_expose.target_name()) {
718 Some(Capability::DirConnectorRouter(router)) => {
719 let source_instance = match expose.source() {
720 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
721 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
722 moniker::ChildName::new(name.clone().to_long(), None),
723 ),
724 other_source => {
725 warn!(
726 "unsupported source found in expose aggregate: {:?}",
727 other_source
728 );
729 continue;
730 }
731 };
732 aggregate_sources
733 .push(AggregateSource::DirectoryRouter { source_instance, router })
734 }
735 None => match expose.source() {
736 cm_rust::ExposeSource::Collection(collection_name) => {
737 aggregate_sources.push(AggregateSource::Collection {
738 collection_name: collection_name.clone(),
739 });
740 }
741 _ => continue,
742 },
743 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
744 }
745 }
746 let availability = *first_expose.availability();
747 let aggregate = (aggregate_router_fn)(
748 component.clone(),
749 aggregate_sources,
750 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
751 capability: AggregateCapability::Service(
752 first_expose.target_name().clone(),
753 ),
754 moniker: component.moniker().clone(),
755 members: expose_bundle
756 .iter()
757 .filter_map(|e| AggregateMember::try_from(*e).ok())
758 .collect(),
759 instances: vec![],
760 }),
761 );
762 let router = aggregate
763 .with_porcelain_with_default(CapabilityTypeName::Service)
764 .availability(availability)
765 .target(component)
766 .error_info(*first_expose)
767 .error_reporter(error_reporter.clone())
768 .build();
769 let target_name = first_expose.target_name().clone();
770 let prev = component_output.capabilities().insert(target_name, router.into());
771 assert!(
772 prev.is_none(),
773 "failed to insert {}: preexisting value",
774 first_expose.target_name()
775 );
776 }
777 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
778 component,
779 &child_component_output_dictionary_routers,
780 &program_output_dict,
781 &framework_router,
782 &capability_sourced_capabilities_dict,
783 first_expose,
784 &component_output,
785 error_reporter.clone(),
786 ),
787 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dictionary, _>(
788 component,
789 &child_component_output_dictionary_routers,
790 &program_output_dict,
791 &framework_router,
792 &capability_sourced_capabilities_dict,
793 first_expose,
794 &component_output,
795 error_reporter.clone(),
796 ),
797 cm_rust::ExposeDecl::Directory(_) => extend_dict_with_expose::<DirConnector, _>(
798 component,
799 &child_component_output_dictionary_routers,
800 &program_output_dict,
801 &framework_router,
802 &capability_sourced_capabilities_dict,
803 first_expose,
804 &component_output,
805 error_reporter.clone(),
806 ),
807 cm_rust::ExposeDecl::Protocol(_)
808 | cm_rust::ExposeDecl::Runner(_)
809 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
810 component,
811 &child_component_output_dictionary_routers,
812 &program_output_dict,
813 &framework_router,
814 &capability_sourced_capabilities_dict,
815 first_expose,
816 &component_output,
817 error_reporter.clone(),
818 ),
819 }
820 }
821
822 ComponentSandbox {
823 component_input,
824 component_output,
825 program_input,
826 program_output_dict,
827 framework_router: Mutex::new(framework_router),
828 capability_sourced_capabilities_dict,
829 declared_dictionaries,
830 child_inputs,
831 collection_inputs,
832 }
833}
834
835fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
836 offer_bundle: &Vec<&cm_rust::offer::OfferDecl>,
837 component: &Arc<C>,
838 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
839 component_input: &ComponentInput,
840 program_output_dict: &Arc<Dictionary>,
841 framework_router: &Arc<Router<Dictionary>>,
842 capability_sourced_capabilities_dict: &Arc<Dictionary>,
843 error_reporter: impl ErrorReporter,
844 aggregate_router_fn: &AggregateRouterFn<C>,
845) -> Arc<Router<DirConnector>> {
846 let mut aggregate_sources = vec![];
847 let dict_for_source_router = Dictionary::new();
848 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
849 for offer in offer_bundle.iter() {
850 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
851 if let cm_rust::offer::OfferDecl::Service(offer_service_decl) = offer {
852 if offer_service_decl
853 .source_instance_filter
854 .as_ref()
855 .and_then(|v| v.first())
856 .is_none()
857 && offer_service_decl
858 .renamed_instances
859 .as_ref()
860 .and_then(|v| v.first())
861 .is_none()
862 {
863 continue;
867 }
868 }
869 }
870 extend_dict_with_offer::<DirConnector, _>(
871 component,
872 &child_component_output_dictionary_routers,
873 &component_input,
874 &program_output_dict,
875 framework_router,
876 &capability_sourced_capabilities_dict,
877 offer,
878 &dict_for_source_router,
879 error_reporter.clone(),
880 );
881 match dict_for_source_router.remove(offer.target_name()) {
882 Some(Capability::DirConnectorRouter(router)) => {
883 let source_instance = match offer.source() {
884 cm_rust::offer::OfferSource::Self_ => AggregateInstance::Self_,
885 cm_rust::offer::OfferSource::Parent => AggregateInstance::Parent,
886 cm_rust::offer::OfferSource::Child(child_ref) => {
887 AggregateInstance::Child(moniker::ChildName::new(
888 child_ref.name.clone(),
889 child_ref.collection.clone(),
890 ))
891 }
892 other_source => {
893 warn!("unsupported source found in offer aggregate: {:?}", other_source);
894 continue;
895 }
896 };
897 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
898 }
899 None => match offer.source() {
900 cm_rust::offer::OfferSource::Collection(collection_name) => {
904 aggregate_sources.push(AggregateSource::Collection {
905 collection_name: collection_name.clone(),
906 });
907 }
908 _ => continue,
909 },
910 other => warn!("found unexpected entry in dictionary: {:?}", other),
911 }
912 }
913 (aggregate_router_fn)(component.clone(), aggregate_sources, source)
914}
915
916fn new_aggregate_capability_source(
917 moniker: Moniker,
918 offers: Vec<&cm_rust::offer::OfferDecl>,
919) -> CapabilitySource {
920 let offer_service_decls = offers
921 .iter()
922 .map(|o| match o {
923 cm_rust::offer::OfferDecl::Service(o) => o.as_ref(),
924 _ => panic!(
925 "cannot aggregate non-service capabilities, manifest validation should prevent this"
926 ),
927 })
928 .collect::<Vec<_>>();
929 let is_filtered_offer = offer_service_decls.iter().any(|o| {
931 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
932 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
933 });
934 let capability =
935 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
936 if is_filtered_offer {
937 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
938 capability,
939 moniker,
940 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
941 })
942 } else {
943 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
944 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
945 capability,
946 moniker,
947 members,
948 instances: vec![],
949 })
950 }
951}
952
953fn group_use_aggregates<'a>(
957 uses: &'a [cm_rust::UseDecl],
958) -> impl Iterator<Item = Vec<&'a cm_rust::UseDecl>> + 'a {
959 let mut groupings = HashMap::with_capacity(uses.len());
960 let mut ungroupable_uses = Vec::new();
961 for use_ in uses.iter() {
962 if let Some(target_path) = use_.path() {
963 groupings.entry(target_path).or_insert_with(|| Vec::with_capacity(1)).push(use_);
964 } else {
965 ungroupable_uses.push(use_);
966 }
967 }
968 groupings.into_values().chain(ungroupable_uses.into_iter().map(|u| vec![u]))
969}
970
971fn group_offer_aggregates<'a>(
975 offers: &'a [cm_rust::offer::OfferDecl],
976) -> impl Iterator<Item = Vec<&'a cm_rust::offer::OfferDecl>> + 'a {
977 let mut groupings = HashMap::with_capacity(offers.len());
978
979 for offer in offers {
980 groupings
981 .entry((offer.target(), offer.target_name()))
982 .or_insert_with(|| Vec::with_capacity(1))
983 .push(offer);
984 }
985 groupings.into_values()
986}
987
988fn group_expose_aggregates<'a>(
990 exposes: &'a [cm_rust::ExposeDecl],
991) -> impl Iterator<Item = Vec<&'a cm_rust::ExposeDecl>> + 'a {
992 let mut groupings = HashMap::with_capacity(exposes.len());
993 for expose in exposes {
994 groupings
995 .entry((expose.target(), expose.target_name()))
996 .or_insert_with(|| Vec::with_capacity(1))
997 .push(expose);
998 }
999 groupings.into_values()
1000}
1001
1002fn build_environment<C: ComponentInstanceInterface + 'static>(
1003 component: &Arc<C>,
1004 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1005 component_input: &ComponentInput,
1006 environment_decl: &cm_rust::EnvironmentDecl,
1007 program_output_dict: &Arc<Dictionary>,
1008 error_reporter: &impl ErrorReporter,
1009) -> ComponentEnvironment {
1010 let mut environment = ComponentEnvironment::new();
1011 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
1012 environment = component_input.environment().shallow_copy();
1013 }
1014 environment.set_name(&environment_decl.name);
1015 if let Some(stop_timeout_ms) = environment_decl.stop_timeout_ms {
1016 environment.set_stop_timeout(stop_timeout_ms as i64);
1017 }
1018 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
1019 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
1020 (
1021 &debug.source_name,
1022 debug.target_name.clone(),
1023 &debug.source,
1024 CapabilityTypeName::Protocol,
1025 RouteRequestErrorInfo::from(debug_registration),
1026 )
1027 });
1028 let runners = environment_decl.runners.iter().map(|runner| {
1029 (
1030 &runner.source_name,
1031 runner.target_name.clone(),
1032 &runner.source,
1033 CapabilityTypeName::Runner,
1034 RouteRequestErrorInfo::from(runner),
1035 )
1036 });
1037 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
1038 (
1039 &resolver.resolver,
1040 Name::new(&resolver.scheme).unwrap(),
1041 &resolver.source,
1042 CapabilityTypeName::Resolver,
1043 RouteRequestErrorInfo::from(resolver),
1044 )
1045 });
1046 let moniker = component.moniker();
1047 for (source_name, target_name, source, porcelain_type, route_request) in
1048 debug.chain(runners).chain(resolvers)
1049 {
1050 let source_path =
1051 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
1052 let router: Arc<Router<Connector>> = match &source {
1053 cm_rust::RegistrationSource::Parent => {
1054 use_from_parent_router::<Connector>(component_input, source_path, moniker)
1055 }
1056 cm_rust::RegistrationSource::Self_ => program_output_dict
1057 .get_router_or_not_found::<Connector>(
1058 &source_path,
1059 RoutingError::use_from_self_not_found(
1060 moniker,
1061 source_path.iter_segments().join("/"),
1062 ),
1063 ),
1064 cm_rust::RegistrationSource::Child(child_name) => {
1065 let child_name = ChildName::parse(child_name).expect("invalid child name");
1066 let Some(child_component_output) =
1067 child_component_output_dictionary_routers.get(&child_name)
1068 else {
1069 continue;
1070 };
1071 child_component_output.clone().lazy_get(
1072 source_path,
1073 RoutingError::use_from_child_expose_not_found(
1074 &child_name,
1075 moniker,
1076 source_name.clone(),
1077 ),
1078 )
1079 }
1080 };
1081 let router = router
1082 .with_porcelain_no_default(porcelain_type)
1083 .availability(Availability::Required)
1084 .target(component)
1085 .error_info(route_request)
1086 .error_reporter(error_reporter.clone())
1087 .build();
1088 let dict_to_insert_to = match porcelain_type {
1089 CapabilityTypeName::Protocol => environment.debug(),
1090 CapabilityTypeName::Runner => environment.runners(),
1091 CapabilityTypeName::Resolver => environment.resolvers(),
1092 c => panic!("unexpected capability type {}", c),
1093 };
1094 let prev =
1095 dict_to_insert_to.insert_capability(&target_name, Capability::ConnectorRouter(router));
1096 if prev.is_some() {
1097 log::warn!("failed to insert {porcelain_type} {target_name}: preexisting value");
1098 }
1099 }
1100 environment
1101}
1102
1103pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
1105 component: &Arc<C>,
1106 static_offers: &[cm_rust::offer::OfferDecl],
1107 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1108 component_input: &ComponentInput,
1109 dynamic_offers: &[cm_rust::offer::OfferDecl],
1110 program_output_dict: &Arc<Dictionary>,
1111 framework_router: &Arc<Router<Dictionary>>,
1112 capability_sourced_capabilities_dict: &Arc<Dictionary>,
1113 target_input: &ComponentInput,
1114 error_reporter: impl ErrorReporter,
1115 aggregate_router_fn: &AggregateRouterFn<C>,
1116) {
1117 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
1118 let first_offer = offer_bundle.first().unwrap();
1119 match first_offer {
1120 cm_rust::offer::OfferDecl::Service(_) => {
1121 let static_offer_bundles = group_offer_aggregates(static_offers);
1122 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
1123 bundle.first().unwrap().target_name() == first_offer.target_name()
1124 });
1125 let mut combined_offer_bundle = offer_bundle.clone();
1126 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
1127 let _ = target_input.capabilities().remove(first_offer.target_name());
1132 combined_offer_bundle.append(&mut static_offer_bundle);
1133 }
1134 if combined_offer_bundle.len() == 1
1135 && !matches!(first_offer.source(), cm_rust::offer::OfferSource::Collection(_))
1136 {
1137 extend_dict_with_offer::<DirConnector, _>(
1138 component,
1139 &child_component_output_dictionary_routers,
1140 &component_input,
1141 &program_output_dict,
1142 framework_router,
1143 &capability_sourced_capabilities_dict,
1144 first_offer,
1145 &target_input.capabilities(),
1146 error_reporter.clone(),
1147 )
1148 } else {
1149 let aggregate_router = new_aggregate_router_from_service_offers(
1150 &combined_offer_bundle,
1151 component,
1152 &child_component_output_dictionary_routers,
1153 &component_input,
1154 &program_output_dict,
1155 framework_router,
1156 &capability_sourced_capabilities_dict,
1157 error_reporter.clone(),
1158 aggregate_router_fn,
1159 );
1160 let prev = target_input
1161 .capabilities()
1162 .insert(first_offer.target_name().clone(), aggregate_router.into());
1163 assert!(prev.is_none(), "failed to insert capability into target dict");
1164 }
1165 }
1166 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
1167 component,
1168 &child_component_output_dictionary_routers,
1169 component_input,
1170 program_output_dict,
1171 framework_router,
1172 capability_sourced_capabilities_dict,
1173 first_offer,
1174 &target_input.capabilities(),
1175 error_reporter.clone(),
1176 ),
1177 cm_rust::offer::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dictionary, _>(
1178 component,
1179 &child_component_output_dictionary_routers,
1180 component_input,
1181 program_output_dict,
1182 framework_router,
1183 capability_sourced_capabilities_dict,
1184 first_offer,
1185 &target_input.capabilities(),
1186 error_reporter.clone(),
1187 ),
1188 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
1189 extend_dict_with_offer::<DirConnector, _>(
1190 component,
1191 &child_component_output_dictionary_routers,
1192 component_input,
1193 program_output_dict,
1194 framework_router,
1195 capability_sourced_capabilities_dict,
1196 first_offer,
1197 &target_input.capabilities(),
1198 error_reporter.clone(),
1199 )
1200 }
1201 cm_rust::offer::OfferDecl::Protocol(_)
1202 | cm_rust::offer::OfferDecl::Runner(_)
1203 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
1204 component,
1205 &child_component_output_dictionary_routers,
1206 component_input,
1207 program_output_dict,
1208 framework_router,
1209 capability_sourced_capabilities_dict,
1210 first_offer,
1211 &target_input.capabilities(),
1212 error_reporter.clone(),
1213 ),
1214 _ => {}
1215 }
1216 }
1217}
1218
1219pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
1220 matches!(
1221 use_,
1222 cm_rust::UseDecl::Config(_)
1223 | cm_rust::UseDecl::Protocol(_)
1224 | cm_rust::UseDecl::Runner(_)
1225 | cm_rust::UseDecl::Service(_)
1226 | cm_rust::UseDecl::Directory(_)
1227 | cm_rust::UseDecl::EventStream(_)
1228 | cm_rust::UseDecl::Dictionary(_)
1229 | cm_rust::UseDecl::Storage(_)
1230 )
1231}
1232
1233fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
1236 component: &Arc<C>,
1237 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1238 component_input: &ComponentInput,
1239 program_input: &ProgramInput,
1240 program_output_dict: &Arc<Dictionary>,
1241 config_use: &cm_rust::UseConfigurationDecl,
1242 error_reporter: impl ErrorReporter,
1243) {
1244 let moniker = component.moniker();
1245 let source_path = config_use.source_path();
1246 let porcelain_type = CapabilityTypeName::Config;
1247 let router: Arc<Router<Data>> = match config_use.source() {
1248 cm_rust::UseSource::Parent => {
1249 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
1250 }
1251 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
1252 &source_path,
1253 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1254 ),
1255 cm_rust::UseSource::Child(child_name) => {
1256 let child_name = ChildName::parse(child_name).expect("invalid child name");
1257 let Some(child_component_output) =
1258 child_component_output_dictionary_routers.get(&child_name)
1259 else {
1260 panic!(
1261 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1262 moniker, child_name
1263 );
1264 };
1265 child_component_output.clone().lazy_get(
1266 source_path.to_owned(),
1267 RoutingError::use_from_child_expose_not_found(
1268 &child_name,
1269 &moniker,
1270 config_use.source_name().clone(),
1271 ),
1272 )
1273 }
1274 cm_rust::UseSource::Environment => return,
1276 cm_rust::UseSource::Debug => return,
1277 cm_rust::UseSource::Capability(_) => return,
1278 cm_rust::UseSource::Framework => return,
1279 cm_rust::UseSource::Collection(_) => return,
1280 };
1281
1282 let availability = *config_use.availability();
1283 let prev = program_input.config().insert_capability(
1284 &config_use.target_name,
1285 Capability::DataRouter(
1286 router
1287 .with_porcelain_with_default(porcelain_type)
1288 .availability(availability)
1289 .target(component)
1290 .error_info(config_use)
1291 .error_reporter(error_reporter)
1292 .log_errors()
1293 .build(),
1294 ),
1295 );
1296 assert!(prev.is_none(), "failed to insert {}: preexisting value", config_use.target_name);
1297}
1298
1299fn extend_dict_with_event_stream_uses<C: ComponentInstanceInterface + 'static>(
1300 component: &Arc<C>,
1301 component_input: &ComponentInput,
1302 program_input: &ProgramInput,
1303 uses: Vec<&cm_rust::UseDecl>,
1304 error_reporter: impl ErrorReporter,
1305 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
1306) {
1307 let use_event_stream_decls = uses.into_iter().map(|u| match u {
1308 cm_rust::UseDecl::EventStream(decl) => decl,
1309 _other_use => panic!("conflicting use types share target path, this should be prevented by manifest validation"),
1310 }).collect::<Vec<_>>();
1311 let moniker = component.moniker();
1312 let porcelain_type = CapabilityTypeName::EventStream;
1313 let target_path = use_event_stream_decls.first().unwrap().target_path.clone();
1314 for use_event_stream_decl in &use_event_stream_decls {
1315 assert_eq!(
1316 &use_event_stream_decl.source,
1317 &cm_rust::UseSource::Parent,
1318 "event streams can only be used from parent, anything else should be caught by \
1319 manifest validation",
1320 );
1321 }
1322 let routers = use_event_stream_decls
1323 .into_iter()
1324 .map(|use_event_stream_decl| {
1325 let source_path = use_event_stream_decl.source_path().to_owned();
1326 let mut router_builder =
1327 use_from_parent_router::<Dictionary>(component_input, source_path, &moniker)
1328 .with_porcelain_with_default(porcelain_type)
1329 .availability(use_event_stream_decl.availability)
1330 .target(component)
1331 .error_info(RouteRequestErrorInfo::from(use_event_stream_decl.as_ref()))
1332 .error_reporter(error_reporter.clone())
1333 .log_errors();
1334 if let Some(scope) = &use_event_stream_decl.scope {
1335 router_builder =
1336 router_builder.event_stream_scope((component.moniker().clone(), scope.clone()));
1337 }
1338 let router = router_builder.build();
1339 let filter = use_event_stream_decl.filter.clone();
1340 EventStreamSourceRouter { router, filter }
1341 })
1342 .collect::<Vec<_>>();
1343
1344 let router = event_stream_use_router_fn(component, routers);
1345 let prev = program_input
1346 .namespace()
1347 .insert_capability(&target_path, Capability::ConnectorRouter(router));
1348 assert!(prev.is_none(), "failed to insert {target_path}: preexisting value");
1349}
1350
1351fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1352 component: &Arc<C>,
1353 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1354 component_input: &ComponentInput,
1355 program_input: &ProgramInput,
1356 program_output_dict: &Arc<Dictionary>,
1357 framework_router: &Arc<Router<Dictionary>>,
1358 capability_sourced_capabilities_dict: &Arc<Dictionary>,
1359 use_: &cm_rust::UseDecl,
1360 error_reporter: impl ErrorReporter,
1361) where
1362 T: CapabilityBound,
1363 Arc<T>: TryFrom<Capability>,
1364 Router<T>: CapabilityBound,
1365 Arc<Router<T>>: TryFrom<Capability>,
1366 Capability: From<Arc<T>>,
1367 Capability: From<Arc<Router<T>>>,
1368{
1369 if !is_supported_use(use_) {
1370 return;
1371 }
1372 let moniker = component.moniker();
1373 if let cm_rust::UseDecl::Config(config) = use_ {
1374 return extend_dict_with_config_use(
1375 component,
1376 child_component_output_dictionary_routers,
1377 component_input,
1378 program_input,
1379 program_output_dict,
1380 config,
1381 error_reporter,
1382 );
1383 };
1384
1385 let source_path = use_.source_path();
1386 let porcelain_type = CapabilityTypeName::from(use_);
1387 let router: Arc<Router<T>> = match use_.source() {
1388 cm_rust::UseSource::Parent => {
1389 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1390 }
1391 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1392 &source_path,
1393 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1394 ),
1395 cm_rust::UseSource::Child(child_name) => {
1396 let child_name = ChildName::parse(child_name).expect("invalid child name");
1397 let Some(child_component_output) =
1398 child_component_output_dictionary_routers.get(&child_name)
1399 else {
1400 panic!(
1401 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1402 moniker, child_name
1403 );
1404 };
1405 child_component_output.clone().lazy_get(
1406 source_path.to_owned(),
1407 RoutingError::use_from_child_expose_not_found(
1408 &child_name,
1409 &moniker,
1410 use_.source_name().clone(),
1411 ),
1412 )
1413 }
1414 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1415 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1416 moniker,
1417 source_path.iter_segments().join("/"),
1418 ))
1419 }
1420 cm_rust::UseSource::Framework => {
1421 query_framework_router_or_not_found(framework_router, &source_path, component)
1422 }
1423 cm_rust::UseSource::Capability(capability_name) => {
1424 let err = RoutingError::capability_from_capability_not_found(
1425 moniker,
1426 capability_name.as_str().to_string(),
1427 );
1428 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1429 || source_path.iter_segments().join("/")
1430 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1431 {
1432 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1433 } else {
1434 Router::<T>::new_error(err)
1435 }
1436 }
1437 cm_rust::UseSource::Debug => {
1438 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1439 panic!(
1440 "non-protocol capability used with a debug source, this should be prevented by manifest validation"
1441 );
1442 };
1443 component_input.environment().debug().get_router_or_not_found::<T>(
1444 &use_protocol.source_name,
1445 RoutingError::use_from_environment_not_found(
1446 moniker,
1447 "protocol",
1448 &use_protocol.source_name,
1449 ),
1450 )
1451 }
1452 cm_rust::UseSource::Environment => {
1453 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1454 panic!(
1455 "non-runner capability used with an environment source, this should be prevented by manifest validation"
1456 );
1457 };
1458 component_input.environment().runners().get_router_or_not_found::<T>(
1459 &use_runner.source_name,
1460 RoutingError::use_from_environment_not_found(
1461 moniker,
1462 "runner",
1463 &use_runner.source_name,
1464 ),
1465 )
1466 }
1467 cm_rust::UseSource::Collection(_) => {
1468 return;
1470 }
1471 };
1472
1473 let availability = *use_.availability();
1474 let mut router_builder = router
1475 .with_porcelain_with_default(porcelain_type)
1476 .availability(availability)
1477 .target(&component)
1478 .error_info(use_)
1479 .error_reporter(error_reporter)
1480 .log_errors()
1481 .with_tracing();
1482 if let cm_rust::UseDecl::Directory(decl) = use_ {
1483 router_builder = router_builder
1484 .rights(Some(decl.rights.into()))
1485 .subdir(decl.subdir.clone().into())
1486 .inherit_rights(false);
1487 }
1488 if let cm_rust::UseDecl::Service(_) = use_ {
1489 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(false);
1490 }
1491 if let cm_rust::UseDecl::Storage(_) = use_ {
1492 router_builder = router_builder
1493 .rights(Some(fidl_fuchsia_io::RW_STAR_DIR.into()))
1494 .subdir(cm_types::RelativePath::dot().into())
1495 .inherit_rights(false);
1496 }
1497 let router = router_builder.build();
1498
1499 match use_ {
1500 cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
1501 numbered_handle: Some(numbered_handle),
1502 ..
1503 }) => {
1504 let numbered_handle = Name::from(*numbered_handle);
1505 let prev =
1506 program_input.numbered_handles().insert_capability(&numbered_handle, router.into());
1507 assert!(prev.is_none(), "failed to insert {numbered_handle}: preexisting value");
1508 }
1509 cm_rust::UseDecl::Runner(_) => {
1510 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1511 program_input.set_runner(router.into());
1512 }
1513 _ => {
1514 let prev =
1515 program_input.namespace().insert_capability(use_.path().unwrap(), router.into());
1516 assert!(prev.is_none(), "failed to insert {}: preexisting value", use_.path().unwrap());
1517 }
1518 }
1519}
1520
1521fn extend_dict_with_dictionary_use<C: ComponentInstanceInterface + 'static>(
1522 component: &Arc<C>,
1523 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1524 component_input: &ComponentInput,
1525 program_input: &ProgramInput,
1526 program_output_dict: &Arc<Dictionary>,
1527 framework_router: &Arc<Router<Dictionary>>,
1528 capability_sourced_capabilities_dict: &Arc<Dictionary>,
1529 use_bundle: Vec<&cm_rust::UseDecl>,
1530 error_reporter: impl ErrorReporter,
1531) {
1532 let path = use_bundle[0].path().unwrap();
1533 let mut dictionary_routers = vec![];
1534 for use_ in use_bundle.iter() {
1535 let dict_for_used_router = ProgramInput::new(Dictionary::new(), None, Dictionary::new());
1536 extend_dict_with_use::<Dictionary, _>(
1537 component,
1538 child_component_output_dictionary_routers,
1539 component_input,
1540 &dict_for_used_router,
1541 program_output_dict,
1542 framework_router,
1543 capability_sourced_capabilities_dict,
1544 use_,
1545 error_reporter.clone(),
1546 );
1547 let dictionary_router = match dict_for_used_router.namespace().get_capability(path) {
1548 Some(Capability::DictionaryRouter(router)) => router,
1549 other_value => panic!("unexpected dictionary get result: {other_value:?}"),
1550 };
1551 dictionary_routers.push(dictionary_router);
1552 }
1553 let original_dictionary = match program_input.namespace().get_capability(path) {
1554 Some(Capability::Dictionary(dictionary)) => dictionary,
1555 _ => Dictionary::new(),
1556 };
1557 let router = UseDictionaryRouter::new(
1558 path.clone(),
1559 component.moniker().clone(),
1560 original_dictionary,
1561 dictionary_routers,
1562 CapabilitySource::Component(ComponentSource {
1563 capability: ComponentCapability::Use_((*use_bundle.first().unwrap()).clone()),
1564 moniker: component.moniker().clone(),
1565 }),
1566 );
1567 let _ = program_input.namespace().insert_capability(path, Capability::DictionaryRouter(router));
1570}
1571
1572fn use_from_parent_router<T>(
1574 component_input: &ComponentInput,
1575 source_path: impl IterablePath + 'static + Debug,
1576 moniker: &Moniker,
1577) -> Arc<Router<T>>
1578where
1579 T: CapabilityBound,
1580 Arc<T>: TryFrom<Capability>,
1581 Arc<Router<T>>: TryFrom<Capability>,
1582 Capability: From<Arc<T>>,
1583 Capability: From<Arc<Router<T>>>,
1584 Router<T>: CapabilityBound,
1585{
1586 let err = if moniker == &Moniker::root() {
1587 RoutingError::register_from_component_manager_not_found(
1588 source_path.iter_segments().join("/"),
1589 )
1590 } else {
1591 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1592 };
1593 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1594}
1595
1596fn is_supported_offer(offer: &cm_rust::offer::OfferDecl) -> bool {
1597 matches!(
1598 offer,
1599 cm_rust::offer::OfferDecl::Config(_)
1600 | cm_rust::offer::OfferDecl::Protocol(_)
1601 | cm_rust::offer::OfferDecl::Dictionary(_)
1602 | cm_rust::offer::OfferDecl::Directory(_)
1603 | cm_rust::offer::OfferDecl::Runner(_)
1604 | cm_rust::offer::OfferDecl::Resolver(_)
1605 | cm_rust::offer::OfferDecl::Service(_)
1606 | cm_rust::offer::OfferDecl::EventStream(_)
1607 | cm_rust::offer::OfferDecl::Storage(_)
1608 )
1609}
1610
1611fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1612 component: &Arc<C>,
1613 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1614 component_input: &ComponentInput,
1615 program_output_dict: &Arc<Dictionary>,
1616 framework_router: &Arc<Router<Dictionary>>,
1617 capability_sourced_capabilities_dict: &Arc<Dictionary>,
1618 offer: &cm_rust::offer::OfferDecl,
1619 target_dict: &Arc<Dictionary>,
1620 error_reporter: impl ErrorReporter,
1621) where
1622 T: CapabilityBound,
1623 Router<T>: CapabilityBound,
1624 Arc<T>: TryFrom<Capability>,
1625 Arc<Router<T>>: WithServiceRenamesAndFilter,
1626 Arc<Router<T>>: TryFrom<Capability>,
1627 Capability: From<Arc<T>>,
1628 Capability: From<Arc<Router<T>>>,
1629{
1630 assert!(is_supported_offer(offer), "{offer:?}");
1631
1632 let source_path = offer.source_path();
1633 let target_name = offer.target_name();
1634 let porcelain_type = CapabilityTypeName::from(offer);
1635 let router: Arc<Router<T>> = match offer.source() {
1636 cm_rust::offer::OfferSource::Parent => {
1637 let err = if component.moniker() == &Moniker::root() {
1638 RoutingError::register_from_component_manager_not_found(
1639 offer.source_name().to_string(),
1640 )
1641 } else {
1642 RoutingError::offer_from_parent_not_found(
1643 &component.moniker(),
1644 source_path.iter_segments().join("/"),
1645 )
1646 };
1647 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1648 }
1649 cm_rust::offer::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1650 &source_path,
1651 RoutingError::offer_from_self_not_found(
1652 &component.moniker(),
1653 source_path.iter_segments().join("/"),
1654 ),
1655 ),
1656 cm_rust::offer::OfferSource::Child(child_ref) => {
1657 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1658 match child_component_output_dictionary_routers.get(&child_name) {
1659 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1660 &child_name,
1661 &component.moniker(),
1662 source_path.iter_segments().join("/"),
1663 )),
1664 Some(child_component_output) => child_component_output.clone().lazy_get(
1665 source_path.to_owned(),
1666 RoutingError::offer_from_child_expose_not_found(
1667 &child_name,
1668 &component.moniker(),
1669 offer.source_name().clone(),
1670 ),
1671 ),
1672 }
1673 }
1674 cm_rust::offer::OfferSource::Framework => {
1675 if offer.is_from_dictionary() {
1676 warn!(
1677 "routing from framework with dictionary path is not supported: {source_path}"
1678 );
1679 return;
1680 }
1681 query_framework_router_or_not_found(framework_router, &source_path, component)
1682 }
1683 cm_rust::offer::OfferSource::Capability(capability_name) => {
1684 let err = RoutingError::capability_from_capability_not_found(
1685 &component.moniker(),
1686 capability_name.as_str().to_string(),
1687 );
1688 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1689 || source_path.iter_segments().join("/")
1690 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1691 {
1692 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1693 } else {
1694 Router::<T>::new_error(err)
1695 }
1696 }
1697 cm_rust::offer::OfferSource::Void => UnavailableRouter::new_from_offer(offer, component),
1698 cm_rust::offer::OfferSource::Collection(_collection_name) => {
1699 return;
1704 }
1705 };
1706
1707 let availability = *offer.availability();
1708 let mut router_builder = router
1709 .with_porcelain_with_default(porcelain_type)
1710 .availability(availability)
1711 .target(component)
1712 .error_info(offer)
1713 .error_reporter(error_reporter);
1714 if let cm_rust::offer::OfferDecl::Directory(decl) = offer {
1715 router_builder = router_builder
1725 .rights(decl.rights.clone().map(Into::into))
1726 .subdir(decl.subdir.clone().into())
1727 .inherit_rights(true);
1728 }
1729 if let cm_rust::offer::OfferDecl::Storage(_) = offer {
1730 router_builder = router_builder
1731 .rights(Some(fio::RW_STAR_DIR.into()))
1732 .inherit_rights(false)
1733 .subdir(cm_types::RelativePath::dot().into());
1734 }
1735 if let cm_rust::offer::OfferDecl::Service(_) = offer {
1736 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1737 }
1738 if let cm_rust::offer::OfferDecl::EventStream(offer_event_stream) = offer {
1739 if let Some(scope) = &offer_event_stream.scope {
1740 router_builder =
1741 router_builder.event_stream_scope((component.moniker().clone(), scope.clone()));
1742 }
1743 }
1744
1745 let router = if let cm_rust::OfferDecl::Service(_) = offer {
1746 router_builder.build().with_service_renames_and_filter(offer.clone())
1747 } else {
1748 router_builder.build().into()
1749 };
1750
1751 let prev = target_dict.insert_capability(target_name, router.into());
1752 assert!(prev.is_none(), "failed to insert {target_name}: preexisting value");
1753}
1754
1755fn query_framework_router_or_not_found<T, C>(
1756 router: &Arc<Router<Dictionary>>,
1757 path: &BorrowedSeparatedPath<'_>,
1758 component: &Arc<C>,
1759) -> Arc<Router<T>>
1760where
1761 T: CapabilityBound,
1762 Router<T>: CapabilityBound,
1763 Arc<Router<T>>: TryFrom<Capability>,
1764 Arc<T>: TryFrom<Capability>,
1765 Capability: From<Arc<T>>,
1766 Capability: From<Arc<Router<T>>>,
1767 C: ComponentInstanceInterface + 'static,
1768{
1769 let dict: Result<Option<Arc<Dictionary>>, RouterError> = router
1770 .route(RouteRequest::default(), component.as_weak().into())
1771 .now_or_never()
1772 .expect("failed to now_or_never");
1773 let dict = match dict {
1774 Ok(Some(dict)) => dict,
1775 _ => Dictionary::new(),
1777 };
1778 dict.get_router_or_not_found::<T>(
1781 path,
1782 RoutingError::capability_from_framework_not_found(
1783 &component.moniker(),
1784 path.iter_segments().join("/"),
1785 ),
1786 )
1787}
1788
1789pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1790 matches!(
1791 expose,
1792 cm_rust::ExposeDecl::Config(_)
1793 | cm_rust::ExposeDecl::Protocol(_)
1794 | cm_rust::ExposeDecl::Dictionary(_)
1795 | cm_rust::ExposeDecl::Directory(_)
1796 | cm_rust::ExposeDecl::Runner(_)
1797 | cm_rust::ExposeDecl::Resolver(_)
1798 | cm_rust::ExposeDecl::Service(_)
1799 )
1800}
1801
1802fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1803 component: &Arc<C>,
1804 child_component_output_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
1805 program_output_dict: &Arc<Dictionary>,
1806 framework_router: &Arc<Router<Dictionary>>,
1807 capability_sourced_capabilities_dict: &Arc<Dictionary>,
1808 expose: &cm_rust::ExposeDecl,
1809 target_component_output: &ComponentOutput,
1810 error_reporter: impl ErrorReporter,
1811) where
1812 T: CapabilityBound,
1813 Router<T>: CapabilityBound,
1814 Arc<T>: TryFrom<Capability>,
1815 Arc<Router<T>>: TryFrom<Capability>,
1816 Capability: From<Arc<T>>,
1817 Capability: From<Arc<Router<T>>>,
1818{
1819 assert!(is_supported_expose(expose), "{expose:?}");
1820
1821 let target_dict = match expose.target() {
1822 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1823 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1824 };
1825 let source_path = expose.source_path();
1826 let target_name = expose.target_name();
1827
1828 let porcelain_type = CapabilityTypeName::from(expose);
1829 let router: Arc<Router<T>> = match expose.source() {
1830 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1831 &source_path,
1832 RoutingError::expose_from_self_not_found(
1833 &component.moniker(),
1834 source_path.iter_segments().join("/"),
1835 ),
1836 ),
1837 cm_rust::ExposeSource::Child(child_name) => {
1838 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1839 if let Some(child_component_output) =
1840 child_component_output_dictionary_routers.get(&child_name)
1841 {
1842 child_component_output.clone().lazy_get(
1843 source_path.to_owned(),
1844 RoutingError::expose_from_child_expose_not_found(
1845 &child_name,
1846 &component.moniker(),
1847 expose.source_name().clone(),
1848 ),
1849 )
1850 } else {
1851 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1852 &child_name,
1853 &component.moniker(),
1854 expose.source_name().clone(),
1855 ))
1856 }
1857 }
1858 cm_rust::ExposeSource::Framework => {
1859 if expose.is_from_dictionary() {
1860 warn!(
1861 "routing from framework with dictionary path is not supported: {source_path}"
1862 );
1863 return;
1864 }
1865 query_framework_router_or_not_found(framework_router, &source_path, component)
1866 }
1867 cm_rust::ExposeSource::Capability(capability_name) => {
1868 let err = RoutingError::capability_from_capability_not_found(
1869 &component.moniker(),
1870 capability_name.as_str().to_string(),
1871 );
1872 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1873 || source_path.iter_segments().join("/")
1874 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1875 {
1876 capability_sourced_capabilities_dict
1877 .clone()
1878 .get_router_or_not_found::<T>(&capability_name, err)
1879 } else {
1880 Router::<T>::new_error(err)
1881 }
1882 }
1883 cm_rust::ExposeSource::Void => UnavailableRouter::new_from_expose(expose, component),
1884 cm_rust::ExposeSource::Collection(_name) => return,
1889 };
1890 let availability = *expose.availability();
1891 let mut router_builder = router
1892 .with_porcelain_with_default(porcelain_type)
1893 .availability(availability)
1894 .target(component)
1895 .error_info(expose)
1896 .error_reporter(error_reporter);
1897 if let cm_rust::ExposeDecl::Directory(decl) = expose {
1898 router_builder = router_builder
1899 .rights(decl.rights.clone().map(Into::into))
1900 .subdir(decl.subdir.clone().into())
1901 .inherit_rights(true);
1902 };
1903 if let cm_rust::ExposeDecl::Service(_) = expose {
1904 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1905 };
1906 let prev = target_dict.insert_capability(target_name, router_builder.build().into());
1907 assert!(prev.is_none(), "failed to insert {target_name}: preexisting value");
1908}
1909
1910struct UnavailableRouter<C: ComponentInstanceInterface> {
1911 capability: InternalCapability,
1912 component: WeakComponentInstanceInterface<C>,
1913}
1914
1915impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1916 fn new<T: CapabilityBound>(
1917 capability: InternalCapability,
1918 component: &Arc<C>,
1919 ) -> Arc<Router<T>> {
1920 Router::<T>::new(Self { capability, component: component.as_weak() })
1921 }
1922
1923 fn new_from_offer<T: CapabilityBound>(offer: &OfferDecl, component: &Arc<C>) -> Arc<Router<T>> {
1924 let name = offer.source_name().clone();
1925 let capability = match offer {
1926 OfferDecl::Service(_) => InternalCapability::Service(name),
1927 OfferDecl::Protocol(_) => InternalCapability::Protocol(name),
1928 OfferDecl::Directory(_) => InternalCapability::Directory(name),
1929 OfferDecl::Storage(_) => InternalCapability::Storage(name),
1930 OfferDecl::Runner(_) => InternalCapability::Runner(name),
1931 OfferDecl::Resolver(_) => InternalCapability::Resolver(name),
1932 OfferDecl::EventStream(_) => {
1933 InternalCapability::EventStream(InternalEventStreamCapability {
1934 name,
1935 scope_moniker: None,
1936 scope: None,
1937 })
1938 }
1939 OfferDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1940 OfferDecl::Config(_) => InternalCapability::Config(name),
1941 };
1942 Self::new(capability, component)
1943 }
1944
1945 fn new_from_expose<T: CapabilityBound>(
1946 expose: &ExposeDecl,
1947 component: &Arc<C>,
1948 ) -> Arc<Router<T>> {
1949 let name = expose.source_name().clone();
1950 let capability = match expose {
1951 ExposeDecl::Service(_) => InternalCapability::Service(name),
1952 ExposeDecl::Protocol(_) => InternalCapability::Protocol(name),
1953 ExposeDecl::Directory(_) => InternalCapability::Directory(name),
1954 ExposeDecl::Runner(_) => InternalCapability::Runner(name),
1955 ExposeDecl::Resolver(_) => InternalCapability::Resolver(name),
1956 ExposeDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1957 ExposeDecl::Config(_) => InternalCapability::Config(name),
1958 };
1959 Self::new(capability, component)
1960 }
1961}
1962
1963#[async_trait]
1964impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1965 for UnavailableRouter<C>
1966{
1967 async fn route(
1968 &self,
1969 request: RouteRequest,
1970 _target: Arc<WeakInstanceToken>,
1971 ) -> Result<Option<Arc<T>>, RouterError> {
1972 let availability = request.availability.ok_or(RouterError::InvalidArgs)?.fidl_into_native();
1973 match availability {
1974 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1975 Err(RoutingError::SourceCapabilityIsVoid {
1976 moniker: self.component.moniker.clone(),
1977 }
1978 .into())
1979 }
1980 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => Ok(None),
1981 }
1982 }
1983
1984 async fn route_debug(
1985 &self,
1986 _request: RouteRequest,
1987 _target: Arc<WeakInstanceToken>,
1988 ) -> Result<CapabilitySource, RouterError> {
1989 Ok(CapabilitySource::Void(VoidSource {
1990 capability: self.capability.clone(),
1991 moniker: self.component.moniker.clone(),
1992 }))
1993 }
1994}