Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Metro Under the Hood

Avatar for Zac Sweers Zac Sweers
May 21, 2026
40

Metro Under the Hood

Avatar for Zac Sweers

Zac Sweers

May 21, 2026

Transcript

  1. SLIDES TODO link •Part 2 of "Navigating Dependency Injection with

    Metro" •(Metro) compiler internals •Focus on "what" and "why" •Occasionally "how" a a a
  2. Recap @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  3. Recap @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  4. Recap @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  5. Recap @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  6. Recap @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  7. Factories @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  8. Factories @Inject class Repository(api: Api) { // Metro generates class

    MetroFactory(api: Provider<Api>) : Factory<Repository> { } }
  9. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } }
  10. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } } val apiProvider = ... val repoProvider = Repository.MetroFactory.create(apiProvider)
  11. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } } val apiProvider = ... val repoProvider = Repository.MetroFactory.create(apiProvider) val repo = repoProvider()
  12. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } } val apiProvider = ... val repoProvider = Repository.MetroFactory.create(apiProvider) val repo = repoProvider()
  13. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } }
  14. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(@Named("authed") api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } }
  15. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api = Api()) = Repository(api) } }
  16. Factories @Deprecated(HIDDEN) class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun

    invoke() : Repository = newInstance(api()) companion object { @JvmStatic @JsStatic @HiddenFromObjC fun create(api: Provider<Api>) = MetroFactory(api) @JvmStatic @JsStatic @HiddenFromObjC fun newInstance(api: Api) = Repository(api) } }
  17. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } fun mirrorFunction(api: Api) : Repository { return error(message = "Never called") } }
  18. Factories class MetroFactory(api: Provider<Api>) : Factory<Repository> { override fun invoke()

    : Repository = newInstance(api()) companion object { fun create(api: Provider<Api>) = MetroFactory(api) fun newInstance(api: Api) = Repository(api) } @Named(name = "authed") @HiddenFromObjC fun mirrorFunction(@Named(name = "authed") api: Api) : Repository { return error(message = "Never called") } }
  19. Factories @Inject class Repository(api: Api) { // Metro generates class

    MetroFactory(api: Provider<Api>) : Factory<Repository> { } }
  20. Factories @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  21. Factories @DependencyGraph interface AppGraph { @Provides fun provideApi() : Api

    = Api() // Metro generates class ProvideApiMetroFactory(instance: AppGraph) : Factory<Api> }
  22. Factories class ProvideApiMetroFactory(instance: AppGraph) : Factory<Api> { override fun invoke()

    : Api = provideApi(instance) companion object { fun create(instance: AppGraph) : Factory<Api> = ProvideApiMetroFactory(instance) fun provideApi(instance: AppGraph) : Api = instance.provideApi() } fun mirrorFunction() : Api = error(message = "Never called") }
  23. Binding Containers @BindingContainer object ApiBindings { @Provides fun provideApi() :

    Api = Api() } @DependencyGraph( bindingContainers = [ApiBindings :: class] ) interface AppGraph { val repository: Repository }
  24. Binding Containers @Metadata( ... ) @BindingContainer object ApiBindings { @Provides

    fun provideApi() : Api = Api() } message DependencyGraphProto { bool is_graph = 1; repeated ProviderFactoryProto provider_factories = 2; repeated string accessor_callable_names = 3; repeated int32 multibinding_accessor_indices = 4; repeated string included_binding_containers = 5; } message ProviderFactoryProto { string class_id = 1; bool invisible = 2; bool is_object = 3; string callable_name = 4; string property_name = 5; string new_instance_name = 6; }
  25. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  26. Graphs @DependencyGraph interface AppGraph { // Metro generates class Impl

    : AppGraph { override val val repository: Repository get() = Repository(provideApi()) } }
  27. Graphs @DependencyGraph(AppScope :: class) interface AppGraph { // Metro generates

    class Impl : AppGraph { private val repositoryProvider = DoubleCheck.provider(ProvideApiMetroFactory.create(this)) override val val repository: Repository get() = Repository(repositoryProvider()) } }
  28. Graphs @DependencyGraph interface AppGraph { // Metro generates class Impl

    : AppGraph { override val val repository: Repository get() = Repository( DoubleCheck.lazy(ProvideApiMetroFactory.create(this)) ) } } @Inject class Repository(api: Lazy<Api>)
  29. Graphs @DependencyGraph interface AppGraph { class Impl : AppGraph {

    override val val repository: Repository get() = Repository( DoubleCheck.lazy(ProvideApiMetroFactory.create(this)) ) } }
  30. Graphs @DependencyGraph interface AppGraph { class Impl(api: Api) : AppGraph

    { override val val repository: Repository get() = Repository( DoubleCheck.lazy(InstanceFactory.create(api)) ) } }
  31. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  32. @Binds @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() @Binds val RepositoryImpl.bind: Repository } interface Repository @Inject class RepositoryImpl(api: Api) : Repository
  33. @Binds @DependencyGraph interface AppGraph { class Impl : AppGraph {

    override val val repository: Repository get() = Repository(provideApi()) } }
  34. @Binds @DependencyGraph interface AppGraph { class Impl : AppGraph {

    override val val repository: Repository get() = RepositoryImpl(provideApi()) } }
  35. @Binds @DependencyGraph interface AppGraph { @Binds val RepositoryImpl.bind: Repository abstract

    class BindsMirror private constructor() { @Binds abstract fun RepositoryImpl.bind() : Repository } }
  36. @Binds @DependencyGraph interface AppGraph { @Binds val RepositoryImpl.bind: Repository @Deprecated(HIDDEN)

    abstract class BindsMirror private constructor() { @Binds abstract fun RepositoryImpl.bind() : Repository } }
  37. @Binds @DependencyGraph interface AppGraph { @Binds val RepositoryImpl.bind: Repository @Deprecated(HIDDEN)

    @ComptimeOnly abstract class BindsMirror private constructor() { @Binds abstract fun RepositoryImpl.bind() : Repository } }
  38. @Binds @DependencyGraph interface AppGraph { @Binds val RepositoryImpl.bind: Repository @Deprecated(HIDDEN)

    @ComptimeOnly abstract class BindsMirror private constructor() { @Binds @CallableMetadata( ... ) abstract fun RepositoryImpl.bind() : Repository } }
  39. @Binds @DependencyGraph interface AppGraph { @Binds val RepositoryImpl.bind: Repository @Deprecated(HIDDEN)

    @ComptimeOnly abstract class BindsMirror private constructor() { @Binds @CallableMetadata( callableName = "<get - bind>", propertyName = "bind", startOffset = 78, endOffset = 113 ) abstract fun RepositoryImpl.bind() : Repository }
  40. @Binds @CallableMetadata( callableName = "provideApi", propertyName = "", startOffset =

    101, endOffset = 149, newInstanceName = "provideApi" ) class ProvideApiMetroFactory : Factory<Api> { ... } @ComptimeOnly fun mirrorFunction() : Api { ... }
  41. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  42. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> @Provides @IntoSet

    fun provideOne() : Int = 1 @Provides @IntoSet fun provideTwo() : Int = 2 }
  43. Multibindings @DependencyGraph interface AppGraph { //... private val setOfInt: Set<Int>

    get() = buildSet(2) { add(provideOne()) add(provideTwo()) } }
  44. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> get() =

    setOfInt //... private val setOfInt: Set<Int> get() = buildSet(2) { add(provideOne()) add(provideTwo()) } }
  45. Multibindings @DependencyGraph interface AppGraph { val ints: () -> Set<Int>

    get() = setOfInt //... private val setOfInt: Set<Int> get() = buildSet(2) { add(provideOne()) add(provideTwo()) } }
  46. Multibindings @DependencyGraph interface AppGraph { val ints: () -> Set<Int>

    get() = setOfIntProvider //... private val setOfIntProvider: () -> Set<Int> get() { val builder = SetFactory.builder<Int>(2, 0) builder.addProvider(ProvideTwoMetroFactory.create()) builder.addProvider(ProvideOneMetroFactory.create()) return builder.build() } }
  47. Multibindings @DependencyGraph interface AppGraph { val ints: () -> Set<Int>

    get() = setOfIntProvider val scalarInts: Set<Int> //... private val setOfIntProvider: () -> Set<Int> get() { // ... } }
  48. Multibindings @DependencyGraph interface AppGraph { val ints: () -> Set<Int>

    get() = setOfIntProvider val scalarInts: Set<Int> get() = setOfIntProvider() //... private val setOfIntProvider: () -> Set<Int> get() { // ... } }
  49. Multibindings @DependencyGraph interface AppGraph { //... private val setOfInt: Set<Int>

    get() { val elements = provideElements() return buildSet(elements.size) { addAll(elements) } } }
  50. Multibindings @DependencyGraph interface AppGraph { //... private val setOfInt: ()

    -> Set<Int> get() { val builder = SetFactory.builder<Int>(0, collectionProviderSize = 1) builder.addCollectionProvider(ProvideElementsMetroFactory.create(this)) return builder.build() } }
  51. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> @Provides @IntoSet

    fun provideOne() : Int = 1 @Provides @IntoSet fun provideTwo() : Int = 2 }
  52. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> @Provides @IntoSet

    fun provideOne() : Int = 1 @Provides @IntoSet fun provideTwo() : Int = 2 }
  53. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> @Provides @IntoSet

    @MultibindingElement( bindingId = ... , elementId = ... ) fun provideOne() : Int = 1 @Provides @IntoSet @MultibindingElement( bindingId = ... , elementId = ... ) fun provideTwo() : Int = 2 }
  54. Multibindings @DependencyGraph interface AppGraph { val ints: Set<Int> @Provides @IntoSet

    fun provideOne() : Int = 1 @Provides @IntoSet fun provideTwo() : Int = 2 }
  55. Multibindings @DependencyGraph interface AppGraph { val ints: Map<Int, Int> @Provides

    @IntoMap @IntKey(1) fun provideOne() : Int = 1 @Provides @IntoMap @IntKey(2) fun provideTwo() : Int = 2 }
  56. Multibindings @DependencyGraph interface AppGraph { //... private val mapOfIntToInt: Map<Int,

    Int> get() = buildMap(2) { put(2, provideTwo()) put(1, provideOne()) } }
  57. Multibindings @DependencyGraph interface AppGraph { //... private val mapOfIntToIntProvider: ()

    -> Map<Int, Int> get() { val builder = MapFactory.builder<Int, Int>(2) builder.put(2, ProvideTwoFactory.create(this)) builder.put(1, ProvideOneFactory.create(this)) return builder.build() } }
  58. Multibindings @DependencyGraph interface AppGraph { val ints: Map<Int, () ->

    Int> private val mapOfIntToIntProvider: Map<Int, () -> Int >> get() = buildMap(2) { put(2, ProvideTwoFactory.create(this)) put(1, ProvideOneFactory.create(this)) } }
  59. Multibindings @DependencyGraph interface AppGraph { val ints: () -> Map<Int,

    () -> Int> private val mapOfIntToIntProvider: () -> Map<Int, Int> get() { val builder = MapProviderFactory.builder<Int, Int>(2) builder.put(2, ProvideTwoFactory.create(this)) builder.put(1, ProvideOneFactory.create(this)) return builder.build() } }
  60. Assisted Injection @AssistedInject class Repository(api: Api, @Assisted token: String) {

    @AssistedFactory interface Factory { fun create(token: String) : Repository } }
  61. Assisted Injection @AssistedInject class Repository(api: Api, @Assisted token: String) {

    } @AssistedFactory interface Factory { fun create(token: String) : Repository }
  62. Assisted Injection @AssistedInject class Repository(api: Api, @Assisted token: String) {

    // Metro Generates class MetroFactory(api: Provider<Api>) { operator fun invoke(token: String) : Repository = newInstance(api(), token) companion object { fun create(api: Provider<Api>) : MetroFactory = MetroFactory(api) fun newInstance(api: Api, token: String) : Repository = Repository(api, token) } } }
  63. Assisted Injection @AssistedInject class Repository(api: Api, @Assisted token: String) {

    // Metro Generates class MetroFactory(api: Provider<Api>) { operator fun invoke(token: String) : Repository = newInstance(api(), token) companion object { fun create(api: Provider<Api>) : MetroFactory = MetroFactory(api) fun newInstance(api: Api, token: String) : Repository = Repository(api, token) } } }
  64. Assisted Injection @AssistedFactory interface Factory { fun create(token: String) :

    Repository // Metro Generates class Impl( private val delegateFactory: Repository.MetroFactory ) : Factory { override fun create(token: String) : Reposistory { return delegateFactory(token) } } }
  65. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @AssistedInject class Repository(api: Api, @Assisted token: String)
  66. Graphs @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph { override val val repositoryFactory: Repository.Factory get() = Repository.Factory.Impl.create( ... ) } }
  67. Graphs @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph { override val val repositoryFactory: Repository.Factory get() = Repository.Factory.Impl.create( delegateFactory = Repository.MetroFactory.create( ... ) ) } }
  68. Graphs @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph { override val val repositoryFactory: Repository.Factory get() = Repository.Factory.Impl.create( delegateFactory = Repository.MetroFactory.create( apiProvider = ProvideApiMetroFactory.create(this) ) ) } }
  69. Aggregation @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class RepositoryImpl(api: Api) : Repository
  70. Aggregation @DependencyGraph interface AppGraph { val repository: Repository } @BindingContainer

    object ApiBindings { @Provides fun provideApi() : Api = Api() } @Inject class RepositoryImpl(api: Api) : Repository
  71. Aggregation @DependencyGraph(AppScope :: class) interface AppGraph { val repository: Repository

    } @BindingContainer @ContributesTo(AppScope :: class) object ApiBindings { @Provides fun provideApi() : Api = Api() } @ContributesBinding(AppScope :: class) @Inject class RepositoryImpl(api: Api) : Repository
  72. Aggregation @BindingContainer @ContributesTo(AppScope :: class) object ApiBindings @ContributesBinding(AppScope :: class)

    @Inject class RepositoryImpl : Repository // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") }
  73. Aggregation @BindingContainer @ContributesTo(AppScope :: class) object ApiBindings @ContributesBinding(AppScope :: class)

    @Inject class RepositoryImpl : Repository // FILE : apiBindingsAppScope.kt package metro.hints fun AppScope(contributed: ApiBindings) { return error(message = "Never called") } // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") }
  74. Aggregation // FILE : apiBindingsAppScope.kt package metro.hints fun AppScope(contributed: ApiBindings)

    { return error(message = "Never called") } // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") } @DependencyGraph(AppScope :: class) interface AppGraph { val repository: Repository }
  75. Aggregation // FILE : apiBindingsAppScope.kt package metro.hints fun AppScope(contributed: ApiBindings)

    { return error(message = "Never called") } // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") } @DependencyGraph(AppScope :: class) interface AppGraph { val repository: Repository }
  76. Aggregation // FILE : apiBindingsAppScope.kt package metro.hints fun AppScope(contributed: ApiBindings)

    { return error(message = "Never called") } // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") } @DependencyGraph(AppScope :: class) interface AppGraph { val repository: Repository }
  77. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  78. Graph Extensions @DependencyGraph interface AppGraph { val loggedInGraph: LoggedInGraph }

    @GraphExtension interface LoggedInGraph { val repository: Repository }
  79. Graph Extensions @DependencyGraph interface AppGraph { //... class Impl :

    AppGraph { override val loggedInGraph: LoggedInGraph get() = //... } } @GraphExtension interface LoggedInGraph { val repository: Repository }
  80. Graph Extensions @DependencyGraph interface AppGraph { //... class Impl :

    AppGraph { override val loggedInGraph: LoggedInGraph get() = //... } @DependencyGraph private class LoggedInGraphImpl(appGraph: AppGraph) : LoggedInGraph { override val repository: Repository } }
  81. Graph Extensions @DependencyGraph interface AppGraph { //... class Impl :

    AppGraph { override val loggedInGraph: LoggedInGraph get() = LoggedInGraphImpl(this) } @DependencyGraph private class LoggedInGraphImpl(appGraph: AppGraph) : LoggedInGraph { override val repository: Repository } }
  82. Graph Extensions @DependencyGraph interface AppGraph { //... @DependencyGraph private class

    LoggedInGraphImpl(appGraph: AppGraph.Impl) : LoggedInGraph { override val repository: Repository get() = Repository(appGraph.provideApi()) } }
  83. Graph Extensions @DependencyGraph interface AppGraph { class Impl : AppGraph

    { private val apiProvider = DoubleCheck.provider(ProvideApiMetroFactory.create(this)) // ... } @DependencyGraph private class LoggedInGraphImpl(appGraph: AppGraph.Impl) : LoggedInGraph { override val repository: Repository get() = Repository(appGraph.apiProvider()) } }
  84. Graphs @DependencyGraph interface AppGraph { val repository: Repository @Provides fun

    provideApi() : Api = Api() } @Inject class Repository(api: Api)
  85. Compiler Pipeline binary f i les Frontend Backend FIR IR

    Lowering Parse/PSI fi r2ir Source f i les
  86. FIR

  87. FIR Declaration Generation @DependencyGraph interface AppGraph { val repository: Repository

    @Provides fun provideApi() : Api = Api() } @Inject class Repository(api: Api)
  88. FIR @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph class ProvideApiMetroFactory } @Inject class Repository(api: Api) { class MetroFactory(api: Provider<Api>) } Declaration Generation
  89. FIR @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph class ProvideApiMetroFactory : Factory<Api> } @Inject class Repository(api: Api) { class MetroFactory(api: Provider<Api>) : Factory<Repository> } Supertype Generation Declaration Generation
  90. FIR @DependencyGraph interface AppGraph { // ... class Impl :

    AppGraph class ProvideApiMetroFactory : Factory<Api> { companion object } } @Inject class Repository(api: Api) { class MetroFactory(api: Provider<Api>) : Factory<Repository> { companion object } } Supertype Generation Declaration Generation
  91. FIR @DependencyGraph(AppScope :: class) interface AppGraph { // ... class

    Impl : AppGraph, ApiBindings.MetroContributionToAppScope, RepositoryImpl.MetroContributionToAppScope } @ContributesIntoSet(AppScope :: class) @Inject class RepositoryImpl { @MetroContribution(AppScope :: class) interface MetroContributionToAppScope { // ... } } Supertype Generation Declaration Generation
  92. Compiler Pipeline binary f i les Frontend Backend FIR IR

    Lowering Parse/PSI fi r2ir Source f i les
  93. IR

  94. IR

  95. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @DefaultBinding<FragmentCreator <*> > interface FragmentCreator<T : Fragment> { // Generated in FIR abstract class DefaultBindingMirror { // Generated in IR abstract fun defaultBinding() : FragmentCreator <*> } }
  96. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @ContributesIntoSet(AppScope :: class) @Inject class RepositoryImpl(api: Api) : Repository { // Generated in FIR @BindingContainer @MetroContribution(AppScope :: class) interface MetroContributionToAppScope }
  97. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @ContributesIntoSet(AppScope :: class) @Inject class RepositoryImpl(api: Api) : Repository { // Generated in FIR @BindingContainer @MetroContribution(AppScope :: class) interface MetroContributionToAppScope { // Generated in IR @IntoSet @Binds fun bindIntoSetAsRepository(instance: RepositoryIm : error(message = "Never called") } }
  98. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    class MainActivity { @Inject lateinit var id: String // FIR class MetroMembersInjector : MembersInjector<MainActivity> { // IR private val id: Provider<String> = ... // FIR companion object { // IR fun injectId(@Assisted instance: MainActivity, id_: String) { instance.id = id } } } }
  99. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @Inject class Repository(api: Api) { // FIR class MetroFactory : Factory<Repository> { // IR private val api = ... // FIR override fun invoke() : Repository { // IR return Repository(api()) } // FIR companion object { // IR fun create( ... ) = ... // IR fun newInstance( ... ) = ... } } }
  100. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @AssistedInject class Repository(@Assisted id: String) { @AssistedFactory interface Factory { fun create(id: String) : Repository // FIR class Impl : Factory { override fun create(id) : Repository } } }
  101. IR DefaultBindingMirrorTransformer ContributionIrTransformer ContributionHintIrTransformer MembersInjectorTransformer InjectedClassTransformer AssistedFactoryTransformer BindingContainerTransformer CreateGraphTransformer AsContributionTransformer

    @AssistedInject class Repository(@Assisted id: String) { @AssistedFactory interface Factory { fun create(id: String) : Repository // FIR class Impl : Factory { private val delegate: Repository.MetroFactory = ... override fun create(id) : Repository = delegate(id) } } }
  102. IR ContributionIrTransformer InjectedClassTransformer BindingContainerTransformer interface MetroGraphData { val contributionData: IrContributionData

    val graphs: List<GraphToProcess> val syntheticGraphs: List<GraphToProcess> val allGraphs get() = graphs + syntheticGraphs } data class GraphToProcess( val declaration: IrClass, val dependencyGraphAnno: IrConstructorCall, val graphImpl: IrClass, val scopes: Set<ClassId>, )
  103. IR ContributionIrTransformer InjectedClassTransformer BindingContainerTransformer interface MetroGraphData { val contributionData: IrContributionData

    val graphs: List<GraphToProcess> val syntheticGraphs: List<GraphToProcess> val allGraphs get() = graphs + syntheticGraphs } data class GraphToProcess( val declaration: IrClass, val dependencyGraphAnno: IrConstructorCall, val graphImpl: IrClass, val scopes: Set<ClassId>, )
  104. IR ContributionIrTransformer InjectedClassTransformer BindingContainerTransformer val scopes: Set<ClassId> = graphData.allGraphs.flatMapToSet {

    it.scopes } // FILE : apiBindingsAppScope.kt package metro.hints fun AppScope(contributed: ApiBindings) { return error(message = "Never called") } // FILE : repositoryImplAppScope.kt package metro.hints fun AppScope(contributed: RepositoryImpl) { return error(message = "Never called") }
  105. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings
  106. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings
  107. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings @DependencyGraph interface AppGraph : ApiBindings, RepositoryImpl.MetroContributionToAppScope
  108. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings @DependencyGraph interface AppGraph : ApiBindings, RepositoryImpl.MetroContributionToAppScope
  109. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings @DependencyGraph( bindingContainers = [ApiBindings :: class] ) interface AppGraph
  110. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings @DependencyGraph( bindingContainers = [ApiBindings :: class] ) interface AppGraph @BindingContainer(includes = [NetworkBindings :: class]) object ApiBindings { //... }
  111. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings
  112. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings @DependencyGraph interface AppGraph { val repository: Repository fun inject(app: MyApp) }
  113. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings sealed interface IrBinding { val typeKey: IrTypeKey val scope: IrAnnotation? val dependencies: List<IrContextualTypeKey> // ... }
  114. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings class ConstructorInjected class ObjectClass // @Provides class Provided // @Binds class Alias class AssistedFactory class BoundInstance // For default values class Absent // For @Includes class GraphDependency class Multibinding class MembersInjected class GraphExtension class GraphExtensionFactory // j.u.Optional interop, etc. class CustomWrapper
  115. IR Collect supertypes Process binding containers - Process transitive closure

    Merge contributions Process declarations (accessor roots, etc) Build BindingGraph - Collect bindings sealed interface IrBinding { val typeKey: IrTypeKey val scope: IrAnnotation? val dependencies: List<IrContextualTypeKey> // ... }
  116. IR sealed interface IrBinding : BaseBinding<IrType, IrTypeKey, IrContextualTypeKey> class IrTypeKey

    : BaseTypeKey<IrType, IrAnnotation, IrTypeKey> class IrContextualTypeKey : BaseContextualTypeKey<IrType, IrTypeKey, IrContextualTypeKey>
  117. IR interface Binding { val typeKey: TypeKey val scope: Annotation?

    val dependencies: List<ContextualTypeKey> // ... }
  118. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) } fun populateGraph(roots) { // BFS from roots val queue = ArrayDeque<Binding>() val visited = HashSet<TypeKey>() while (queue.isNotEmpty()) { val next = queue.removeFirst() // Skipping code // Visit dependencies } } BindingLookup
  119. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) } fun populateGraph(roots) { // BFS from roots val queue = ArrayDeque<Binding>() val visited = HashSet<TypeKey>() while (queue.isNotEmpty()) { val next = queue.removeFirst() // Skipping code // Visit dependencies } } BindingLookup
  120. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) } fun populateGraph(roots) { // BFS from roots val queue = ArrayDeque<Binding>() val visited = HashSet<TypeKey>() while (queue.isNotEmpty()) { val next = queue.removeFirst() // Skipping code // Visit dependencies } } BindingLookup
  121. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) } fun populateGraph(roots) { // BFS from roots val queue = ArrayDeque<Binding>() val visited = HashSet<TypeKey>() while (queue.isNotEmpty()) { val next = queue.removeFirst() // Skipping code // Visit dependencies } } BindingLookup Absent(MissingKey)
  122. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) } fun populateGraph(roots) { // BFS from roots val queue = ArrayDeque<Binding>() val visited = HashSet<TypeKey>() while (queue.isNotEmpty()) { val next = queue.removeFirst() // Skipping code // Visit dependencies } } BindingLookup
  123. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val missingBindings = populateGraph(roots) if (missingBindings.isNotEmpty()) { for (missing in missingBindings) { reportMissingBinding(missing) } } }
  124. IR fun reportMissingBinding(missing: TypeKey, bindingStack: BindingStack) { if (reported(missing)) return

    val message = buildString { append( "[Metro/MissingBinding] Cannot f i nd ... " ) appendLine(bold(typeKey.render(short = false))) appendLine() appendBindingStack(bindingStack, short = false) } }
  125. IR fun reportMissingBinding(missing: TypeKey, bindingStack: BindingStack) { if (reported(missing)) return

    val message = buildString { //... val (hints, similarBindings) = missingBindingHints(typeKey) if (hints.isNotEmpty()) { appendLine("(Hint)") hints.joinTo(this, separator = "\n\n") } if (similarBindings.isNotEmpty()) { appendLine("Similar bindings:") for (binding in similarBindings.values.map { " - $it" }.sorted()) { appendLine(binding) } } } }
  126. IR fun reportMissingBinding(missing: TypeKey, bindingStack: BindingStack) { if (reported(missing)) return

    val message = buildString { //... val (hints, similarBindings) = missingBindingHints(typeKey) if (hints.isNotEmpty()) { appendLine("(Hint)") hints.joinTo(this, separator = "\n\n") } if (similarBindings.isNotEmpty()) { appendLine("Similar bindings:") for (binding in similarBindings.values.map { " - $it" }.sorted()) { appendLine(binding) } } } }
  127. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val fullAdjacency = buildFullAdjacency(bindings) metroSort(fullAdjacency, roots, ... ) }
  128. Tarjan Strongly Connected Components (SCCs) @DependencyGraph interface AppGraph { val

    homePresenter: HomePresenter } @Inject class HomePresenter(repository: Repository, logger: Logger) @Inject class Repository(api: Api, logger: Logger) @Inject class Logger(clock: Clock) @Inject class Clock
  129. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter SCCs emitted: N : 1 idx=1 low=1
  130. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo SCCs emitted: N : 2 idx=1 low=1 idx=2 low=2
  131. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo, Api SCCs emitted: N : 3 idx=1 low=1 idx=2 low=2
  132. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo SCCs emitted: Api N : 3 idx=1 low=1 idx=2 low=2
  133. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo SCCs emitted: Api N : 3 idx=1 low=1 idx=2 low=2 idx=3 low=3
  134. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo, Logger SCCs emitted: Api N : 4 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  135. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo, Logger, Clock SCCs emitted: Api N : 5 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  136. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo, Logger SCCs emitted: Api, Clock N : 5 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  137. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter, Repo SCCs emitted: Api, Clock, Logger N : 5 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  138. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: HomePresenter SCCs emitted: Api, Clock, Logger, Repo N : 5 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  139. HomePresenter Tarjan SCCs Repository Logger Api Clock AppGraph idx=0 low=0

    On - stack: SCCs emitted: Api, Clock, Logger, Repo, HomePresenter N : 5 idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4
  140. Tarjan SCCs class Impl : AppGraph { private val apiProvider:

    Provider<Api> = ... private val clockProvider: Provider<Clock> = ... private val loggerProvider: Provider<Logger> = ... private val repositoryProvider: Provider<Repository> = ... private val homePresenterProvider: Provider<HomePresenter> = ... }
  141. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4 On - stack: HomePresenter, Repo, Logger, Clock SCCs emitted: Api N : 4
  142. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=4 On - stack: HomePresenter, Repo, Logger, Clock SCCs emitted: Api N : 5
  143. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=3 On - stack: HomePresenter, Repo, Logger, Clock SCCs emitted: Api N : 5
  144. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=3 On - stack: HomePresenter, Repo SCCs emitted: Api, [Logger, Clock] N : 5
  145. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=3 On - stack: HomePresenter, Repo SCCs emitted: Api, [Logger, Clock] N : 5 Lazy<Logger>
  146. Tarjan SCCs HomePresenter Repository Logger Api Clock AppGraph idx=0 low=0

    idx=1 low=1 idx=2 low=2 idx=3 low=3 idx=4 low=3 On - stack: HomePresenter, Repo SCCs emitted: Api, [Logger, Clock] N : 5 Lazy<Logger> Deferred types: Clock
  147. Tarjan SCCs class Impl : AppGraph { private val apiProvider:

    Provider<Api> = ... private val clockProvider: DelegateFactory<Clock> = ... private val loggerProvider: Provider<Logger> = ... private val repositoryProvider: Provider<Repository> = ... private val homePresenterProvider: Provider<HomePresenter> = ... }
  148. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val fullAdjacency = buildFullAdjacency(bindings) metroSort(fullAdjacency, roots, ... ) }
  149. IR fun seal( roots: Map<ContextualTypeKey, BindingStackEntry> ) : GraphTopology {

    val fullAdjacency = buildFullAdjacency(bindings) val topology = metroSort(fullAdjacency, roots, ... ) return topology }
  150. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    Api Clock Logger Repo HomePresenter BindingPropertyCollector
  151. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : HumongousAppGraph { // HUNDREDS of f i elds !! }
  152. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    IrGraphShardGenerator class Impl : HumongousAppGraph { val shard1 = Shard1(this) val shard2 = Shard2(this) class Shard1(parent: Impl) { // Dozens of f i elds } class Shard2(parent: Impl) { // Dozens of f i elds } }
  153. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    @DependencyGraph interface AppGraph { val homePresenter: HomePresenter }
  154. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : AppGraph { val apiProvider = DoubleCheck.provider(Api.MetroFactory.create()) val clockProvider = DoubleCheck.provider(Clock.MetroFactory.create()) val loggerProvider = DoubleCheck.provider(Logger.MetroFactory.create(clockProvider)) }
  155. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : AppGraph { val apiProvider = DoubleCheck.provider(Api.MetroFactory.create()) val clockProvider = DelegateFactory.create<Clock>() val loggerProvider = DoubleCheck.provider(Logger.MetroFactory.create(clockProvider)) init { clockProvider.setDelegate( DoubleCheck.provider(Clock.MetroFactory.create(loggerProvider))) } }
  156. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : AppGraph { val apiProvider = ... val clockProvider = ... val loggerProvider = ... override val homePresenter: HomePresenter get() = }
  157. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : AppGraph { val apiProvider = ... val clockProvider = ... val loggerProvider = ... override val homePresenter: HomePresenter get() = HomePresenter( Repository(apiProvider(), loggerProvider()) ) }
  158. IR 1. Collect properties 2. Collect shards 3. Implement accessors

    class Impl : AppGraph { val shard1 = Shard1(this) val shard2 = Shard2(this) override val homePresenter: HomePresenter get() = HomePresenter( Repository(shard1.apiProvider(), shard2.loggerProvider()) ) }
  159. SwitchingProviders class Impl : AppGraph { val clockProvider = DoubleCheck.provider(Clock.MetroFactory.create())

    val loggerProvider = DoubleCheck.provider(Logger.MetroFactory.create(clockProvider)) override val logger: Logger get() = loggerProvider() }
  160. SwitchingProviders class Impl : AppGraph { val clockProvider = DoubleCheck.provider(SwitchingProvider(1))

    val loggerProvider = DoubleCheck.provider(SwitchingProvider(0)) //... private class SwitchingProvider<T>(graph: Impl, id: Int) : Provider<T> { override fun invoke() : T { return when (id) { 1 -> Clock() 0 -> Logger(graph.clockProvider()) } } } }
  161. Dynamic Graphs @DependencyGraph interface AppGraph { val value: String }

    @BindingContainer class TestBindings { @Provides fun provideTestString() : String = "test" } // FILE : test.kt fun example() { val graph = createDynamicGraph<AppGraph>( TestBindings() ) assertEquals("test", graph.value) }
  162. Dynamic Graphs // FILE : test.kt fun example() { val

    graph = createDynamicGraph<AppGraph>( TestBindings() ) assertEquals("test", graph.value) } class DynamicAppGraphImpl_s058e : AppGraph { // ... }
  163. Dynamic Graphs // FILE : test.kt fun example() { val

    graph = DynamicAppGraphImpl_s058e(TestBindings()) assertEquals("test", graph.value) } class DynamicAppGraphImpl_s058e : AppGraph { // ... }