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

Metro Under the Hood

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Zac Sweers
May 21, 2026
280

Metro Under the Hood

Zac Sweers

May 21, 2026

Transcript

258 slides
  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 { // ... }