aboutsummaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/dagger/BUILD26
-rw-r--r--java/dagger/android/BUILD42
-rw-r--r--java/dagger/android/internal/proguard/BUILD11
-rw-r--r--java/dagger/android/internal/proguard/KspProguardProcessor.java69
-rw-r--r--java/dagger/android/internal/proguard/ProguardProcessingStep.java89
-rw-r--r--java/dagger/android/internal/proguard/ProguardProcessor.java57
-rw-r--r--java/dagger/android/processor/AndroidInjectorDescriptor.java116
-rw-r--r--java/dagger/android/processor/AndroidMapKeyProcessingStep.java163
-rw-r--r--java/dagger/android/processor/AndroidMapKeyValidator.java175
-rw-r--r--java/dagger/android/processor/AndroidMapKeys.java21
-rw-r--r--java/dagger/android/processor/AndroidProcessor.java59
-rw-r--r--java/dagger/android/processor/BUILD60
-rw-r--r--java/dagger/android/processor/BaseProcessingStep.java93
-rw-r--r--java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java (renamed from java/dagger/android/processor/ContributesAndroidInjectorGenerator.java)114
-rw-r--r--java/dagger/android/processor/DelegateAndroidProcessor.java55
-rw-r--r--java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java59
-rw-r--r--java/dagger/android/processor/KspAndroidProcessor.java (renamed from java/dagger/hilt/processor/internal/definecomponent/KspDefineComponentValidationProcessor.java)34
-rw-r--r--java/dagger/android/processor/MoreDaggerElements.java71
-rw-r--r--java/dagger/android/processor/MoreDaggerTypes.java114
-rw-r--r--java/dagger/android/support/BUILD45
-rw-r--r--java/dagger/hilt/android/BUILD22
-rw-r--r--java/dagger/hilt/android/UnstableApi.java29
-rw-r--r--java/dagger/hilt/android/internal/BUILD1
-rw-r--r--java/dagger/hilt/android/internal/OnReceiveBytecodeInjectionMarker.java31
-rw-r--r--java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java6
-rw-r--r--java/dagger/hilt/android/internal/builders/BUILD1
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/BUILD1
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java8
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/HiltViewModelAssistedMap.java32
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java115
-rw-r--r--java/dagger/hilt/android/internal/managers/ActivityComponentManager.java6
-rw-r--r--java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java26
-rw-r--r--java/dagger/hilt/android/internal/managers/BUILD21
-rw-r--r--java/dagger/hilt/android/internal/managers/SavedStateHandleHolder.java79
-rw-r--r--java/dagger/hilt/android/internal/managers/SavedStateHandleModule.java40
-rw-r--r--java/dagger/hilt/android/lifecycle/ActivityRetainedSavedState.java31
-rw-r--r--java/dagger/hilt/android/lifecycle/BUILD23
-rw-r--r--java/dagger/hilt/android/lifecycle/HiltViewModel.java50
-rw-r--r--java/dagger/hilt/android/lifecycle/HiltViewModelExtensions.kt48
-rw-r--r--java/dagger/hilt/android/lifecycle/proguard-rules.pro2
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper-7-0/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper-7-1/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper-7-2/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper-impl/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/agp-wrapper/src/main/kotlin/dagger/hilt/android/plugin/util/ComponentCompat.kt2
-rw-r--r--java/dagger/hilt/android/plugin/build.gradle9
-rw-r--r--java/dagger/hilt/android/plugin/main/build.gradle13
-rw-r--r--java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt49
-rw-r--r--java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt10
-rw-r--r--java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt4
-rw-r--r--java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt41
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/android-libraryA/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/android-libraryC/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/app/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/feature/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/AndroidManifest.xml2
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/java/simple/app/SimpleReceiver.java (renamed from java/dagger/spi/model/CompilerEnvironment.java)17
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/feature/build.gradle6
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/kotlin/AGPCompatibilityTest.kt17
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/kotlin/BuildCacheTest.kt5
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/kotlin/GradleTestRunner.kt21
-rw-r--r--java/dagger/hilt/android/plugin/main/src/test/kotlin/IncrementalProcessorTest.kt1291
-rw-r--r--java/dagger/hilt/android/plugin/settings.gradle1
-rw-r--r--java/dagger/hilt/android/processor/BUILD1
-rw-r--r--java/dagger/hilt/android/processor/internal/AndroidClassNames.java14
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java158
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java3
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD2
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java24
-rw-r--r--java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java9
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/BUILD2
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt164
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt81
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessingStep.kt11
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt95
-rw-r--r--java/dagger/hilt/android/testing/BUILD10
-rw-r--r--java/dagger/hilt/android/testing/SkipTestInjection.java33
-rw-r--r--java/dagger/hilt/android/testing/compile/HiltCompilerTests.java21
-rw-r--r--java/dagger/hilt/migration/BUILD4
-rw-r--r--java/dagger/hilt/processor/BUILD1
-rw-r--r--java/dagger/hilt/processor/internal/BUILD16
-rw-r--r--java/dagger/hilt/processor/internal/ClassNames.java10
-rw-r--r--java/dagger/hilt/processor/internal/DaggerModels.kt40
-rw-r--r--java/dagger/hilt/processor/internal/HiltCompilerOptions.java11
-rw-r--r--java/dagger/hilt/processor/internal/MethodSignature.java81
-rw-r--r--java/dagger/hilt/processor/internal/kotlin/KotlinMetadataUtil.java5
-rw-r--r--java/dagger/hilt/processor/internal/root/RootGenerator.java9
-rw-r--r--java/dagger/hilt/processor/internal/root/RootMetadata.java29
-rw-r--r--java/dagger/hilt/processor/internal/root/RootProcessingStep.java13
-rw-r--r--java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java9
-rw-r--r--java/dagger/hilt/processor/internal/root/TestRootMetadata.java25
-rw-r--r--java/dagger/internal/AbstractMapFactory.java1
-rw-r--r--java/dagger/internal/DelegateFactory.java37
-rw-r--r--java/dagger/internal/DoubleCheck.java23
-rw-r--r--java/dagger/internal/Factory.java1
-rw-r--r--java/dagger/internal/IdentifierNameString.java36
-rw-r--r--java/dagger/internal/KeepFieldType.java36
-rw-r--r--java/dagger/internal/LazyClassKeyMap.java131
-rw-r--r--java/dagger/internal/MapFactory.java20
-rw-r--r--java/dagger/internal/MapProviderFactory.java36
-rw-r--r--java/dagger/internal/Provider.java25
-rw-r--r--java/dagger/internal/ProviderOfLazy.java11
-rw-r--r--java/dagger/internal/Providers.java35
-rw-r--r--java/dagger/internal/SetFactory.java21
-rw-r--r--java/dagger/internal/SingleCheck.java14
-rw-r--r--java/dagger/internal/codegen/BUILD2
-rw-r--r--java/dagger/internal/codegen/DelegateComponentProcessor.java47
-rw-r--r--java/dagger/internal/codegen/base/ElementFormatter.java25
-rw-r--r--java/dagger/internal/codegen/base/FrameworkTypes.java1
-rw-r--r--java/dagger/internal/codegen/base/MapType.java7
-rw-r--r--java/dagger/internal/codegen/base/OptionalType.java11
-rw-r--r--java/dagger/internal/codegen/base/SourceFileGenerator.java4
-rw-r--r--java/dagger/internal/codegen/base/SourceFileHjarGenerator.java211
-rw-r--r--java/dagger/internal/codegen/base/TarjanSCCs.java7
-rw-r--r--java/dagger/internal/codegen/binding/AnnotationExpression.java2
-rw-r--r--java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java33
-rw-r--r--java/dagger/internal/codegen/binding/BUILD3
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraph.java72
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraphConverter.java140
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraphFactory.java271
-rw-r--r--java/dagger/internal/codegen/binding/CancellationPolicy.java39
-rw-r--r--java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java5
-rw-r--r--java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java13
-rw-r--r--java/dagger/internal/codegen/binding/ComponentDescriptor.java375
-rw-r--r--java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java241
-rw-r--r--java/dagger/internal/codegen/binding/ComponentRequirement.java2
-rw-r--r--java/dagger/internal/codegen/binding/DependencyRequestFormatter.java5
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkField.java46
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkType.java5
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkTypeMapper.java4
-rw-r--r--java/dagger/internal/codegen/binding/InjectionAnnotations.java68
-rw-r--r--java/dagger/internal/codegen/binding/InjectionSiteFactory.java7
-rw-r--r--java/dagger/internal/codegen/binding/LegacyBindingGraph.java81
-rw-r--r--java/dagger/internal/codegen/binding/MapKeys.java21
-rw-r--r--java/dagger/internal/codegen/binding/MethodSignatureFormatter.java48
-rw-r--r--java/dagger/internal/codegen/binding/MonitoringModules.java46
-rw-r--r--java/dagger/internal/codegen/binding/Nullability.java57
-rw-r--r--java/dagger/internal/codegen/binding/ResolvedBindings.java11
-rw-r--r--java/dagger/internal/codegen/binding/SourceFiles.java22
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java180
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java24
-rw-r--r--java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jarbin10401292 -> 17059978 bytes
-rw-r--r--java/dagger/internal/codegen/compileroption/CompilerOptions.java10
-rw-r--r--java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java40
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java28
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java9
-rw-r--r--java/dagger/internal/codegen/javac/BUILD14
-rw-r--r--java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java5
-rw-r--r--java/dagger/internal/codegen/javac/JavacPluginModule.java6
-rw-r--r--java/dagger/internal/codegen/javapoet/AnnotationSpecs.java3
-rw-r--r--java/dagger/internal/codegen/javapoet/CodeBlocks.java20
-rw-r--r--java/dagger/internal/codegen/javapoet/TypeNames.java21
-rw-r--r--java/dagger/internal/codegen/kythe/BUILD2
-rw-r--r--java/dagger/internal/codegen/kythe/DaggerKythePlugin.java4
-rw-r--r--java/dagger/internal/codegen/model/BUILD2
-rw-r--r--java/dagger/internal/codegen/model/DaggerAnnotation.java2
-rw-r--r--java/dagger/internal/codegen/model/DaggerElement.java2
-rw-r--r--java/dagger/internal/codegen/model/DaggerExecutableElement.java2
-rw-r--r--java/dagger/internal/codegen/model/DaggerProcessingEnv.java2
-rw-r--r--java/dagger/internal/codegen/model/DaggerType.java2
-rw-r--r--java/dagger/internal/codegen/model/DaggerTypeElement.java2
-rw-r--r--java/dagger/internal/codegen/model/MoreAnnotationMirrors.java2
-rw-r--r--java/dagger/internal/codegen/model/RequestKind.java15
-rw-r--r--java/dagger/internal/codegen/processingstep/AssistedFactoryProcessingStep.java28
-rw-r--r--java/dagger/internal/codegen/processingstep/AssistedInjectProcessingStep.java13
-rw-r--r--java/dagger/internal/codegen/processingstep/ComponentHjarProcessingStep.java5
-rw-r--r--java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java10
-rw-r--r--java/dagger/internal/codegen/processingstep/MonitoringModuleGenerator.java9
-rw-r--r--java/dagger/internal/codegen/processingstep/TypeCheckingProcessingStep.java12
-rw-r--r--java/dagger/internal/codegen/validation/BUILD1
-rw-r--r--java/dagger/internal/codegen/validation/BindingMethodValidator.java7
-rw-r--r--java/dagger/internal/codegen/validation/ComponentValidator.java13
-rw-r--r--java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java42
-rw-r--r--java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java116
-rw-r--r--java/dagger/internal/codegen/validation/InjectValidator.java10
-rw-r--r--java/dagger/internal/codegen/validation/ModelBindingGraphConverter.java18
-rw-r--r--java/dagger/internal/codegen/validation/ModuleValidator.java6
-rw-r--r--java/dagger/internal/codegen/validation/ProducesMethodValidator.java2
-rw-r--r--java/dagger/internal/codegen/validation/SpiModelBindingGraphConverter.java365
-rw-r--r--java/dagger/internal/codegen/writing/AssistedInjectionParameters.java6
-rw-r--r--java/dagger/internal/codegen/writing/BUILD2
-rw-r--r--java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java6
-rw-r--r--java/dagger/internal/codegen/writing/ComponentImplementation.java37
-rw-r--r--java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java8
-rw-r--r--java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java50
-rw-r--r--java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java12
-rw-r--r--java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java10
-rw-r--r--java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java55
-rw-r--r--java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java127
-rw-r--r--java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java275
-rw-r--r--java/dagger/internal/codegen/writing/FactoryGenerator.java21
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java5
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java6
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkInstanceKind.java16
-rw-r--r--java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java122
-rw-r--r--java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java33
-rw-r--r--java/dagger/internal/codegen/writing/InjectionMethods.java36
-rw-r--r--java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java48
-rw-r--r--java/dagger/internal/codegen/writing/LazyClassKeyProviders.java117
-rw-r--r--java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java37
-rw-r--r--java/dagger/internal/codegen/writing/MapRequestRepresentation.java57
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectionMethods.java60
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java3
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectorGenerator.java19
-rw-r--r--java/dagger/internal/codegen/writing/OptionalFactories.java23
-rw-r--r--java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java23
-rw-r--r--java/dagger/internal/codegen/writing/ProducerEntryPointView.java14
-rw-r--r--java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java18
-rw-r--r--java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java3
-rw-r--r--java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java4
-rw-r--r--java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java41
-rw-r--r--java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java27
-rw-r--r--java/dagger/internal/codegen/writing/SetRequestRepresentation.java12
-rw-r--r--java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java25
-rw-r--r--java/dagger/internal/codegen/writing/StaticMemberSelects.java4
-rw-r--r--java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java23
-rw-r--r--java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java8
-rw-r--r--java/dagger/internal/codegen/writing/SwitchingProviders.java4
-rw-r--r--java/dagger/internal/codegen/xprocessing/BUILD1
-rw-r--r--java/dagger/internal/codegen/xprocessing/DaggerElements.java79
-rw-r--r--java/dagger/internal/codegen/xprocessing/JavaPoetExt.java3
-rw-r--r--java/dagger/internal/codegen/xprocessing/MethodSpecs.java3
-rw-r--r--java/dagger/internal/codegen/xprocessing/XElements.java34
-rw-r--r--java/dagger/internal/codegen/xprocessing/XMethodElements.java17
-rw-r--r--java/dagger/internal/codegen/xprocessing/XTypeElements.java20
-rw-r--r--java/dagger/internal/codegen/xprocessing/XTypes.java46
-rw-r--r--java/dagger/internal/codegen/xprocessing/xprocessing-testing.jarbin217319 -> 219524 bytes
-rw-r--r--java/dagger/internal/codegen/xprocessing/xprocessing.jarbin2263056 -> 2420617 bytes
-rw-r--r--java/dagger/lint/DaggerKotlinIssueDetector.kt3
-rw-r--r--java/dagger/model/BUILD1
-rw-r--r--java/dagger/model/BindingGraph.java15
-rw-r--r--java/dagger/model/BindingKind.java12
-rw-r--r--java/dagger/model/ComponentPath.java3
-rw-r--r--java/dagger/model/RequestKind.java19
-rw-r--r--java/dagger/model/Scope.java3
-rw-r--r--java/dagger/multibindings/LazyClassKey.java39
-rw-r--r--java/dagger/producers/internal/AbstractMapProducer.java12
-rw-r--r--java/dagger/producers/internal/AbstractProducesMethodProducer.java15
-rw-r--r--java/dagger/producers/internal/DelegateProducer.java2
-rw-r--r--java/dagger/producers/internal/MapOfProducedProducer.java12
-rw-r--r--java/dagger/producers/internal/MapOfProducerProducer.java12
-rw-r--r--java/dagger/producers/internal/MapProducer.java12
-rw-r--r--java/dagger/producers/internal/Producers.java12
-rw-r--r--java/dagger/proguard.pro3
-rw-r--r--java/dagger/r8.pro3
-rw-r--r--java/dagger/spi/BUILD1
-rw-r--r--java/dagger/spi/model/BUILD2
-rw-r--r--java/dagger/spi/model/BindingGraph.java16
-rw-r--r--java/dagger/spi/model/BindingKind.java16
-rw-r--r--java/dagger/spi/model/ComponentPath.java5
-rw-r--r--java/dagger/spi/model/DaggerAnnotation.java52
-rw-r--r--java/dagger/spi/model/DaggerElement.java48
-rw-r--r--java/dagger/spi/model/DaggerExecutableElement.java58
-rw-r--r--java/dagger/spi/model/DaggerProcessingEnv.java49
-rw-r--r--java/dagger/spi/model/DaggerType.java52
-rw-r--r--java/dagger/spi/model/DaggerTypeElement.java83
-rw-r--r--java/dagger/spi/model/Key.java22
-rw-r--r--java/dagger/spi/model/MoreAnnotationMirrors.java2
-rw-r--r--java/dagger/spi/model/RequestKind.java19
-rw-r--r--java/dagger/spi/model/Scope.java16
-rw-r--r--java/dagger/spi/model/testing/BindingGraphSubject.java2
-rw-r--r--java/dagger/testing/compile/BUILD3
-rw-r--r--java/dagger/testing/compile/CompilerTests.java54
265 files changed, 6034 insertions, 4131 deletions
diff --git a/java/dagger/BUILD b/java/dagger/BUILD
index 4675e3054..918340825 100644
--- a/java/dagger/BUILD
+++ b/java/dagger/BUILD
@@ -22,8 +22,7 @@ load(
"JAVA_RELEASE_MIN",
"POM_VERSION",
)
-load("//tools:maven.bzl", "pom_file")
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load("//tools:maven.bzl", "gen_maven_artifact")
package(default_visibility = ["//:src"])
@@ -38,22 +37,21 @@ java_library(
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger:" + POM_VERSION,
artifact_name = "Dagger",
- targets = [":core"],
+ artifact_target = ":core",
+ artifact_target_maven_deps = [
+ "javax.inject:javax.inject",
+ ],
+ javadoc_root_packages = ["dagger"],
+ javadoc_srcs = [":javadoc-srcs"],
+ proguard_specs = [":proguard.pro"],
+ r8_specs = [":r8.pro"],
)
filegroup(
name = "javadoc-srcs",
srcs = glob(["**/*"]),
)
-
-javadoc_library(
- name = "core-javadoc",
- srcs = [":javadoc-srcs"],
- exclude_packages = ["dagger.internal"],
- root_packages = ["dagger"],
- deps = ["//third_party/java/jsr330_inject"],
-)
diff --git a/java/dagger/android/BUILD b/java/dagger/android/BUILD
index ef41dde4a..686e6aef5 100644
--- a/java/dagger/android/BUILD
+++ b/java/dagger/android/BUILD
@@ -17,14 +17,14 @@
load(
"//:build_defs.bzl",
- "DOCLINT_HTML_AND_SYNTAX",
- "DOCLINT_REFERENCES",
- "JAVA_RELEASE_MIN",
"POM_VERSION",
)
load("//tools:dejetify.bzl", "dejetified_library")
-load("//tools:maven.bzl", "pom_file")
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load(
+ "//tools:maven.bzl",
+ "gen_maven_artifact",
+ "pom_file",
+)
package(default_visibility = ["//:src"])
@@ -43,8 +43,6 @@ filegroup(
android_library(
name = "android",
srcs = SRCS,
- javacopts = JAVA_RELEASE_MIN + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
- manifest = "AndroidManifest.xml",
plugins = [
"//java/dagger/android/internal/proguard:plugin",
],
@@ -60,17 +58,28 @@ android_library(
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-android",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-android:" + POM_VERSION,
artifact_name = "Dagger Android",
+ artifact_target = ":android",
+ artifact_target_maven_deps = [
+ "androidx.annotation:annotation",
+ "com.google.dagger:dagger",
+ "com.google.dagger:dagger-lint-aar",
+ ],
+ javadoc_android_api_level = 32,
+ javadoc_root_packages = [
+ "dagger.android",
+ ],
+ javadoc_srcs = [":android-srcs"],
+ manifest = "AndroidManifest.xml",
packaging = "aar",
- targets = [":android"],
)
dejetified_library(
name = "dejetified-android",
- input = ":android.aar",
+ input = ":artifact.aar",
output = "android-legacy.aar",
)
@@ -92,12 +101,3 @@ pom_file(
packaging = "aar",
targets = [":legacy-deps"],
)
-
-javadoc_library(
- name = "android-javadoc",
- srcs = [":android-srcs"],
- android_api_level = 30,
- exclude_packages = ["dagger.android.internal"],
- root_packages = ["dagger.android"],
- deps = [":android"],
-)
diff --git a/java/dagger/android/internal/proguard/BUILD b/java/dagger/android/internal/proguard/BUILD
index 5a85279fb..7cb2cbc5d 100644
--- a/java/dagger/android/internal/proguard/BUILD
+++ b/java/dagger/android/internal/proguard/BUILD
@@ -22,10 +22,19 @@ package(default_visibility = ["//:src"])
java_library(
name = "proguard-processor",
- srcs = ["ProguardProcessor.java"],
+ srcs = [
+ "KspProguardProcessor.java",
+ "ProguardProcessingStep.java",
+ "ProguardProcessor.java",
+ ],
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
deps = [
+ "//java/dagger/android/processor:base_processing_step",
+ "//java/dagger/internal/codegen/xprocessing",
"//third_party/java/auto:service",
+ "//third_party/java/guava/collect",
+ "//third_party/java/javapoet",
+ "@maven//:com_google_devtools_ksp_symbol_processing_api",
],
)
diff --git a/java/dagger/android/internal/proguard/KspProguardProcessor.java b/java/dagger/android/internal/proguard/KspProguardProcessor.java
new file mode 100644
index 000000000..e2d4c4c3b
--- /dev/null
+++ b/java/dagger/android/internal/proguard/KspProguardProcessor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.internal.proguard;
+
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingEnvConfig;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.ksp.processing.SymbolProcessor;
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment;
+import com.google.devtools.ksp.processing.SymbolProcessorProvider;
+
+/**
+ * An annotation processor to generate dagger-android's specific proguard needs. This is only
+ * intended to run over the dagger-android project itself, as the alternative is to create an
+ * intermediary java_library for proguard rules to be consumed by the project.
+ *
+ * <p>Basic structure looks like this:
+ *
+ * <pre><code>
+ * resources/META-INF/com.android.tools/proguard/dagger-android.pro
+ * resources/META-INF/com.android.tools/r8/dagger-android.pro
+ * resources/META-INF/proguard/dagger-android.pro
+ * </code></pre>
+ */
+public final class KspProguardProcessor extends KspBasicAnnotationProcessor {
+ private static final XProcessingEnvConfig PROCESSING_ENV_CONFIG =
+ new XProcessingEnvConfig.Builder().build();
+ private XProcessingEnv env;
+
+ private KspProguardProcessor(SymbolProcessorEnvironment symbolProcessorEnvironment) {
+ super(symbolProcessorEnvironment, PROCESSING_ENV_CONFIG);
+ }
+
+ @Override
+ public void initialize(XProcessingEnv env) {
+ this.env = env;
+ }
+
+ @Override
+ public Iterable<XProcessingStep> processingSteps() {
+ return ImmutableList.of(new ProguardProcessingStep(env));
+ }
+
+ /** Provides the {@link KspProguardProcessor}. */
+ @AutoService(SymbolProcessorProvider.class)
+ public static final class Provider implements SymbolProcessorProvider {
+ @Override
+ public SymbolProcessor create(SymbolProcessorEnvironment symbolProcessorEnvironment) {
+ return new KspProguardProcessor(symbolProcessorEnvironment);
+ }
+ }
+}
diff --git a/java/dagger/android/internal/proguard/ProguardProcessingStep.java b/java/dagger/android/internal/proguard/ProguardProcessingStep.java
new file mode 100644
index 000000000..c4cc0612d
--- /dev/null
+++ b/java/dagger/android/internal/proguard/ProguardProcessingStep.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.internal.proguard;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XProcessingEnv;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.android.processor.BaseProcessingStep;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.file.Path;
+
+/**
+ * A annotation processing step to generate dagger-android's specific proguard needs. This is only
+ * intended to run over the dagger-android project itself, as the alternative is to create an
+ * intermediary java_library for proguard rules to be consumed by the project.
+ *
+ * <p>Basic structure looks like this:
+ *
+ * <pre><code>
+ * resources/META-INF/com.android.tools/proguard/dagger-android.pro
+ * resources/META-INF/com.android.tools/r8/dagger-android.pro
+ * resources/META-INF/proguard/dagger-android.pro
+ * </code></pre>
+ */
+public final class ProguardProcessingStep extends BaseProcessingStep {
+ private final XProcessingEnv processingEnv;
+
+ ProguardProcessingStep(XProcessingEnv processingEnv) {
+ this.processingEnv = processingEnv;
+ }
+
+ static final ClassName GENERATE_RULES_ANNOTATION_NAME =
+ ClassName.get("dagger.android.internal", "GenerateAndroidInjectionProguardRules");
+
+ @Override
+ public ImmutableSet<ClassName> annotationClassNames() {
+ return ImmutableSet.of(GENERATE_RULES_ANNOTATION_NAME);
+ }
+
+ @Override
+ public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
+ XFiler filer = processingEnv.getFiler();
+
+ String errorProneRule = "-dontwarn com.google.errorprone.annotations.**\n";
+ String androidInjectionKeysRule =
+ "-identifiernamestring class dagger.android.internal.AndroidInjectionKeys {\n"
+ + " java.lang.String of(java.lang.String);\n"
+ + "}\n";
+
+ writeFile(filer, "com.android.tools/proguard", errorProneRule);
+ writeFile(filer, "com.android.tools/r8", errorProneRule + androidInjectionKeysRule);
+ writeFile(filer, "proguard", errorProneRule);
+ }
+
+ private void writeFile(XFiler filer, String intermediatePath, String contents) {
+ try (OutputStream outputStream =
+ filer.writeResource(
+ Path.of("META-INF/" + intermediatePath + "/dagger-android.pro"),
+ ImmutableList.<XElement>of(),
+ XFiler.Mode.Isolating);
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, UTF_8))) {
+ writer.write(contents);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/java/dagger/android/internal/proguard/ProguardProcessor.java b/java/dagger/android/internal/proguard/ProguardProcessor.java
index 49274e94f..8677bf179 100644
--- a/java/dagger/android/internal/proguard/ProguardProcessor.java
+++ b/java/dagger/android/internal/proguard/ProguardProcessor.java
@@ -16,19 +16,13 @@
package dagger.android.internal.proguard;
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
-
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor;
import com.google.auto.service.AutoService;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Filer;
+import com.google.common.collect.ImmutableList;
import javax.annotation.processing.Processor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
-import javax.lang.model.element.TypeElement;
/**
* An {@linkplain Processor annotation processor} to generate dagger-android's specific proguard
@@ -44,46 +38,17 @@ import javax.lang.model.element.TypeElement;
* </code></pre>
*/
@AutoService(Processor.class)
-@SupportedAnnotationTypes(ProguardProcessor.GENERATE_RULES_ANNOTATION_NAME)
-public final class ProguardProcessor extends AbstractProcessor {
-
- static final String GENERATE_RULES_ANNOTATION_NAME =
- "dagger.android.internal.GenerateAndroidInjectionProguardRules";
+public final class ProguardProcessor extends JavacBasicAnnotationProcessor {
+ private XProcessingEnv env;
@Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- roundEnv
- .getElementsAnnotatedWith(
- processingEnv.getElementUtils().getTypeElement(GENERATE_RULES_ANNOTATION_NAME))
- .forEach(element -> generate());
-
- return false;
- }
-
- private void generate() {
- Filer filer = processingEnv.getFiler();
-
- String errorProneRule = "-dontwarn com.google.errorprone.annotations.**\n";
- String androidInjectionKeysRule =
- "-identifiernamestring class dagger.android.internal.AndroidInjectionKeys {\n"
- + " java.lang.String of(java.lang.String);\n"
- + "}\n";
-
- writeFile(filer, "com.android.tools/proguard", errorProneRule);
- writeFile(filer, "com.android.tools/r8", errorProneRule + androidInjectionKeysRule);
- writeFile(filer, "proguard", errorProneRule);
+ public void initialize(XProcessingEnv env) {
+ this.env = env;
}
- private static void writeFile(Filer filer, String intermediatePath, String contents) {
- try (Writer writer =
- filer
- .createResource(
- CLASS_OUTPUT, "", "META-INF/" + intermediatePath + "/dagger-android.pro")
- .openWriter()) {
- writer.write(contents);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
+ @Override
+ public Iterable<XProcessingStep> processingSteps() {
+ return ImmutableList.of(new ProguardProcessingStep(env));
}
@Override
diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java
index 12f2edf24..758d895aa 100644
--- a/java/dagger/android/processor/AndroidInjectorDescriptor.java
+++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java
@@ -16,29 +16,23 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static dagger.android.processor.MoreDaggerElements.getAnnotatedAnnotations;
-import static java.util.stream.Collectors.toList;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.JavaPoetExtKt;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeName;
-import java.util.List;
+import dagger.internal.codegen.xprocessing.XElements;
import java.util.Optional;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.tools.Diagnostic.Kind;
/**
@@ -60,7 +54,7 @@ abstract class AndroidInjectorDescriptor {
abstract ClassName enclosingModule();
/** The method annotated with {@code ContributesAndroidInjector}. */
- abstract ExecutableElement method();
+ abstract XExecutableElement method();
@AutoValue.Builder
abstract static class Builder {
@@ -72,15 +66,15 @@ abstract class AndroidInjectorDescriptor {
abstract Builder enclosingModule(ClassName enclosingModule);
- abstract Builder method(ExecutableElement method);
+ abstract Builder method(XExecutableElement method);
abstract AndroidInjectorDescriptor build();
}
static final class Validator {
- private final Messager messager;
+ private final XMessager messager;
- Validator(Messager messager) {
+ Validator(XMessager messager) {
this.messager = messager;
}
@@ -88,10 +82,10 @@ abstract class AndroidInjectorDescriptor {
* Validates a {@code ContributesAndroidInjector} method, returning an {@link
* AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
*/
- Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
+ Optional<AndroidInjectorDescriptor> createIfValid(XMethodElement method) {
ErrorReporter reporter = new ErrorReporter(method, messager);
- if (!method.getModifiers().contains(ABSTRACT)) {
+ if (!method.isAbstract()) {
reporter.reportError("@ContributesAndroidInjector methods must be abstract");
}
@@ -101,41 +95,40 @@ abstract class AndroidInjectorDescriptor {
AndroidInjectorDescriptor.Builder builder =
new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
- TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
- if (!MoreDaggerElements.isAnnotationPresent(enclosingElement, TypeNames.MODULE)) {
+ XTypeElement enclosingElement = XElements.asTypeElement(method.getEnclosingElement());
+ if (!enclosingElement.hasAnnotation(TypeNames.MODULE)) {
reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
}
- builder.enclosingModule(ClassName.get(enclosingElement));
+ builder.enclosingModule(enclosingElement.getClassName());
- TypeMirror injectedType = method.getReturnType();
- if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) {
- builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType)));
+ XType injectedType = method.getReturnType();
+ if (injectedType.getTypeArguments().isEmpty()) {
+ builder.injectedType(injectedType.getTypeElement().getClassName());
} else {
reporter.reportError(
"@ContributesAndroidInjector methods cannot return parameterized types");
}
- AnnotationMirror annotation =
- MoreDaggerElements.getAnnotationMirror(method, TypeNames.CONTRIBUTES_ANDROID_INJECTOR)
- .get();
- for (TypeMirror module :
- getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
- if (MoreDaggerElements.isAnnotationPresent(MoreTypes.asElement(module), TypeNames.MODULE)) {
- builder.modulesBuilder().add((ClassName) TypeName.get(module));
+ XAnnotation annotation = method.getAnnotation(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
+ for (XType module : getTypeList(annotation.getAnnotationValue("modules"))) {
+ if (module.getTypeElement().hasAnnotation(TypeNames.MODULE)) {
+ builder.modulesBuilder().add((ClassName) module.getTypeName());
} else {
reporter.reportError(String.format("%s is not a @Module", module), annotation);
}
}
- for (AnnotationMirror scope : Sets.union(
- getAnnotatedAnnotations(method, TypeNames.SCOPE),
- getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) {
- builder.scopesBuilder().add(AnnotationSpec.get(scope));
+ for (XAnnotation scope :
+ Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE),
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE_JAVAX))) {
+ builder.scopesBuilder().add(JavaPoetExtKt.toAnnotationSpec(scope));
}
- for (AnnotationMirror qualifier : Sets.union(
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) {
+ for (XAnnotation qualifier :
+ Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER),
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER_JAVAX))) {
reporter.reportError(
"@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
}
@@ -143,13 +136,23 @@ abstract class AndroidInjectorDescriptor {
return reporter.hasError ? Optional.empty() : Optional.of(builder.build());
}
+ private static ImmutableList<XType> getTypeList(XAnnotationValue annotationValue) {
+ if (annotationValue.hasTypeListValue()) {
+ return ImmutableList.copyOf(annotationValue.asTypeList());
+ }
+ if (annotationValue.hasTypeValue()) {
+ return ImmutableList.of(annotationValue.asType());
+ }
+ throw new IllegalArgumentException("Does not have type list");
+ }
+
// TODO(ronshapiro): use ValidationReport once it is moved out of the compiler
private static class ErrorReporter {
- private final Element subject;
- private final Messager messager;
+ private final XElement subject;
+ private final XMessager messager;
private boolean hasError;
- ErrorReporter(Element subject, Messager messager) {
+ ErrorReporter(XElement subject, XMessager messager) {
this.subject = subject;
this.messager = messager;
}
@@ -159,29 +162,10 @@ abstract class AndroidInjectorDescriptor {
messager.printMessage(Kind.ERROR, error, subject);
}
- void reportError(String error, AnnotationMirror annotation) {
+ void reportError(String error, XAnnotation annotation) {
hasError = true;
messager.printMessage(Kind.ERROR, error, subject, annotation);
}
}
}
-
- private static final class AllTypesVisitor
- extends SimpleAnnotationValueVisitor8<ImmutableSet<TypeMirror>, Void> {
- @Override
- public ImmutableSet<TypeMirror> visitArray(List<? extends AnnotationValue> values, Void aVoid) {
- return ImmutableSet.copyOf(
- values.stream().flatMap(v -> v.accept(this, null).stream()).collect(toList()));
- }
-
- @Override
- public ImmutableSet<TypeMirror> visitType(TypeMirror a, Void aVoid) {
- return ImmutableSet.of(a);
- }
-
- @Override
- protected ImmutableSet<TypeMirror> defaultAction(Object o, Void aVoid) {
- throw new AssertionError(o);
- }
- }
}
diff --git a/java/dagger/android/processor/AndroidMapKeyProcessingStep.java b/java/dagger/android/processor/AndroidMapKeyProcessingStep.java
new file mode 100644
index 000000000..8a7e83c77
--- /dev/null
+++ b/java/dagger/android/processor/AndroidMapKeyProcessingStep.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
+import static dagger.internal.codegen.xprocessing.XTypes.toStableString;
+
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
+import javax.tools.Diagnostic.Kind;
+
+/** Validates the correctness of {@link dagger.MapKey}s used with {@code dagger.android}. */
+final class AndroidMapKeyProcessingStep extends BaseProcessingStep {
+ private final XProcessingEnv processingEnv;
+
+ AndroidMapKeyProcessingStep(XProcessingEnv processingEnv) {
+ this.processingEnv = processingEnv;
+ }
+
+ @Override
+ public ImmutableSet<ClassName> annotationClassNames() {
+ return ImmutableSet.of(TypeNames.ANDROID_INJECTION_KEY, TypeNames.CLASS_KEY);
+ }
+
+ @Override
+ public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
+ for (ClassName annotationName : annotationNames) {
+ validateMethod(annotationName, XElements.asMethod(element));
+ }
+ }
+
+ private void validateMethod(ClassName annotation, XMethodElement method) {
+ if (!Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER),
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER_JAVAX))
+ .isEmpty()) {
+ return;
+ }
+
+ XType returnType = method.getReturnType();
+ if (!factoryElement().getType().getRawType().isAssignableFrom(returnType.getRawType())) {
+ // if returnType is not related to AndroidInjector.Factory, ignore the method
+ return;
+ }
+
+ if (!Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE),
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE_JAVAX))
+ .isEmpty()) {
+ XAnnotation suppressedWarnings = method.getAnnotation(ClassName.get(SuppressWarnings.class));
+ if (suppressedWarnings == null
+ || !ImmutableSet.copyOf(suppressedWarnings.getAsStringList("value"))
+ .contains("dagger.android.ScopedInjectorFactory")) {
+ XAnnotation mapKeyAnnotation =
+ getOnlyElement(method.getAnnotationsAnnotatedWith(TypeNames.MAP_KEY));
+ XTypeElement mapKeyValueElement =
+ processingEnv.requireTypeElement(injectedTypeFromMapKey(mapKeyAnnotation).get());
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s bindings should not be scoped. Scoping this method may leak instances of"
+ + " %s.",
+ TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName(),
+ mapKeyValueElement.getQualifiedName()),
+ method);
+ }
+ }
+
+ validateReturnType(method);
+
+ // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
+ // in javac, so do a basic check for valid form
+ if (method.hasAnnotation(TypeNames.BINDS) && method.getParameters().size() == 1) {
+ validateMapKeyMatchesBindsParameter(annotation, method);
+ }
+ }
+
+ /** Report an error if the method's return type is not {@code AndroidInjector.Factory<?>}. */
+ private void validateReturnType(XMethodElement method) {
+ XType returnType = method.getReturnType();
+ XType requiredReturnType = injectorFactoryOf(processingEnv.getWildcardType(null, null));
+
+ // TODO(b/311460276) use XType.isSameType when the bug is fixed.
+ if (!returnType.getTypeName().equals(requiredReturnType.getTypeName())) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s should bind %s, not %s. See https://dagger.dev/android",
+ method, toStableString(requiredReturnType), toStableString(returnType)),
+ method);
+ }
+ }
+
+ /**
+ * A valid @Binds method could bind an {@code AndroidInjector.Factory} for one type, while giving
+ * it a map key of a different type. The return type and parameter type would pass typical @Binds
+ * validation, but the map lookup in {@code DispatchingAndroidInjector} would retrieve the wrong
+ * injector factory.
+ *
+ * <pre>{@code
+ * {@literal @Binds}
+ * {@literal @IntoMap}
+ * {@literal @ClassKey(GreenActivity.class)}
+ * abstract AndroidInjector.Factory<?> bindBlueActivity(
+ * BlueActivityComponent.Builder builder);
+ * }</pre>
+ */
+ private void validateMapKeyMatchesBindsParameter(
+ ClassName annotationName, XMethodElement method) {
+ XType parameterType = getOnlyElement(method.getParameters()).getType();
+ XAnnotation annotation = method.getAnnotation(annotationName);
+ XType mapKeyType =
+ processingEnv.requireTypeElement(injectedTypeFromMapKey(annotation).get()).getType();
+ if (!XTypes.isAssignableTo(parameterType, injectorFactoryOf(mapKeyType))) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s does not implement AndroidInjector<%s>",
+ toStableString(parameterType), toStableString(mapKeyType)),
+ method,
+ annotation);
+ }
+ }
+
+ /** Returns a {@link XType} for {@code AndroidInjector.Factory<implementationType>}. */
+ private XType injectorFactoryOf(XType implementationType) {
+ return processingEnv.getDeclaredType(factoryElement(), implementationType);
+ }
+
+ private XTypeElement factoryElement() {
+ return processingEnv.requireTypeElement(TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName());
+ }
+}
diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java
deleted file mode 100644
index 8d674666b..000000000
--- a/java/dagger/android/processor/AndroidMapKeyValidator.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android.processor;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
-import static dagger.android.processor.MoreDaggerElements.getAnnotatedAnnotations;
-
-import com.google.auto.common.BasicAnnotationProcessor.Step;
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.ClassName;
-import dagger.MapKey;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic.Kind;
-
-/** Validates the correctness of {@link dagger.MapKey}s used with {@code dagger.android}. */
-final class AndroidMapKeyValidator implements Step {
- private static final ImmutableMap<String, ClassName> SUPPORTED_ANNOTATIONS =
- ImmutableMap.of(
- TypeNames.ANDROID_INJECTION_KEY.toString(), TypeNames.ANDROID_INJECTION_KEY,
- TypeNames.CLASS_KEY.toString(), TypeNames.CLASS_KEY);
-
- private final Elements elements;
- private final Types types;
- private final Messager messager;
-
- AndroidMapKeyValidator(Elements elements, Types types, Messager messager) {
- this.elements = elements;
- this.types = types;
- this.messager = messager;
- }
-
- @Override
- public ImmutableSet<String> annotations() {
- return SUPPORTED_ANNOTATIONS.keySet();
- }
-
- @Override
- public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
- elementsByAnnotation
- .entries()
- .forEach(
- entry -> {
- try {
- validateMethod(entry.getKey(), MoreElements.asExecutable(entry.getValue()));
- } catch (TypeNotPresentException e) {
- deferredElements.add(entry.getValue());
- }
- });
- return deferredElements.build();
- }
-
- private void validateMethod(String annotation, ExecutableElement method) {
- if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX)).isEmpty()) {
- return;
- }
-
- TypeMirror returnType = method.getReturnType();
- if (!types.isAssignable(types.erasure(returnType), factoryElement().asType())) {
- // if returnType is not related to AndroidInjector.Factory, ignore the method
- return;
- }
-
- if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.SCOPE),
- getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX)).isEmpty()) {
- SuppressWarnings suppressedWarnings = method.getAnnotation(SuppressWarnings.class);
- if (suppressedWarnings == null
- || !ImmutableSet.copyOf(suppressedWarnings.value())
- .contains("dagger.android.ScopedInjectorFactory")) {
- AnnotationMirror mapKeyAnnotation =
- getOnlyElement(getAnnotatedAnnotations(method, MapKey.class));
- TypeElement mapKeyValueElement =
- elements.getTypeElement(injectedTypeFromMapKey(mapKeyAnnotation).get());
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "%s bindings should not be scoped. Scoping this method may leak instances of %s.",
- TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName(),
- mapKeyValueElement.getQualifiedName()),
- method);
- }
- }
-
- validateReturnType(method);
-
- // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
- // in javac, so do a basic check for valid form
- if (MoreDaggerElements.isAnnotationPresent(method, TypeNames.BINDS)
- && method.getParameters().size() == 1) {
- validateMapKeyMatchesBindsParameter(annotation, method);
- }
- }
-
- /** Report an error if the method's return type is not {@code AndroidInjector.Factory<?>}. */
- private void validateReturnType(ExecutableElement method) {
- TypeMirror returnType = method.getReturnType();
- DeclaredType requiredReturnType = injectorFactoryOf(types.getWildcardType(null, null));
-
- if (!types.isSameType(returnType, requiredReturnType)) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "%s should bind %s, not %s. See https://dagger.dev/android",
- method, requiredReturnType, returnType),
- method);
- }
- }
-
- /**
- * A valid @Binds method could bind an {@code AndroidInjector.Factory} for one type, while giving
- * it a map key of a different type. The return type and parameter type would pass typical @Binds
- * validation, but the map lookup in {@code DispatchingAndroidInjector} would retrieve the wrong
- * injector factory.
- *
- * <pre>{@code
- * {@literal @Binds}
- * {@literal @IntoMap}
- * {@literal @ClassKey(GreenActivity.class)}
- * abstract AndroidInjector.Factory<?> bindBlueActivity(
- * BlueActivityComponent.Builder builder);
- * }</pre>
- */
- private void validateMapKeyMatchesBindsParameter(String annotation, ExecutableElement method) {
- TypeMirror parameterType = getOnlyElement(method.getParameters()).asType();
- AnnotationMirror annotationMirror =
- MoreDaggerElements.getAnnotationMirror(method, SUPPORTED_ANNOTATIONS.get(annotation)).get();
- TypeMirror mapKeyType =
- elements.getTypeElement(injectedTypeFromMapKey(annotationMirror).get()).asType();
- if (!types.isAssignable(parameterType, injectorFactoryOf(mapKeyType))) {
- messager.printMessage(
- Kind.ERROR,
- String.format("%s does not implement AndroidInjector<%s>", parameterType, mapKeyType),
- method,
- annotationMirror);
- }
- }
-
- /** Returns a {@link DeclaredType} for {@code AndroidInjector.Factory<implementationType>}. */
- private DeclaredType injectorFactoryOf(TypeMirror implementationType) {
- return types.getDeclaredType(factoryElement(), implementationType);
- }
-
- private TypeElement factoryElement() {
- return elements.getTypeElement(TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName());
- }
-}
diff --git a/java/dagger/android/processor/AndroidMapKeys.java b/java/dagger/android/processor/AndroidMapKeys.java
index 28da2715a..e3d890e29 100644
--- a/java/dagger/android/processor/AndroidMapKeys.java
+++ b/java/dagger/android/processor/AndroidMapKeys.java
@@ -16,13 +16,9 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
final class AndroidMapKeys {
/**
@@ -30,13 +26,12 @@ final class AndroidMapKeys {
* it's {@link dagger.multibindings.ClassKey}, returns the fully-qualified class name of the
* annotation value. Otherwise returns {@link Optional#empty()}.
*/
- static Optional<String> injectedTypeFromMapKey(AnnotationMirror mapKey) {
- Object mapKeyClass = getAnnotationValue(mapKey, "value").getValue();
- if (mapKeyClass instanceof String) {
- return Optional.of((String) mapKeyClass);
- } else if (mapKeyClass instanceof TypeMirror) {
- TypeElement type = MoreTypes.asTypeElement((TypeMirror) mapKeyClass);
- return Optional.of(type.getQualifiedName().toString());
+ static Optional<String> injectedTypeFromMapKey(XAnnotation mapKey) {
+ XAnnotationValue mapKeyClass = mapKey.getAnnotationValue("value");
+ if (mapKeyClass.hasStringValue()) {
+ return Optional.of(mapKeyClass.asString());
+ } else if (mapKeyClass.hasTypeValue()) {
+ return Optional.of(mapKeyClass.asType().getTypeElement().getQualifiedName());
} else {
return Optional.empty();
}
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
index 2a8ab345b..beb8d0de7 100644
--- a/java/dagger/android/processor/AndroidProcessor.java
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -16,21 +16,15 @@
package dagger.android.processor;
-import static javax.tools.Diagnostic.Kind.ERROR;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
-import com.google.auto.common.BasicAnnotationProcessor;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor;
import com.google.auto.service.AutoService;
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
/**
@@ -49,51 +43,22 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
*/
@IncrementalAnnotationProcessor(ISOLATING)
@AutoService(Processor.class)
-public final class AndroidProcessor extends BasicAnnotationProcessor {
- private static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
- "dagger.android.experimentalUseStringKeys";
+public final class AndroidProcessor extends JavacBasicAnnotationProcessor {
+ private final DelegateAndroidProcessor delegate = new DelegateAndroidProcessor();
@Override
- protected Iterable<? extends Step> steps() {
- Filer filer = processingEnv.getFiler();
- Messager messager = processingEnv.getMessager();
- Elements elements = processingEnv.getElementUtils();
- Types types = processingEnv.getTypeUtils();
-
- return ImmutableList.of(
- new AndroidMapKeyValidator(elements, types, messager),
- new ContributesAndroidInjectorGenerator(
- new AndroidInjectorDescriptor.Validator(messager),
- useStringKeys(),
- filer,
- elements,
- processingEnv.getSourceVersion()));
+ public void initialize(XProcessingEnv env) {
+ delegate.initialize(env);
}
- private boolean useStringKeys() {
- if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
- return false;
- }
- String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
- if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
- return true;
- } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
- return false;
- } else {
- processingEnv
- .getMessager()
- .printMessage(
- ERROR,
- String.format(
- "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
- flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
- return false;
- }
+ @Override
+ public Iterable<XProcessingStep> processingSteps() {
+ return delegate.processingSteps();
}
@Override
- public Set<String> getSupportedOptions() {
- return ImmutableSet.of(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ public final ImmutableSet<String> getSupportedOptions() {
+ return ImmutableSet.of(DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS);
}
@Override
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
index d545ef8ef..f70c09102 100644
--- a/java/dagger/android/processor/BUILD
+++ b/java/dagger/android/processor/BUILD
@@ -22,8 +22,7 @@ load(
"DOCLINT_REFERENCES",
"POM_VERSION",
)
-load("//tools:maven.bzl", "pom_file")
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load("//tools:maven.bzl", "gen_maven_artifact")
package(default_visibility = ["//:src"])
@@ -33,29 +32,65 @@ filegroup(
)
java_library(
+ name = "base_processing_step",
+ srcs = ["BaseProcessingStep.java"],
+ deps = [
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/xprocessing",
+ "//third_party/java/guava/base",
+ "//third_party/java/guava/collect",
+ "//third_party/java/javapoet",
+ ],
+)
+
+java_library(
name = "processor",
- srcs = [":srcs"],
+ srcs = glob(
+ ["*.java"],
+ exclude = ["BaseProcessingStep.java"],
+ ),
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
deps = [
+ ":base_processing_step",
"//java/dagger:core",
- "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/xprocessing",
"//java/dagger/spi",
- "//third_party/java/auto:common",
"//third_party/java/auto:service",
"//third_party/java/auto:value",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
"//third_party/java/incap",
"//third_party/java/javapoet",
+ "@maven//:com_google_devtools_ksp_symbol_processing_api",
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-android-processor",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-android-processor:" + POM_VERSION,
artifact_name = "Dagger Android Processor",
- targets = [":processor"],
+ artifact_target = ":processor",
+ artifact_target_libs = [
+ "//java/dagger/internal/codegen/xprocessing",
+ "//java/dagger/android/processor:base_processing_step",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.dagger:dagger",
+ "com.google.devtools.ksp:symbol-processing-api",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-spi",
+ "com.google.guava:failureaccess",
+ "com.squareup:kotlinpoet",
+ "net.ltgt.gradle.incap:incap",
+ "org.jetbrains.kotlin:kotlin-stdlib",
+ ],
+ javadoc_root_packages = [
+ "dagger.android.processor",
+ ],
+ javadoc_srcs = [":srcs"],
)
java_plugin(
@@ -64,10 +99,3 @@ java_plugin(
processor_class = "dagger.android.processor.AndroidProcessor",
deps = [":processor"],
)
-
-javadoc_library(
- name = "processor-javadoc",
- srcs = [":srcs"],
- root_packages = ["dagger.android.processor"],
- deps = [":processor"],
-)
diff --git a/java/dagger/android/processor/BaseProcessingStep.java b/java/dagger/android/processor/BaseProcessingStep.java
new file mode 100644
index 000000000..100ded0a0
--- /dev/null
+++ b/java/dagger/android/processor/BaseProcessingStep.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Sets.difference;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A {@link XProcessingStep} that processes one element at a time and defers any for which {@link
+ * TypeNotPresentException} is thrown.
+ */
+public abstract class BaseProcessingStep implements XProcessingStep {
+ @Override
+ public final ImmutableSet<String> annotations() {
+ return annotationClassNames().stream().map(ClassName::canonicalName).collect(toImmutableSet());
+ }
+
+ // Subclass must ensure all annotated targets are of valid type.
+ @Override
+ public ImmutableSet<XElement> process(
+ XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+ ImmutableSet.Builder<XElement> deferredElements = ImmutableSet.builder();
+ inverse(elementsByAnnotation)
+ .forEach(
+ (element, annotations) -> {
+ try {
+ process(element, annotations);
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(element);
+ }
+ });
+ return deferredElements.build();
+ }
+
+ /**
+ * Processes one element. If this method throws {@link TypeNotPresentException}, the element will
+ * be deferred until the next round of processing.
+ *
+ * @param annotations the subset of {@link XProcessingStep#annotations()} that annotate {@code
+ * element}
+ */
+ protected abstract void process(XElement element, ImmutableSet<ClassName> annotations);
+
+ private ImmutableMap<XElement, ImmutableSet<ClassName>> inverse(
+ Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+ ImmutableMap<String, ClassName> annotationClassNames =
+ annotationClassNames().stream()
+ .collect(toImmutableMap(ClassName::canonicalName, className -> className));
+ checkState(
+ annotationClassNames.keySet().containsAll(elementsByAnnotation.keySet()),
+ "Unexpected annotations for %s: %s",
+ this.getClass().getCanonicalName(),
+ difference(elementsByAnnotation.keySet(), annotationClassNames.keySet()));
+
+ ImmutableSetMultimap.Builder<XElement, ClassName> builder = ImmutableSetMultimap.builder();
+ elementsByAnnotation.forEach(
+ (annotationName, elementSet) ->
+ elementSet.forEach(
+ element -> builder.put(element, annotationClassNames.get(annotationName))));
+
+ return ImmutableMap.copyOf(Maps.transformValues(builder.build().asMap(), ImmutableSet::copyOf));
+ }
+
+ /** Returns the set of annotations processed by this processing step. */
+ protected abstract Set<ClassName> annotationClassNames();
+}
diff --git a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java b/java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java
index f3f4d18ab..9e9ed2b20 100644
--- a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
+++ b/java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java
@@ -16,23 +16,27 @@
package dagger.android.processor;
-import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
+import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
+import static dagger.android.processor.DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.tools.Diagnostic.Kind.ERROR;
-import com.google.auto.common.BasicAnnotationProcessor.Step;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
@@ -41,52 +45,26 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
-import dagger.android.processor.AndroidInjectorDescriptor.Validator;
-import java.io.IOException;
-import javax.annotation.processing.Filer;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.util.Elements;
+import dagger.internal.codegen.xprocessing.XElements;
/** Generates the implementation specified in {@code ContributesAndroidInjector}. */
-final class ContributesAndroidInjectorGenerator implements Step {
-
+final class ContributesAndroidInjectorProcessingStep extends BaseProcessingStep {
private final AndroidInjectorDescriptor.Validator validator;
- private final Filer filer;
- private final Elements elements;
- private final boolean useStringKeys;
- private final SourceVersion sourceVersion;
-
- ContributesAndroidInjectorGenerator(
- Validator validator,
- boolean useStringKeys,
- Filer filer,
- Elements elements,
- SourceVersion sourceVersion) {
- this.validator = validator;
- this.useStringKeys = useStringKeys;
- this.filer = filer;
- this.elements = elements;
- this.sourceVersion = sourceVersion;
+ private final XProcessingEnv processingEnv;
+
+ ContributesAndroidInjectorProcessingStep(XProcessingEnv processingEnv) {
+ this.processingEnv = processingEnv;
+ this.validator = new AndroidInjectorDescriptor.Validator(processingEnv.getMessager());
}
@Override
- public ImmutableSet<String> annotations() {
- return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR.toString());
+ public ImmutableSet<ClassName> annotationClassNames() {
+ return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
}
@Override
- public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
- for (ExecutableElement method : methodsIn(elementsByAnnotation.values())) {
- try {
- validator.createIfValid(method).ifPresent(this::generate);
- } catch (TypeNotPresentException e) {
- deferredElements.add(method);
- }
- }
- return deferredElements.build();
+ public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
+ validator.createIfValid(XElements.asMethod(element)).ifPresent(this::generate);
}
private void generate(AndroidInjectorDescriptor descriptor) {
@@ -97,7 +75,7 @@ final class ContributesAndroidInjectorGenerator implements Step {
.peerClass(
Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
+ "_"
- + LOWER_CAMEL.to(UPPER_CAMEL, descriptor.method().getSimpleName().toString()));
+ + LOWER_CAMEL.to(UPPER_CAMEL, XElements.getSimpleName(descriptor.method())));
String baseName = descriptor.injectedType().simpleName();
ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
@@ -105,7 +83,6 @@ final class ContributesAndroidInjectorGenerator implements Step {
TypeSpec.Builder module =
classBuilder(moduleName)
- .addOriginatingElement(descriptor.method())
.addAnnotation(
AnnotationSpec.builder(TypeNames.MODULE)
.addMember("subcomponents", "$T.class", subcomponentName)
@@ -114,16 +91,45 @@ final class ContributesAndroidInjectorGenerator implements Step {
.addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
.addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
- generatedAnnotationSpec(elements, sourceVersion, AndroidProcessor.class)
- .ifPresent(module::addAnnotation);
-
- try {
- JavaFile.builder(moduleName.packageName(), module.build())
- .skipJavaLangImports(true)
- .build()
- .writeTo(filer);
- } catch (IOException e) {
- throw new AssertionError(e);
+
+ addOriginatingElement(module, descriptor.method());
+
+ XTypeElement generatedAnnotation = processingEnv.findGeneratedAnnotation();
+ if (generatedAnnotation != null) {
+ module.addAnnotation(
+ AnnotationSpec.builder(generatedAnnotation.getClassName())
+ .addMember(
+ "value", "$S", ClassName.get("dagger.android.processor", "AndroidProcessor"))
+ .build());
+ }
+
+ processingEnv
+ .getFiler()
+ .write(
+ JavaFile.builder(moduleName.packageName(), module.build())
+ .skipJavaLangImports(true)
+ .build(),
+ XFiler.Mode.Isolating);
+ }
+
+ private static boolean useStringKeys(XProcessingEnv processingEnv) {
+ if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
+ return false;
+ }
+ String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
+ return true;
+ } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
+ return false;
+ } else {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ ERROR,
+ String.format(
+ "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
+ flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
+ return false;
}
}
@@ -142,7 +148,7 @@ final class ContributesAndroidInjectorGenerator implements Step {
}
private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
- if (useStringKeys) {
+ if (useStringKeys(processingEnv)) {
return AnnotationSpec.builder(TypeNames.ANDROID_INJECTION_KEY)
.addMember("value", "$S", descriptor.injectedType().toString())
.build();
diff --git a/java/dagger/android/processor/DelegateAndroidProcessor.java b/java/dagger/android/processor/DelegateAndroidProcessor.java
new file mode 100644
index 000000000..7c141f041
--- /dev/null
+++ b/java/dagger/android/processor/DelegateAndroidProcessor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.processor;
+
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingEnvConfig;
+import androidx.room.compiler.processing.XProcessingStep;
+import com.google.common.collect.ImmutableList;
+import dagger.BindsInstance;
+import dagger.Component;
+import javax.inject.Singleton;
+
+/** An implementation of Dagger Android processor that is shared between Javac and KSP. */
+final class DelegateAndroidProcessor {
+ static final XProcessingEnvConfig PROCESSING_ENV_CONFIG =
+ new XProcessingEnvConfig.Builder().build();
+ static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
+ "dagger.android.experimentalUseStringKeys";
+
+ private XProcessingEnv env;
+
+ public void initialize(XProcessingEnv env) {
+ this.env = env;
+ }
+
+ public ImmutableList<XProcessingStep> processingSteps() {
+ return ImmutableList.of(
+ new AndroidMapKeyProcessingStep(env), new ContributesAndroidInjectorProcessingStep(env));
+ }
+
+ @Singleton
+ @Component
+ interface Injector {
+ void inject(DelegateAndroidProcessor delegateAndroidProcessor);
+
+ @Component.Factory
+ interface Factory {
+ Injector create(@BindsInstance XProcessingEnv env);
+ }
+ }
+}
diff --git a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
index bcc8e5a1e..22270381c 100644
--- a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
+++ b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
@@ -16,34 +16,34 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static javax.tools.Diagnostic.Kind.ERROR;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XType;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
-import dagger.MapKey;
-import dagger.model.Binding;
-import dagger.model.BindingGraph;
-import dagger.model.BindingKind;
-import dagger.model.Key;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
+import dagger.internal.codegen.xprocessing.DaggerElements;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DaggerProcessingEnv;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
/**
* Validates that the two maps that {@code DispatchingAndroidInjector} injects have logically
@@ -53,6 +53,13 @@ import javax.lang.model.type.TypeMirror;
*/
@AutoService(BindingGraphPlugin.class)
public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugin {
+ private DaggerProcessingEnv processingEnv;
+
+ @Override
+ public void init(DaggerProcessingEnv processingEnv, Map<String, String> options) {
+ this.processingEnv = processingEnv;
+ }
+
@Override
public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
for (Binding binding : graph.bindings()) {
@@ -64,7 +71,10 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
private boolean isDispatchingAndroidInjector(Binding binding) {
Key key = binding.key();
- return MoreDaggerTypes.isTypeOf(TypeNames.DISPATCHING_ANDROID_INJECTOR, key.type())
+
+ return XTypes.isTypeOf(
+ DaggerElements.toXProcessing(key.type(), processingEnv),
+ TypeNames.DISPATCHING_ANDROID_INJECTOR)
&& !key.qualifier().isPresent();
}
@@ -79,7 +89,7 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
ImmutableListMultimap.Builder<String, Binding> mapKeyIndex = ImmutableListMultimap.builder();
for (Binding injectorFactory : injectorFactories) {
- AnnotationMirror mapKey = mapKey(injectorFactory).get();
+ XAnnotation mapKey = mapKey(injectorFactory).get();
Optional<String> injectedType = injectedTypeFromMapKey(mapKey);
if (injectedType.isPresent()) {
mapKeyIndex.put(injectedType.get(), injectorFactory);
@@ -113,21 +123,26 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
.filter(requestedBinding -> requestedBinding.kind().equals(BindingKind.MULTIBOUND_MAP))
.filter(
requestedBinding -> {
- TypeMirror valueType =
- MoreTypes.asDeclared(requestedBinding.key().type()).getTypeArguments().get(1);
- if (!MoreDaggerTypes.isTypeOf(TypeNames.PROVIDER, valueType)
- || !valueType.getKind().equals(TypeKind.DECLARED)) {
+ XType valueType =
+ DaggerElements.toXProcessing(requestedBinding.key().type(), processingEnv)
+ .getTypeArguments()
+ .get(1);
+ if (!XTypes.isTypeOf(valueType, TypeNames.PROVIDER)
+ || !XTypes.isDeclared(valueType)) {
return false;
}
- TypeMirror providedType = MoreTypes.asDeclared(valueType).getTypeArguments().get(0);
- return MoreDaggerTypes.isTypeOf(TypeNames.ANDROID_INJECTOR_FACTORY, providedType);
+ XType providedType = valueType.getTypeArguments().get(0);
+ return XTypes.isTypeOf(providedType, TypeNames.ANDROID_INJECTOR_FACTORY);
});
}
- private Optional<AnnotationMirror> mapKey(Binding binding) {
+ private Optional<XAnnotation> mapKey(Binding binding) {
return binding
.bindingElement()
- .map(bindingElement -> getAnnotatedAnnotations(bindingElement, MapKey.class))
+ .map(
+ bindingElement ->
+ XElements.getAnnotatedAnnotations(
+ DaggerElements.toXProcessing(bindingElement, processingEnv), TypeNames.MAP_KEY))
.flatMap(
annotations ->
annotations.isEmpty()
diff --git a/java/dagger/hilt/processor/internal/definecomponent/KspDefineComponentValidationProcessor.java b/java/dagger/android/processor/KspAndroidProcessor.java
index d46921ec2..7fe4f52d8 100644
--- a/java/dagger/hilt/processor/internal/definecomponent/KspDefineComponentValidationProcessor.java
+++ b/java/dagger/android/processor/KspAndroidProcessor.java
@@ -14,36 +14,40 @@
* limitations under the License.
*/
-package dagger.hilt.processor.internal.definecomponent;
+package dagger.android.processor;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor;
import com.google.auto.service.AutoService;
import com.google.devtools.ksp.processing.SymbolProcessor;
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment;
import com.google.devtools.ksp.processing.SymbolProcessorProvider;
-import dagger.hilt.processor.internal.BaseProcessingStep;
-import dagger.hilt.processor.internal.KspBaseProcessingStepProcessor;
-/**
- * A processor for {@link dagger.hilt.DefineComponent} and {@link
- * dagger.hilt.DefineComponent.Builder}.
- */
-public final class KspDefineComponentValidationProcessor extends KspBaseProcessingStepProcessor {
- public KspDefineComponentValidationProcessor(
- SymbolProcessorEnvironment symbolProcessorEnvironment) {
- super(symbolProcessorEnvironment);
+/** Ksp Processor for verifying usage of {@code dagger.android} code. */
+final class KspAndroidProcessor extends KspBasicAnnotationProcessor {
+ private final DelegateAndroidProcessor delegate = new DelegateAndroidProcessor();
+
+ private KspAndroidProcessor(SymbolProcessorEnvironment symbolProcessorEnvironment) {
+ super(symbolProcessorEnvironment, DelegateAndroidProcessor.PROCESSING_ENV_CONFIG);
+ }
+
+ @Override
+ public void initialize(XProcessingEnv env) {
+ delegate.initialize(env);
}
@Override
- protected BaseProcessingStep processingStep() {
- return new DefineComponentValidationProcessingStep(getXProcessingEnv());
+ public Iterable<XProcessingStep> processingSteps() {
+ return delegate.processingSteps();
}
- /** Provides the {@link KspDefineComponentValidationProcessor}. */
+ /** Provides the {@link KspAndroidProcessor}. */
@AutoService(SymbolProcessorProvider.class)
public static final class Provider implements SymbolProcessorProvider {
@Override
public SymbolProcessor create(SymbolProcessorEnvironment symbolProcessorEnvironment) {
- return new KspDefineComponentValidationProcessor(symbolProcessorEnvironment);
+ return new KspAndroidProcessor(symbolProcessorEnvironment);
}
}
}
diff --git a/java/dagger/android/processor/MoreDaggerElements.java b/java/dagger/android/processor/MoreDaggerElements.java
deleted file mode 100644
index 572caa31a..000000000
--- a/java/dagger/android/processor/MoreDaggerElements.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android.processor;
-
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerElements.java?
-// TODO(bcorso): Contribute upstream to auto common?
-/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
-final class MoreDaggerElements {
- /**
- * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
- * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
- * {@code annotationClass}. This method is a safer alternative to calling {@link
- * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
- * annotation proxies.
- */
- public static boolean isAnnotationPresent(Element element, ClassName annotationName) {
- return getAnnotationMirror(element, annotationName).isPresent();
- }
-
- /**
- * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
- * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a
- * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
- * annotation proxies.
- */
- public static Optional<AnnotationMirror> getAnnotationMirror(
- Element element, ClassName annotationName) {
- String annotationClassName = annotationName.canonicalName();
- for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
- TypeElement annotationTypeElement =
- MoreElements.asType(annotationMirror.getAnnotationType().asElement());
- if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
- return Optional.of(annotationMirror);
- }
- }
- return Optional.empty();
- }
-
- public static ImmutableSet<AnnotationMirror> getAnnotatedAnnotations(
- Element element, ClassName annotationName) {
- return element.getAnnotationMirrors().stream()
- .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
- .collect(toImmutableSet());
- }
-
- private MoreDaggerElements() {}
-}
diff --git a/java/dagger/android/processor/MoreDaggerTypes.java b/java/dagger/android/processor/MoreDaggerTypes.java
deleted file mode 100644
index 4bde405e1..000000000
--- a/java/dagger/android/processor/MoreDaggerTypes.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2021 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android.processor;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.auto.common.MoreElements;
-import com.squareup.javapoet.ArrayTypeName;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeName;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ErrorType;
-import javax.lang.model.type.NoType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerTypes.java?
-// TODO(bcorso): Contribute upstream to auto common?
-/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
-final class MoreDaggerTypes {
-
- /**
- * Returns true if the raw type underlying the given {@link TypeMirror} represents the same raw
- * type as the given {@link Class} and throws an IllegalArgumentException if the {@link
- * TypeMirror} does not represent a type that can be referenced by a {@link Class}
- */
- public static boolean isTypeOf(final TypeName typeName, TypeMirror type) {
- checkNotNull(typeName);
- return type.accept(new IsTypeOf(typeName), null);
- }
-
- private static final class IsTypeOf extends SimpleTypeVisitor8<Boolean, Void> {
- private final TypeName typeName;
-
- IsTypeOf(TypeName typeName) {
- this.typeName = typeName;
- }
-
- @Override
- protected Boolean defaultAction(TypeMirror type, Void ignored) {
- throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
- }
-
- @Override
- public Boolean visitNoType(NoType noType, Void p) {
- if (noType.getKind().equals(TypeKind.VOID)) {
- return typeName.equals(TypeName.VOID);
- }
- throw new IllegalArgumentException(noType + " cannot be represented as a Class<?>.");
- }
-
- @Override
- public Boolean visitError(ErrorType errorType, Void p) {
- return false;
- }
-
- @Override
- public Boolean visitPrimitive(PrimitiveType type, Void p) {
- switch (type.getKind()) {
- case BOOLEAN:
- return typeName.equals(TypeName.BOOLEAN);
- case BYTE:
- return typeName.equals(TypeName.BYTE);
- case CHAR:
- return typeName.equals(TypeName.CHAR);
- case DOUBLE:
- return typeName.equals(TypeName.DOUBLE);
- case FLOAT:
- return typeName.equals(TypeName.FLOAT);
- case INT:
- return typeName.equals(TypeName.INT);
- case LONG:
- return typeName.equals(TypeName.LONG);
- case SHORT:
- return typeName.equals(TypeName.SHORT);
- default:
- throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
- }
- }
-
- @Override
- public Boolean visitArray(ArrayType array, Void p) {
- return (typeName instanceof ArrayTypeName)
- && isTypeOf(((ArrayTypeName) typeName).componentType, array.getComponentType());
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType type, Void ignored) {
- TypeElement typeElement = MoreElements.asType(type.asElement());
- return (typeName instanceof ClassName)
- && typeElement.getQualifiedName().contentEquals(((ClassName) typeName).canonicalName());
- }
- }
-
- private MoreDaggerTypes() {}
-}
diff --git a/java/dagger/android/support/BUILD b/java/dagger/android/support/BUILD
index 2fc9e2eb7..2f4c407b8 100644
--- a/java/dagger/android/support/BUILD
+++ b/java/dagger/android/support/BUILD
@@ -17,12 +17,14 @@
load(
"//:build_defs.bzl",
- "JAVA_RELEASE_MIN",
"POM_VERSION",
)
load("//tools:dejetify.bzl", "dejetified_library")
-load("//tools:maven.bzl", "pom_file")
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load(
+ "//tools:maven.bzl",
+ "gen_maven_artifact",
+ "pom_file",
+)
package(default_visibility = ["//:src"])
@@ -34,8 +36,6 @@ filegroup(
android_library(
name = "support",
srcs = glob(["*.java"]),
- javacopts = JAVA_RELEASE_MIN,
- manifest = "AndroidManifest.xml",
tags = ["maven_coordinates=com.google.dagger:dagger-android-support:" + POM_VERSION],
deps = [
"//:dagger_with_compiler",
@@ -51,17 +51,34 @@ android_library(
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-android-support",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-android-support:" + POM_VERSION,
artifact_name = "Dagger Android Support",
+ artifact_target = ":support",
+ artifact_target_maven_deps = [
+ "androidx.activity:activity",
+ "androidx.annotation:annotation",
+ "androidx.appcompat:appcompat",
+ "androidx.fragment:fragment",
+ "androidx.lifecycle:lifecycle-common",
+ "androidx.lifecycle:lifecycle-viewmodel",
+ "androidx.lifecycle:lifecycle-viewmodel-savedstate",
+ "com.google.dagger:dagger",
+ "com.google.dagger:dagger-android",
+ ],
+ javadoc_android_api_level = 32,
+ javadoc_root_packages = [
+ "dagger.android.support",
+ ],
+ javadoc_srcs = [":support-srcs"],
+ manifest = "AndroidManifest.xml",
packaging = "aar",
- targets = [":support"],
)
dejetified_library(
name = "dejetified-support",
- input = ":support.aar",
+ input = ":artifact.aar",
output = "support-legacy.aar",
)
@@ -85,11 +102,3 @@ pom_file(
packaging = "aar",
targets = [":legacy-deps"],
)
-
-javadoc_library(
- name = "support-javadoc",
- srcs = [":support-srcs"],
- android_api_level = 32,
- root_packages = ["dagger.android.support"],
- deps = [":support"],
-)
diff --git a/java/dagger/hilt/android/BUILD b/java/dagger/hilt/android/BUILD
index ca950a3ee..b30f6625d 100644
--- a/java/dagger/hilt/android/BUILD
+++ b/java/dagger/hilt/android/BUILD
@@ -15,8 +15,8 @@
# Description:
# A library based on Hilt that provides standard components and automated injection for Android.
load("//:build_defs.bzl", "POM_VERSION")
-load("//tools:maven.bzl", "gen_maven_artifact")
load("//tools:bazel_compat.bzl", "compat_kt_android_library")
+load("//tools:maven.bzl", "gen_maven_artifact")
package(default_visibility = ["//:src"])
@@ -39,6 +39,7 @@ android_library(
"//java/dagger/hilt/android/internal/managers:component_supplier",
"//java/dagger/hilt/android/internal/modules",
"//java/dagger/hilt/android/lifecycle:hilt_view_model",
+ "//java/dagger/hilt/android/lifecycle:hilt_view_model_extensions",
"//java/dagger/hilt/codegen:originating_element",
"//java/dagger/hilt/internal:component_entry_point",
"//java/dagger/hilt/internal:component_manager",
@@ -146,6 +147,14 @@ android_library(
],
)
+android_library(
+ name = "unstable_api",
+ srcs = ["UnstableApi.java"],
+ deps = [
+ "@maven//:androidx_annotation_annotation_experimental",
+ ],
+)
+
java_library(
name = "package_info",
srcs = ["package-info.java"],
@@ -163,6 +172,7 @@ android_library(
":entry_point_accessors",
":hilt_android_app",
":package_info",
+ ":unstable_api",
"//java/dagger/hilt:artifact-core-lib",
"//java/dagger/hilt/android/migration:custom_inject",
"//java/dagger/hilt/android/migration:optional_inject",
@@ -180,6 +190,7 @@ gen_maven_artifact(
"//java/dagger/hilt/android:activity_retained_lifecycle",
"//java/dagger/hilt/android:android_entry_point",
"//java/dagger/hilt/android:hilt_android_app",
+ "//java/dagger/hilt/android:unstable_api",
"//java/dagger/hilt/android:early_entry_point",
"//java/dagger/hilt/android:package_info",
"//java/dagger/hilt/android:view_model_lifecycle",
@@ -193,10 +204,13 @@ gen_maven_artifact(
"//java/dagger/hilt/android/internal/lifecycle",
"//java/dagger/hilt/android/internal/managers",
"//java/dagger/hilt/android/internal/managers:component_supplier",
+ "//java/dagger/hilt/android/internal/managers:saved_state_handle_holder",
"//java/dagger/hilt/android/internal/migration:has_custom_inject",
"//java/dagger/hilt/android/internal/migration:injected_by_hilt",
"//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/android/lifecycle:activity_retained_saved_state",
"//java/dagger/hilt/android/lifecycle:hilt_view_model",
+ "//java/dagger/hilt/android/lifecycle:hilt_view_model_extensions",
"//java/dagger/hilt/android/lifecycle:package_info",
"//java/dagger/hilt/android/lifecycle:retained_lifecycle",
"//java/dagger/hilt/android/migration:custom_inject",
@@ -216,6 +230,7 @@ gen_maven_artifact(
artifact_target_maven_deps = [
"androidx.activity:activity",
"androidx.annotation:annotation",
+ "androidx.annotation:annotation-experimental",
"androidx.fragment:fragment",
"androidx.lifecycle:lifecycle-common",
"androidx.lifecycle:lifecycle-viewmodel",
@@ -244,10 +259,9 @@ gen_maven_artifact(
],
manifest = "AndroidManifest.xml",
packaging = "aar",
- proguard_specs = [
+ proguard_and_r8_specs = [
"//java/dagger/hilt:proguard-rules.pro",
- ":proguard-rules.pro",
- "//java/dagger/hilt/android/lifecycle:proguard-rules.pro",
+ "//java/dagger/hilt/android:proguard-rules.pro",
"//java/dagger/hilt/internal:proguard-rules.pro",
],
)
diff --git a/java/dagger/hilt/android/UnstableApi.java b/java/dagger/hilt/android/UnstableApi.java
new file mode 100644
index 000000000..dd328ecb3
--- /dev/null
+++ b/java/dagger/hilt/android/UnstableApi.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import androidx.annotation.RequiresOptIn;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Mark unstable Api usage. */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
+@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
+public @interface UnstableApi {}
diff --git a/java/dagger/hilt/android/internal/BUILD b/java/dagger/hilt/android/internal/BUILD
index ae8a06692..4a231a85e 100644
--- a/java/dagger/hilt/android/internal/BUILD
+++ b/java/dagger/hilt/android/internal/BUILD
@@ -21,6 +21,7 @@ android_library(
name = "internal",
srcs = [
"Contexts.java",
+ "OnReceiveBytecodeInjectionMarker.java",
"ThreadUtil.java",
],
)
diff --git a/java/dagger/hilt/android/internal/OnReceiveBytecodeInjectionMarker.java b/java/dagger/hilt/android/internal/OnReceiveBytecodeInjectionMarker.java
new file mode 100644
index 000000000..ed5abbe4a
--- /dev/null
+++ b/java/dagger/hilt/android/internal/OnReceiveBytecodeInjectionMarker.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The marker annotation used to denote that we need to inject super.onReceive() call
+ * to the @AndroidEntryPoint-annotated BroadcastReceiver's onReceive() method.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface OnReceiveBytecodeInjectionMarker { }
+
diff --git a/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java
index 110b3fef1..ff5998c01 100644
--- a/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java
+++ b/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java
@@ -16,11 +16,17 @@
package dagger.hilt.android.internal.builders;
+import dagger.BindsInstance;
import dagger.hilt.DefineComponent;
import dagger.hilt.android.components.ActivityRetainedComponent;
+import dagger.hilt.android.internal.managers.SavedStateHandleHolder;
/** Interface for creating a {@link ActivityRetainedComponent}. */
@DefineComponent.Builder
public interface ActivityRetainedComponentBuilder {
+
+ ActivityRetainedComponentBuilder savedStateHandleHolder(
+ @BindsInstance SavedStateHandleHolder savedStateHandleHolder);
+
ActivityRetainedComponent build();
}
diff --git a/java/dagger/hilt/android/internal/builders/BUILD b/java/dagger/hilt/android/internal/builders/BUILD
index 598503eff..7f26e1dce 100644
--- a/java/dagger/hilt/android/internal/builders/BUILD
+++ b/java/dagger/hilt/android/internal/builders/BUILD
@@ -25,6 +25,7 @@ android_library(
"//java/dagger/hilt:define_component",
"//java/dagger/hilt/android:view_model_lifecycle",
"//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/internal/managers:saved_state_handle_holder",
"@maven//:androidx_activity_activity",
"@maven//:androidx_fragment_fragment",
"@maven//:androidx_lifecycle_lifecycle_common",
diff --git a/java/dagger/hilt/android/internal/lifecycle/BUILD b/java/dagger/hilt/android/internal/lifecycle/BUILD
index f7314d279..7afd1dd9d 100644
--- a/java/dagger/hilt/android/internal/lifecycle/BUILD
+++ b/java/dagger/hilt/android/internal/lifecycle/BUILD
@@ -38,6 +38,7 @@ android_library(
"@maven//:androidx_lifecycle_lifecycle_viewmodel",
"@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
"@maven//:androidx_savedstate_savedstate",
+ "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
],
)
diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
index 78b8eb16b..67e68e71a 100644
--- a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
+++ b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
@@ -29,7 +29,7 @@ import dagger.hilt.android.components.ActivityComponent;
import dagger.hilt.android.components.FragmentComponent;
import dagger.hilt.android.internal.builders.ViewModelComponentBuilder;
import dagger.multibindings.Multibinds;
-import java.util.Set;
+import java.util.Map;
import javax.inject.Inject;
/**
@@ -69,12 +69,12 @@ public final class DefaultViewModelFactories {
/** Internal factory for the Hilt ViewModel Factory. */
public static final class InternalFactoryFactory {
- private final Set<String> keySet;
+ private final Map<Class<?>, Boolean> keySet;
private final ViewModelComponentBuilder viewModelComponentBuilder;
@Inject
InternalFactoryFactory(
- @HiltViewModelMap.KeySet Set<String> keySet,
+ @HiltViewModelMap.KeySet Map<Class<?>, Boolean> keySet,
ViewModelComponentBuilder viewModelComponentBuilder) {
this.keySet = keySet;
this.viewModelComponentBuilder = viewModelComponentBuilder;
@@ -103,7 +103,7 @@ public final class DefaultViewModelFactories {
interface ActivityModule {
@Multibinds
@HiltViewModelMap.KeySet
- abstract Set<String> viewModelKeys();
+ abstract Map<Class<?>, Boolean> viewModelKeys();
}
/** The activity entry point to retrieve the factory. */
diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelAssistedMap.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelAssistedMap.java
new file mode 100644
index 000000000..69bb2b118
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelAssistedMap.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/**
+ * Internal qualifier for the multibinding map of assisted factories for @AssistedInject-annotated
+ * ViewModels used by the {@link dagger.hilt.android.lifecycle.HiltViewModelFactory}.
+ */
+@Qualifier
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.PARAMETER})
+public @interface HiltViewModelAssistedMap {}
diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
index 52f31b920..6819c24bd 100644
--- a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
+++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
@@ -16,12 +16,12 @@
package dagger.hilt.android.internal.lifecycle;
+import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle;
+
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.lifecycle.AbstractSavedStateViewModelFactory;
-import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;
@@ -35,8 +35,8 @@ import dagger.hilt.android.components.ViewModelComponent;
import dagger.hilt.android.internal.builders.ViewModelComponentBuilder;
import dagger.multibindings.Multibinds;
import java.util.Map;
-import java.util.Set;
import javax.inject.Provider;
+import kotlin.jvm.functions.Function1;
/**
* View Model Provider Factory for the Hilt Extension.
@@ -54,54 +54,110 @@ public final class HiltViewModelFactory implements ViewModelProvider.Factory {
@InstallIn(ViewModelComponent.class)
public interface ViewModelFactoriesEntryPoint {
@HiltViewModelMap
- Map<String, Provider<ViewModel>> getHiltViewModelMap();
+ Map<Class<?>, Provider<ViewModel>> getHiltViewModelMap();
+
+ // From ViewModel class names to user defined @AssistedFactory-annotated implementations.
+ @HiltViewModelAssistedMap
+ Map<Class<?>, Object> getHiltViewModelAssistedMap();
}
+ /** Creation extra key for the callbacks that create @AssistedInject-annotated ViewModels. */
+ public static final CreationExtras.Key<Function1<Object, ViewModel>> CREATION_CALLBACK_KEY =
+ new CreationExtras.Key<Function1<Object, ViewModel>>() {};
+
/** Hilt module for providing the empty multi-binding map of ViewModels. */
@Module
@InstallIn(ViewModelComponent.class)
interface ViewModelModule {
@Multibinds
@HiltViewModelMap
- Map<String, ViewModel> hiltViewModelMap();
+ Map<Class<?>, ViewModel> hiltViewModelMap();
+
+ @Multibinds
+ @HiltViewModelAssistedMap
+ Map<Class<?>, Object> hiltViewModelAssistedMap();
}
- private final Set<String> hiltViewModelKeys;
+ private final Map<Class<?>, Boolean> hiltViewModelKeys;
private final ViewModelProvider.Factory delegateFactory;
- private final AbstractSavedStateViewModelFactory hiltViewModelFactory;
+ private final ViewModelProvider.Factory hiltViewModelFactory;
public HiltViewModelFactory(
- @NonNull Set<String> hiltViewModelKeys,
+ @NonNull Map<Class<?>, Boolean> hiltViewModelKeys,
@NonNull ViewModelProvider.Factory delegateFactory,
@NonNull ViewModelComponentBuilder viewModelComponentBuilder) {
this.hiltViewModelKeys = hiltViewModelKeys;
this.delegateFactory = delegateFactory;
this.hiltViewModelFactory =
- new AbstractSavedStateViewModelFactory() {
+ new ViewModelProvider.Factory() {
@NonNull
@Override
- @SuppressWarnings("unchecked")
- protected <T extends ViewModel> T create(
- @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle) {
+ public <T extends ViewModel> T create(
+ @NonNull Class<T> modelClass, @NonNull CreationExtras extras) {
RetainedLifecycleImpl lifecycle = new RetainedLifecycleImpl();
- ViewModelComponent component = viewModelComponentBuilder
- .savedStateHandle(handle)
- .viewModelLifecycle(lifecycle)
- .build();
+ ViewModelComponent component =
+ viewModelComponentBuilder
+ .savedStateHandle(createSavedStateHandle(extras))
+ .viewModelLifecycle(lifecycle)
+ .build();
+ T viewModel = createViewModel(component, modelClass, extras);
+ viewModel.addCloseable(lifecycle::dispatchOnCleared);
+ return viewModel;
+ }
+
+ private <T extends ViewModel> T createViewModel(
+ @NonNull ViewModelComponent component,
+ @NonNull Class<T> modelClass,
+ @NonNull CreationExtras extras) {
Provider<? extends ViewModel> provider =
EntryPoints.get(component, ViewModelFactoriesEntryPoint.class)
.getHiltViewModelMap()
- .get(modelClass.getName());
- if (provider == null) {
- throw new IllegalStateException(
- "Expected the @HiltViewModel-annotated class '"
- + modelClass.getName()
- + "' to be available in the multi-binding of "
- + "@HiltViewModelMap but none was found.");
+ .get(modelClass);
+ Function1<Object, ViewModel> creationCallback = extras.get(CREATION_CALLBACK_KEY);
+ Object assistedFactory =
+ EntryPoints.get(component, ViewModelFactoriesEntryPoint.class)
+ .getHiltViewModelAssistedMap()
+ .get(modelClass);
+
+ if (assistedFactory == null) {
+ if (creationCallback == null) {
+ if (provider == null) {
+ throw new IllegalStateException(
+ "Expected the @HiltViewModel-annotated class "
+ + modelClass.getName()
+ + " to be available in the multi-binding of "
+ + "@HiltViewModelMap"
+ + " but none was found.");
+ } else {
+ return (T) provider.get();
+ }
+ } else {
+ // Provider could be null or non-null.
+ throw new IllegalStateException(
+ "Found creation callback but class "
+ + modelClass.getName()
+ + " does not have an assisted factory specified in @HiltViewModel.");
+ }
+ } else {
+ if (provider == null) {
+ if (creationCallback == null) {
+ throw new IllegalStateException(
+ "Found @HiltViewModel-annotated class "
+ + modelClass.getName()
+ + " using @AssistedInject but no creation callback"
+ + " was provided in CreationExtras.");
+ } else {
+ return (T) creationCallback.invoke(assistedFactory);
+ }
+ } else {
+ // Creation callback could be null or non-null.
+ throw new AssertionError(
+ "Found the @HiltViewModel-annotated class "
+ + modelClass.getName()
+ + " in both the multi-bindings of "
+ + "@HiltViewModelMap and @HiltViewModelAssistedMap.");
+ }
}
- ViewModel viewModel = provider.get();
- viewModel.addCloseable(lifecycle::dispatchOnCleared);
- return (T) viewModel;
}
};
}
@@ -110,7 +166,7 @@ public final class HiltViewModelFactory implements ViewModelProvider.Factory {
@Override
public <T extends ViewModel> T create(
@NonNull Class<T> modelClass, @NonNull CreationExtras extras) {
- if (hiltViewModelKeys.contains(modelClass.getName())) {
+ if (hiltViewModelKeys.containsKey(modelClass)) {
return hiltViewModelFactory.create(modelClass, extras);
} else {
return delegateFactory.create(modelClass, extras);
@@ -120,7 +176,7 @@ public final class HiltViewModelFactory implements ViewModelProvider.Factory {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
- if (hiltViewModelKeys.contains(modelClass.getName())) {
+ if (hiltViewModelKeys.containsKey(modelClass)) {
return hiltViewModelFactory.create(modelClass);
} else {
return delegateFactory.create(modelClass);
@@ -131,7 +187,8 @@ public final class HiltViewModelFactory implements ViewModelProvider.Factory {
@InstallIn(ActivityComponent.class)
interface ActivityCreatorEntryPoint {
@HiltViewModelMap.KeySet
- Set<String> getViewModelKeys();
+ Map<Class<?>, Boolean> getViewModelKeys();
+
ViewModelComponentBuilder getViewModelComponentBuilder();
}
diff --git a/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java
index 50e4ce11d..3fa4910d0 100644
--- a/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java
+++ b/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java
@@ -70,6 +70,12 @@ public class ActivityComponentManager implements GeneratedComponentManager<Objec
return component;
}
+ public final SavedStateHandleHolder getSavedStateHandleHolder() {
+ // This will only be used on base activity that extends ComponentActivity.
+ return ((ActivityRetainedComponentManager) activityRetainedComponentManager)
+ .getSavedStateHandleHolder();
+ }
+
protected Object createComponent() {
if (!(activity.getApplication() instanceof GeneratedComponentManager)) {
throw new IllegalStateException(
diff --git a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
index df63bd3d1..dc3539c34 100644
--- a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
+++ b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
@@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
+import androidx.lifecycle.viewmodel.CreationExtras;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.EntryPoint;
@@ -57,15 +58,22 @@ final class ActivityRetainedComponentManager
static final class ActivityRetainedComponentViewModel extends ViewModel {
private final ActivityRetainedComponent component;
+ private final SavedStateHandleHolder savedStateHandleHolder;
- ActivityRetainedComponentViewModel(ActivityRetainedComponent component) {
+ ActivityRetainedComponentViewModel(
+ ActivityRetainedComponent component, SavedStateHandleHolder savedStateHandleHolder) {
this.component = component;
+ this.savedStateHandleHolder = savedStateHandleHolder;
}
ActivityRetainedComponent getComponent() {
return component;
}
+ SavedStateHandleHolder getSavedStateHandleHolder() {
+ return savedStateHandleHolder;
+ }
+
@Override
protected void onCleared() {
super.onCleared();
@@ -95,13 +103,17 @@ final class ActivityRetainedComponentManager
@NonNull
@Override
@SuppressWarnings("unchecked")
- public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
+ public <T extends ViewModel> T create(
+ @NonNull Class<T> aClass, CreationExtras creationExtras) {
+ SavedStateHandleHolder savedStateHandleHolder =
+ new SavedStateHandleHolder(creationExtras);
ActivityRetainedComponent component =
EntryPointAccessors.fromApplication(
- context, ActivityRetainedComponentBuilderEntryPoint.class)
+ context, ActivityRetainedComponentBuilderEntryPoint.class)
.retainedComponentBuilder()
+ .savedStateHandleHolder(savedStateHandleHolder)
.build();
- return (T) new ActivityRetainedComponentViewModel(component);
+ return (T) new ActivityRetainedComponentViewModel(component, savedStateHandleHolder);
}
});
}
@@ -118,6 +130,12 @@ final class ActivityRetainedComponentManager
return component;
}
+ public SavedStateHandleHolder getSavedStateHandleHolder() {
+ return getViewModelProvider(viewModelStoreOwner, context)
+ .get(ActivityRetainedComponentViewModel.class)
+ .getSavedStateHandleHolder();
+ }
+
private ActivityRetainedComponent createComponent() {
return getViewModelProvider(viewModelStoreOwner, context)
.get(ActivityRetainedComponentViewModel.class)
diff --git a/java/dagger/hilt/android/internal/managers/BUILD b/java/dagger/hilt/android/internal/managers/BUILD
index e553c6e8c..950b51129 100644
--- a/java/dagger/hilt/android/internal/managers/BUILD
+++ b/java/dagger/hilt/android/internal/managers/BUILD
@@ -30,25 +30,46 @@ android_library(
"ApplicationComponentManager.java",
"BroadcastReceiverComponentManager.java",
"FragmentComponentManager.java",
+ "SavedStateHandleModule.java",
"ServiceComponentManager.java",
"ViewComponentManager.java",
],
+ exports = [":saved_state_handle_holder"],
deps = [
":component_supplier",
+ ":saved_state_handle_holder",
"//:dagger_with_compiler",
"//java/dagger/hilt:entry_point",
"//java/dagger/hilt:install_in",
"//java/dagger/hilt/android:activity_retained_lifecycle",
"//java/dagger/hilt/android:entry_point_accessors",
+ "//java/dagger/hilt/android:unstable_api",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/builders",
"//java/dagger/hilt/android/internal/lifecycle",
+ "//java/dagger/hilt/android/lifecycle:activity_retained_saved_state",
"//java/dagger/hilt/android/scopes",
"//java/dagger/hilt/internal:component_manager",
"//java/dagger/hilt/internal:preconditions",
"@maven//:androidx_activity_activity",
"@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_annotation_annotation_experimental",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:androidx_lifecycle_lifecycle_common",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ ],
+)
+
+android_library(
+ name = "saved_state_handle_holder",
+ srcs = ["SavedStateHandleHolder.java"],
+ deps = [
+ "//java/dagger/hilt/android/internal",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_annotation_annotation",
"@maven//:androidx_fragment_fragment",
"@maven//:androidx_lifecycle_lifecycle_common",
"@maven//:androidx_lifecycle_lifecycle_viewmodel",
diff --git a/java/dagger/hilt/android/internal/managers/SavedStateHandleHolder.java b/java/dagger/hilt/android/internal/managers/SavedStateHandleHolder.java
new file mode 100644
index 000000000..835015900
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/SavedStateHandleHolder.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import static dagger.hilt.internal.Preconditions.checkNotNull;
+import static dagger.hilt.internal.Preconditions.checkState;
+
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.SavedStateHandle;
+import androidx.lifecycle.SavedStateHandleSupport;
+import androidx.lifecycle.viewmodel.CreationExtras;
+import androidx.lifecycle.viewmodel.MutableCreationExtras;
+import dagger.hilt.android.internal.ThreadUtil;
+
+/** Implementation for SavedStateHandleHolder. */
+public final class SavedStateHandleHolder {
+ private CreationExtras extras;
+ private SavedStateHandle handle;
+ private final boolean nonComponentActivity;
+
+ SavedStateHandleHolder(@Nullable CreationExtras extras) {
+ nonComponentActivity = (extras == null);
+ this.extras = extras;
+ }
+
+ SavedStateHandle getSavedStateHandle() {
+ ThreadUtil.ensureMainThread();
+ checkState(
+ !nonComponentActivity,
+ "Activity that does not extend ComponentActivity cannot use SavedStateHandle");
+ if (handle != null) {
+ return handle;
+ }
+ checkNotNull(
+ extras,
+ "The first access to SavedStateHandle should happen between super.onCreate() and"
+ + " super.onDestroy()");
+ // Clean up default args, since those are unused and we don't want to duplicate those for each
+ // SavedStateHandle
+ MutableCreationExtras mutableExtras = new MutableCreationExtras(extras);
+ mutableExtras.set(SavedStateHandleSupport.DEFAULT_ARGS_KEY, Bundle.EMPTY);
+ extras = mutableExtras;
+ handle = SavedStateHandleSupport.createSavedStateHandle(extras);
+
+ extras = null;
+ return handle;
+ }
+
+ public void clear() {
+ extras = null;
+ }
+
+ public void setExtras(CreationExtras extras) {
+ if (handle != null) {
+ // If handle is already created, we don't need to store CreationExtras.
+ return;
+ }
+ this.extras = extras;
+ }
+
+ public boolean isInvalid() {
+ return handle == null && extras == null;
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/SavedStateHandleModule.java b/java/dagger/hilt/android/internal/managers/SavedStateHandleModule.java
new file mode 100644
index 000000000..18ca508bc
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/SavedStateHandleModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import androidx.annotation.OptIn;
+import androidx.lifecycle.SavedStateHandle;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.UnstableApi;
+import dagger.hilt.android.components.ActivityRetainedComponent;
+import dagger.hilt.android.lifecycle.ActivityRetainedSavedState;
+import dagger.hilt.android.scopes.ActivityRetainedScoped;
+
+/** Module providing SavedStateHandle from ActivityRetainedComponent. */
+@Module
+@InstallIn(ActivityRetainedComponent.class)
+abstract class SavedStateHandleModule {
+ @OptIn(markerClass = UnstableApi.class)
+ @ActivityRetainedSavedState
+ @ActivityRetainedScoped
+ @Provides
+ static SavedStateHandle provideSavedStateHandle(SavedStateHandleHolder savedStateHandleHolder) {
+ return savedStateHandleHolder.getSavedStateHandle();
+ }
+}
diff --git a/java/dagger/hilt/android/lifecycle/ActivityRetainedSavedState.java b/java/dagger/hilt/android/lifecycle/ActivityRetainedSavedState.java
new file mode 100644
index 000000000..629843538
--- /dev/null
+++ b/java/dagger/hilt/android/lifecycle/ActivityRetainedSavedState.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.lifecycle;
+
+import dagger.hilt.android.UnstableApi;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/** Qualifies a binding that belongs to ActivityRetainedComponent. */
+@Qualifier
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@UnstableApi
+@Retention(RetentionPolicy.CLASS)
+public @interface ActivityRetainedSavedState {}
diff --git a/java/dagger/hilt/android/lifecycle/BUILD b/java/dagger/hilt/android/lifecycle/BUILD
index 456dbe76a..26394f0f8 100644
--- a/java/dagger/hilt/android/lifecycle/BUILD
+++ b/java/dagger/hilt/android/lifecycle/BUILD
@@ -15,6 +15,8 @@
# Description:
# Hilt ViewModel integration.
+load("//tools:bazel_compat.bzl", "compat_kt_android_library")
+
package(default_visibility = ["//:src"])
java_library(
@@ -31,7 +33,6 @@ android_library(
exported_plugins = [
"//java/dagger/hilt/android/processor/internal/viewmodel:processor",
],
- proguard_specs = ["proguard-rules.pro"],
exports = [
"//:dagger_with_compiler",
"//java/dagger/hilt:install_in",
@@ -54,6 +55,26 @@ android_library(
],
)
+android_library(
+ name = "activity_retained_saved_state",
+ srcs = ["ActivityRetainedSavedState.java"],
+ deps = [
+ "//java/dagger/hilt/android:unstable_api",
+ "//third_party/java/jsr330_inject",
+ ],
+)
+
+compat_kt_android_library(
+ name = "hilt_view_model_extensions",
+ srcs = ["HiltViewModelExtensions.kt"],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt/android/internal/lifecycle",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ ],
+)
+
filegroup(
name = "srcs_filegroup",
srcs = glob(["*"]),
diff --git a/java/dagger/hilt/android/lifecycle/HiltViewModel.java b/java/dagger/hilt/android/lifecycle/HiltViewModel.java
index 198ec8abe..a5e486f21 100644
--- a/java/dagger/hilt/android/lifecycle/HiltViewModel.java
+++ b/java/dagger/hilt/android/lifecycle/HiltViewModel.java
@@ -53,7 +53,46 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * <p>Exactly one constructor in the {@code ViewModel} must be annotated with {@code Inject}.
+ * <p>{@code ViewModel}s annotated with {@link HiltViewModel} can also be used with assisted
+ * injection:
+ *
+ * <pre>
+ * &#64;HiltViewModel(assistedFactory = DonutViewModel.Factory.class)
+ * public class DonutViewModel extends ViewModel {
+ * &#64;AssistedInject
+ * public DonutViewModel(
+ * SavedStateHandle handle,
+ * RecipeRepository repository,
+ * $#64;Assisted int donutId
+ * ) {
+ * // ...
+ * }
+ *
+ * &#64;AssistedFactory
+ * public interface Factory {
+ * DonutViewModel create(int donutId);
+ * }
+ * }
+ * </pre>
+ *
+ * <pre>
+ * &#64;AndroidEntryPoint
+ * public class CookingActivity extends AppCompatActivity {
+ * public void onCreate(Bundle savedInstanceState) {
+ * DonutViewModel vm = new ViewModelProvider(
+ * getViewModelStore(),
+ * getDefaultViewModelProviderFactory(),
+ * HiltViewModelExtensions.withCreationCallback(
+ * getDefaultViewModelCreationExtras(),
+ * (DonutViewModel.Factory factory) -> factory.create(1)
+ * )
+ * ).get(DonutViewModel.class);
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Exactly one constructor in the {@code ViewModel} must be annotated with {@code Inject} or
+ * {@code AssistedInject}.
*
* <p>Only dependencies available in the {@link dagger.hilt.android.components.ViewModelComponent}
* can be injected into the {@code ViewModel}.
@@ -65,4 +104,11 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@GeneratesRootInput
-public @interface HiltViewModel {}
+public @interface HiltViewModel {
+ /**
+ * Returns a factory class that can be used to create this ViewModel with assisted injection. The
+ * default value `Object.class` denotes that no factory is specified and the ViewModel is not
+ * assisted injected.
+ */
+ Class<?> assistedFactory() default Object.class;
+}
diff --git a/java/dagger/hilt/android/lifecycle/HiltViewModelExtensions.kt b/java/dagger/hilt/android/lifecycle/HiltViewModelExtensions.kt
new file mode 100644
index 000000000..f212c58ba
--- /dev/null
+++ b/java/dagger/hilt/android/lifecycle/HiltViewModelExtensions.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("HiltViewModelExtensions")
+
+package dagger.hilt.android.lifecycle
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
+import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory
+
+/**
+ * Returns a new {@code CreationExtras} with the original entries plus the passed in creation
+ * callback. The callback is used by Hilt to create {@link AssistedInject}-annotated {@link
+ * HiltViewModel}s.
+ *
+ * @param callback A creation callback that takes an assisted factory and returns a {@code
+ * ViewModel}.
+ */
+fun <VMF> CreationExtras.withCreationCallback(callback: (VMF) -> ViewModel): CreationExtras =
+ MutableCreationExtras(this).addCreationCallback(callback)
+
+/**
+ * Returns the {@code MutableCreationExtras} with the passed in creation callback added. The
+ * callback is used by Hilt to create {@link AssistedInject}-annotated {@link HiltViewModel}s.
+ *
+ * @param callback A creation callback that takes an assisted factory and returns a {@code
+ * ViewModel}.
+ */
+@Suppress("UNCHECKED_CAST")
+fun <VMF> MutableCreationExtras.addCreationCallback(callback: (VMF) -> ViewModel): CreationExtras =
+ this.apply {
+ this[HiltViewModelFactory.CREATION_CALLBACK_KEY] = { factory -> callback(factory as VMF) }
+ }
diff --git a/java/dagger/hilt/android/lifecycle/proguard-rules.pro b/java/dagger/hilt/android/lifecycle/proguard-rules.pro
deleted file mode 100644
index 6c647f105..000000000
--- a/java/dagger/hilt/android/lifecycle/proguard-rules.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-# Keep class names of Hilt injected ViewModels since their name are used as a multibinding map key.
--keepnames @dagger.hilt.android.lifecycle.HiltViewModel class * extends androidx.lifecycle.ViewModel \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper-7-0/build.gradle b/java/dagger/hilt/android/plugin/agp-wrapper-7-0/build.gradle
index 3c0349565..5509261c5 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper-7-0/build.gradle
+++ b/java/dagger/hilt/android/plugin/agp-wrapper-7-0/build.gradle
@@ -2,8 +2,10 @@ plugins {
id 'org.jetbrains.kotlin.jvm'
}
-kotlin {
- jvmToolchain(11)
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = 11
+ }
}
dependencies {
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper-7-1/build.gradle b/java/dagger/hilt/android/plugin/agp-wrapper-7-1/build.gradle
index 5bc8d3d1c..982949a49 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper-7-1/build.gradle
+++ b/java/dagger/hilt/android/plugin/agp-wrapper-7-1/build.gradle
@@ -2,8 +2,10 @@ plugins {
id 'org.jetbrains.kotlin.jvm'
}
-kotlin {
- jvmToolchain(11)
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = 11
+ }
}
dependencies {
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper-7-2/build.gradle b/java/dagger/hilt/android/plugin/agp-wrapper-7-2/build.gradle
index a41330196..e0331c2a8 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper-7-2/build.gradle
+++ b/java/dagger/hilt/android/plugin/agp-wrapper-7-2/build.gradle
@@ -2,8 +2,10 @@ plugins {
id 'org.jetbrains.kotlin.jvm'
}
-kotlin {
- jvmToolchain(11)
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = 11
+ }
}
dependencies {
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper-impl/build.gradle b/java/dagger/hilt/android/plugin/agp-wrapper-impl/build.gradle
index 3ad558a8a..f270efb9f 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper-impl/build.gradle
+++ b/java/dagger/hilt/android/plugin/agp-wrapper-impl/build.gradle
@@ -2,8 +2,10 @@ plugins {
id 'org.jetbrains.kotlin.jvm'
}
-kotlin {
- jvmToolchain(11)
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = 11
+ }
}
dependencies {
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper/build.gradle b/java/dagger/hilt/android/plugin/agp-wrapper/build.gradle
index 71ea72424..d8238e8f1 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper/build.gradle
+++ b/java/dagger/hilt/android/plugin/agp-wrapper/build.gradle
@@ -2,8 +2,10 @@ plugins {
id 'org.jetbrains.kotlin.jvm'
}
-kotlin {
- jvmToolchain(11)
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = 11
+ }
}
dependencies {
diff --git a/java/dagger/hilt/android/plugin/agp-wrapper/src/main/kotlin/dagger/hilt/android/plugin/util/ComponentCompat.kt b/java/dagger/hilt/android/plugin/agp-wrapper/src/main/kotlin/dagger/hilt/android/plugin/util/ComponentCompat.kt
index acc59e79d..1f0d589a2 100644
--- a/java/dagger/hilt/android/plugin/agp-wrapper/src/main/kotlin/dagger/hilt/android/plugin/util/ComponentCompat.kt
+++ b/java/dagger/hilt/android/plugin/agp-wrapper/src/main/kotlin/dagger/hilt/android/plugin/util/ComponentCompat.kt
@@ -20,6 +20,8 @@ import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationParameters
import com.android.build.api.instrumentation.InstrumentationScope
+import java.io.File
+import org.gradle.api.Project
/**
* Compatibility version of [com.android.build.api.variant.Component]
diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle
index 869d88516..685ea7e37 100644
--- a/java/dagger/hilt/android/plugin/build.gradle
+++ b/java/dagger/hilt/android/plugin/build.gradle
@@ -1,8 +1,8 @@
buildscript {
ext {
- kotlin_version = "1.8.20"
+ kotlin_version = "1.9.20"
agp_version = System.getenv('AGP_VERSION') ?: "7.2.0"
- ksp_version = "$kotlin_version-1.0.11"
+ ksp_version = "$kotlin_version-1.0.14"
pluginArtifactId = 'hilt-android-gradle-plugin'
pluginId = 'com.google.dagger.hilt.android'
}
@@ -24,7 +24,12 @@ allprojects {
mavenCentral()
}
}
+
+// Avoids conflict with BUILD file
+project.buildDir = 'buildOut'
+
subprojects {
+ project.buildDir = 'buildOut'
afterEvaluate {
dependencies {
// This is needed to align older versions of kotlin-stdlib.
diff --git a/java/dagger/hilt/android/plugin/main/build.gradle b/java/dagger/hilt/android/plugin/main/build.gradle
index 96a28b937..035ecea22 100644
--- a/java/dagger/hilt/android/plugin/main/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/build.gradle
@@ -65,16 +65,16 @@ dependencies {
compileOnly "com.android.tools.build:gradle:$agp_version"
compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
compileOnly "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version"
- implementation 'org.ow2.asm:asm:9.0'
+ implementation 'org.ow2.asm:asm:9.6'
implementation "com.squareup:javapoet:1.13.0"
testImplementation gradleTestKit()
testImplementation 'junit:junit:4.12'
testImplementation 'com.google.truth:truth:1.0.1'
testImplementation 'org.javassist:javassist:3.26.0-GA'
- testPluginCompile 'com.android.tools.build:gradle:7.1.2'
- testPluginCompile 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0'
- testPluginCompile 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.8.0-1.0.9'
+ testPluginCompile "com.android.tools.build:gradle:$agp_version"
+ testPluginCompile "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ testPluginCompile "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version"
}
// Configure the generating task of plugin-under-test-metadata.properties to
@@ -85,14 +85,11 @@ tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").c
it.pluginClasspath.from(configurations.testPluginCompile)
}
-kotlin {
- jvmToolchain(11)
-}
-
compileKotlin {
kotlinOptions {
allWarningsAsErrors = true
freeCompilerArgs += [ "-opt-in=kotlin.ExperimentalStdlibApi" ]
+ jvmTarget = 11
}
}
diff --git a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt
index da5fb3c3c..16e4af9c0 100644
--- a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt
+++ b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt
@@ -36,15 +36,10 @@ import org.objectweb.asm.Opcodes
class AndroidEntryPointClassVisitor(
private val apiVersion: Int,
nextClassVisitor: ClassVisitor,
- private val additionalClasses: File
+ private val classContext: ClassContext
) : ClassVisitor(apiVersion, nextClassVisitor) {
- interface AndroidEntryPointParams : InstrumentationParameters {
- @get:Internal
- val additionalClassesDir: Property<File>
- }
-
- abstract class Factory : AsmClassVisitorFactory<AndroidEntryPointParams> {
+ abstract class Factory : AsmClassVisitorFactory<InstrumentationParameters.None> {
override fun createClassVisitor(
classContext: ClassContext,
nextClassVisitor: ClassVisitor
@@ -52,7 +47,7 @@ class AndroidEntryPointClassVisitor(
return AndroidEntryPointClassVisitor(
apiVersion = instrumentationContext.apiVersion.get(),
nextClassVisitor = nextClassVisitor,
- additionalClasses = parameters.get().additionalClassesDir.get()
+ classContext = classContext
)
}
@@ -198,34 +193,21 @@ class AndroidEntryPointClassVisitor(
}
/**
- * Check if Hilt generated class is a BroadcastReceiver with the marker field which means
+ * Check if Hilt generated class is a BroadcastReceiver with the marker annotation which means
* a super.onReceive invocation has to be inserted in the implementation.
*/
- private fun hasOnReceiveBytecodeInjectionMarker() =
- findAdditionalClassFile(newSuperclassName).inputStream().use {
- var hasMarker = false
- ClassReader(it).accept(
- object : ClassVisitor(apiVersion) {
- override fun visitField(
- access: Int,
- name: String,
- descriptor: String,
- signature: String?,
- value: Any?
- ): FieldVisitor? {
- if (name == "onReceiveBytecodeInjectionMarker") {
- hasMarker = true
- }
- return null
- }
- },
- ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES
- )
- return@use hasMarker
- }
+ private fun hasOnReceiveBytecodeInjectionMarker(): Boolean {
+ val newSuperclassFQName = newSuperclassName.toFQName()
+ return classContext.loadClassData(newSuperclassFQName)
+ ?.classAnnotations?.contains(ON_RECEIVE_MARKER_ANNOTATION)
+ ?: error("Cannot load class $newSuperclassFQName!")
+ }
- private fun findAdditionalClassFile(className: String) =
- File(additionalClasses, "$className.class")
+ /**
+ * Return a fully qualified name from an internal name.
+ * See https://asm.ow2.io/javadoc/org/objectweb/asm/Type.html#getInternalName()
+ */
+ private fun String.toFQName() = this.replace('/', '.')
companion object {
val ANDROID_ENTRY_POINT_ANNOTATIONS = setOf(
@@ -235,5 +217,6 @@ class AndroidEntryPointClassVisitor(
const val ON_RECEIVE_METHOD_NAME = "onReceive"
const val ON_RECEIVE_METHOD_DESCRIPTOR =
"(Landroid/content/Context;Landroid/content/Intent;)V"
+ const val ON_RECEIVE_MARKER_ANNOTATION = "dagger.hilt.android.internal.OnReceiveBytecodeInjectionMarker"
}
}
diff --git a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
index 4e2b8788a..7b03e434a 100644
--- a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
+++ b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
@@ -38,7 +38,6 @@ import dagger.hilt.android.plugin.util.getKaptConfigName
import dagger.hilt.android.plugin.util.getKspConfigName
import dagger.hilt.android.plugin.util.isKspTask
import dagger.hilt.processor.internal.optionvalues.GradleProjectType
-import java.io.File
import javax.inject.Inject
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
@@ -246,12 +245,9 @@ class HiltGradlePlugin @Inject constructor(
fun registerTransform(androidComponent: ComponentCompat) {
androidComponent.transformClassesWith(
classVisitorFactoryImplClass = AndroidEntryPointClassVisitor.Factory::class.java,
- scope = InstrumentationScope.PROJECT
- ) { params ->
- val classesDir =
- File(project.buildDir, "intermediates/javac/${androidComponent.name}/classes")
- params.additionalClassesDir.set(classesDir)
- }
+ scope = InstrumentationScope.PROJECT,
+ instrumentationParamsConfig = {}
+ )
androidComponent.setAsmFramesComputationMode(
FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
)
diff --git a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt
index 7c8326b0f..f7c33dca4 100644
--- a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt
+++ b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt
@@ -16,7 +16,6 @@
package dagger.hilt.android.plugin.util
-import org.gradle.api.artifacts.transform.CacheableTransform
import org.gradle.api.artifacts.transform.InputArtifact
import org.gradle.api.artifacts.transform.TransformAction
import org.gradle.api.artifacts.transform.TransformOutputs
@@ -24,12 +23,13 @@ import org.gradle.api.artifacts.transform.TransformParameters
import org.gradle.api.file.FileSystemLocation
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Classpath
+import org.gradle.work.DisableCachingByDefault
/**
* A transform that registers the input file (usually a jar or a class) as an output and thus
* changing from one artifact type to another.
*/
-@CacheableTransform
+@DisableCachingByDefault(because = "Copying files does not benefit from caching")
abstract class CopyTransform : TransformAction<TransformParameters.None> {
@get:Classpath
@get:InputArtifact
diff --git a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt
index 2c803b71a..a91ce35c6 100644
--- a/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt
+++ b/java/dagger/hilt/android/plugin/main/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt
@@ -38,8 +38,20 @@ internal fun addKaptTaskProcessorOptions(
component: ComponentCompat,
produceArgProvider: (Task) -> CommandLineArgumentProvider
) = project.plugins.withId("kotlin-kapt") {
+ checkClass("org.jetbrains.kotlin.gradle.internal.KaptTask") {
+ """
+ The KAPT plugin was detected to be applied but its task class could not be found.
+
+ This is an indicator that the Hilt Gradle Plugin is using a different class loader because
+ it was declared at the root while KAPT was declared in a sub-project. To fix this, declare
+ both plugins in the same scope, i.e. either at the root (without applying them) or at the
+ sub-projects.
+ """.trimIndent()
+ }
project.tasks.withType(KaptTask::class.java) { task ->
- if (task.name == "kapt${component.name.capitalize()}Kotlin") {
+ if (task.name == "kapt${component.name.capitalize()}Kotlin" ||
+ // Task names in shared/src/AndroidMain in KMP projects has a platform suffix.
+ task.name == "kapt${component.name.capitalize()}KotlinAndroid") {
val argProvider = produceArgProvider.invoke(task)
// TODO: Update once KT-58009 is fixed.
try {
@@ -60,16 +72,39 @@ internal fun addKspTaskProcessorOptions(
component: ComponentCompat,
produceArgProvider: (Task) -> CommandLineArgumentProvider
) = project.plugins.withId("com.google.devtools.ksp") {
+ checkClass("com.google.devtools.ksp.gradle.KspTaskJvm") {
+ """
+ The KSP plugin was detected to be applied but its task class could not be found.
+
+ This is an indicator that the Hilt Gradle Plugin is using a different class loader because
+ it was declared at the root while KSP was declared in a sub-project. To fix this, declare
+ both plugins in the same scope, i.e. either at the root (without applying them) or at the
+ sub-projects.
+
+ See https://github.com/google/dagger/issues/3965 for more details.
+ """.trimIndent()
+ }
project.tasks.withType(KspTaskJvm::class.java) { task ->
- if (task.name == "ksp${component.name.capitalize()}Kotlin") {
+ if (task.name == "ksp${component.name.capitalize()}Kotlin" ||
+ // Task names in shared/src/AndroidMain in KMP projects has a platform suffix.
+ task.name == "ksp${component.name.capitalize()}KotlinAndroid") {
task.commandLineArgumentProviders.add(produceArgProvider.invoke(task))
}
}
}
+private inline fun checkClass(fqn: String, msg: () -> String) {
+ try {
+ Class.forName(fqn)
+ } catch (ex: ClassNotFoundException) {
+ throw IllegalStateException(msg.invoke(), ex)
+ }
+}
+
internal fun Task.isKspTask(): Boolean = try {
val kspTaskClass = Class.forName("com.google.devtools.ksp.gradle.KspTask")
kspTaskClass.isAssignableFrom(this::class.java)
} catch (ex: ClassNotFoundException) {
false
-} \ No newline at end of file
+}
+
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryA/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryA/build.gradle
index e0ec23097..9e8a097b2 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryA/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryA/build.gradle
@@ -4,12 +4,12 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
defaultConfig {
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
}
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryC/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryC/build.gradle
index 68c6a97f3..450f128aa 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryC/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/android-libraryC/build.gradle
@@ -4,12 +4,12 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
defaultConfig {
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
}
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/app/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/app/build.gradle
index 23cd028b9..954e209d9 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/app/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/app/build.gradle
@@ -20,8 +20,8 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
flavorDimensions 'api', 'version'
productFlavors {
@@ -46,7 +46,7 @@ android {
defaultConfig {
applicationId "simple.app"
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
}
compileOptions {
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/feature/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/feature/build.gradle
index 8e3495156..068d402fa 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/feature/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/flavored-project/feature/build.gradle
@@ -20,8 +20,8 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
flavorDimensions 'api', 'version'
productFlavors {
@@ -45,7 +45,7 @@ android {
defaultConfig {
minSdkVersion 16
- targetSdkVersion 32
+ targetSdkVersion 33
}
compileOptions {
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/build.gradle
index 2173a8280..506d40a81 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/build.gradle
@@ -20,13 +20,13 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ namespace "simple.app"
defaultConfig {
applicationId "simple.app"
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
}
compileOptions {
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/AndroidManifest.xml b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/AndroidManifest.xml
index da45ecc46..ce521fba8 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/AndroidManifest.xml
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/AndroidManifest.xml
@@ -16,5 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="simple.app">
<application android:name=".SimpleApp" android:label="Flavored App">
+ <receiver android:name=".SimpleReceiver" android:exported="false">
+ </receiver>
</application>
</manifest> \ No newline at end of file
diff --git a/java/dagger/spi/model/CompilerEnvironment.java b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/java/simple/app/SimpleReceiver.java
index 28553e4c3..ce483add8 100644
--- a/java/dagger/spi/model/CompilerEnvironment.java
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/app/src/main/java/simple/app/SimpleReceiver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Dagger Authors.
+ * Copyright (C) 2023 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package dagger.spi.model;
+package simple.app;
-/** Types for the compiler in use for annotation processing. */
-public enum CompilerEnvironment {
- JAVA,
- KSP
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import dagger.hilt.android.AndroidEntryPoint;
+
+@AndroidEntryPoint
+class SimpleReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {}
}
diff --git a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/feature/build.gradle b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/feature/build.gradle
index e19f1d571..57f7a742b 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/feature/build.gradle
+++ b/java/dagger/hilt/android/plugin/main/src/test/data/simple-project-for-agp-test/feature/build.gradle
@@ -20,12 +20,12 @@ plugins {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ namespace "simple.library"
defaultConfig {
minSdkVersion 16
- targetSdkVersion 32
+ targetSdkVersion 33
}
compileOptions {
diff --git a/java/dagger/hilt/android/plugin/main/src/test/kotlin/AGPCompatibilityTest.kt b/java/dagger/hilt/android/plugin/main/src/test/kotlin/AGPCompatibilityTest.kt
index d5bc48802..f488fb332 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/kotlin/AGPCompatibilityTest.kt
+++ b/java/dagger/hilt/android/plugin/main/src/test/kotlin/AGPCompatibilityTest.kt
@@ -26,11 +26,13 @@ import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-const val TASK = ":app:hiltJavaCompileDebug"
+// `hiltJavaCompileDebug` gets to run as well as `transformDebugClassesWithAsm` depends on it.
+const val TASK = ":app:transformDebugClassesWithAsm"
@RunWith(Parameterized::class)
class AGPCompatibilityTest(
- private val agpVersion: String
+ private val agpVersion: String,
+ private val gradleVersion: String
) {
@get:Rule val testProjectDir = TemporaryFolder()
@@ -75,18 +77,21 @@ class AGPCompatibilityTest(
GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments(*args)
+ .withGradleVersion(gradleVersion)
.forwardOutput()
return gradleRunner.build()
}
companion object {
@JvmStatic
- @Parameterized.Parameters(name = "agpVersion = {0}")
+ @Parameterized.Parameters(name = "agpVersion = {0}, gradleVersion = {1}")
fun parameters() =
listOf(
- arrayOf("7.2.0"),
- arrayOf("7.1.0"),
- arrayOf("7.0.0"),
+ // AGP 8.3 requires Gradle 8.4 and JDK 17.
+ arrayOf("8.3.0-alpha11", "8.4"),
+ arrayOf("7.2.0", "7.4.2"),
+ arrayOf("7.1.0", "7.4.2"),
+ arrayOf("7.0.0", "7.4.2"),
)
}
}
diff --git a/java/dagger/hilt/android/plugin/main/src/test/kotlin/BuildCacheTest.kt b/java/dagger/hilt/android/plugin/main/src/test/kotlin/BuildCacheTest.kt
index 4c34dd621..c5a67afbb 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/kotlin/BuildCacheTest.kt
+++ b/java/dagger/hilt/android/plugin/main/src/test/kotlin/BuildCacheTest.kt
@@ -82,8 +82,6 @@ class BuildCacheTest(private val enableAggregatingTask: Boolean) {
val secondResult = secondGradleRunner.build()
val cacheableTasks: List<String> =
mutableListOf<String>().apply {
- add(":checkDebugAarMetadata")
- add(":checkDebugDuplicateClasses")
add(":compileDebugJavaWithJavac")
add(":compressDebugAssets")
add(":desugarDebugFileDependencies")
@@ -105,9 +103,6 @@ class BuildCacheTest(private val enableAggregatingTask: Boolean) {
add(":mergeProjectDexDebug")
add(":processDebugManifestForPackage")
add(":transformDebugClassesWithAsm")
- add(":validateSigningDebug")
- add(":writeDebugAppMetadata")
- add(":writeDebugSigningConfigVersions")
}
val tasksFromCache =
diff --git a/java/dagger/hilt/android/plugin/main/src/test/kotlin/GradleTestRunner.kt b/java/dagger/hilt/android/plugin/main/src/test/kotlin/GradleTestRunner.kt
index fb47bdb13..221dfcfd4 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/kotlin/GradleTestRunner.kt
+++ b/java/dagger/hilt/android/plugin/main/src/test/kotlin/GradleTestRunner.kt
@@ -156,13 +156,13 @@ class GradleTestRunner(val tempFolder: TemporaryFolder) {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
defaultConfig {
${ if (isAppProject) "applicationId \"plugin.test\"" else "" }
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
}
compileOptions {
@@ -189,7 +189,8 @@ class GradleTestRunner(val tempFolder: TemporaryFolder) {
${hiltOptions.joinToString(separator = "\n")}
}
${additionalClosures.joinToString(separator = "\n")}
- """.trimIndent()
+ """
+ .trimIndent()
)
}
}
@@ -198,9 +199,14 @@ class GradleTestRunner(val tempFolder: TemporaryFolder) {
gradlePropertiesFile?.delete()
gradlePropertiesFile =
tempFolder.newFile("gradle.properties").apply {
- writeText("""
+ writeText(
+ """
android.useAndroidX=true
- """.trimIndent())
+ // TODO(b/296583777): See if there's a better way to fix the OOM error.
+ org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g
+ """
+ .trimIndent()
+ )
}
}
@@ -218,7 +224,8 @@ class GradleTestRunner(val tempFolder: TemporaryFolder) {
${activities.joinToString(separator = "\n")}
</application>
</manifest>
- """.trimIndent()
+ """
+ .trimIndent()
)
}
}
diff --git a/java/dagger/hilt/android/plugin/main/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/main/src/test/kotlin/IncrementalProcessorTest.kt
index 1e8490f1b..4fb25a0b3 100644
--- a/java/dagger/hilt/android/plugin/main/src/test/kotlin/IncrementalProcessorTest.kt
+++ b/java/dagger/hilt/android/plugin/main/src/test/kotlin/IncrementalProcessorTest.kt
@@ -34,11 +34,9 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class IncrementalProcessorTest(private val incapMode: String) {
- @get:Rule
- val testProjectDir = TemporaryFolder()
+ @get:Rule val testProjectDir = TemporaryFolder()
- @get:Rule
- val expect: Expect = Expect.create()
+ @get:Rule val expect: Expect = Expect.create()
// Original source files
private lateinit var srcApp: File
@@ -108,16 +106,18 @@ class IncrementalProcessorTest(private val incapMode: String) {
private lateinit var unchangedFiles: Set<File>
private lateinit var deletedFiles: Set<File>
- private val compileTaskName = if (incapMode == ISOLATING_MODE) {
- ":hiltJavaCompileDebug"
- } else {
- ":compileDebugJavaWithJavac"
- }
- private val testCompileTaskName = if (incapMode == ISOLATING_MODE) {
- ":hiltJavaCompileDebugUnitTest"
- } else {
- ":compileDebugUnitTestJavaWithJavac"
- }
+ private val compileTaskName =
+ if (incapMode == ISOLATING_MODE) {
+ ":hiltJavaCompileDebug"
+ } else {
+ ":compileDebugJavaWithJavac"
+ }
+ private val testCompileTaskName =
+ if (incapMode == ISOLATING_MODE) {
+ ":hiltJavaCompileDebugUnitTest"
+ } else {
+ ":compileDebugUnitTestJavaWithJavac"
+ }
private val aggregatingTaskName = ":hiltAggregateDepsDebug"
private val testAggregatingTaskName = ":hiltAggregateDepsDebugUnitTest"
@@ -128,8 +128,9 @@ class IncrementalProcessorTest(private val incapMode: String) {
File("src/test/data/simple-project").copyRecursively(projectRoot)
// set up build file
- File(projectRoot, "build.gradle").writeText(
- """
+ File(projectRoot, "build.gradle")
+ .writeText(
+ """
buildscript {
repositories {
google()
@@ -146,13 +147,13 @@ class IncrementalProcessorTest(private val incapMode: String) {
}
android {
- compileSdkVersion 32
- buildToolsVersion "32.0.0"
+ compileSdkVersion 33
+ buildToolsVersion "33.0.0"
defaultConfig {
applicationId "hilt.simple"
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
javaCompileOptions {
annotationProcessorOptions {
arguments += ["dagger.hilt.shareTestComponents" : "true"]
@@ -190,32 +191,36 @@ class IncrementalProcessorTest(private val incapMode: String) {
hilt {
enableAggregatingTask = ${if (incapMode == ISOLATING_MODE) "true" else "false"}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
// Compute directory paths
val defaultGenSrcDir = "build/generated/ap_generated_sources/debug/out/"
- fun getComponentTreeDepsGenSrcDir(variant: String) = if (incapMode == ISOLATING_MODE) {
- "build/generated/hilt/component_trees/$variant/"
- } else {
- "build/generated/ap_generated_sources/$variant/out/"
- }
+ fun getComponentTreeDepsGenSrcDir(variant: String) =
+ if (incapMode == ISOLATING_MODE) {
+ "build/generated/hilt/component_trees/$variant/"
+ } else {
+ "build/generated/ap_generated_sources/$variant/out/"
+ }
val componentTreeDepsGenSrcDir = getComponentTreeDepsGenSrcDir("debug")
val testComponentTreeDepsGenSrcDir = getComponentTreeDepsGenSrcDir("debugUnitTest")
- fun getRootGenSrcDir(variant: String) = if (incapMode == ISOLATING_MODE) {
- "build/generated/hilt/component_sources/$variant/"
- } else {
- "build/generated/ap_generated_sources/$variant/out/"
- }
+ fun getRootGenSrcDir(variant: String) =
+ if (incapMode == ISOLATING_MODE) {
+ "build/generated/hilt/component_sources/$variant/"
+ } else {
+ "build/generated/ap_generated_sources/$variant/out/"
+ }
val rootGenSrcDir = getRootGenSrcDir("debug")
val testRootGenSrcDir = getRootGenSrcDir("debugUnitTest")
val defaultClassesDir = "build/intermediates/javac/debug/classes"
val testDefaultClassesDir = "build/intermediates/javac/debugUnitTest/classes"
- fun getRootClassesDir(variant: String) = if (incapMode == ISOLATING_MODE) {
- "build/intermediates/hilt/component_classes/$variant/"
- } else {
- "build/intermediates/javac/$variant/classes"
- }
+ fun getRootClassesDir(variant: String) =
+ if (incapMode == ISOLATING_MODE) {
+ "build/intermediates/hilt/component_classes/$variant/"
+ } else {
+ "build/intermediates/javac/$variant/classes"
+ }
val rootClassesDir = getRootClassesDir("debug")
val testRootClassesDir = getRootClassesDir("debugUnitTest")
@@ -236,59 +241,64 @@ class IncrementalProcessorTest(private val incapMode: String) {
File(projectRoot, "$defaultGenSrcDir/simple/Activity1_GeneratedInjector.java")
genActivityInjector2 =
File(projectRoot, "$defaultGenSrcDir/simple/Activity2_GeneratedInjector.java")
- genAppInjectorDeps = File(
- projectRoot,
- "$defaultGenSrcDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.java"
- )
- genActivityInjectorDeps1 = File(
- projectRoot,
- "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.java"
- )
- genActivityInjectorDeps2 = File(
- projectRoot,
- "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.java"
- )
- genModuleDeps1 = File(
- projectRoot,
- "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Module1.java"
- )
+ genAppInjectorDeps =
+ File(
+ projectRoot,
+ "$defaultGenSrcDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.java"
+ )
+ genActivityInjectorDeps1 =
+ File(
+ projectRoot,
+ "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.java"
+ )
+ genActivityInjectorDeps2 =
+ File(
+ projectRoot,
+ "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.java"
+ )
+ genModuleDeps1 =
+ File(projectRoot, "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Module1.java")
genModuleDeps2 =
File(projectRoot, "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Module2.java")
genComponentTreeDeps =
File(projectRoot, "$componentTreeDepsGenSrcDir/simple/SimpleApp_ComponentTreeDeps.java")
genHiltComponents = File(projectRoot, "$rootGenSrcDir/simple/SimpleApp_HiltComponents.java")
- genDaggerHiltApplicationComponent = File(
- projectRoot,
- "$rootGenSrcDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.java"
- )
- genTest1ComponentTreeDeps = File(
- projectRoot,
- testComponentTreeDepsGenSrcDir +
- "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.java"
- )
- genTest2ComponentTreeDeps = File(
- projectRoot,
- testComponentTreeDepsGenSrcDir +
- "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.java"
- )
- genTest1HiltComponents = File(
- projectRoot,
- "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.java"
- )
- genTest2HiltComponents = File(
- projectRoot,
- "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.java"
- )
- genTest1DaggerHiltApplicationComponent = File(
- projectRoot,
- testRootGenSrcDir +
- "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.java"
- )
- genTest2DaggerHiltApplicationComponent = File(
- projectRoot,
- testRootGenSrcDir +
- "/dagger/hilt/android/internal/testing/root/DaggerTest2_HiltComponents_SingletonC.java"
- )
+ genDaggerHiltApplicationComponent =
+ File(projectRoot, "$rootGenSrcDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.java")
+ genTest1ComponentTreeDeps =
+ File(
+ projectRoot,
+ testComponentTreeDepsGenSrcDir +
+ "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.java"
+ )
+ genTest2ComponentTreeDeps =
+ File(
+ projectRoot,
+ testComponentTreeDepsGenSrcDir +
+ "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.java"
+ )
+ genTest1HiltComponents =
+ File(
+ projectRoot,
+ "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.java"
+ )
+ genTest2HiltComponents =
+ File(
+ projectRoot,
+ "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.java"
+ )
+ genTest1DaggerHiltApplicationComponent =
+ File(
+ projectRoot,
+ testRootGenSrcDir +
+ "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.java"
+ )
+ genTest2DaggerHiltApplicationComponent =
+ File(
+ projectRoot,
+ testRootGenSrcDir +
+ "/dagger/hilt/android/internal/testing/root/DaggerTest2_HiltComponents_SingletonC.java"
+ )
classSrcApp = File(projectRoot, "$defaultClassesDir/simple/SimpleApp.class")
classSrcActivity1 = File(projectRoot, "$defaultClassesDir/simple/Activity1.class")
@@ -302,70 +312,69 @@ class IncrementalProcessorTest(private val incapMode: String) {
classGenHiltActivity2 = File(projectRoot, "$defaultClassesDir/simple/Hilt_Activity2.class")
classGenAppInjector =
File(projectRoot, "$defaultClassesDir/simple/SimpleApp_GeneratedInjector.class")
- classGenActivityInjector1 = File(
- projectRoot,
- "$defaultClassesDir/simple/Activity1_GeneratedInjector.class"
- )
- classGenActivityInjector2 = File(
- projectRoot,
- "$defaultClassesDir/simple/Activity2_GeneratedInjector.class"
- )
- classGenAppInjectorDeps = File(
- projectRoot,
- "$defaultClassesDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.class"
- )
- classGenActivityInjectorDeps1 = File(
- projectRoot,
- "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.class"
- )
- classGenActivityInjectorDeps2 = File(
- projectRoot,
- "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.class"
- )
+ classGenActivityInjector1 =
+ File(projectRoot, "$defaultClassesDir/simple/Activity1_GeneratedInjector.class")
+ classGenActivityInjector2 =
+ File(projectRoot, "$defaultClassesDir/simple/Activity2_GeneratedInjector.class")
+ classGenAppInjectorDeps =
+ File(
+ projectRoot,
+ "$defaultClassesDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.class"
+ )
+ classGenActivityInjectorDeps1 =
+ File(
+ projectRoot,
+ "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.class"
+ )
+ classGenActivityInjectorDeps2 =
+ File(
+ projectRoot,
+ "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.class"
+ )
classGenModuleDeps1 =
File(projectRoot, "$defaultClassesDir/hilt_aggregated_deps/_simple_Module1.class")
classGenModuleDeps2 =
File(projectRoot, "$defaultClassesDir/hilt_aggregated_deps/_simple_Module2.class")
- classGenComponentTreeDeps = File(
- projectRoot,
- "$rootClassesDir/simple/SimpleApp_ComponentTreeDeps.class"
- )
- classGenHiltComponents = File(
- projectRoot,
- "$rootClassesDir/simple/SimpleApp_HiltComponents.class"
- )
- classGenDaggerHiltApplicationComponent = File(
- projectRoot,
- "$rootClassesDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.class"
- )
- classGenTest1ComponentTreeDeps = File(
- projectRoot,
- testRootClassesDir +
- "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.class"
- )
- classGenTest2ComponentTreeDeps = File(
- projectRoot,
- testRootClassesDir +
- "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.class"
- )
- classGenTest1HiltComponents = File(
- projectRoot,
- "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.class"
- )
- classGenTest2HiltComponents = File(
- projectRoot,
- "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.class"
- )
- classGenTest1DaggerHiltApplicationComponent = File(
- projectRoot,
- testRootClassesDir +
- "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.class"
- )
- classGenTest2DaggerHiltApplicationComponent = File(
- projectRoot,
- testRootClassesDir +
- "/dagger/hilt/android/internal/testing/root/DaggerTest2_HiltComponents_SingletonC.class"
- )
+ classGenComponentTreeDeps =
+ File(projectRoot, "$rootClassesDir/simple/SimpleApp_ComponentTreeDeps.class")
+ classGenHiltComponents =
+ File(projectRoot, "$rootClassesDir/simple/SimpleApp_HiltComponents.class")
+ classGenDaggerHiltApplicationComponent =
+ File(projectRoot, "$rootClassesDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.class")
+ classGenTest1ComponentTreeDeps =
+ File(
+ projectRoot,
+ testRootClassesDir +
+ "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.class"
+ )
+ classGenTest2ComponentTreeDeps =
+ File(
+ projectRoot,
+ testRootClassesDir +
+ "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.class"
+ )
+ classGenTest1HiltComponents =
+ File(
+ projectRoot,
+ "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.class"
+ )
+ classGenTest2HiltComponents =
+ File(
+ projectRoot,
+ "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.class"
+ )
+ classGenTest1DaggerHiltApplicationComponent =
+ File(
+ projectRoot,
+ testRootClassesDir +
+ "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.class"
+ )
+ classGenTest2DaggerHiltApplicationComponent =
+ File(
+ projectRoot,
+ testRootClassesDir +
+ "/dagger/hilt/android/internal/testing/root/DaggerTest2_HiltComponents_SingletonC.class"
+ )
}
@Test
@@ -428,13 +437,15 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Activity 1 source
searchAndReplace(
- srcActivity1, "// Insert-change",
+ srcActivity1,
+ "// Insert-change",
"""
@Override
public void onResume() {
super.onResume();
}
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalBuild()
@@ -442,66 +453,69 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Check annotation processing outputs
// * Only activity 1 sources are re-generated, isolation in modules and from other activities
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task did not run, no change in deps
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
- // * Components are re-generated due to a recompilation of a dep
- listOf(
- genHiltApp, // Re-gen because components got re-gen
- genHiltActivity1,
- genActivityInjector1,
- genActivityInjectorDeps1,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- } else {
- // * Root classes along with components are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genHiltActivity1,
- genAppInjector,
- genActivityInjector1,
- genAppInjectorDeps,
- genActivityInjectorDeps1,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task did not run, no change in deps
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+ // * Components are re-generated due to a recompilation of a dep
+ listOf(
+ genHiltApp, // Re-gen because components got re-gen
+ genHiltActivity1,
+ genActivityInjector1,
+ genActivityInjectorDeps1,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ } else {
+ // * Root classes along with components are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genHiltActivity1,
+ genAppInjector,
+ genActivityInjector1,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(componentTreeDepsFullBuild)
- .isEqualTo(componentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(componentTreeDepsFullBuild)
+ .isEqualTo(componentTreeDepsIncrementalBuild)
// Check compilation outputs
// * Gen sources from activity 1 are re-compiled
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classSrcActivity1,
- classGenHiltApp,
- classGenHiltActivity1,
- classGenActivityInjector1,
- classGenActivityInjectorDeps1,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent,
- )
- } else {
- // * All aggregating processor gen sources are re-compiled
- listOf(
- classSrcActivity1,
- classGenHiltApp,
- classGenHiltActivity1,
- classGenAppInjector,
- classGenActivityInjector1,
- classGenAppInjectorDeps,
- classGenActivityInjectorDeps1,
- classGenHiltComponents,
- classGenComponentTreeDeps,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classSrcActivity1,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenActivityInjector1,
+ classGenActivityInjectorDeps1,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent,
+ )
+ } else {
+ // * All aggregating processor gen sources are re-compiled
+ listOf(
+ classSrcActivity1,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenHiltComponents,
+ classGenComponentTreeDeps,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -512,76 +526,82 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Activity 1 source
searchAndReplace(
- srcActivity1, "// Insert-change",
+ srcActivity1,
+ "// Insert-change",
"""
private void foo() { }
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalBuild()
- val expectedOutcome = if (incapMode == ISOLATING_MODE) {
- // In isolating mode, changes that do not affect ABI will not cause re-compilation.
- TaskOutcome.UP_TO_DATE
- } else {
- TaskOutcome.SUCCESS
- }
+ val expectedOutcome =
+ if (incapMode == ISOLATING_MODE) {
+ // In isolating mode, changes that do not affect ABI will not cause re-compilation.
+ TaskOutcome.UP_TO_DATE
+ } else {
+ TaskOutcome.SUCCESS
+ }
expect.that(result.task(compileTaskName)!!.outcome).isEqualTo(expectedOutcome)
// Check annotation processing outputs
// * Only activity 1 sources are re-generated, isolation in modules and from other activities
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task did not run, no change in deps
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
- listOf(
- genHiltActivity1,
- genActivityInjector1,
- genActivityInjectorDeps1,
- )
- } else {
- // * Root classes along with components are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genHiltActivity1,
- genAppInjector,
- genActivityInjector1,
- genAppInjectorDeps,
- genActivityInjectorDeps1,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task did not run, no change in deps
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+ listOf(
+ genHiltActivity1,
+ genActivityInjector1,
+ genActivityInjectorDeps1,
+ )
+ } else {
+ // * Root classes along with components are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genHiltActivity1,
+ genAppInjector,
+ genActivityInjector1,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(componentTreeDepsFullBuild)
- .isEqualTo(componentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(componentTreeDepsFullBuild)
+ .isEqualTo(componentTreeDepsIncrementalBuild)
// Check compilation outputs
// * Gen sources from activity 1 are re-compiled
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classSrcActivity1,
- classGenHiltActivity1,
- classGenActivityInjector1,
- classGenActivityInjectorDeps1
- )
- } else {
- // * All aggregating processor gen sources are re-compiled
- listOf(
- classSrcActivity1,
- classGenHiltApp,
- classGenHiltActivity1,
- classGenAppInjector,
- classGenActivityInjector1,
- classGenAppInjectorDeps,
- classGenActivityInjectorDeps1,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classSrcActivity1,
+ classGenHiltActivity1,
+ classGenActivityInjector1,
+ classGenActivityInjectorDeps1
+ )
+ } else {
+ // * All aggregating processor gen sources are re-compiled
+ listOf(
+ classSrcActivity1,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -592,13 +612,15 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Module 1 source
searchAndReplace(
- srcModule1, "// Insert-change",
+ srcModule1,
+ "// Insert-change",
"""
@Provides
static double provideDouble() {
return 10.10;
}
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalBuild()
@@ -606,58 +628,61 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Check annotation processing outputs
// * Only module 1 sources are re-generated, isolation from other modules
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task did not run, no change in deps
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
- // * Components are re-generated due to a recompilation of a dep
- listOf(
- genHiltApp, // Re-generated because components got re-generated
- genModuleDeps1,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- } else {
- // * Root classes along with components are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genAppInjector,
- genAppInjectorDeps,
- genModuleDeps1,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task did not run, no change in deps
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+ // * Components are re-generated due to a recompilation of a dep
+ listOf(
+ genHiltApp, // Re-generated because components got re-generated
+ genModuleDeps1,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ } else {
+ // * Root classes along with components are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genModuleDeps1,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(componentTreeDepsFullBuild)
- .isEqualTo(componentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(componentTreeDepsFullBuild)
+ .isEqualTo(componentTreeDepsIncrementalBuild)
// Check compilation outputs
// * Gen sources from module 1 are re-compiled
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classSrcModule1,
- classGenHiltApp,
- classGenModuleDeps1,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- } else {
- // * All aggregating processor gen sources are re-compiled
- listOf(
- classSrcModule1,
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenModuleDeps1,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classSrcModule1,
+ classGenHiltApp,
+ classGenModuleDeps1,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ } else {
+ // * All aggregating processor gen sources are re-compiled
+ listOf(
+ classSrcModule1,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenModuleDeps1,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -668,13 +693,15 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Application source
searchAndReplace(
- srcApp, "// Insert-change",
+ srcApp,
+ "// Insert-change",
"""
@Override
public void onCreate() {
super.onCreate();
}
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalBuild()
@@ -682,57 +709,60 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Check annotation processing outputs
// * No modules or activities (or any other non-root) classes should be generated
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task did not run, no change in deps
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
- // * Components are re-generated due to a recompilation of a dep
- listOf(
- genHiltApp, // Re-generated because components got re-generated
- genAppInjector,
- genAppInjectorDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- } else {
- // * Root classes along with components are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genAppInjector,
- genAppInjectorDeps,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task did not run, no change in deps
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+ // * Components are re-generated due to a recompilation of a dep
+ listOf(
+ genHiltApp, // Re-generated because components got re-generated
+ genAppInjector,
+ genAppInjectorDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ } else {
+ // * Root classes along with components are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(componentTreeDepsFullBuild)
- .isEqualTo(componentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(componentTreeDepsFullBuild)
+ .isEqualTo(componentTreeDepsIncrementalBuild)
// Check compilation outputs
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classSrcApp,
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- } else {
- // * All aggregating processor gen sources are re-compiled
- listOf(
- classSrcApp,
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classSrcApp,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ } else {
+ // * All aggregating processor gen sources are re-compiled
+ listOf(
+ classSrcApp,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -749,33 +779,28 @@ class IncrementalProcessorTest(private val incapMode: String) {
// * All related gen classes from activity 2 should be deleted
// * Unrelated activities and modules are in isolation and should be unchanged
// * Root classes along with components are always re-generated (aggregated processor)
- assertDeletedFiles(
- listOf(
- genHiltActivity2,
- genActivityInjector2,
- genActivityInjectorDeps2
- )
- )
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task ran due to a change in dep
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
- // * Components are re-generated since there was a change in dep
- listOf(
- genHiltApp, // Re-generated because components got re-generated
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- } else {
- listOf(
- genHiltApp,
- genAppInjector,
- genAppInjectorDeps,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ assertDeletedFiles(listOf(genHiltActivity2, genActivityInjector2, genActivityInjectorDeps2))
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task ran due to a change in dep
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+ // * Components are re-generated since there was a change in dep
+ listOf(
+ genHiltApp, // Re-generated because components got re-generated
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ } else {
+ listOf(
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
// Check compilation outputs
@@ -789,23 +814,24 @@ class IncrementalProcessorTest(private val incapMode: String) {
classGenActivityInjectorDeps2
)
)
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classGenHiltApp,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- } else {
- listOf(
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classGenHiltApp,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ } else {
+ listOf(
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -822,58 +848,53 @@ class IncrementalProcessorTest(private val incapMode: String) {
// * All related gen classes from module 2 should be deleted
// * Unrelated activities and modules are in isolation and should be unchanged
- assertDeletedFiles(
- listOf(genModuleDeps2)
- )
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task ran due to a change in dep
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
- // * Components are re-generated since there was a change in dep
- listOf(
- genHiltApp, // Re-generated because components got re-generated
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- } else {
- // * Root classes along with components are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genAppInjector,
- genAppInjectorDeps,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ assertDeletedFiles(listOf(genModuleDeps2))
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task ran due to a change in dep
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+ // * Components are re-generated since there was a change in dep
+ listOf(
+ genHiltApp, // Re-generated because components got re-generated
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ } else {
+ // * Root classes along with components are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
// Check compilation outputs
// * All compiled classes from module 2 should be deleted
// * Unrelated activities and modules are in isolation and should be unchanged
- assertDeletedFiles(
- listOf(
- classSrcModule2,
- classGenModuleDeps2
- )
- )
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classGenHiltApp,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- } else {
- listOf(
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ assertDeletedFiles(listOf(classSrcModule2, classGenModuleDeps2))
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classGenHiltApp,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ } else {
+ listOf(
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -887,48 +908,52 @@ class IncrementalProcessorTest(private val incapMode: String) {
package simple;
public class Foo { }
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalBuild()
- val expectedOutcome = if (incapMode == ISOLATING_MODE) {
- // In isolating mode, component compile task does not re-compile.
- TaskOutcome.UP_TO_DATE
- } else {
- TaskOutcome.SUCCESS
- }
+ val expectedOutcome =
+ if (incapMode == ISOLATING_MODE) {
+ // In isolating mode, component compile task does not re-compile.
+ TaskOutcome.UP_TO_DATE
+ } else {
+ TaskOutcome.SUCCESS
+ }
expect.that(result.task(compileTaskName)!!.outcome).isEqualTo(expectedOutcome)
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- // * Aggregating task did not run, no change in deps
- expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
- // * Non-DI related source causes no files to be generated
- emptyList()
- } else {
- // * Root classes are always re-generated (aggregated processor)
- listOf(
- genHiltApp,
- genAppInjector,
- genAppInjectorDeps,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ // * Aggregating task did not run, no change in deps
+ expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+ // * Non-DI related source causes no files to be generated
+ emptyList()
+ } else {
+ // * Root classes are always re-generated (aggregated processor)
+ listOf(
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- emptyList()
- } else {
- listOf(
- classGenHiltApp,
- classGenAppInjector,
- classGenAppInjectorDeps,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent
- )
- }
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ emptyList()
+ } else {
+ listOf(
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -970,11 +995,13 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Test 1 source
searchAndReplace(
- srcTest1, "// Insert-change",
+ srcTest1,
+ "// Insert-change",
"""
@Test
public void newTest() { }
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalTestBuild()
@@ -983,49 +1010,53 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Check annotation processing outputs
// * Unrelated test components should be unchanged
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- genTest1HiltComponents,
- genTest1DaggerHiltApplicationComponent,
- )
- } else {
- listOf(
- genTest1ComponentTreeDeps,
- genTest2ComponentTreeDeps,
- genTest1HiltComponents,
- genTest2HiltComponents,
- genTest1DaggerHiltApplicationComponent,
- genTest2DaggerHiltApplicationComponent,
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ genTest1HiltComponents,
+ genTest1DaggerHiltApplicationComponent,
+ )
+ } else {
+ listOf(
+ genTest1ComponentTreeDeps,
+ genTest2ComponentTreeDeps,
+ genTest1HiltComponents,
+ genTest2HiltComponents,
+ genTest1DaggerHiltApplicationComponent,
+ genTest2DaggerHiltApplicationComponent,
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val test1ComponentTreeDepsIncrementalBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
val test2ComponentTreeDepsIncrementalBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(test1ComponentTreeDepsFullBuild)
- .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
- expect.withMessage("Full build")
- .that(test2ComponentTreeDepsFullBuild)
- .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
-
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(
- classSrcTest1,
- classGenTest1HiltComponents,
- classGenTest1DaggerHiltApplicationComponent,
- )
- } else {
- listOf(
- classSrcTest1,
- classGenTest1ComponentTreeDeps,
- classGenTest2ComponentTreeDeps,
- classGenTest1HiltComponents,
- classGenTest2HiltComponents,
- classGenTest1DaggerHiltApplicationComponent,
- classGenTest2DaggerHiltApplicationComponent,
- )
- }
+ expect
+ .withMessage("Full build")
+ .that(test1ComponentTreeDepsFullBuild)
+ .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(test2ComponentTreeDepsFullBuild)
+ .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
+
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(
+ classSrcTest1,
+ classGenTest1HiltComponents,
+ classGenTest1DaggerHiltApplicationComponent,
+ )
+ } else {
+ listOf(
+ classSrcTest1,
+ classGenTest1ComponentTreeDeps,
+ classGenTest2ComponentTreeDeps,
+ classGenTest1HiltComponents,
+ classGenTest2HiltComponents,
+ classGenTest1DaggerHiltApplicationComponent,
+ classGenTest2DaggerHiltApplicationComponent,
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -1037,60 +1068,67 @@ class IncrementalProcessorTest(private val incapMode: String) {
// Change Test 1 source
searchAndReplace(
- srcTest1, "// Insert-change",
+ srcTest1,
+ "// Insert-change",
"""
private void newMethod() { }
- """.trimIndent()
+ """
+ .trimIndent()
)
val result = runIncrementalTestBuild()
- val expectedOutcome = if (incapMode == ISOLATING_MODE) {
- // In isolating mode, changes that do not affect ABI will not cause re-compilation.
- TaskOutcome.UP_TO_DATE
- } else {
- TaskOutcome.SUCCESS
- }
+ val expectedOutcome =
+ if (incapMode == ISOLATING_MODE) {
+ // In isolating mode, changes that do not affect ABI will not cause re-compilation.
+ TaskOutcome.UP_TO_DATE
+ } else {
+ TaskOutcome.SUCCESS
+ }
expect.that(result.task(testCompileTaskName)!!.outcome).isEqualTo(expectedOutcome)
// Check annotation processing outputs
// * Unrelated test components should be unchanged
- val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
- emptyList()
- } else {
- listOf(
- genTest1ComponentTreeDeps,
- genTest2ComponentTreeDeps,
- genTest1HiltComponents,
- genTest2HiltComponents,
- genTest1DaggerHiltApplicationComponent,
- genTest2DaggerHiltApplicationComponent,
- )
- }
+ val regeneratedSourceFiles =
+ if (incapMode == ISOLATING_MODE) {
+ emptyList()
+ } else {
+ listOf(
+ genTest1ComponentTreeDeps,
+ genTest2ComponentTreeDeps,
+ genTest1HiltComponents,
+ genTest2HiltComponents,
+ genTest1DaggerHiltApplicationComponent,
+ genTest2DaggerHiltApplicationComponent,
+ )
+ }
assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
val test1ComponentTreeDepsIncrementalBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
val test2ComponentTreeDepsIncrementalBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
- expect.withMessage("Full build")
- .that(test1ComponentTreeDepsFullBuild)
- .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
- expect.withMessage("Full build")
- .that(test2ComponentTreeDepsFullBuild)
- .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
-
- val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
- listOf(classSrcTest1)
- } else {
- listOf(
- classSrcTest1,
- classGenTest1ComponentTreeDeps,
- classGenTest2ComponentTreeDeps,
- classGenTest1HiltComponents,
- classGenTest2HiltComponents,
- classGenTest1DaggerHiltApplicationComponent,
- classGenTest2DaggerHiltApplicationComponent,
- )
- }
+ expect
+ .withMessage("Full build")
+ .that(test1ComponentTreeDepsFullBuild)
+ .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
+ expect
+ .withMessage("Full build")
+ .that(test2ComponentTreeDepsFullBuild)
+ .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
+
+ val recompiledClassFiles =
+ if (incapMode == ISOLATING_MODE) {
+ listOf(classSrcTest1)
+ } else {
+ listOf(
+ classSrcTest1,
+ classGenTest1ComponentTreeDeps,
+ classGenTest2ComponentTreeDeps,
+ classGenTest1HiltComponents,
+ classGenTest2HiltComponents,
+ classGenTest1DaggerHiltApplicationComponent,
+ classGenTest2DaggerHiltApplicationComponent,
+ )
+ }
assertChangedFiles(FileType.CLASS, recompiledClassFiles)
}
@@ -1129,83 +1167,93 @@ class IncrementalProcessorTest(private val incapMode: String) {
}
private fun recordTimestamps() {
- val files = listOf(
- genHiltApp,
- genHiltActivity1,
- genHiltActivity2,
- genAppInjector,
- genActivityInjector1,
- genActivityInjector2,
- genAppInjectorDeps,
- genActivityInjectorDeps1,
- genActivityInjectorDeps2,
- genModuleDeps1,
- genModuleDeps2,
- genComponentTreeDeps,
- genHiltComponents,
- genDaggerHiltApplicationComponent,
- genTest1ComponentTreeDeps,
- genTest2ComponentTreeDeps,
- genTest1HiltComponents,
- genTest2HiltComponents,
- genTest1DaggerHiltApplicationComponent,
- genTest2DaggerHiltApplicationComponent,
- classSrcApp,
- classSrcActivity1,
- classSrcActivity2,
- classSrcModule1,
- classSrcModule2,
- classSrcTest1,
- classSrcTest2,
- classGenHiltApp,
- classGenHiltActivity1,
- classGenHiltActivity2,
- classGenAppInjector,
- classGenActivityInjector1,
- classGenActivityInjector2,
- classGenAppInjectorDeps,
- classGenActivityInjectorDeps1,
- classGenActivityInjectorDeps2,
- classGenModuleDeps1,
- classGenModuleDeps2,
- classGenComponentTreeDeps,
- classGenHiltComponents,
- classGenDaggerHiltApplicationComponent,
- classGenTest1ComponentTreeDeps,
- classGenTest2ComponentTreeDeps,
- classGenTest1HiltComponents,
- classGenTest2HiltComponents,
- classGenTest1DaggerHiltApplicationComponent,
- classGenTest2DaggerHiltApplicationComponent,
- )
+ val files =
+ listOf(
+ genHiltApp,
+ genHiltActivity1,
+ genHiltActivity2,
+ genAppInjector,
+ genActivityInjector1,
+ genActivityInjector2,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genActivityInjectorDeps2,
+ genModuleDeps1,
+ genModuleDeps2,
+ genComponentTreeDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent,
+ genTest1ComponentTreeDeps,
+ genTest2ComponentTreeDeps,
+ genTest1HiltComponents,
+ genTest2HiltComponents,
+ genTest1DaggerHiltApplicationComponent,
+ genTest2DaggerHiltApplicationComponent,
+ classSrcApp,
+ classSrcActivity1,
+ classSrcActivity2,
+ classSrcModule1,
+ classSrcModule2,
+ classSrcTest1,
+ classSrcTest2,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenHiltActivity2,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenActivityInjector2,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenActivityInjectorDeps2,
+ classGenModuleDeps1,
+ classGenModuleDeps2,
+ classGenComponentTreeDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent,
+ classGenTest1ComponentTreeDeps,
+ classGenTest2ComponentTreeDeps,
+ classGenTest1HiltComponents,
+ classGenTest2HiltComponents,
+ classGenTest1DaggerHiltApplicationComponent,
+ classGenTest2DaggerHiltApplicationComponent,
+ )
- fileToTimestampMap = mutableMapOf<File, Long>().apply {
- for (file in files) {
- this[file] = file.lastModified()
+ fileToTimestampMap =
+ mutableMapOf<File, Long>().apply {
+ for (file in files) {
+ this[file] = file.lastModified()
+ }
}
- }
}
private fun recordFileChanges() {
- changedFiles = fileToTimestampMap.filter { (file, previousTimestamp) ->
- file.exists() && file.lastModified() != previousTimestamp
- }.keys
+ changedFiles =
+ fileToTimestampMap
+ .filter { (file, previousTimestamp) ->
+ file.exists() && file.lastModified() != previousTimestamp
+ }
+ .keys
- unchangedFiles = fileToTimestampMap.filter { (file, previousTimestamp) ->
- file.exists() && file.lastModified() == previousTimestamp
- }.keys
+ unchangedFiles =
+ fileToTimestampMap
+ .filter { (file, previousTimestamp) ->
+ file.exists() && file.lastModified() == previousTimestamp
+ }
+ .keys
deletedFiles = fileToTimestampMap.filter { (file, _) -> !file.exists() }.keys
}
private fun assertFilesExist(files: Collection<File>) {
- expect.withMessage("Existing files")
+ expect
+ .withMessage("Existing files")
.that(files.filter { it.exists() })
.containsExactlyElementsIn(files)
}
private fun assertChangedFiles(type: FileType, files: Collection<File>) {
- expect.withMessage("Changed files")
+ expect
+ .withMessage("Changed files")
.that(changedFiles.filter { it.name.endsWith(type.extension) })
.containsExactlyElementsIn(files)
}
@@ -1227,10 +1275,7 @@ class IncrementalProcessorTest(private val incapMode: String) {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
- fun parameters() = listOf(
- ISOLATING_MODE,
- AGGREGATING_MODE
- )
+ fun parameters() = listOf(ISOLATING_MODE, AGGREGATING_MODE)
private const val ISOLATING_MODE = "isolating"
private const val AGGREGATING_MODE = "aggregating"
diff --git a/java/dagger/hilt/android/plugin/settings.gradle b/java/dagger/hilt/android/plugin/settings.gradle
index 55724bf48..778bf22e4 100644
--- a/java/dagger/hilt/android/plugin/settings.gradle
+++ b/java/dagger/hilt/android/plugin/settings.gradle
@@ -21,3 +21,4 @@ include ':agp-wrapper-impl'
include ':agp-wrapper-7-0'
include ':agp-wrapper-7-1'
include ':agp-wrapper-7-2'
+
diff --git a/java/dagger/hilt/android/processor/BUILD b/java/dagger/hilt/android/processor/BUILD
index 6f45216f1..bf5a6c3b5 100644
--- a/java/dagger/hilt/android/processor/BUILD
+++ b/java/dagger/hilt/android/processor/BUILD
@@ -52,6 +52,7 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal:component_names",
"//java/dagger/hilt/processor/internal:components",
"//java/dagger/hilt/processor/internal:hilt_processing_env_configs",
+ "//java/dagger/hilt/processor/internal:method_signature",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
diff --git a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
index 2d954481a..e37c6b7bd 100644
--- a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
+++ b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
@@ -99,6 +99,10 @@ public final class AndroidClassNames {
get("dagger.hilt.android.internal.managers", "ServiceComponentManager");
public static final ClassName VIEW_COMPONENT_MANAGER =
get("dagger.hilt.android.internal.managers", "ViewComponentManager");
+ public static final ClassName SAVED_STATE_HANDLE_ENTRY_POINTS =
+ get("dagger.hilt.android.internal.managers", "SavedStateHandleEntryPoints");
+ public static final ClassName SAVED_STATE_HANDLE_HOLDER =
+ get("dagger.hilt.android.internal.managers", "SavedStateHandleHolder");
public static final ClassName HAS_CUSTOM_INJECT =
get("dagger.hilt.android.internal.migration", "HasCustomInject");
@@ -114,6 +118,10 @@ public final class AndroidClassNames {
get("dagger.hilt.android.lifecycle", "HiltViewModel");
public static final ClassName HILT_VIEW_MODEL_MAP_QUALIFIER =
get("dagger.hilt.android.internal.lifecycle", "HiltViewModelMap");
+
+ public static final ClassName HILT_VIEW_MODEL_ASSISTED_FACTORY_MAP_QUALIFIER =
+ get("dagger.hilt.android.internal.lifecycle", "HiltViewModelAssistedMap");
+
public static final ClassName HILT_VIEW_MODEL_KEYS_QUALIFIER =
get("dagger.hilt.android.internal.lifecycle", "HiltViewModelMap", "KeySet");
public static final ClassName VIEW_MODEL = get("androidx.lifecycle", "ViewModel");
@@ -121,9 +129,13 @@ public final class AndroidClassNames {
get("androidx.lifecycle", "ViewModelProvider", "Factory");
public static final ClassName SAVED_STATE_HANDLE =
get("androidx.lifecycle", "SavedStateHandle");
-
+ public static final ClassName DEFAULT_LIFECYCLE_OBSERVER =
+ get("androidx.lifecycle", "DefaultLifecycleObserver");
+ public static final ClassName LIFECYCLE_OWNER = get("androidx.lifecycle", "LifecycleOwner");
public static final ClassName ON_CONTEXT_AVAILABLE_LISTENER =
get("androidx.activity.contextaware", "OnContextAvailableListener");
+ public static final ClassName UI_THREAD = get("androidx.annotation", "UiThread");
+
public static final ClassName INJECT_VIA_ON_CONTEXT_AVAILABLE_LISTENER =
get("dagger.hilt.android", "InjectViaOnContextAvailableListener");
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
index 49f439ec9..e1bf74f50 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
@@ -16,22 +16,58 @@
package dagger.hilt.android.processor.internal.androidentrypoint;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static kotlin.streams.jdk8.StreamsKt.asStream;
+
import androidx.room.compiler.processing.JavaPoetExtKt;
+import androidx.room.compiler.processing.XAnnotated;
+import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeParameterElement;
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.MethodSignature;
import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.xprocessing.XElements;
import java.io.IOException;
import javax.lang.model.element.Modifier;
+import javax.tools.Diagnostic;
/** Generates an Hilt Activity class for the @AndroidEntryPoint annotated class. */
public final class ActivityGenerator {
+ private enum ActivityMethod {
+ ON_CREATE(AndroidClassNames.BUNDLE),
+ ON_STOP(),
+ ON_DESTROY();
+
+ @SuppressWarnings("ImmutableEnumChecker")
+ private final MethodSignature signature;
+
+ ActivityMethod(TypeName... parameterTypes) {
+ String methodName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
+ this.signature = MethodSignature.of(methodName, parameterTypes);
+ }
+ }
+
+ private static final FieldSpec SAVED_STATE_HANDLE_HOLDER_FIELD =
+ FieldSpec.builder(AndroidClassNames.SAVED_STATE_HANDLE_HOLDER, "savedStateHandleHolder")
+ .addModifiers(Modifier.PRIVATE)
+ .build();
+
private final XProcessingEnv env;
private final AndroidEntryPointMetadata metadata;
private final ClassName generatedClassName;
@@ -61,6 +97,13 @@ public final class ActivityGenerator {
CodeBlock.builder().addStatement("_initHiltInternal()").build(),
builder);
builder.addMethod(init());
+ if (!metadata.overridesAndroidEntryPointClass()) {
+ builder
+ .addField(SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .addMethod(initSavedStateHandleHolderMethod())
+ .addMethod(onCreateComponentActivity())
+ .addMethod(onDestroyComponentActivity());
+ }
metadata.baseElement().getTypeParameters().stream()
.map(XTypeParameterElement::getTypeVariableName)
@@ -133,4 +176,119 @@ public final class ActivityGenerator {
AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES)
.build();
}
+
+ // @Override
+ // public void onCreate(Bundle bundle) {
+ // super.onCreate(savedInstanceState);
+ // initSavedStateHandleHolder();
+ // }
+ //
+ private MethodSpec onCreateComponentActivity() {
+ XMethodElement nearestOverrideMethod =
+ requireNearestOverrideMethod(ActivityMethod.ON_CREATE, metadata);
+ if (nearestOverrideMethod.isFinal()) {
+ env.getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ "Do not mark onCreate as final in base Activity class, as Hilt needs to override it"
+ + " to inject SavedStateHandle.",
+ nearestOverrideMethod);
+ }
+ ParameterSpec.Builder parameterBuilder =
+ ParameterSpec.builder(AndroidClassNames.BUNDLE, "savedInstanceState");
+ MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("onCreate");
+ // If the sub class is overriding onCreate with @Nullable parameter, then this generated
+ // method will also prefix the parameter with @Nullable.
+ if (isNullable(nearestOverrideMethod.getParameters().get(0))) {
+ parameterBuilder.addAnnotation(AndroidClassNames.NULLABLE);
+ }
+ if (nearestOverrideMethod.hasAnnotation(AndroidClassNames.UI_THREAD)) {
+ methodBuilder.addAnnotation(AndroidClassNames.UI_THREAD);
+ }
+ return methodBuilder
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addAnnotation(Override.class)
+ .addModifiers(XElements.getModifiers(nearestOverrideMethod))
+ .addParameter(parameterBuilder.build())
+ .addStatement("super.onCreate(savedInstanceState)")
+ .addStatement("initSavedStateHandleHolder()")
+ .build();
+ }
+
+ // private void initSavedStateHandleHolder() {
+ // savedStateHandleHolder = componentManager().getSavedStateHandleHolder();
+ // if (savedStateHandleHolder.isInvalid()) {
+ // savedStateHandleHolder.setExtras(getDefaultViewModelCreationExtras());
+ // }
+ // }
+ private static MethodSpec initSavedStateHandleHolderMethod() {
+ return MethodSpec.methodBuilder("initSavedStateHandleHolder")
+ .addModifiers(Modifier.PRIVATE)
+ .beginControlFlow(
+ "if (getApplication() instanceof $T)", ClassNames.GENERATED_COMPONENT_MANAGER)
+ .addStatement(
+ "$N = componentManager().getSavedStateHandleHolder()", SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .beginControlFlow("if ($N.isInvalid())", SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .addStatement(
+ "$N.setExtras(getDefaultViewModelCreationExtras())", SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .endControlFlow()
+ .endControlFlow()
+ .build();
+ }
+
+ private static boolean isNullable(XExecutableParameterElement element) {
+ return hasNullableAnnotation(element) || hasNullableAnnotation(element.getType());
+ }
+
+ private static boolean hasNullableAnnotation(XAnnotated element) {
+ return element.getAllAnnotations().stream()
+ .anyMatch(annotation -> annotation.getClassName().simpleName().equals("Nullable"));
+ }
+
+ // @Override
+ // public void onDestroy() {
+ // super.onDestroy();
+ // if (savedStateHandleHolder != null) {
+ // savedStateHandleHolder.clear();
+ // }
+ // }
+ private MethodSpec onDestroyComponentActivity() {
+ XMethodElement nearestOverrideMethod =
+ requireNearestOverrideMethod(ActivityMethod.ON_DESTROY, metadata);
+ if (nearestOverrideMethod.isFinal()) {
+ env.getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ "Do not mark onDestroy as final in base Activity class, as Hilt needs to override it"
+ + " to clean up SavedStateHandle.",
+ nearestOverrideMethod);
+ }
+ return MethodSpec.methodBuilder("onDestroy")
+ .addAnnotation(Override.class)
+ .addModifiers(XElements.getModifiers(nearestOverrideMethod))
+ .addStatement("super.onDestroy()")
+ .beginControlFlow("if ($N != null)", SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .addStatement("$N.clear()", SAVED_STATE_HANDLE_HOLDER_FIELD)
+ .endControlFlow()
+ .build();
+ }
+
+ private static XMethodElement requireNearestOverrideMethod(
+ ActivityMethod activityMethod, AndroidEntryPointMetadata metadata) {
+ XMethodElement methodOnAndroidEntryPointElement =
+ metadata.element().getDeclaredMethods().stream()
+ .filter(method -> MethodSignature.of(method).equals(activityMethod.signature))
+ .findFirst()
+ .orElse(null);
+ if (methodOnAndroidEntryPointElement != null) {
+ return methodOnAndroidEntryPointElement;
+ }
+
+ ImmutableList<XMethodElement> methodOnBaseElement =
+ asStream(metadata.baseElement().getAllMethods())
+ .filter(method -> MethodSignature.of(method).equals(activityMethod.signature))
+ .collect(toImmutableList());
+ checkState(methodOnBaseElement.size() >= 1);
+ return Iterables.getLast(methodOnBaseElement);
+ }
}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
index 6f925dace..71ad88eee 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
@@ -23,7 +23,6 @@ import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.hilt.processor.internal.HiltCompilerOptions.isAndroidSuperClassValidationDisabled;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
-import static dagger.internal.codegen.xprocessing.XTypeElements.isKotlinSource;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import androidx.room.compiler.processing.XAnnotation;
@@ -159,7 +158,7 @@ public abstract class AndroidEntryPointMetadata {
public Modifier[] generatedClassModifiers() {
// Note XElement#isPublic() refers to the jvm visibility. Since "internal" visibility is
// represented as public in the jvm, we have to check XElement#isInternal() explicitly.
- return isKotlinSource(element()) && element().isPublic() && !element().isInternal()
+ return element().isFromKotlin() && element().isPublic() && !element().isInternal()
? new Modifier[] {Modifier.ABSTRACT, Modifier.PUBLIC}
: new Modifier[] {Modifier.ABSTRACT};
}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
index 96beff3fd..327412b52 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
@@ -71,11 +71,11 @@ java_library(
"//java/dagger/hilt/android/processor/internal:android_classnames",
"//java/dagger/hilt/processor/internal:classnames",
"//java/dagger/hilt/processor/internal:component_names",
+ "//java/dagger/hilt/processor/internal:method_signature",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/xprocessing",
- "//third_party/java/auto:common",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
"//third_party/java/javapoet",
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
index 156842bbd..78a7976d1 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
@@ -30,13 +30,12 @@ import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XTypeParameterElement;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.Processors;
import dagger.internal.codegen.xprocessing.XExecutableTypes;
import java.io.IOException;
@@ -69,6 +68,13 @@ public final class BroadcastReceiverGenerator {
.addModifiers(metadata.generatedClassModifiers())
.addMethod(onReceiveMethod());
+ // Add an annotation used as a marker to let the bytecode injector know this receiver
+ // will need to be injected with a super.onReceive call. This is only necessary if no concrete
+ // onReceive call is implemented in any of the super classes.
+ if (metadata.requiresBytecodeInjection() && !isOnReceiveImplemented(metadata.baseElement())) {
+ builder.addAnnotation(ClassNames.ON_RECEIVE_BYTECODE_INJECTION_MARKER);
+ }
+
JavaPoetExtKt.addOriginatingElement(builder, metadata.element());
Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
Processors.addGeneratedAnnotation(builder, env, getClass());
@@ -82,20 +88,6 @@ public final class BroadcastReceiverGenerator {
Generators.copyLintAnnotations(metadata.element(), builder);
Generators.copySuppressAnnotations(metadata.element(), builder);
- // Add an unused field used as a marker to let the bytecode injector know this receiver will
- // need to be injected with a super.onReceive call. This is only necessary if no concrete
- // onReceive call is implemented in any of the super classes.
- if (metadata.requiresBytecodeInjection() && !isOnReceiveImplemented(metadata.baseElement())) {
- builder.addField(
- FieldSpec.builder(
- TypeName.BOOLEAN,
- "onReceiveBytecodeInjectionMarker",
- Modifier.PRIVATE,
- Modifier.FINAL)
- .initializer("false")
- .build());
- }
-
env.getFiler()
.write(
JavaFile.builder(generatedClassName.packageName(), builder.build()).build(),
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
index 00a64028a..b59760960 100644
--- a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
@@ -33,8 +33,6 @@ import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
-import dagger.hilt.processor.internal.kotlin.KotlinMetadataUtil;
-import dagger.hilt.processor.internal.kotlin.KotlinMetadataUtils;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.Collection;
@@ -111,12 +109,7 @@ abstract class BindValueMetadata {
XElements.toStableString(element));
XFieldElement field = asField(element);
-
- KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
- Optional<XMethodElement> propertyGetter =
- metadataUtil.hasMetadata(field)
- ? metadataUtil.getPropertyGetter(field)
- : Optional.empty();
+ Optional<XMethodElement> propertyGetter = Optional.ofNullable(field.getGetter());
if (propertyGetter.isPresent()) {
ProcessorErrors.checkState(
!propertyGetter.get().isPrivate(),
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/BUILD b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
index f3686a1c8..90760ea31 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
@@ -38,6 +38,7 @@ kt_jvm_library(
"//java/dagger/hilt/android/processor/internal:android_classnames",
"//java/dagger/hilt/processor/internal:base_processor",
"//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:compiler_options",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/internal/codegen/xprocessing",
@@ -63,6 +64,7 @@ kt_jvm_library(
"//:spi",
"//java/dagger/hilt/android/processor/internal:android_classnames",
"//java/dagger/hilt/processor/internal:dagger_models",
+ "//java/dagger/internal/codegen/xprocessing",
"//third_party/java/auto:service",
"//third_party/java/guava/graph",
"//third_party/java/javapoet",
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt
index 49b35a7d9..920410a0b 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt
@@ -16,83 +16,181 @@
package dagger.hilt.android.processor.internal.viewmodel
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.toJavaPoet
import androidx.room.compiler.processing.ExperimentalProcessingApi
+import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XTypeElement
import com.squareup.javapoet.ClassName
import dagger.hilt.android.processor.internal.AndroidClassNames
import dagger.hilt.processor.internal.ClassNames
+import dagger.hilt.processor.internal.HiltCompilerOptions
import dagger.hilt.processor.internal.ProcessorErrors
import dagger.hilt.processor.internal.Processors
import dagger.internal.codegen.xprocessing.XAnnotations
+import dagger.internal.codegen.xprocessing.XElements
+import dagger.internal.codegen.xprocessing.XTypeElements
import dagger.internal.codegen.xprocessing.XTypes
/** Data class that represents a Hilt injected ViewModel */
@OptIn(ExperimentalProcessingApi::class)
-internal class ViewModelMetadata private constructor(val typeElement: XTypeElement) {
- val className = typeElement.className
+internal class ViewModelMetadata
+private constructor(val viewModelElement: XTypeElement, val assistedFactory: XTypeElement) {
+ val className = viewModelElement.asClassName().toJavaPoet()
+
+ val assistedFactoryClassName: ClassName = assistedFactory.asClassName().toJavaPoet()
val modulesClassName =
ClassName.get(
- typeElement.packageName,
+ viewModelElement.packageName,
"${className.simpleNames().joinToString("_")}_HiltModules"
)
companion object {
+
+ private const val ASSISTED_FACTORY_VALUE = "assistedFactory"
+
+ fun getAssistedFactoryMethods(factory: XTypeElement?): List<XMethodElement> {
+ return XTypeElements.getAllNonPrivateInstanceMethods(factory)
+ .filter { it.isAbstract() }
+ .filter { !it.isJavaDefault() }
+ }
+
internal fun create(
processingEnv: XProcessingEnv,
- typeElement: XTypeElement,
+ viewModelElement: XTypeElement,
): ViewModelMetadata? {
ProcessorErrors.checkState(
- XTypes.isSubtype(typeElement.type, processingEnv.requireType(AndroidClassNames.VIEW_MODEL)),
- typeElement,
+ XTypes.isSubtype(
+ viewModelElement.type,
+ processingEnv.requireType(AndroidClassNames.VIEW_MODEL)
+ ),
+ viewModelElement,
"@HiltViewModel is only supported on types that subclass %s.",
AndroidClassNames.VIEW_MODEL
)
- typeElement
- .getConstructors()
- .filter { constructor ->
- ProcessorErrors.checkState(
- !constructor.hasAnnotation(ClassNames.ASSISTED_INJECT),
- constructor,
- "ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
- )
- constructor.hasAnnotation(ClassNames.INJECT)
- }
- .let { injectConstructors ->
- ProcessorErrors.checkState(
- injectConstructors.size == 1,
- typeElement,
- "@HiltViewModel annotated class should contain exactly one @Inject " +
- "annotated constructor."
- )
-
- injectConstructors.forEach { injectConstructor ->
+ val isAssistedInjectFeatureEnabled =
+ HiltCompilerOptions.isAssistedInjectViewModelsEnabled(viewModelElement)
+
+ val assistedFactoryType =
+ viewModelElement
+ .requireAnnotation(AndroidClassNames.HILT_VIEW_MODEL)
+ .getAsType(ASSISTED_FACTORY_VALUE)
+ val assistedFactory = assistedFactoryType.typeElement!!
+
+ if (assistedFactoryType.asTypeName() != XTypeName.ANY_OBJECT) {
+ ProcessorErrors.checkState(
+ isAssistedInjectFeatureEnabled,
+ viewModelElement,
+ "Specified assisted factory %s for %s in @HiltViewModel but compiler option 'enableAssistedInjectViewModels' was not enabled.",
+ assistedFactoryType.asTypeName().toJavaPoet(),
+ XElements.toStableString(viewModelElement),
+ )
+
+ ProcessorErrors.checkState(
+ assistedFactory.hasAnnotation(ClassNames.ASSISTED_FACTORY),
+ viewModelElement,
+ "Class %s is not annotated with @AssistedFactory.",
+ assistedFactoryType.asTypeName().toJavaPoet()
+ )
+
+ val assistedFactoryMethod = getAssistedFactoryMethods(assistedFactory).singleOrNull()
+
+ ProcessorErrors.checkState(
+ assistedFactoryMethod != null,
+ assistedFactory,
+ "Cannot find assisted factory method in %s.",
+ XElements.toStableString(assistedFactory)
+ )
+
+ val assistedFactoryMethodType = assistedFactoryMethod!!.asMemberOf(assistedFactoryType)
+
+ ProcessorErrors.checkState(
+ assistedFactoryMethodType.returnType.asTypeName() == viewModelElement.asClassName(),
+ assistedFactoryMethod,
+ "Class %s must have a factory method that returns a %s. Found %s.",
+ XElements.toStableString(assistedFactory),
+ XElements.toStableString(viewModelElement),
+ XTypes.toStableString(assistedFactoryMethodType.returnType)
+ )
+ }
+
+ val injectConstructors =
+ viewModelElement.getConstructors().filter { constructor ->
+ if (isAssistedInjectFeatureEnabled) {
+ constructor.hasAnnotation(ClassNames.INJECT) ||
+ constructor.hasAnnotation(ClassNames.ASSISTED_INJECT)
+ } else {
ProcessorErrors.checkState(
- !injectConstructor.isPrivate(),
- injectConstructor,
- "@Inject annotated constructors must not be private."
+ !constructor.hasAnnotation(ClassNames.ASSISTED_INJECT),
+ constructor,
+ "ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
)
+ constructor.hasAnnotation(ClassNames.INJECT)
}
}
+ val injectAnnotationsMessage =
+ if (isAssistedInjectFeatureEnabled) {
+ "@Inject or @AssistedInject"
+ } else {
+ "@Inject"
+ }
+
+ ProcessorErrors.checkState(
+ injectConstructors.size == 1,
+ viewModelElement,
+ "@HiltViewModel annotated class should contain exactly one %s annotated constructor.",
+ injectAnnotationsMessage
+ )
+
+ val injectConstructor = injectConstructors.single()
+
+ ProcessorErrors.checkState(
+ !injectConstructor.isPrivate(),
+ injectConstructor,
+ "%s annotated constructors must not be private.",
+ injectAnnotationsMessage
+ )
+
+ if (injectConstructor.hasAnnotation(ClassNames.ASSISTED_INJECT)) {
+ // If "enableAssistedInjectViewModels" is not enabled we'll get error:
+ // "ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
+
+ ProcessorErrors.checkState(
+ assistedFactoryType.asTypeName() != XTypeName.ANY_OBJECT,
+ viewModelElement,
+ "%s must have a valid assisted factory specified in @HiltViewModel when used with assisted injection. Found %s.",
+ XElements.toStableString(viewModelElement),
+ XTypes.toStableString(assistedFactoryType)
+ )
+ } else {
+ ProcessorErrors.checkState(
+ assistedFactoryType.asTypeName() == XTypeName.ANY_OBJECT,
+ injectConstructor,
+ "Found assisted factory %s in @HiltViewModel but the constructor was annotated with @Inject instead of @AssistedInject.",
+ XTypes.toStableString(assistedFactoryType),
+ )
+ }
+
ProcessorErrors.checkState(
- !typeElement.isNested() || typeElement.isStatic(),
- typeElement,
+ !viewModelElement.isNested() || viewModelElement.isStatic(),
+ viewModelElement,
"@HiltViewModel may only be used on inner classes if they are static."
)
- Processors.getScopeAnnotations(typeElement).let { scopeAnnotations ->
+ Processors.getScopeAnnotations(viewModelElement).let { scopeAnnotations ->
ProcessorErrors.checkState(
scopeAnnotations.isEmpty(),
- typeElement,
+ viewModelElement,
"@HiltViewModel classes should not be scoped. Found: %s",
scopeAnnotations.joinToString { XAnnotations.toStableString(it) }
)
}
- return ViewModelMetadata(typeElement)
+ return ViewModelMetadata(viewModelElement, assistedFactory)
}
}
}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt
index 29f8c3ce5..6a156fa79 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt
@@ -16,6 +16,7 @@
package dagger.hilt.android.processor.internal.viewmodel
+import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.ExperimentalProcessingApi
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.addOriginatingElement
@@ -23,6 +24,7 @@ import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import dagger.hilt.android.processor.internal.AndroidClassNames
import dagger.hilt.processor.internal.ClassNames
@@ -40,18 +42,20 @@ import javax.lang.model.element.Modifier
* public static abstract class BindsModule {
* @Binds
* @IntoMap
- * @StringKey("pkg.$")
+ * @LazyClassKey(pkg.$)
* @HiltViewModelMap
* public abstract ViewModel bind($ vm)
* }
* @Module
* @InstallIn(ActivityRetainedComponent.class)
* public static final class KeyModule {
+ * private static String className = "pkg.$";
* @Provides
- * @IntoSet
+ * @IntoMap
* @HiltViewModelMap.KeySet
- * public static String provide() {
- * return "pkg.$";
+ * @LazyClassKey(pkg.$)
+ * public static boolean provide() {
+ * return true;
* }
* }
* }
@@ -60,20 +64,20 @@ import javax.lang.model.element.Modifier
@OptIn(ExperimentalProcessingApi::class)
internal class ViewModelModuleGenerator(
private val processingEnv: XProcessingEnv,
- private val injectedViewModel: ViewModelMetadata
+ private val viewModelMetadata: ViewModelMetadata,
) {
fun generate() {
val modulesTypeSpec =
- TypeSpec.classBuilder(injectedViewModel.modulesClassName)
+ TypeSpec.classBuilder(viewModelMetadata.modulesClassName)
.apply {
- addOriginatingElement(injectedViewModel.typeElement)
+ addOriginatingElement(viewModelMetadata.viewModelElement)
Processors.addGeneratedAnnotation(this, processingEnv, ViewModelProcessor::class.java)
addAnnotation(
AnnotationSpec.builder(ClassNames.ORIGINATING_ELEMENT)
.addMember(
"topLevelClass",
"$T.class",
- injectedViewModel.className.topLevelClassName()
+ viewModelMetadata.className.topLevelClassName(),
)
.build()
)
@@ -85,18 +89,24 @@ internal class ViewModelModuleGenerator(
.build()
processingEnv.filer.write(
- JavaFile.builder(injectedViewModel.modulesClassName.packageName(), modulesTypeSpec).build()
+ JavaFile.builder(viewModelMetadata.modulesClassName.packageName(), modulesTypeSpec).build()
)
}
private fun getBindsModuleTypeSpec() =
createModuleTypeSpec(
className = "BindsModule",
- component = AndroidClassNames.VIEW_MODEL_COMPONENT
+ component = AndroidClassNames.VIEW_MODEL_COMPONENT,
)
.addModifiers(Modifier.ABSTRACT)
.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build())
- .addMethod(getViewModelBindsMethod())
+ .addMethod(
+ if (viewModelMetadata.assistedFactory.asClassName() != XTypeName.ANY_OBJECT) {
+ getAssistedViewModelBindsMethod()
+ } else {
+ getViewModelBindsMethod()
+ }
+ )
.build()
private fun getViewModelBindsMethod() =
@@ -104,20 +114,20 @@ internal class ViewModelModuleGenerator(
.addAnnotation(ClassNames.BINDS)
.addAnnotation(ClassNames.INTO_MAP)
.addAnnotation(
- AnnotationSpec.builder(ClassNames.STRING_KEY)
- .addMember("value", S, injectedViewModel.className.reflectionName())
+ AnnotationSpec.builder(ClassNames.LAZY_CLASS_KEY)
+ .addMember("value", "$T.class", viewModelMetadata.className)
.build()
)
.addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_MAP_QUALIFIER)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(AndroidClassNames.VIEW_MODEL)
- .addParameter(injectedViewModel.className, "vm")
+ .addParameter(viewModelMetadata.className, "vm")
.build()
private fun getKeyModuleTypeSpec() =
createModuleTypeSpec(
className = "KeyModule",
- component = AndroidClassNames.ACTIVITY_RETAINED_COMPONENT
+ component = AndroidClassNames.ACTIVITY_RETAINED_COMPONENT,
)
.addModifiers(Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build())
@@ -127,16 +137,49 @@ internal class ViewModelModuleGenerator(
private fun getViewModelKeyProvidesMethod() =
MethodSpec.methodBuilder("provide")
.addAnnotation(ClassNames.PROVIDES)
- .addAnnotation(ClassNames.INTO_SET)
+ .addAnnotation(ClassNames.INTO_MAP)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.LAZY_CLASS_KEY)
+ .addMember("value", "$T.class", viewModelMetadata.className)
+ .build()
+ )
.addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_KEYS_QUALIFIER)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
- .returns(String::class.java)
- .addStatement("return $S", injectedViewModel.className.reflectionName())
+ .returns(Boolean::class.java)
+ .addStatement("return true")
+ .build()
+
+ /**
+ * Should generate:
+ * ```
+ * @Binds
+ * @IntoMap
+ * @LazyClassKey(pkg.FooViewModel.class)
+ * @HiltViewModelAssistedMap
+ * public abstract Object bind(FooViewModelAssistedFactory factory);
+ * ```
+ *
+ * So that we have a HiltViewModelAssistedMap that maps from fully qualified ViewModel names to
+ * its assisted factory instance.
+ */
+ private fun getAssistedViewModelBindsMethod() =
+ MethodSpec.methodBuilder("bind")
+ .addAnnotation(ClassNames.BINDS)
+ .addAnnotation(ClassNames.INTO_MAP)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.LAZY_CLASS_KEY)
+ .addMember("value", "$T.class", viewModelMetadata.className)
+ .build()
+ )
+ .addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_ASSISTED_FACTORY_MAP_QUALIFIER)
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .addParameter(viewModelMetadata.assistedFactoryClassName, "factory")
+ .returns(TypeName.OBJECT)
.build()
private fun createModuleTypeSpec(className: String, component: ClassName) =
TypeSpec.classBuilder(className)
- .addOriginatingElement(injectedViewModel.typeElement)
+ .addOriginatingElement(viewModelMetadata.viewModelElement)
.addAnnotation(ClassNames.MODULE)
.addAnnotation(
AnnotationSpec.builder(ClassNames.INSTALL_IN)
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessingStep.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessingStep.kt
index 69f7ac284..55cb289fc 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessingStep.kt
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessingStep.kt
@@ -28,16 +28,13 @@ import dagger.internal.codegen.xprocessing.XElements
@OptIn(ExperimentalProcessingApi::class)
/** Annotation processor for @ViewModelInject. */
class ViewModelProcessingStep(env: XProcessingEnv) : BaseProcessingStep(env) {
+
override fun annotationClassNames() = ImmutableSet.of(AndroidClassNames.HILT_VIEW_MODEL)
override fun processEach(annotation: ClassName, element: XElement) {
val typeElement = XElements.asTypeElement(element)
- ViewModelMetadata.create(
- processingEnv(),
- typeElement,
- )
- ?.let { viewModelMetadata ->
- ViewModelModuleGenerator(processingEnv(), viewModelMetadata).generate()
- }
+ ViewModelMetadata.create(processingEnv(), typeElement)?.let { viewModelMetadata ->
+ ViewModelModuleGenerator(processingEnv(), viewModelMetadata).generate()
+ }
}
}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt
index d5c3666b3..69094c9a5 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt
@@ -14,27 +14,45 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalProcessingApi::class)
+
package dagger.hilt.android.processor.internal.viewmodel
+import androidx.room.compiler.processing.ExperimentalProcessingApi
+import androidx.room.compiler.processing.XMethodElement
+import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.XProcessingEnv.Companion.create
+import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.XTypeElement
+import androidx.room.compiler.processing.compat.XConverters.toXProcessing
import com.google.auto.service.AutoService
import com.google.common.graph.EndpointPair
import com.google.common.graph.ImmutableNetwork
import dagger.hilt.android.processor.internal.AndroidClassNames
-import dagger.hilt.processor.internal.asElement
import dagger.hilt.processor.internal.getQualifiedName
import dagger.hilt.processor.internal.hasAnnotation
+import dagger.internal.codegen.xprocessing.XTypeElements
import dagger.spi.model.Binding
import dagger.spi.model.BindingGraph
import dagger.spi.model.BindingGraph.Edge
import dagger.spi.model.BindingGraph.Node
import dagger.spi.model.BindingGraphPlugin
import dagger.spi.model.BindingKind
+import dagger.spi.model.DaggerProcessingEnv
+import dagger.spi.model.DaggerType
import dagger.spi.model.DiagnosticReporter
import javax.tools.Diagnostic.Kind
/** Plugin to validate users do not inject @HiltViewModel classes. */
@AutoService(BindingGraphPlugin::class)
class ViewModelValidationPlugin : BindingGraphPlugin {
+
+ private lateinit var env: XProcessingEnv
+
+ override fun init(processingEnv: DaggerProcessingEnv, options: MutableMap<String, String>) {
+ env = processingEnv.toXProcessingEnv()
+ }
+
override fun visitGraph(bindingGraph: BindingGraph, diagnosticReporter: DiagnosticReporter) {
if (bindingGraph.rootComponentNode().isSubcomponent()) {
// This check does not work with partial graphs since it needs to take into account the source
@@ -47,10 +65,10 @@ class ViewModelValidationPlugin : BindingGraphPlugin {
val pair: EndpointPair<Node> = network.incidentNodes(edge)
val target: Node = pair.target()
val source: Node = pair.source()
- if (
- target is Binding &&
- isHiltViewModelBinding(target) && !isInternalHiltViewModelUsage(source)
- ) {
+ if (target !is Binding) {
+ return@forEach
+ }
+ if (isHiltViewModelBinding(target) && !isInternalHiltViewModelUsage(source)) {
diagnosticReporter.reportDependency(
Kind.ERROR,
edge,
@@ -59,6 +77,17 @@ class ViewModelValidationPlugin : BindingGraphPlugin {
"(e.g. ViewModelProvider) instead." +
"\nInjected ViewModel: ${target.key().type()}\n"
)
+ } else if (
+ isViewModelAssistedFactory(target) && !isInternalViewModelAssistedFactoryUsage(source)
+ ) {
+ diagnosticReporter.reportDependency(
+ Kind.ERROR,
+ edge,
+ "\nInjection of an assisted factory for Hilt ViewModel is prohibited since it " +
+ "can not be used to create a ViewModel instance correctly.\nAccess the ViewModel via " +
+ "the Android APIs (e.g. ViewModelProvider) instead." +
+ "\nInjected factory: ${target.key().type()}\n"
+ )
}
}
}
@@ -67,7 +96,7 @@ class ViewModelValidationPlugin : BindingGraphPlugin {
// Make sure this is from an @Inject constructor rather than an overridden binding like an
// @Provides and that the class is annotated with @HiltViewModel.
return target.kind() == BindingKind.INJECTION &&
- target.key().type().asElement().hasAnnotation(AndroidClassNames.HILT_VIEW_MODEL)
+ target.key().type().hasAnnotation(AndroidClassNames.HILT_VIEW_MODEL)
}
private fun isInternalHiltViewModelUsage(source: Node): Boolean {
@@ -86,4 +115,58 @@ class ViewModelValidationPlugin : BindingGraphPlugin {
AndroidClassNames.HILT_VIEW_MODEL_MAP_QUALIFIER.canonicalName() &&
source.key().multibindingContributionIdentifier().isPresent()
}
+
+ private fun isViewModelAssistedFactory(target: Binding): Boolean {
+ if (target.kind() != BindingKind.ASSISTED_FACTORY) return false
+ val factoryType = target.key().type()
+ return getAssistedInjectTypeElement(factoryType.toXType(env))
+ .hasAnnotation(AndroidClassNames.HILT_VIEW_MODEL)
+ }
+
+ private fun getAssistedInjectTypeElement(factoryType: XType): XTypeElement =
+ // The factory method and the type element for its return type cannot be
+ // null as the BindingGraph won't be created if the
+ // @AssistedFactory-annotated class is invalid.
+ getAssistedFactoryMethods(factoryType.typeElement)
+ .single()
+ .asMemberOf(factoryType)
+ .returnType
+ .typeElement!!
+
+ private fun getAssistedFactoryMethods(factory: XTypeElement?): List<XMethodElement> {
+ return XTypeElements.getAllNonPrivateInstanceMethods(factory)
+ .filter { it.isAbstract() }
+ .filter { !it.isJavaDefault() }
+ }
+
+ private fun isInternalViewModelAssistedFactoryUsage(source: Node): Boolean {
+ // We expect the only usage of the assisted factory for a Hilt ViewModel is in the
+ // code we generate:
+ // @Binds
+ // @IntoMap
+ // @StringKey(...)
+ // @HiltViewModelAssistedMap
+ // public abstract Object bind(FooFactory factory);
+ return source is Binding &&
+ source.key().qualifier().isPresent() &&
+ source.key().qualifier().get().getQualifiedName() ==
+ AndroidClassNames.HILT_VIEW_MODEL_ASSISTED_FACTORY_MAP_QUALIFIER.canonicalName() &&
+ source.key().multibindingContributionIdentifier().isPresent()
+ }
+}
+
+private fun DaggerType.toXType(processingEnv: XProcessingEnv): XType {
+ return when (backend()) {
+ DaggerProcessingEnv.Backend.JAVAC -> javac().toXProcessing(processingEnv)
+ DaggerProcessingEnv.Backend.KSP -> ksp().toXProcessing(processingEnv)
+ else -> error("Backend ${ backend() } not supported yet.")
+ }
+}
+
+private fun DaggerProcessingEnv.toXProcessingEnv(): XProcessingEnv {
+ return when (backend()) {
+ DaggerProcessingEnv.Backend.JAVAC -> create(javac())
+ DaggerProcessingEnv.Backend.KSP -> create(ksp(), resolver())
+ else -> error("Backend ${ backend() } not supported yet.")
+ }
}
diff --git a/java/dagger/hilt/android/testing/BUILD b/java/dagger/hilt/android/testing/BUILD
index 6c3c6e564..ee3d78ab1 100644
--- a/java/dagger/hilt/android/testing/BUILD
+++ b/java/dagger/hilt/android/testing/BUILD
@@ -168,6 +168,15 @@ android_library(
],
)
+android_library(
+ name = "skip_test_injection",
+ testonly = 1,
+ srcs = ["SkipTestInjection.java"],
+ deps = [
+ ":package_info",
+ ],
+)
+
java_library(
name = "package_info",
srcs = ["package-info.java"],
@@ -220,6 +229,7 @@ gen_maven_artifact(
artifact_target_maven_deps = [
"androidx.activity:activity",
"androidx.annotation:annotation",
+ "androidx.annotation:annotation-experimental",
"androidx.fragment:fragment",
"androidx.lifecycle:lifecycle-common",
"androidx.lifecycle:lifecycle-viewmodel",
diff --git a/java/dagger/hilt/android/testing/SkipTestInjection.java b/java/dagger/hilt/android/testing/SkipTestInjection.java
new file mode 100644
index 000000000..f8e015ba0
--- /dev/null
+++ b/java/dagger/hilt/android/testing/SkipTestInjection.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used for skipping test injection in a Hilt Android Test. This may be useful if
+ * building separate custom test infrastructure to inject the test class from another Hilt
+ * component. This may be used on either the test class or an annotation that annotates
+ * the test class.
+ */
+@Retention(CLASS)
+@Target({ElementType.TYPE})
+public @interface SkipTestInjection {}
diff --git a/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java b/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
index 8f2abd39d..89bf67882 100644
--- a/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
+++ b/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
@@ -117,7 +117,7 @@ public final class HiltCompilerTests {
.collect(toMap((Processor e) -> e.getClass(), (Processor e) -> e));
// Adds extra processors, and allows overriding any processors of the same class.
- extraProcessors.stream().forEach(processor -> processors.put(processor.getClass(), processor));
+ extraProcessors.forEach(processor -> processors.put(processor.getClass(), processor));
return CompilerTests.compiler().withProcessors(processors.values());
}
@@ -191,9 +191,9 @@ public final class HiltCompilerTests {
private static ImmutableList<SymbolProcessorProvider> kspDefaultProcessors() {
// TODO(bcorso): Add the rest of the KSP processors here.
return ImmutableList.of(
- new KspAndroidEntryPointProcessor.Provider(),
- new KspAliasOfProcessor.Provider(),
new KspAggregatedDepsProcessor.Provider(),
+ new KspAliasOfProcessor.Provider(),
+ new KspAndroidEntryPointProcessor.Provider(),
new KspComponentProcessor.Provider(),
new KspComponentTreeDepsProcessor.Provider(),
new KspCustomTestApplicationProcessor.Provider(),
@@ -279,16 +279,14 @@ public final class HiltCompilerTests {
"-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"),
/* config= */ HiltProcessingEnvConfigs.CONFIGS,
/* javacProcessors= */ ImmutableList.<Processor>builder()
- .addAll(defaultProcessors())
- .addAll(additionalJavacProcessors())
+ .addAll(mergeProcessors(defaultProcessors(), additionalJavacProcessors()))
.addAll(
processingSteps().stream()
.map(HiltCompilerProcessors.JavacProcessor::new)
.collect(toImmutableList()))
.build(),
/* symbolProcessorProviders= */ ImmutableList.<SymbolProcessorProvider>builder()
- .addAll(kspDefaultProcessors())
- .addAll(additionalKspProcessors())
+ .addAll(mergeProcessors(kspDefaultProcessors(), additionalKspProcessors()))
.addAll(
processingSteps().stream()
.map(HiltCompilerProcessors.KspProcessor.Provider::new)
@@ -300,6 +298,15 @@ public final class HiltCompilerTests {
});
}
+ private static <T> ImmutableList<T> mergeProcessors(
+ Collection<T> defaultProcessors, Collection<T> extraProcessors) {
+ Map<Class<?>, T> processors =
+ defaultProcessors.stream().collect(toMap((T e) -> e.getClass(), (T e) -> e));
+ // Adds extra processors, and allows overriding any processors of the same class.
+ extraProcessors.forEach(processor -> processors.put(processor.getClass(), processor));
+ return ImmutableList.copyOf(processors.values());
+ }
+
/** Used to build a {@link HiltCompiler}. */
@AutoValue.Builder
public abstract static class Builder {
diff --git a/java/dagger/hilt/migration/BUILD b/java/dagger/hilt/migration/BUILD
index 446e7a2ea..27f472a18 100644
--- a/java/dagger/hilt/migration/BUILD
+++ b/java/dagger/hilt/migration/BUILD
@@ -18,9 +18,7 @@ package(default_visibility = ["//:src"])
java_library(
name = "alias_of",
- srcs = [
- "AliasOf.java",
- ],
+ srcs = ["AliasOf.java"],
exported_plugins = [
"//java/dagger/hilt/processor/internal/aliasof:processor",
],
diff --git a/java/dagger/hilt/processor/BUILD b/java/dagger/hilt/processor/BUILD
index ba7c9eee4..ecbc29495 100644
--- a/java/dagger/hilt/processor/BUILD
+++ b/java/dagger/hilt/processor/BUILD
@@ -74,6 +74,7 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal:component_names",
"//java/dagger/hilt/processor/internal:components",
"//java/dagger/hilt/processor/internal:hilt_processing_env_configs",
+ "//java/dagger/hilt/processor/internal:method_signature",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
diff --git a/java/dagger/hilt/processor/internal/BUILD b/java/dagger/hilt/processor/internal/BUILD
index a2746baf6..8828227aa 100644
--- a/java/dagger/hilt/processor/internal/BUILD
+++ b/java/dagger/hilt/processor/internal/BUILD
@@ -57,9 +57,7 @@ java_library(
"ProcessorErrors.java",
],
deps = [
- "//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/xprocessing",
- "//third_party/java/auto:common",
"//third_party/java/error_prone:annotations",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
@@ -87,6 +85,20 @@ java_library(
)
java_library(
+ name = "method_signature",
+ srcs = [
+ "MethodSignature.java",
+ ],
+ deps = [
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/xprocessing",
+ "//third_party/java/auto:value",
+ "//third_party/java/guava/collect",
+ "//third_party/java/javapoet",
+ ],
+)
+
+java_library(
name = "classnames",
srcs = [
"ClassNames.java",
diff --git a/java/dagger/hilt/processor/internal/ClassNames.java b/java/dagger/hilt/processor/internal/ClassNames.java
index ec2e73ed4..00de786f0 100644
--- a/java/dagger/hilt/processor/internal/ClassNames.java
+++ b/java/dagger/hilt/processor/internal/ClassNames.java
@@ -72,7 +72,11 @@ public final class ClassNames {
public static final ClassName DEFINE_COMPONENT_CLASSES =
get("dagger.hilt.internal.definecomponent", "DefineComponentClasses");
+ public static final ClassName IDENTIFIER_NAME_STRING =
+ get("dagger.internal", "IdentifierNameString");
+
public static final ClassName ASSISTED_INJECT = get("dagger.assisted", "AssistedInject");
+ public static final ClassName ASSISTED_FACTORY = get("dagger.assisted", "AssistedFactory");
public static final ClassName BINDS =
get("dagger", "Binds");
public static final ClassName BINDS_OPTIONAL_OF =
@@ -85,6 +89,7 @@ public final class ClassNames {
public static final ClassName INTO_SET = get("dagger.multibindings", "IntoSet");
public static final ClassName ELEMENTS_INTO_SET = get("dagger.multibindings", "ElementsIntoSet");
public static final ClassName STRING_KEY = get("dagger.multibindings", "StringKey");
+ public static final ClassName LAZY_CLASS_KEY = get("dagger.multibindings", "LazyClassKey");
public static final ClassName PROVIDES =
get("dagger", "Provides");
public static final ClassName COMPONENT = get("dagger", "Component");
@@ -169,6 +174,8 @@ public final class ClassNames {
get("dagger.hilt.android.internal.testing", "TestInstanceHolder");
public static final ClassName HILT_ANDROID_TEST =
get("dagger.hilt.android.testing", "HiltAndroidTest");
+ public static final ClassName SKIP_TEST_INJECTION =
+ get("dagger.hilt.android.testing", "SkipTestInjection");
public static final ClassName CUSTOM_TEST_APPLICATION =
get("dagger.hilt.android.testing", "CustomTestApplication");
public static final ClassName ON_COMPONENT_READY_RUNNER =
@@ -214,6 +221,9 @@ public final class ClassNames {
public static final ClassName SUPPRESS_WARNINGS = get("java.lang", "SuppressWarnings");
public static final ClassName KOTLIN_SUPPRESS = get("kotlin", "Suppress");
+ public static final ClassName ON_RECEIVE_BYTECODE_INJECTION_MARKER =
+ get("dagger.hilt.android.internal", "OnReceiveBytecodeInjectionMarker");
+
// Kotlin-specific class names
public static final ClassName KOTLIN_METADATA = get("kotlin", "Metadata");
diff --git a/java/dagger/hilt/processor/internal/DaggerModels.kt b/java/dagger/hilt/processor/internal/DaggerModels.kt
index 7d64d2396..9fe0cf4f7 100644
--- a/java/dagger/hilt/processor/internal/DaggerModels.kt
+++ b/java/dagger/hilt/processor/internal/DaggerModels.kt
@@ -18,47 +18,37 @@ package dagger.hilt.processor.internal
import com.google.auto.common.MoreTypes
import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSDeclaration
import com.squareup.javapoet.ClassName
import dagger.spi.model.DaggerAnnotation
import dagger.spi.model.DaggerElement
import dagger.spi.model.DaggerProcessingEnv
+import dagger.spi.model.DaggerProcessingEnv.Backend.JAVAC
+import dagger.spi.model.DaggerProcessingEnv.Backend.KSP
import dagger.spi.model.DaggerType
-fun DaggerType.asElement(): DaggerElement =
+fun DaggerType.hasAnnotation(className: ClassName): Boolean =
when (checkNotNull(backend())) {
- DaggerProcessingEnv.Backend.JAVAC -> {
- val javaType = checkNotNull(java())
- DaggerElement.fromJavac(MoreTypes.asElement(javaType))
- }
- DaggerProcessingEnv.Backend.KSP -> {
- val kspType = checkNotNull(ksp())
- DaggerElement.fromKsp(kspType.declaration)
- }
+ JAVAC -> Processors.hasAnnotation(MoreTypes.asTypeElement(javac()), className)
+ KSP -> ksp().declaration.hasAnnotation(className.canonicalName())
+ }
+
+fun KSDeclaration.hasAnnotation(annotationName: String): Boolean =
+ annotations.any {
+ it.annotationType.resolve().declaration.qualifiedName?.asString().equals(annotationName)
}
fun DaggerElement.hasAnnotation(className: ClassName) =
when (checkNotNull(backend())) {
- DaggerProcessingEnv.Backend.JAVAC -> {
- val javaElement = checkNotNull(java())
- Processors.hasAnnotation(javaElement, className)
- }
- DaggerProcessingEnv.Backend.KSP -> {
- val kspAnnotated = checkNotNull(ksp())
- kspAnnotated.hasAnnotation(className)
- }
+ JAVAC -> Processors.hasAnnotation(javac(), className)
+ KSP -> ksp().hasAnnotation(className)
}
fun DaggerAnnotation.getQualifiedName() =
when (checkNotNull(backend())) {
- DaggerProcessingEnv.Backend.JAVAC -> {
- val javaAnnotation = checkNotNull(java())
- MoreTypes.asTypeElement(javaAnnotation.annotationType).qualifiedName.toString()
- }
- DaggerProcessingEnv.Backend.KSP -> {
- val kspAnnotation = checkNotNull(ksp())
- kspAnnotation.annotationType.resolve().declaration.qualifiedName!!.asString()
- }
+ JAVAC -> MoreTypes.asTypeElement(javac().annotationType).qualifiedName.toString()
+ KSP -> ksp().annotationType.resolve().declaration.qualifiedName!!.asString()
}
private fun KSAnnotated.hasAnnotation(className: ClassName) =
diff --git a/java/dagger/hilt/processor/internal/HiltCompilerOptions.java b/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
index cdb2d6c98..90575599b 100644
--- a/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
+++ b/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
@@ -21,6 +21,7 @@ import static com.google.common.base.Ascii.toUpperCase;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.compat.XConverters;
import com.google.common.collect.ImmutableSet;
import dagger.hilt.processor.internal.optionvalues.BooleanValue;
import dagger.hilt.processor.internal.optionvalues.GradleProjectType;
@@ -101,6 +102,13 @@ public final class HiltCompilerOptions {
return GRADLE_PROJECT_TYPE.get(env);
}
+ public static boolean isAssistedInjectViewModelsEnabled(XTypeElement viewModelElement) {
+ boolean enabled =
+ ENABLE_ASSISTED_INJECT_VIEWMODELS.get(XConverters.getProcessingEnv(viewModelElement))
+ == BooleanValue.TRUE;
+ return enabled;
+ }
+
/** Do not use! This is for internal use only. */
private static final EnumOption<BooleanValue> DISABLE_ANDROID_SUPERCLASS_VALIDATION =
new EnumOption<>("android.internal.disableAndroidSuperclassValidation", BooleanValue.FALSE);
@@ -124,6 +132,9 @@ public final class HiltCompilerOptions {
private static final EnumOption<GradleProjectType> GRADLE_PROJECT_TYPE =
new EnumOption<>("android.internal.projectType", GradleProjectType.UNSET);
+ private static final EnumOption<BooleanValue> ENABLE_ASSISTED_INJECT_VIEWMODELS =
+ new EnumOption<>(
+ "enableAssistedInjectViewModels", BooleanValue.TRUE );
private static final ImmutableSet<String> DEPRECATED_OPTIONS =
ImmutableSet.of("dagger.hilt.android.useFragmentGetContextFix");
diff --git a/java/dagger/hilt/processor/internal/MethodSignature.java b/java/dagger/hilt/processor/internal/MethodSignature.java
new file mode 100644
index 000000000..7fffef2bb
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/MethodSignature.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static java.util.stream.Collectors.joining;
+
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.xprocessing.XElements;
+
+/** Represents the method signature needed to uniquely identify a method. */
+@AutoValue
+public abstract class MethodSignature {
+ MethodSignature() {}
+
+ abstract String name();
+
+ abstract ImmutableList<TypeName> parameters();
+
+ /** Creates a {@link MethodSignature} from a method name and parameter {@link TypeNames} */
+ public static MethodSignature of(String methodName, TypeName... typeNames) {
+ return new AutoValue_MethodSignature(methodName, ImmutableList.copyOf(typeNames));
+ }
+
+ /** Creates a {@link MethodSignature} from a {@link MethodSpec} */
+ public static MethodSignature of(MethodSpec method) {
+ return new AutoValue_MethodSignature(
+ method.name, method.parameters.stream().map(p -> p.type).collect(toImmutableList()));
+ }
+
+ /** Creates a {@link MethodSignature} from an {@link XExecutableElement} */
+ public static MethodSignature of(XExecutableElement executableElement) {
+ return new AutoValue_MethodSignature(
+ XElements.getSimpleName(executableElement),
+ executableElement.getParameters().stream()
+ .map(p -> p.getType().getTypeName())
+ .collect(toImmutableList()));
+ }
+
+ /**
+ * Creates a {@link MethodSignature} from an {@link XMethodElement}.
+ *
+ * <p>This version will resolve type parameters as declared by {@code enclosing}.
+ */
+ static MethodSignature ofDeclaredType(XMethodElement method, XType enclosing) {
+ XMethodType executableType = method.asMemberOf(enclosing);
+ return new AutoValue_MethodSignature(
+ XElements.getSimpleName(method),
+ executableType.getParameterTypes().stream()
+ .map(XType::getTypeName)
+ .collect(toImmutableList()));
+ }
+
+ /** Returns a string in the format: METHOD_NAME(PARAM_TYPE1,PARAM_TYEP2,...) */
+ @Override
+ public final String toString() {
+ return String.format(
+ "%s(%s)", name(), parameters().stream().map(Object::toString).collect(joining(",")));
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/kotlin/KotlinMetadataUtil.java b/java/dagger/hilt/processor/internal/kotlin/KotlinMetadataUtil.java
index 771a4b8a5..915a47de7 100644
--- a/java/dagger/hilt/processor/internal/kotlin/KotlinMetadataUtil.java
+++ b/java/dagger/hilt/processor/internal/kotlin/KotlinMetadataUtil.java
@@ -34,7 +34,6 @@ import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
-import java.util.Optional;
import javax.inject.Inject;
/** Utility class for interacting with Kotlin Metadata. */
@@ -133,10 +132,6 @@ public final class KotlinMetadataUtil {
: ImmutableList.of();
}
- public Optional<XMethodElement> getPropertyGetter(XFieldElement fieldElement) {
- return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
- }
-
public boolean containsConstructorWithDefaultParam(XTypeElement typeElement) {
return hasMetadata(typeElement)
&& metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
diff --git a/java/dagger/hilt/processor/internal/root/RootGenerator.java b/java/dagger/hilt/processor/internal/root/RootGenerator.java
index 2868f7c8f..f43327200 100644
--- a/java/dagger/hilt/processor/internal/root/RootGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/RootGenerator.java
@@ -23,9 +23,7 @@ import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import androidx.room.compiler.processing.JavaPoetExtKt;
-import androidx.room.compiler.processing.XFiler.Mode;
import androidx.room.compiler.processing.XProcessingEnv;
-import androidx.room.compiler.processing.XProcessingEnv.Backend;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -132,12 +130,7 @@ final class RootGenerator {
JavaFile componentsWrapperJavaFile =
JavaFile.builder(componentsWrapperClassName.packageName(), componentsWrapper.build())
.build();
- // TODO(danysantiago): Support formatting in KSP: b/288572563
- if (env.getBackend() == Backend.KSP) {
- env.getFiler().write(componentsWrapperJavaFile, Mode.Isolating);
- } else {
- RootFileFormatter.write(componentsWrapperJavaFile, env);
- }
+ RootFileFormatter.write(componentsWrapperJavaFile, env);
}
private static ComponentTree filterDescriptors(ComponentTree componentTree) {
diff --git a/java/dagger/hilt/processor/internal/root/RootMetadata.java b/java/dagger/hilt/processor/internal/root/RootMetadata.java
index 8166d668e..55f6e9c4e 100644
--- a/java/dagger/hilt/processor/internal/root/RootMetadata.java
+++ b/java/dagger/hilt/processor/internal/root/RootMetadata.java
@@ -21,7 +21,6 @@ import static com.google.common.base.Suppliers.memoize;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import androidx.room.compiler.processing.XConstructorElement;
-import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.base.Supplier;
@@ -40,6 +39,7 @@ import javax.tools.Diagnostic;
/** Contains metadata about the given hilt root. */
public final class RootMetadata {
+
private static final ClassName APPLICATION_CONTEXT_MODULE =
ClassName.get("dagger.hilt.android.internal.modules", "ApplicationContextModule");
@@ -195,33 +195,10 @@ public final class RootMetadata {
}
private static boolean daggerCanConstruct(XTypeElement type) {
- if (type.isKotlinObject()) {
- // Treat Kotlin object modules as if Dagger can construct them (it technically can't, but it
- // doesn't need to as it can use them since all their provision methods are static).
+ if (!Processors.requiresModuleInstance(type)) {
return true;
}
-
- return !isInnerClass(type)
- && !hasNonDaggerAbstractMethod(type)
- && (hasOnlyStaticProvides(type) || hasVisibleEmptyConstructor(type));
- }
-
- private static boolean isInnerClass(XTypeElement type) {
- return type.isNested() && !type.isStatic();
- }
-
- private static boolean hasNonDaggerAbstractMethod(XTypeElement type) {
- // TODO(erichang): Actually this isn't really supported b/28989613
- return type.getDeclaredMethods().stream()
- .filter(XMethodElement::isAbstract)
- .anyMatch(method -> !Processors.hasDaggerAbstractMethodAnnotation(method));
- }
-
- private static boolean hasOnlyStaticProvides(XTypeElement type) {
- // TODO(erichang): Check for @Produces too when we have a producers story
- return type.getDeclaredMethods().stream()
- .filter(method -> method.hasAnnotation(ClassNames.PROVIDES))
- .allMatch(XMethodElement::isStatic);
+ return hasVisibleEmptyConstructor(type) && (!type.isNested() || type.isStatic());
}
private static boolean hasVisibleEmptyConstructor(XTypeElement type) {
diff --git a/java/dagger/hilt/processor/internal/root/RootProcessingStep.java b/java/dagger/hilt/processor/internal/root/RootProcessingStep.java
index f281995d9..d6de432bc 100644
--- a/java/dagger/hilt/processor/internal/root/RootProcessingStep.java
+++ b/java/dagger/hilt/processor/internal/root/RootProcessingStep.java
@@ -57,6 +57,9 @@ import java.util.Set;
public final class RootProcessingStep extends BaseProcessingStep {
private boolean processed;
+ // TODO(b/297889547) do not run preProcess and postProcess if supported annotation isn't present
+ // in the environment.
+ private boolean hasElementsToProcess = false;
private GeneratesRootInputs generatesRootInputs;
public RootProcessingStep(XProcessingEnv env) {
@@ -75,13 +78,16 @@ public final class RootProcessingStep extends BaseProcessingStep {
@Override
public void processEach(ClassName annotation, XElement element) throws Exception {
+ hasElementsToProcess = true;
XTypeElement rootElement = XElements.asTypeElement(element);
// TODO(bcorso): Move this logic into a separate isolating processor to avoid regenerating it
// for unrelated changes in Gradle.
RootType rootType = RootType.of(rootElement);
if (rootType.isTestRoot()) {
- new TestInjectorGenerator(processingEnv(), TestRootMetadata.of(processingEnv(), rootElement))
- .generate();
+ TestRootMetadata testRootMetadata = TestRootMetadata.of(processingEnv(), rootElement);
+ if (testRootMetadata.skipTestInjectionAnnotation().isEmpty()) {
+ new TestInjectorGenerator(processingEnv(), testRootMetadata).generate();
+ }
}
XTypeElement originatingRootElement =
@@ -93,6 +99,9 @@ public final class RootProcessingStep extends BaseProcessingStep {
@Override
protected void postProcess(XProcessingEnv env, XRoundEnv roundEnv) throws Exception {
+ if (!hasElementsToProcess) {
+ return;
+ }
if (!useAggregatingRootProcessor(processingEnv())) {
return;
}
diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
index 689dae7dc..bacb75b87 100644
--- a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
@@ -25,6 +25,7 @@ import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import androidx.room.compiler.processing.JavaPoetExtKt;
+import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XConstructorElement;
import androidx.room.compiler.processing.XFiler.Mode;
import androidx.room.compiler.processing.XProcessingEnv;
@@ -41,6 +42,7 @@ import dagger.hilt.processor.internal.ComponentNames;
import dagger.hilt.processor.internal.Processors;
import java.io.IOException;
import java.util.List;
+import java.util.Optional;
/** Generates an implementation of {@link dagger.hilt.android.internal.TestComponentData}. */
public final class TestComponentDataGenerator {
@@ -222,6 +224,13 @@ public final class TestComponentDataGenerator {
}
private CodeBlock callInjectTest(XTypeElement testElement) {
+ Optional<XAnnotation> skipTestInjection =
+ rootMetadata.testRootMetadata().skipTestInjectionAnnotation();
+ if (skipTestInjection.isPresent()) {
+ return CodeBlock.of(
+ "throw new IllegalStateException(\"Cannot inject test when using @$L\")",
+ skipTestInjection.get().getName());
+ }
return CodeBlock.of(
"(($T) (($T) $T.getApplication($T.getApplicationContext()))"
+ ".generatedComponent()).injectTest(testInstance)",
diff --git a/java/dagger/hilt/processor/internal/root/TestRootMetadata.java b/java/dagger/hilt/processor/internal/root/TestRootMetadata.java
index 5dc6feeaa..b0523034f 100644
--- a/java/dagger/hilt/processor/internal/root/TestRootMetadata.java
+++ b/java/dagger/hilt/processor/internal/root/TestRootMetadata.java
@@ -16,6 +16,7 @@
package dagger.hilt.processor.internal.root;
+import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
@@ -25,6 +26,8 @@ import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
import dagger.internal.codegen.xprocessing.XElements;
+import java.util.Optional;
+import java.util.Set;
import javax.lang.model.element.TypeElement;
/** Metadata class for {@code InternalTestRoot} annotated classes. */
@@ -57,6 +60,28 @@ abstract class TestRootMetadata {
return Processors.append(Processors.getEnclosedClassName(testName()), "_GeneratedInjector");
}
+ /**
+ * Returns either the SkipTestInjection annotation or the first annotation that was annotated
+ * with SkipTestInjection, if present.
+ */
+ Optional<XAnnotation> skipTestInjectionAnnotation() {
+ XAnnotation skipTestAnnotation = testElement().getAnnotation(ClassNames.SKIP_TEST_INJECTION);
+ if (skipTestAnnotation != null) {
+ return Optional.of(skipTestAnnotation);
+ }
+
+ Set<XAnnotation> annotatedAnnotations = testElement().getAnnotationsAnnotatedWith(
+ ClassNames.SKIP_TEST_INJECTION);
+ if (!annotatedAnnotations.isEmpty()) {
+ // Just return the first annotation that skips test injection if there are multiple since
+ // at this point it doesn't really matter and the specific annotation is only really useful
+ // for communicating back to the user.
+ return Optional.of(annotatedAnnotations.iterator().next());
+ }
+
+ return Optional.empty();
+ }
+
static TestRootMetadata of(XProcessingEnv env, XElement element) {
XTypeElement testElement = XElements.asTypeElement(element);
diff --git a/java/dagger/internal/AbstractMapFactory.java b/java/dagger/internal/AbstractMapFactory.java
index 1cf83fa1d..22512e9d3 100644
--- a/java/dagger/internal/AbstractMapFactory.java
+++ b/java/dagger/internal/AbstractMapFactory.java
@@ -22,7 +22,6 @@ import static java.util.Collections.unmodifiableMap;
import java.util.LinkedHashMap;
import java.util.Map;
-import javax.inject.Provider;
/**
* An {@code abstract} {@link Factory} implementation used to implement {@link Map} bindings.
diff --git a/java/dagger/internal/DelegateFactory.java b/java/dagger/internal/DelegateFactory.java
index 3b4a30f23..bc5cd9a29 100644
--- a/java/dagger/internal/DelegateFactory.java
+++ b/java/dagger/internal/DelegateFactory.java
@@ -17,12 +17,11 @@
package dagger.internal;
import static dagger.internal.Preconditions.checkNotNull;
-
-import javax.inject.Provider;
+import static dagger.internal.Providers.asDaggerProvider;
/**
* A DelegateFactory that is used to stitch Provider/Lazy indirection based dependency cycles.
- *
+ *
* @since 2.0.1
*/
public final class DelegateFactory<T> implements Factory<T> {
@@ -44,18 +43,43 @@ public final class DelegateFactory<T> implements Factory<T> {
}
/**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public void setDelegatedProvider(javax.inject.Provider<T> delegate) {
+ setDelegatedProvider(asDaggerProvider(delegate));
+ }
+
+ /**
* Sets {@code delegateFactory}'s delegate provider to {@code delegate}.
*
* <p>{@code delegateFactory} must be an instance of {@link DelegateFactory}, otherwise this
* method will throw a {@link ClassCastException}.
*/
public static <T> void setDelegate(Provider<T> delegateFactory, Provider<T> delegate) {
- checkNotNull(delegate);
DelegateFactory<T> asDelegateFactory = (DelegateFactory<T>) delegateFactory;
- if (asDelegateFactory.delegate != null) {
+ setDelegateInternal(asDelegateFactory, delegate);
+ }
+
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public static <T> void setDelegate(
+ javax.inject.Provider<T> delegateFactory, javax.inject.Provider<T> delegate) {
+ DelegateFactory<T> asDelegateFactory = (DelegateFactory<T>) delegateFactory;
+ setDelegateInternal(asDelegateFactory, asDaggerProvider(delegate));
+ }
+
+ private static <T> void setDelegateInternal(
+ DelegateFactory<T> delegateFactory, Provider<T> delegate) {
+ checkNotNull(delegate);
+ if (delegateFactory.delegate != null) {
throw new IllegalStateException();
}
- asDelegateFactory.delegate = delegate;
+ delegateFactory.delegate = delegate;
}
/**
@@ -67,4 +91,3 @@ public final class DelegateFactory<T> implements Factory<T> {
return checkNotNull(delegate);
}
}
-
diff --git a/java/dagger/internal/DoubleCheck.java b/java/dagger/internal/DoubleCheck.java
index af7d7f69a..6af866175 100644
--- a/java/dagger/internal/DoubleCheck.java
+++ b/java/dagger/internal/DoubleCheck.java
@@ -17,9 +17,9 @@
package dagger.internal;
import static dagger.internal.Preconditions.checkNotNull;
+import static dagger.internal.Providers.asDaggerProvider;
import dagger.Lazy;
-import javax.inject.Provider;
/**
* A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a
@@ -73,7 +73,8 @@ public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
// This method is declared this way instead of "<T> Provider<T> provider(Provider<T> delegate)"
// to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
- public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
+ public static <P extends dagger.internal.Provider<T>, T> dagger.internal.Provider<T> provider(
+ P delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
@@ -83,6 +84,16 @@ public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
return new DoubleCheck<T>(delegate);
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public static <P extends javax.inject.Provider<T>, T> javax.inject.Provider<T> provider(
+ P delegate) {
+ return provider(asDaggerProvider(delegate));
+ }
+
/** Returns a {@link Lazy} that caches the value from the given provider. */
// This method is declared this way instead of "<T> Lazy<T> lazy(Provider<T> delegate)"
// to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
@@ -99,4 +110,12 @@ public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
}
return new DoubleCheck<T>(checkNotNull(provider));
}
+
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ public static <P extends javax.inject.Provider<T>, T> Lazy<T> lazy(P provider) {
+ return lazy(asDaggerProvider(provider));
+ }
}
diff --git a/java/dagger/internal/Factory.java b/java/dagger/internal/Factory.java
index 9c03f81aa..73bcfbc13 100644
--- a/java/dagger/internal/Factory.java
+++ b/java/dagger/internal/Factory.java
@@ -18,7 +18,6 @@ package dagger.internal;
import dagger.Provides;
import javax.inject.Inject;
-import javax.inject.Provider;
import javax.inject.Scope;
/**
diff --git a/java/dagger/internal/IdentifierNameString.java b/java/dagger/internal/IdentifierNameString.java
new file mode 100644
index 000000000..95d2bfa6f
--- /dev/null
+++ b/java/dagger/internal/IdentifierNameString.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates the dagger generated class that requires applying -identifiernamestring rule.
+ *
+ * <p>When applied, all the strings fields that corresponds to a class name within the annotated
+ * class will be obfuscated if its corresponding class is obfuscated. This only works with r8.
+ *
+ */
+@Documented
+@Retention(CLASS)
+@Target(TYPE)
+public @interface IdentifierNameString {}
diff --git a/java/dagger/internal/KeepFieldType.java b/java/dagger/internal/KeepFieldType.java
new file mode 100644
index 000000000..090550210
--- /dev/null
+++ b/java/dagger/internal/KeepFieldType.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates the dagger generated field that requires keeping the field types.
+ *
+ * <p>Annotating a field with this annotation, the field type's class name won't be discarded or
+ * obfuscated when compiles with proguard. Note:This will cause the containing class to be kept, and
+ * only works with proguard.
+ */
+@Documented
+@Retention(CLASS)
+@Target(FIELD)
+public @interface KeepFieldType {}
diff --git a/java/dagger/internal/LazyClassKeyMap.java b/java/dagger/internal/LazyClassKeyMap.java
new file mode 100644
index 000000000..dabf86f91
--- /dev/null
+++ b/java/dagger/internal/LazyClassKeyMap.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class keyed map that delegates to a string keyed map under the hood.
+ *
+ * <p>A {@code LazyClassKeyMap} is created for @LazyClassKey contributed map binding.
+ */
+public final class LazyClassKeyMap<V> implements Map<Class<?>, V> {
+ private final Map<String, V> delegate;
+
+ public static <V> Map<Class<?>, V> of(Map<String, V> delegate) {
+ return new LazyClassKeyMap<>(delegate);
+ }
+
+ private LazyClassKeyMap(Map<String, V> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public V get(Object key) {
+ if (!(key instanceof Class)) {
+ throw new IllegalArgumentException("Key must be a class");
+ }
+ return delegate.get(((Class<?>) key).getName());
+ }
+
+ @Override
+ public Set<Class<?>> keySet() {
+ // This method will load all class keys, therefore no need to use @LazyClassKey annotated
+ // bindings.
+ throw new UnsupportedOperationException(
+ "Maps created with @LazyClassKey do not support usage of keySet(). Consider @ClassKey"
+ + " instead.");
+ }
+
+ @Override
+ public Collection<V> values() {
+ return delegate.values();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ if (!(key instanceof Class)) {
+ throw new IllegalArgumentException("Key must be a class");
+ }
+ return delegate.containsKey(((Class<?>) key).getName());
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public Set<Map.Entry<Class<?>, V>> entrySet() {
+ // This method will load all class keys, therefore no need to use @LazyClassKey annotated
+ // bindings.
+ throw new UnsupportedOperationException(
+ "Maps created with @LazyClassKey do not support usage of entrySet(). Consider @ClassKey"
+ + " instead.");
+ }
+
+ // The dagger map binding should be a immutable map.
+ @Override
+ public V remove(Object key) {
+ throw new UnsupportedOperationException("Dagger map bindings are immutable");
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException("Dagger map bindings are immutable");
+ }
+
+ @Override
+ public V put(Class<?> key, V value) {
+ throw new UnsupportedOperationException("Dagger map bindings are immutable");
+ }
+
+ @Override
+ public void putAll(Map<? extends Class<?>, ? extends V> map) {
+ throw new UnsupportedOperationException("Dagger map bindings are immutable");
+ }
+
+ /** A factory for {@code LazyClassKeyMap}. */
+ public static class Factory<V> implements Provider<Map<Class<?>, V>> {
+ MapFactory<String, V> delegate;
+
+ public static <V> Factory<V> of(MapFactory<String, V> delegate) {
+ return new Factory<>(delegate);
+ }
+
+ private Factory(MapFactory<String, V> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Map<Class<?>, V> get() {
+ return LazyClassKeyMap.of(delegate.get());
+ }
+ }
+}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
index 39748c9ad..376cfdc1c 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -17,12 +17,12 @@
package dagger.internal;
import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Providers.asDaggerProvider;
import static java.util.Collections.unmodifiableMap;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
-import javax.inject.Provider;
/**
* A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
@@ -72,12 +72,30 @@ public final class MapFactory<K, V> extends AbstractMapFactory<K, V, V> {
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
@Override
public Builder<K, V> putAll(Provider<Map<K, V>> mapFactory) {
super.putAll(mapFactory);
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> putAll(javax.inject.Provider<Map<K, V>> mapFactory) {
+ return putAll(asDaggerProvider(mapFactory));
+ }
+
/** Returns a new {@link MapProviderFactory}. */
public MapFactory<K, V> build() {
return new MapFactory<>(map);
diff --git a/java/dagger/internal/MapProviderFactory.java b/java/dagger/internal/MapProviderFactory.java
index 1fe478856..8491ffc85 100644
--- a/java/dagger/internal/MapProviderFactory.java
+++ b/java/dagger/internal/MapProviderFactory.java
@@ -16,9 +16,12 @@
package dagger.internal;
+import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
+import static dagger.internal.Providers.asDaggerProvider;
+
import dagger.Lazy;
+import java.util.Collections;
import java.util.Map;
-import javax.inject.Provider;
/**
* A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
@@ -57,12 +60,43 @@ public final class MapProviderFactory<K, V> extends AbstractMapFactory<K, V, Pro
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
@Override
public Builder<K, V> putAll(Provider<Map<K, Provider<V>>> mapProviderFactory) {
super.putAll(mapProviderFactory);
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> putAll(
+ final javax.inject.Provider<Map<K, javax.inject.Provider<V>>> mapProviderFactory) {
+ return putAll(new Provider<Map<K, Provider<V>>>() {
+ @Override public Map<K, Provider<V>> get() {
+ Map<K, javax.inject.Provider<V>> javaxMap = mapProviderFactory.get();
+ if (javaxMap.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<K, Provider<V>> daggerMap = newLinkedHashMapWithExpectedSize(javaxMap.size());
+ for (Map.Entry<K, javax.inject.Provider<V>> e : javaxMap.entrySet()) {
+ daggerMap.put(e.getKey(), asDaggerProvider(e.getValue()));
+ }
+ return Collections.unmodifiableMap(daggerMap);
+ }
+ });
+ }
+
/** Returns a new {@link MapProviderFactory}. */
public MapProviderFactory<K, V> build() {
return new MapProviderFactory<>(map);
diff --git a/java/dagger/internal/Provider.java b/java/dagger/internal/Provider.java
new file mode 100644
index 000000000..e38860187
--- /dev/null
+++ b/java/dagger/internal/Provider.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+/**
+ * Internal Provider interface to make support for {@code javax.inject.Provider} and
+ * {@code jakarta.inject.Provider} easier. Do not use outside of Dagger implementation code.
+ */
+// TODO(erichang): Make this also extend the Jakarta Provider
+public interface Provider<T> extends javax.inject.Provider<T> {
+}
diff --git a/java/dagger/internal/ProviderOfLazy.java b/java/dagger/internal/ProviderOfLazy.java
index 23b6afd75..0430cbd6e 100644
--- a/java/dagger/internal/ProviderOfLazy.java
+++ b/java/dagger/internal/ProviderOfLazy.java
@@ -17,9 +17,9 @@
package dagger.internal;
import static dagger.internal.Preconditions.checkNotNull;
+import static dagger.internal.Providers.asDaggerProvider;
import dagger.Lazy;
-import javax.inject.Provider;
/**
* A {@link Provider} of {@link Lazy} instances that each delegate to a given {@link Provider}.
@@ -51,4 +51,13 @@ public final class ProviderOfLazy<T> implements Provider<Lazy<T>> {
public static <T> Provider<Lazy<T>> create(Provider<T> provider) {
return new ProviderOfLazy<T>(checkNotNull(provider));
}
+
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public static <T> Provider<Lazy<T>> create(javax.inject.Provider<T> provider) {
+ return create(asDaggerProvider(provider));
+ }
}
diff --git a/java/dagger/internal/Providers.java b/java/dagger/internal/Providers.java
new file mode 100644
index 000000000..60ec83fa4
--- /dev/null
+++ b/java/dagger/internal/Providers.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import static dagger.internal.Preconditions.checkNotNull;
+
+/** Helper class for utility functions dealing with Providers. */
+public final class Providers {
+
+ /** Converts a javax provider to a Dagger internal provider. */
+ public static <T> Provider<T> asDaggerProvider(final javax.inject.Provider<T> provider) {
+ checkNotNull(provider);
+ return new Provider<T>() {
+ @Override public T get() {
+ return provider.get();
+ }
+ };
+ }
+
+ private Providers() {}
+}
diff --git a/java/dagger/internal/SetFactory.java b/java/dagger/internal/SetFactory.java
index 349399b3e..f16076708 100644
--- a/java/dagger/internal/SetFactory.java
+++ b/java/dagger/internal/SetFactory.java
@@ -20,6 +20,7 @@ import static dagger.internal.DaggerCollections.hasDuplicates;
import static dagger.internal.DaggerCollections.newHashSetWithExpectedSize;
import static dagger.internal.DaggerCollections.presizedList;
import static dagger.internal.Preconditions.checkNotNull;
+import static dagger.internal.Providers.asDaggerProvider;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
@@ -27,7 +28,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
-import javax.inject.Provider;
/**
* A {@link Factory} implementation used to implement {@link Set} bindings. This factory always
@@ -73,6 +73,15 @@ public final class SetFactory<T> implements Factory<Set<T>> {
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<T> addProvider(javax.inject.Provider<? extends T> individualProvider) {
+ return addProvider(asDaggerProvider(individualProvider));
+ }
+
@SuppressWarnings("unchecked")
public Builder<T> addCollectionProvider(
Provider<? extends Collection<? extends T>> collectionProvider) {
@@ -81,6 +90,16 @@ public final class SetFactory<T> implements Factory<Set<T>> {
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<T> addCollectionProvider(
+ javax.inject.Provider<? extends Collection<? extends T>> collectionProvider) {
+ return addCollectionProvider(asDaggerProvider(collectionProvider));
+ }
+
public SetFactory<T> build() {
assert !hasDuplicates(individualProviders)
: "Codegen error? Duplicates in the provider list";
diff --git a/java/dagger/internal/SingleCheck.java b/java/dagger/internal/SingleCheck.java
index 41280699d..32ba83a6f 100644
--- a/java/dagger/internal/SingleCheck.java
+++ b/java/dagger/internal/SingleCheck.java
@@ -17,8 +17,7 @@
package dagger.internal;
import static dagger.internal.Preconditions.checkNotNull;
-
-import javax.inject.Provider;
+import static dagger.internal.Providers.asDaggerProvider;
/**
* A {@link Provider} implementation that memoizes the result of another {@link Provider} using
@@ -58,7 +57,7 @@ public final class SingleCheck<T> implements Provider<T> {
}
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
- // This method is declared this way instead of "<T> Provider<T> provider(Provider<T> provider)"
+ // This method is declared this way instead of "<T> Provider<T> provider(Provider<T> provider)"
// to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
public static <P extends Provider<T>, T> Provider<T> provider(P provider) {
// If a scoped @Binds delegates to a scoped binding, don't cache the value again.
@@ -67,4 +66,13 @@ public final class SingleCheck<T> implements Provider<T> {
}
return new SingleCheck<T>(checkNotNull(provider));
}
+
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ public static <P extends javax.inject.Provider<T>, T> javax.inject.Provider<T> provider(
+ P delegate) {
+ return provider(asDaggerProvider(delegate));
+ }
}
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index a3868b529..80ca8d7dc 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -43,7 +43,6 @@ java_library(
"//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/componentgenerator",
"//java/dagger/internal/codegen/kotlin",
- "//java/dagger/internal/codegen/model",
"//java/dagger/internal/codegen/processingstep",
"//java/dagger/internal/codegen/validation",
"//java/dagger/internal/codegen/writing",
@@ -89,7 +88,6 @@ gen_maven_artifact(
],
artifact_target_maven_deps = [
"com.google.code.findbugs:jsr305",
- "com.google.dagger:dagger-producers",
"com.google.dagger:dagger-spi",
"com.google.dagger:dagger",
"com.google.devtools.ksp:symbol-processing-api",
diff --git a/java/dagger/internal/codegen/DelegateComponentProcessor.java b/java/dagger/internal/codegen/DelegateComponentProcessor.java
index 96d433b40..432daa686 100644
--- a/java/dagger/internal/codegen/DelegateComponentProcessor.java
+++ b/java/dagger/internal/codegen/DelegateComponentProcessor.java
@@ -32,10 +32,13 @@ import dagger.Provides;
import dagger.internal.codegen.base.ClearableCache;
import dagger.internal.codegen.base.SourceFileGenerationException;
import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.base.SourceFileHjarGenerator;
import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.ComponentDescriptor;
import dagger.internal.codegen.binding.InjectBindingRegistry;
import dagger.internal.codegen.binding.MembersInjectionBinding;
import dagger.internal.codegen.binding.ModuleDescriptor;
+import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.binding.ProductionBinding;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.bindinggraphvalidation.BindingGraphValidationModule;
@@ -53,7 +56,6 @@ import dagger.internal.codegen.validation.InjectBindingRegistryModule;
import dagger.internal.codegen.validation.InjectValidator;
import dagger.internal.codegen.validation.ValidationBindingGraphPlugins;
import dagger.internal.codegen.writing.FactoryGenerator;
-import dagger.internal.codegen.writing.HjarSourceFileGenerator;
import dagger.internal.codegen.writing.MembersInjectorGenerator;
import dagger.internal.codegen.writing.ModuleGenerator;
import dagger.internal.codegen.writing.ModuleProxies.ModuleConstructorProxyGenerator;
@@ -98,8 +100,9 @@ final class DelegateComponentProcessor {
+ legacyPlugin.pluginName()
+ ". Either compile with KAPT or migrate the plugin to implement "
+ "dagger.spi.model.BindingGraphPlugin."));
- // We've already reported warnings on the invalid legacy plugins above. We can't actually
- // process these plugins in KSP, so just skip them to allow processing of the valid plugins.
+ // Even though we've reported an error, processing will still continue for the remainder of
+ // the processing round to try to catch other errors. We set the javac plugins to empty to
+ // skip processing since it would just result in ClassCastExceptions in KSP.
legacyPlugins = ImmutableSet.of();
}
DaggerDelegateComponentProcessor_Injector.factory()
@@ -171,6 +174,14 @@ final class DelegateComponentProcessor {
@Binds
@IntoSet
+ ClearableCache componentDescriptorFactory(ComponentDescriptor.Factory cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache monitoringModules(MonitoringModules cache);
+
+ @Binds
+ @IntoSet
ClearableCache bindingGraphFactory(BindingGraphFactory cache);
@Binds
@@ -190,34 +201,44 @@ final class DelegateComponentProcessor {
interface SourceFileGeneratorsModule {
@Provides
static SourceFileGenerator<ProvisionBinding> factoryGenerator(
- FactoryGenerator generator, CompilerOptions compilerOptions) {
- return hjarWrapper(generator, compilerOptions);
+ FactoryGenerator generator,
+ CompilerOptions compilerOptions,
+ XProcessingEnv processingEnv) {
+ return hjarWrapper(generator, compilerOptions, processingEnv);
}
@Provides
static SourceFileGenerator<ProductionBinding> producerFactoryGenerator(
- ProducerFactoryGenerator generator, CompilerOptions compilerOptions) {
- return hjarWrapper(generator, compilerOptions);
+ ProducerFactoryGenerator generator,
+ CompilerOptions compilerOptions,
+ XProcessingEnv processingEnv) {
+ return hjarWrapper(generator, compilerOptions, processingEnv);
}
@Provides
static SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator(
- MembersInjectorGenerator generator, CompilerOptions compilerOptions) {
- return hjarWrapper(generator, compilerOptions);
+ MembersInjectorGenerator generator,
+ CompilerOptions compilerOptions,
+ XProcessingEnv processingEnv) {
+ return hjarWrapper(generator, compilerOptions, processingEnv);
}
@Provides
@ModuleGenerator
static SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator(
- ModuleConstructorProxyGenerator generator, CompilerOptions compilerOptions) {
- return hjarWrapper(generator, compilerOptions);
+ ModuleConstructorProxyGenerator generator,
+ CompilerOptions compilerOptions,
+ XProcessingEnv processingEnv) {
+ return hjarWrapper(generator, compilerOptions, processingEnv);
}
}
private static <T> SourceFileGenerator<T> hjarWrapper(
- SourceFileGenerator<T> generator, CompilerOptions compilerOptions) {
+ SourceFileGenerator<T> generator,
+ CompilerOptions compilerOptions,
+ XProcessingEnv processingEnv) {
return compilerOptions.headerCompilation()
- ? HjarSourceFileGenerator.wrap(generator)
+ ? SourceFileHjarGenerator.wrap(generator, processingEnv)
: generator;
}
}
diff --git a/java/dagger/internal/codegen/base/ElementFormatter.java b/java/dagger/internal/codegen/base/ElementFormatter.java
index 16381e32f..8e3edc955 100644
--- a/java/dagger/internal/codegen/base/ElementFormatter.java
+++ b/java/dagger/internal/codegen/base/ElementFormatter.java
@@ -36,7 +36,8 @@ import javax.inject.Inject;
*
* <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
*
- * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ * <p>If the element is a parameter, the returned string will include the enclosing executable,
+ * with other parameters elided.
*/
public final class ElementFormatter extends Formatter<XElement> {
@Inject
@@ -52,15 +53,29 @@ public final class ElementFormatter extends Formatter<XElement> {
*
* <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
*
- * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ * <p>If the element is a parameter, the returned string will include the enclosing executable,
+ * with other parameters elided.
*/
public static String elementToString(XElement element) {
+ return elementToString(element, /* elideMethodParameterTypes= */ false);
+ }
+
+ /**
+ * Returns a useful string form for an element.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+ public static String elementToString(XElement element, boolean elideMethodParameterTypes) {
if (isExecutable(element)) {
return enclosingTypeAndMemberName(element)
.append(
- asExecutable(element).getParameters().stream()
- .map(parameter -> XTypes.toStableString(parameter.getType()))
- .collect(joining(", ", "(", ")")))
+ elideMethodParameterTypes
+ ? (asExecutable(element).getParameters().isEmpty() ? "()" : "(…)")
+ : asExecutable(element).getParameters().stream()
+ .map(parameter -> XTypes.toStableString(parameter.getType()))
+ .collect(joining(", ", "(", ")")))
.toString();
} else if (isMethodParameter(element)) {
XExecutableElement methodOrConstructor = asMethodParameter(element).getEnclosingElement();
diff --git a/java/dagger/internal/codegen/base/FrameworkTypes.java b/java/dagger/internal/codegen/base/FrameworkTypes.java
index 59588caf5..e39aeabea 100644
--- a/java/dagger/internal/codegen/base/FrameworkTypes.java
+++ b/java/dagger/internal/codegen/base/FrameworkTypes.java
@@ -29,6 +29,7 @@ import java.util.Set;
* type that the framework itself defines.
*/
public final class FrameworkTypes {
+ // TODO(erichang): Add the Jakarta Provider here
private static final ImmutableSet<ClassName> PROVISION_TYPES =
ImmutableSet.of(TypeNames.PROVIDER, TypeNames.LAZY, TypeNames.MEMBERS_INJECTOR);
diff --git a/java/dagger/internal/codegen/base/MapType.java b/java/dagger/internal/codegen/base/MapType.java
index 00401ed72..c4ba838e8 100644
--- a/java/dagger/internal/codegen/base/MapType.java
+++ b/java/dagger/internal/codegen/base/MapType.java
@@ -115,6 +115,13 @@ public abstract class MapType {
return isMap(key.type().xprocessing());
}
+ public static boolean isMapOfProvider(XType keyType) {
+ if (MapType.isMap(keyType)) {
+ return MapType.from(keyType).valuesAreTypeOf(TypeNames.PROVIDER);
+ }
+ return false;
+ }
+
/**
* Returns a {@link MapType} for {@code type}.
*
diff --git a/java/dagger/internal/codegen/base/OptionalType.java b/java/dagger/internal/codegen/base/OptionalType.java
index 79b638d4a..5544eaeba 100644
--- a/java/dagger/internal/codegen/base/OptionalType.java
+++ b/java/dagger/internal/codegen/base/OptionalType.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.extension.DaggerStreams.valuesOf;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
@@ -155,4 +156,14 @@ public abstract class OptionalType {
public static OptionalType from(Key key) {
return from(key.type().xprocessing());
}
+
+ public static boolean isOptionalProviderType(XType type) {
+ if (OptionalType.isOptional(type)) {
+ OptionalType optionalType = OptionalType.from(type);
+ if (isTypeOf(optionalType.valueType(), TypeNames.PROVIDER)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/java/dagger/internal/codegen/base/SourceFileGenerator.java b/java/dagger/internal/codegen/base/SourceFileGenerator.java
index c43499a50..dfe14b1d5 100644
--- a/java/dagger/internal/codegen/base/SourceFileGenerator.java
+++ b/java/dagger/internal/codegen/base/SourceFileGenerator.java
@@ -18,6 +18,7 @@ package dagger.internal.codegen.base;
import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.CAST;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.KOTLIN_INTERNAL;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
@@ -100,13 +101,12 @@ public abstract class SourceFileGenerator<T> {
.build());
generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation);
- // TODO(b/134590785): Remove UNCHECKED/RAWTYPES and suppress locally where necessary.
// TODO(b/263891456): Remove KOTLIN_INTERNAL and use Object/raw types where necessary.
typeSpecBuilder.addAnnotation(
AnnotationSpecs.suppressWarnings(
ImmutableSet.<Suppression>builder()
.addAll(warningSuppressions())
- .add(UNCHECKED, RAWTYPES, KOTLIN_INTERNAL)
+ .add(UNCHECKED, RAWTYPES, KOTLIN_INTERNAL, CAST)
.build()));
String packageName = closestEnclosingTypeElement(originatingElement).getPackageName();
diff --git a/java/dagger/internal/codegen/base/SourceFileHjarGenerator.java b/java/dagger/internal/codegen/base/SourceFileHjarGenerator.java
new file mode 100644
index 000000000..6857c366f
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SourceFileHjarGenerator.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.TypeNames;
+import java.util.Optional;
+import javax.lang.model.element.Modifier;
+
+/**
+ * A source file generator that only writes the relevant code necessary for Bazel to create a
+ * correct header (ABI) jar.
+ */
+public final class SourceFileHjarGenerator<T> extends SourceFileGenerator<T> {
+ public static <T> SourceFileGenerator<T> wrap(
+ SourceFileGenerator<T> delegate, XProcessingEnv processingEnv) {
+ return new SourceFileHjarGenerator<>(delegate, processingEnv);
+ }
+
+ private final SourceFileGenerator<T> delegate;
+ private final XProcessingEnv processingEnv;
+
+ private SourceFileHjarGenerator(SourceFileGenerator<T> delegate, XProcessingEnv processingEnv) {
+ super(delegate);
+ this.delegate = delegate;
+ this.processingEnv = processingEnv;
+ }
+
+ @Override
+ public XElement originatingElement(T input) {
+ return delegate.originatingElement(input);
+ }
+
+ @Override
+ public ImmutableList<TypeSpec.Builder> topLevelTypes(T input) {
+ String packageName = closestEnclosingTypeElement(originatingElement(input)).getPackageName();
+ return delegate.topLevelTypes(input).stream()
+ .map(completeType -> skeletonType(packageName, completeType.build()))
+ .collect(toImmutableList());
+ }
+
+ private TypeSpec.Builder skeletonType(String packageName, TypeSpec completeType) {
+ TypeSpec.Builder skeleton =
+ classBuilder(completeType.name)
+ .addSuperinterfaces(completeType.superinterfaces)
+ .addTypeVariables(completeType.typeVariables)
+ .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeType.annotations);
+
+ if (!completeType.superclass.equals(ClassName.OBJECT)) {
+ skeleton.superclass(completeType.superclass);
+ }
+
+ completeType.methodSpecs.stream()
+ .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
+ .map(completeMethod -> skeletonMethod(packageName, completeType, completeMethod))
+ .forEach(skeleton::addMethod);
+
+ completeType.fieldSpecs.stream()
+ .filter(field -> !field.modifiers.contains(PRIVATE))
+ .map(this::skeletonField)
+ .forEach(skeleton::addField);
+
+ completeType.typeSpecs.stream()
+ .map(type -> skeletonType(packageName, type).build())
+ .forEach(skeleton::addType);
+
+ completeType.alwaysQualifiedNames
+ .forEach(skeleton::alwaysQualify);
+
+ return skeleton;
+ }
+
+ private MethodSpec skeletonMethod(
+ String packageName, TypeSpec completeType, MethodSpec completeMethod) {
+ MethodSpec.Builder skeleton =
+ completeMethod.isConstructor()
+ ? constructorBuilder()
+ : methodBuilder(completeMethod.name).returns(completeMethod.returnType);
+
+ if (completeMethod.isConstructor()) {
+ getRequiredSuperCall(packageName, completeType)
+ .ifPresent(superCall -> skeleton.addStatement("$L", superCall));
+ } else if (!completeMethod.returnType.equals(TypeName.VOID)) {
+ skeleton.addStatement("return $L", getDefaultValueCodeBlock(completeMethod.returnType));
+ }
+
+ return skeleton
+ .addModifiers(completeMethod.modifiers)
+ .addTypeVariables(completeMethod.typeVariables)
+ .addParameters(completeMethod.parameters)
+ .addExceptions(completeMethod.exceptions)
+ .varargs(completeMethod.varargs)
+ .addAnnotations(completeMethod.annotations)
+ .build();
+ }
+
+ private Optional<CodeBlock> getRequiredSuperCall(String packageName, TypeSpec completeType) {
+ if (completeType.superclass.equals(TypeName.OBJECT)) {
+ return Optional.empty();
+ }
+
+ ClassName rawSuperClass = (ClassName) TypeNames.rawTypeName(completeType.superclass);
+ XTypeElement superTypeElement =
+ processingEnv.requireTypeElement(rawSuperClass.canonicalName());
+
+ ImmutableSet<XConstructorElement> accessibleConstructors =
+ superTypeElement.getConstructors().stream()
+ .filter(
+ constructor ->
+ // isElementAccessibleFrom doesn't take protected into account so check manually
+ constructor.isProtected()
+ || isElementAccessibleFrom(constructor, packageName))
+ .collect(toImmutableSet());
+
+ // If there's an accessible default constructor we don't need to call super() manually.
+ if (accessibleConstructors.isEmpty()
+ || accessibleConstructors.stream()
+ .anyMatch(constructor -> constructor.getParameters().isEmpty())) {
+ return Optional.empty();
+ }
+
+ return Optional.of(
+ CodeBlock.of(
+ "super($L)",
+ CodeBlocks.makeParametersCodeBlock(
+ // We just choose the first constructor (it doesn't really matter since we're just
+ // trying to ensure the constructor body compiles).
+ accessibleConstructors.stream().findFirst().get().getParameters().stream()
+ .map(XExecutableParameterElement::getType)
+ .map(XType::getTypeName)
+ .map(SourceFileHjarGenerator::getDefaultValueCodeBlock)
+ .collect(toImmutableList()))));
+ }
+
+ /**
+ * Returns a {@link CodeBlock} containing the default value for the given {@code typeName}.
+ *
+ * <p>See https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html.
+ */
+ private static CodeBlock getDefaultValueCodeBlock(TypeName typeName) {
+ if (typeName.isPrimitive()) {
+ if (typeName.equals(TypeName.BOOLEAN)) {
+ return CodeBlock.of("false");
+ } else if (typeName.equals(TypeName.CHAR)) {
+ return CodeBlock.of("'\u0000'");
+ } else if (typeName.equals(TypeName.BYTE)) {
+ return CodeBlock.of("0");
+ } else if (typeName.equals(TypeName.SHORT)) {
+ return CodeBlock.of("0");
+ } else if (typeName.equals(TypeName.INT)) {
+ return CodeBlock.of("0");
+ } else if (typeName.equals(TypeName.LONG)) {
+ return CodeBlock.of("0L");
+ } else if (typeName.equals(TypeName.FLOAT)) {
+ return CodeBlock.of("0.0f");
+ } else if (typeName.equals(TypeName.DOUBLE)) {
+ return CodeBlock.of("0.0d");
+ } else {
+ throw new AssertionError("Unexpected type: " + typeName);
+ }
+ }
+ return CodeBlock.of("null");
+ }
+
+ private FieldSpec skeletonField(FieldSpec completeField) {
+ return FieldSpec.builder(
+ completeField.type,
+ completeField.name,
+ completeField.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeField.annotations)
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/base/TarjanSCCs.java b/java/dagger/internal/codegen/base/TarjanSCCs.java
index ab9a0fdae..b089333b0 100644
--- a/java/dagger/internal/codegen/base/TarjanSCCs.java
+++ b/java/dagger/internal/codegen/base/TarjanSCCs.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkState;
import static java.lang.Math.min;
import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -39,7 +40,7 @@ import java.util.Set;
public final class TarjanSCCs {
/** Returns the set of strongly connected components in reverse topological order. */
- public static <NodeT> ImmutableSet<ImmutableSet<NodeT>> compute(
+ public static <NodeT> ImmutableList<ImmutableSet<NodeT>> compute(
ImmutableCollection<NodeT> nodes, SuccessorsFunction<NodeT> successorsFunction) {
return new TarjanSCC<>(nodes, successorsFunction).compute();
}
@@ -62,14 +63,14 @@ public final class TarjanSCCs {
this.lowLinks = Maps.newHashMapWithExpectedSize(nodes.size());
}
- private ImmutableSet<ImmutableSet<NodeT>> compute() {
+ private ImmutableList<ImmutableSet<NodeT>> compute() {
checkState(indexes.isEmpty(), "TarjanSCC#compute() can only be called once per instance!");
for (NodeT node : nodes) {
if (!indexes.containsKey(node)) {
stronglyConnect(node);
}
}
- return ImmutableSet.copyOf(stronglyConnectedComponents);
+ return ImmutableList.copyOf(stronglyConnectedComponents);
}
private void stronglyConnect(NodeT node) {
diff --git a/java/dagger/internal/codegen/binding/AnnotationExpression.java b/java/dagger/internal/codegen/binding/AnnotationExpression.java
index 6980c1437..535a7ef57 100644
--- a/java/dagger/internal/codegen/binding/AnnotationExpression.java
+++ b/java/dagger/internal/codegen/binding/AnnotationExpression.java
@@ -112,7 +112,7 @@ public final class AnnotationExpression {
} else if (value.hasAnnotationValue()) {
return getAnnotationInstanceExpression(value.asAnnotation());
} else if (value.hasTypeValue()) {
- return CodeBlock.of("$T.class", value.asType().getTypeName());
+ return CodeBlock.of("$T.class", value.asType().getTypeElement().getClassName());
} else if (value.hasStringValue()) {
return CodeBlock.of("$S", value.asString());
} else if (value.hasByteValue()) {
diff --git a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
index a8bca0956..50a36b937 100644
--- a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
+++ b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
@@ -22,11 +22,11 @@ import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
-import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import androidx.room.compiler.processing.XConstructorElement;
import androidx.room.compiler.processing.XConstructorType;
import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XHasModifiers;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XMethodType;
@@ -90,14 +90,15 @@ public final class AssistedInjectionAnnotations {
}
private static ImmutableList<ParameterSpec> assistedParameterSpecs(
- List<? extends XVariableElement> paramElements, List<XType> paramTypes) {
+ List<? extends XExecutableParameterElement> paramElements, List<XType> paramTypes) {
ImmutableList.Builder<ParameterSpec> assistedParameterSpecs = ImmutableList.builder();
for (int i = 0; i < paramElements.size(); i++) {
- XVariableElement paramElement = paramElements.get(i);
+ XExecutableParameterElement paramElement = paramElements.get(i);
XType paramType = paramTypes.get(i);
if (isAssistedParameter(paramElement)) {
assistedParameterSpecs.add(
- ParameterSpec.builder(paramType.getTypeName(), getSimpleName(paramElement)).build());
+ ParameterSpec.builder(paramType.getTypeName(), paramElement.getJvmName())
+ .build());
}
}
return assistedParameterSpecs.build();
@@ -133,7 +134,7 @@ public final class AssistedInjectionAnnotations {
.collect(toImmutableSet());
}
- public static ImmutableList<XVariableElement> assistedParameters(Binding binding) {
+ public static ImmutableList<XExecutableParameterElement> assistedParameters(Binding binding) {
return binding.kind() == BindingKind.ASSISTED_INJECTION
? asConstructor(binding.bindingElement().get()).getParameters().stream()
.filter(AssistedInjectionAnnotations::isAssistedParameter)
@@ -184,8 +185,10 @@ public final class AssistedInjectionAnnotations {
public abstract ImmutableList<AssistedParameter> assistedFactoryAssistedParameters();
@Memoized
- public ImmutableMap<AssistedParameter, XVariableElement> assistedInjectAssistedParametersMap() {
- ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
+ public ImmutableMap<AssistedParameter, XExecutableParameterElement>
+ assistedInjectAssistedParametersMap() {
+ ImmutableMap.Builder<AssistedParameter, XExecutableParameterElement> builder =
+ ImmutableMap.builder();
for (AssistedParameter assistedParameter : assistedInjectAssistedParameters()) {
builder.put(assistedParameter, assistedParameter.element());
}
@@ -193,9 +196,10 @@ public final class AssistedInjectionAnnotations {
}
@Memoized
- public ImmutableMap<AssistedParameter, XVariableElement>
+ public ImmutableMap<AssistedParameter, XExecutableParameterElement>
assistedFactoryAssistedParametersMap() {
- ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
+ ImmutableMap.Builder<AssistedParameter, XExecutableParameterElement> builder =
+ ImmutableMap.builder();
for (AssistedParameter assistedParameter : assistedFactoryAssistedParameters()) {
builder.put(assistedParameter, assistedParameter.element());
}
@@ -211,7 +215,8 @@ public final class AssistedInjectionAnnotations {
*/
@AutoValue
public abstract static class AssistedParameter {
- public static AssistedParameter create(XVariableElement parameter, XType parameterType) {
+ public static AssistedParameter create(
+ XExecutableParameterElement parameter, XType parameterType) {
AssistedParameter assistedParameter =
new AutoValue_AssistedInjectionAnnotations_AssistedParameter(
Optional.ofNullable(parameter.getAnnotation(TypeNames.ASSISTED))
@@ -223,7 +228,7 @@ public final class AssistedInjectionAnnotations {
return assistedParameter;
}
- private XVariableElement parameterElement;
+ private XExecutableParameterElement parameterElement;
private XType parameterType;
/** Returns the string qualifier from the {@link Assisted#value()}. */
@@ -237,7 +242,7 @@ public final class AssistedInjectionAnnotations {
return parameterType;
}
- public final XVariableElement element() {
+ public final XExecutableParameterElement element() {
return parameterElement;
}
@@ -260,7 +265,7 @@ public final class AssistedInjectionAnnotations {
ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
for (int i = 0; i < assistedInjectConstructor.getParameters().size(); i++) {
- XVariableElement parameter = assistedInjectConstructor.getParameters().get(i);
+ XExecutableParameterElement parameter = assistedInjectConstructor.getParameters().get(i);
XType parameterType = assistedInjectConstructorType.getParameterTypes().get(i);
if (parameter.hasAnnotation(TypeNames.ASSISTED)) {
builder.add(AssistedParameter.create(parameter, parameterType));
@@ -273,7 +278,7 @@ public final class AssistedInjectionAnnotations {
XMethodElement factoryMethod, XMethodType factoryMethodType) {
ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
for (int i = 0; i < factoryMethod.getParameters().size(); i++) {
- XVariableElement parameter = factoryMethod.getParameters().get(i);
+ XExecutableParameterElement parameter = factoryMethod.getParameters().get(i);
XType parameterType = factoryMethodType.getParameterTypes().get(i);
builder.add(AssistedParameter.create(parameter, parameterType));
}
diff --git a/java/dagger/internal/codegen/binding/BUILD b/java/dagger/internal/codegen/binding/BUILD
index e606eb414..7ba349e5b 100644
--- a/java/dagger/internal/codegen/binding/BUILD
+++ b/java/dagger/internal/codegen/binding/BUILD
@@ -31,11 +31,8 @@ java_library(
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/kotlin",
- "//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/model",
"//java/dagger/internal/codegen/xprocessing",
- "//java/dagger/producers",
- "//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/error_prone:annotations",
"//third_party/java/guava/base",
diff --git a/java/dagger/internal/codegen/binding/BindingGraph.java b/java/dagger/internal/codegen/binding/BindingGraph.java
index 8dfe74f5f..0090b3d2a 100644
--- a/java/dagger/internal/codegen/binding/BindingGraph.java
+++ b/java/dagger/internal/codegen/binding/BindingGraph.java
@@ -149,7 +149,7 @@ public abstract class BindingGraph {
/** Returns the set of strongly connected nodes in this graph in reverse topological order. */
@Memoized
- public ImmutableSet<ImmutableSet<Node>> stronglyConnectedNodes() {
+ public ImmutableList<ImmutableSet<Node>> stronglyConnectedNodes() {
return TarjanSCCs.<Node>compute(
ImmutableSet.copyOf(network().nodes()),
// NetworkBuilder does not have a stable successor order, so we have to roll our own
@@ -212,14 +212,7 @@ public abstract class BindingGraph {
// particular BindingNode.
Map<Key, BindingNode> contributionBindings = new LinkedHashMap<>();
Map<Key, BindingNode> membersInjectionBindings = new LinkedHashMap<>();
-
- // Construct the maps of the ContributionBindings and MembersInjectionBindings by iterating
- // bindings from this component and then from each successive parent. If a binding exists in
- // multple components, this order ensures that the child-most binding is always chosen first.
- Stream.iterate(componentNode.componentPath(), ComponentPath::parent)
- // Stream.iterate is inifinte stream so we need limit it to the known size of the path.
- .limit(componentNode.componentPath().components().size())
- .flatMap(path -> topLevelBindingGraph.bindingsByComponent().get(path).stream())
+ topLevelBindingGraph.bindingsByComponent().get(componentNode.componentPath())
.forEach(
bindingNode -> {
if (bindingNode.delegate() instanceof ContributionBinding) {
@@ -233,16 +226,19 @@ public abstract class BindingGraph {
BindingGraph bindingGraph = new AutoValue_BindingGraph(componentNode, topLevelBindingGraph);
- ImmutableSet<ModuleDescriptor> modules =
- ((ComponentNodeImpl) componentNode).componentDescriptor().modules();
+ ImmutableSet<XTypeElement> modules =
+ ((ComponentNodeImpl) componentNode).componentDescriptor().modules().stream()
+ .map(ModuleDescriptor::moduleElement)
+ .collect(toImmutableSet());
- ImmutableSet<ModuleDescriptor> inheritedModules =
+ ImmutableSet<XTypeElement> inheritedModules =
parent.isPresent()
? Sets.union(parent.get().ownedModules, parent.get().inheritedModules).immutableCopy()
: ImmutableSet.of();
// Set these fields directly on the instance rather than passing these in as input to the
// AutoValue to prevent exposing this data outside of the class.
+ bindingGraph.parent = parent;
bindingGraph.inheritedModules = inheritedModules;
bindingGraph.ownedModules = Sets.difference(modules, inheritedModules).immutableCopy();
bindingGraph.contributionBindings = ImmutableMap.copyOf(contributionBindings);
@@ -257,10 +253,11 @@ public abstract class BindingGraph {
return bindingGraph;
}
+ private Optional<BindingGraph> parent;
private ImmutableMap<Key, BindingNode> contributionBindings;
private ImmutableMap<Key, BindingNode> membersInjectionBindings;
- private ImmutableSet<ModuleDescriptor> inheritedModules;
- private ImmutableSet<ModuleDescriptor> ownedModules;
+ private ImmutableSet<XTypeElement> inheritedModules;
+ private ImmutableSet<XTypeElement> ownedModules;
private ImmutableSet<XTypeElement> bindingModules;
BindingGraph() {}
@@ -287,9 +284,7 @@ public abstract class BindingGraph {
*/
public final Optional<Binding> localContributionBinding(Key key) {
return contributionBindings.containsKey(key)
- ? Optional.of(contributionBindings.get(key))
- .filter(bindingNode -> bindingNode.componentPath().equals(componentPath()))
- .map(BindingNode::delegate)
+ ? Optional.of(contributionBindings.get(key).delegate())
: Optional.empty();
}
@@ -299,15 +294,18 @@ public abstract class BindingGraph {
*/
public final Optional<Binding> localMembersInjectionBinding(Key key) {
return membersInjectionBindings.containsKey(key)
- ? Optional.of(membersInjectionBindings.get(key))
- .filter(bindingNode -> bindingNode.componentPath().equals(componentPath()))
- .map(BindingNode::delegate)
+ ? Optional.of(membersInjectionBindings.get(key).delegate())
: Optional.empty();
}
/** Returns the {@link ContributionBinding} for the given {@link Key}. */
public final ContributionBinding contributionBinding(Key key) {
- return (ContributionBinding) contributionBindings.get(key).delegate();
+ if (contributionBindings.containsKey(key)) {
+ return (ContributionBinding) contributionBindings.get(key).delegate();
+ } else if (parent.isPresent()) {
+ return parent.get().contributionBinding(key);
+ }
+ throw new AssertionError("Contribution binding not found for key: " + key);
}
/**
@@ -315,9 +313,12 @@ public abstract class BindingGraph {
* Optional#empty()} if one does not exist.
*/
public final Optional<MembersInjectionBinding> membersInjectionBinding(Key key) {
- return membersInjectionBindings.containsKey(key)
- ? Optional.of((MembersInjectionBinding) membersInjectionBindings.get(key).delegate())
- : Optional.empty();
+ if (membersInjectionBindings.containsKey(key)) {
+ return Optional.of((MembersInjectionBinding) membersInjectionBindings.get(key).delegate());
+ } else if (parent.isPresent()) {
+ return parent.get().membersInjectionBinding(key);
+ }
+ return Optional.empty();
}
/** Returns the {@link XTypeElement} for the component this graph represents. */
@@ -334,9 +335,7 @@ public abstract class BindingGraph {
* ancestors.
*/
public final ImmutableSet<XTypeElement> ownedModuleTypes() {
- return ownedModules.stream()
- .map(ModuleDescriptor::moduleElement)
- .collect(toImmutableSet());
+ return ownedModules;
}
/**
@@ -394,7 +393,7 @@ public abstract class BindingGraph {
ImmutableSet<XTypeElement> requiredModules =
stream(Traverser.forTree(BindingGraph::subgraphs).depthFirstPostOrder(this))
.flatMap(graph -> graph.bindingModules.stream())
- .filter(ownedModuleTypes()::contains)
+ .filter(ownedModules::contains)
.collect(toImmutableSet());
ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
componentDescriptor().requirements().stream()
@@ -433,11 +432,18 @@ public abstract class BindingGraph {
return topLevelBindingGraph().bindingsByComponent().get(componentPath());
}
- @Memoized
+ // TODO(bcorso): This method can be costly. Consider removing this method and inlining it into its
+ // only usage, BindingGraphJsonGenerator.
public ImmutableSet<BindingNode> bindingNodes() {
- return ImmutableSet.<BindingNode>builder()
- .addAll(contributionBindings.values())
- .addAll(membersInjectionBindings.values())
- .build();
+ // Construct the set of bindings by iterating bindings from this component and then from each
+ // successive parent. If a binding exists in multiple components, this order ensures that the
+ // child-most binding is always chosen first.
+ Map<Key, BindingNode> bindings = new LinkedHashMap<>();
+ Stream.iterate(componentPath(), ComponentPath::parent)
+ // Stream.iterate() is infinite stream so we need limit it to the known size of the path.
+ .limit(componentPath().components().size())
+ .flatMap(path -> topLevelBindingGraph().bindingsByComponent().get(path).stream())
+ .forEach(bindingNode -> bindings.putIfAbsent(bindingNode.key(), bindingNode));
+ return ImmutableSet.copyOf(bindings.values());
}
}
diff --git a/java/dagger/internal/codegen/binding/BindingGraphConverter.java b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
index 5928b8fb0..adb4435fd 100644
--- a/java/dagger/internal/codegen/binding/BindingGraphConverter.java
+++ b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
@@ -19,23 +19,19 @@ package dagger.internal.codegen.binding;
import static com.google.common.base.Verify.verify;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.extension.DaggerGraphs.unreachableNodes;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.model.BindingKind.SUBCOMPONENT_CREATOR;
-import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import com.google.common.graph.ImmutableNetwork;
import com.google.common.graph.MutableNetwork;
-import com.google.common.graph.Network;
import com.google.common.graph.NetworkBuilder;
import dagger.internal.codegen.binding.BindingGraph.TopLevelBindingGraph;
+import dagger.internal.codegen.binding.BindingGraphFactory.LegacyBindingGraph;
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.model.BindingGraph.ComponentNode;
import dagger.internal.codegen.model.BindingGraph.DependencyEdge;
@@ -43,7 +39,6 @@ import dagger.internal.codegen.model.BindingGraph.Edge;
import dagger.internal.codegen.model.BindingGraph.MissingBinding;
import dagger.internal.codegen.model.BindingGraph.Node;
import dagger.internal.codegen.model.ComponentPath;
-import dagger.internal.codegen.model.DaggerExecutableElement;
import dagger.internal.codegen.model.DaggerTypeElement;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.model.Key;
@@ -70,7 +65,7 @@ final class BindingGraphConverter {
*/
BindingGraph convert(LegacyBindingGraph legacyBindingGraph, boolean isFullBindingGraph) {
MutableNetwork<Node, Edge> network = asNetwork(legacyBindingGraph);
- ComponentNode rootNode = rootComponentNode(network);
+ ComponentNode rootNode = legacyBindingGraph.componentNode();
// When bindings are copied down into child graphs because they transitively depend on local
// multibindings or optional bindings, the parent-owned binding is still there. If that
@@ -92,47 +87,19 @@ final class BindingGraphConverter {
return converter.network;
}
- // TODO(dpb): Example of BindingGraph logic applied to derived networks.
- private ComponentNode rootComponentNode(Network<Node, Edge> network) {
- return (ComponentNode)
- Iterables.find(
- network.nodes(),
- node -> node instanceof ComponentNode && node.componentPath().atRoot());
- }
-
- /**
- * Used as a cache key to make sure resolved bindings are cached per component path.
- * This is required so that binding nodes are not reused across different branches of the
- * graph since the ResolvedBindings class only contains the component and not the path.
- */
- @AutoValue
- abstract static class ResolvedBindingsWithPath {
- abstract ResolvedBindings resolvedBindings();
- abstract ComponentPath componentPath();
-
- static ResolvedBindingsWithPath create(
- ResolvedBindings resolvedBindings, ComponentPath componentPath) {
- return new AutoValue_BindingGraphConverter_ResolvedBindingsWithPath(
- resolvedBindings, componentPath);
- }
- }
-
private final class Converter {
/** The path from the root graph to the currently visited graph. */
private final Deque<LegacyBindingGraph> bindingGraphPath = new ArrayDeque<>();
- /** The {@link ComponentPath} for each component in {@link #bindingGraphPath}. */
- private final Deque<ComponentPath> componentPaths = new ArrayDeque<>();
-
private final MutableNetwork<Node, Edge> network =
NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
private final Set<BindingNode> bindings = new HashSet<>();
- private final Map<ResolvedBindingsWithPath, ImmutableSet<BindingNode>> resolvedBindingsMap =
+ private final Map<ResolvedBindings, ImmutableSet<BindingNode>> resolvedBindingsMap =
new HashMap<>();
private void visitRootComponent(LegacyBindingGraph graph) {
- visitComponent(graph, null);
+ visitComponent(graph);
}
/**
@@ -141,34 +108,23 @@ final class BindingGraphConverter {
* <p>This implementation does the following:
*
* <ol>
- * <li>If this component is installed in its parent by a subcomponent factory method, calls
- * {@link #visitSubcomponentFactoryMethod(ComponentNode, ComponentNode, XMethodElement)}.
- * <li>For each entry point in the component, calls {@link #visitEntryPoint(ComponentNode,
- * DependencyRequest)}.
- * <li>For each child component, calls {@link #visitComponent(LegacyBindingGraph,
- * ComponentNode)}, updating the traversal state.
+ * <li>If this component is installed in its parent by a subcomponent factory method, adds
+ * an edge between the parent and child components.
+ * <li>For each entry point, adds an edge between the component and the entry point.
+ * <li>For each child component, calls {@link #visitComponent(LegacyBindingGraph)},
+ * updating the traversal state.
* </ol>
*
* @param graph the currently visited graph
*/
- private void visitComponent(LegacyBindingGraph graph, ComponentNode parentComponent) {
+ private void visitComponent(LegacyBindingGraph graph) {
bindingGraphPath.addLast(graph);
- ComponentPath graphPath =
- ComponentPath.create(
- bindingGraphPath.stream()
- .map(LegacyBindingGraph::componentDescriptor)
- .map(ComponentDescriptor::typeElement)
- .map(DaggerTypeElement::from)
- .collect(toImmutableList()));
- componentPaths.addLast(graphPath);
- ComponentNode currentComponent =
- ComponentNodeImpl.create(componentPath(), graph.componentDescriptor());
-
- network.addNode(currentComponent);
+
+ network.addNode(graph.componentNode());
for (ComponentMethodDescriptor entryPointMethod :
graph.componentDescriptor().entryPointMethods()) {
- visitEntryPoint(currentComponent, entryPointMethod.dependencyRequest().get());
+ addDependencyEdges(graph.componentNode(), entryPointMethod.dependencyRequest().get());
}
for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
@@ -180,7 +136,7 @@ final class BindingGraphConverter {
}
}
if (binding.kind().equals(SUBCOMPONENT_CREATOR)
- && binding.componentPath().equals(currentComponent.componentPath())) {
+ && binding.componentPath().equals(graph.componentPath())) {
network.addEdge(
binding,
subcomponentNode(binding.key().type().xprocessing(), graph),
@@ -190,51 +146,20 @@ final class BindingGraphConverter {
}
}
- if (bindingGraphPath.size() > 1) {
- LegacyBindingGraph parent = Iterators.get(bindingGraphPath.descendingIterator(), 1);
- parent
+ for (LegacyBindingGraph childGraph : graph.subgraphs()) {
+ visitComponent(childGraph);
+ graph
.componentDescriptor()
- .getFactoryMethodForChildComponent(graph.componentDescriptor())
+ .getFactoryMethodForChildComponent(childGraph.componentDescriptor())
.ifPresent(
childFactoryMethod ->
- visitSubcomponentFactoryMethod(
- parentComponent, currentComponent, childFactoryMethod.methodElement()));
- }
-
- for (LegacyBindingGraph child : graph.subgraphs()) {
- visitComponent(child, currentComponent);
+ network.addEdge(
+ graph.componentNode(),
+ childGraph.componentNode(),
+ new ChildFactoryMethodEdgeImpl(childFactoryMethod.methodElement())));
}
verify(bindingGraphPath.removeLast().equals(graph));
- verify(componentPaths.removeLast().equals(graphPath));
- }
-
- /**
- * Called once for each entry point in a component.
- *
- * @param componentNode the component that contains the entry point
- * @param entryPoint the entry point to visit
- */
- private void visitEntryPoint(ComponentNode componentNode, DependencyRequest entryPoint) {
- addDependencyEdges(componentNode, entryPoint);
- }
-
- /**
- * Called if this component was installed in its parent by a subcomponent factory method.
- *
- * @param parentComponent the parent graph
- * @param currentComponent the currently visited graph
- * @param factoryMethod the factory method in the parent component that declares that the
- * current component is a child
- */
- private void visitSubcomponentFactoryMethod(
- ComponentNode parentComponent,
- ComponentNode currentComponent,
- XMethodElement factoryMethod) {
- network.addEdge(
- parentComponent,
- currentComponent,
- new ChildFactoryMethodEdgeImpl(DaggerExecutableElement.from(factoryMethod)));
}
/**
@@ -242,7 +167,7 @@ final class BindingGraphConverter {
* component.
*/
private ComponentPath componentPath() {
- return componentPaths.getLast();
+ return bindingGraphPath.getLast().componentPath();
}
/**
@@ -250,9 +175,9 @@ final class BindingGraphConverter {
* component.
*/
private ComponentPath pathFromRootToAncestor(XTypeElement ancestor) {
- for (ComponentPath componentPath : componentPaths) {
- if (componentPath.currentComponent().xprocessing().equals(ancestor)) {
- return componentPath;
+ for (LegacyBindingGraph graph : bindingGraphPath) {
+ if (graph.componentDescriptor().typeElement().equals(ancestor)) {
+ return graph.componentPath();
}
}
throw new IllegalArgumentException(
@@ -325,23 +250,18 @@ final class BindingGraphConverter {
}
private ImmutableSet<BindingNode> bindingNodes(ResolvedBindings resolvedBindings) {
- ResolvedBindingsWithPath resolvedBindingsWithPath =
- ResolvedBindingsWithPath.create(resolvedBindings, componentPath());
- return resolvedBindingsMap.computeIfAbsent(
- resolvedBindingsWithPath, this::uncachedBindingNodes);
+ return resolvedBindingsMap.computeIfAbsent(resolvedBindings, this::uncachedBindingNodes);
}
- private ImmutableSet<BindingNode> uncachedBindingNodes(
- ResolvedBindingsWithPath resolvedBindingsWithPath) {
+ private ImmutableSet<BindingNode> uncachedBindingNodes(ResolvedBindings resolvedBindings) {
ImmutableSet.Builder<BindingNode> bindingNodes = ImmutableSet.builder();
- resolvedBindingsWithPath.resolvedBindings()
+ resolvedBindings
.allBindings()
.asMap()
.forEach(
(component, bindings) -> {
for (Binding binding : bindings) {
- bindingNodes.add(
- bindingNode(resolvedBindingsWithPath.resolvedBindings(), binding, component));
+ bindingNodes.add(bindingNode(resolvedBindings, binding, component));
}
});
return bindingNodes.build();
diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
index ba1157d50..503435901 100644
--- a/java/dagger/internal/codegen/binding/BindingGraphFactory.java
+++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
@@ -37,7 +37,6 @@ import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
@@ -51,11 +50,15 @@ import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.base.OptionalType;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.model.BindingGraph.ComponentNode;
+import dagger.internal.codegen.model.BindingKind;
+import dagger.internal.codegen.model.ComponentPath;
+import dagger.internal.codegen.model.DaggerTypeElement;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.model.Key;
+import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.model.Scope;
import dagger.internal.codegen.xprocessing.XTypeElements;
-import dagger.producers.internal.ProductionExecutorModule;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
@@ -182,7 +185,8 @@ public final class BindingGraphFactory implements ClearableCache {
ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();
// Collect transitive module bindings and multibinding declarations.
- for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
+ ImmutableSet<ModuleDescriptor> modules = modules(componentDescriptor, parentResolver);
+ for (ModuleDescriptor moduleDescriptor : modules) {
explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
@@ -190,8 +194,14 @@ public final class BindingGraphFactory implements ClearableCache {
optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
}
+ DaggerTypeElement component = DaggerTypeElement.from(componentDescriptor.typeElement());
+ ComponentPath componentPath =
+ parentResolver.isPresent()
+ ? parentResolver.get().componentPath.childPath(component)
+ : ComponentPath.create(ImmutableList.of(component));
final Resolver requestResolver =
new Resolver(
+ componentPath,
parentResolver,
componentDescriptor,
indexBindingDeclarationsByKey(explicitBindingsBuilder.build()),
@@ -214,7 +224,7 @@ public final class BindingGraphFactory implements ClearableCache {
if (createFullBindingGraph) {
// Resolve the keys for all bindings in all modules, stripping any multibinding contribution
// identifier so that the multibinding itself is resolved.
- modules(componentDescriptor, parentResolver).stream()
+ modules.stream()
.flatMap(module -> module.allBindingKeys().stream())
.map(Key::withoutMultibindingContributionIdentifier)
.forEach(requestResolver::resolve);
@@ -236,11 +246,7 @@ public final class BindingGraphFactory implements ClearableCache {
}
}
- return new LegacyBindingGraph(
- componentDescriptor,
- ImmutableMap.copyOf(requestResolver.getResolvedContributionBindings()),
- ImmutableMap.copyOf(requestResolver.getResolvedMembersInjectionBindings()),
- ImmutableList.copyOf(subgraphs.build()));
+ return new LegacyBindingGraph(requestResolver, subgraphs.build());
}
/**
@@ -254,38 +260,23 @@ public final class BindingGraphFactory implements ClearableCache {
return shouldIncludeImplicitProductionModules(componentDescriptor, parentResolver)
? new ImmutableSet.Builder<ModuleDescriptor>()
.addAll(componentDescriptor.modules())
- .add(descriptorForMonitoringModule(componentDescriptor.typeElement()))
- .add(descriptorForProductionExecutorModule())
+ .add(
+ moduleDescriptorFactory.create(
+ DaggerSuperficialValidation.requireTypeElement(
+ processingEnv,
+ generatedMonitoringModuleName(componentDescriptor.typeElement()))))
+ .add(
+ moduleDescriptorFactory.create(
+ processingEnv.requireTypeElement(TypeNames.PRODUCTION_EXECTUTOR_MODULE)))
.build()
: componentDescriptor.modules();
}
private boolean shouldIncludeImplicitProductionModules(
- ComponentDescriptor component, Optional<Resolver> parentResolver) {
- return component.isProduction()
- && ((!component.isSubcomponent() && component.isRealComponent())
- || (parentResolver.isPresent()
- && !parentResolver.get().componentDescriptor.isProduction()));
- }
-
- /**
- * Returns a descriptor for a generated module that handles monitoring for production components.
- * This module is generated in the {@link
- * dagger.internal.codegen.validation.MonitoringModuleProcessingStep}.
- *
- * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
- * processor to retry in a later processing round.
- */
- private ModuleDescriptor descriptorForMonitoringModule(XTypeElement componentDefinitionType) {
- return moduleDescriptorFactory.create(
- DaggerSuperficialValidation.requireTypeElement(
- processingEnv, generatedMonitoringModuleName(componentDefinitionType)));
- }
-
- /** Returns a descriptor {@link ProductionExecutorModule}. */
- private ModuleDescriptor descriptorForProductionExecutorModule() {
- return moduleDescriptorFactory.create(
- processingEnv.findTypeElement(TypeNames.PRODUCTION_EXECTUTOR_MODULE));
+ ComponentDescriptor componentDescriptor, Optional<Resolver> parentResolver) {
+ return componentDescriptor.isProduction()
+ && componentDescriptor.isRealComponent()
+ && (parentResolver.isEmpty() || !parentResolver.get().componentDescriptor.isProduction());
}
/** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
@@ -299,7 +290,70 @@ public final class BindingGraphFactory implements ClearableCache {
keysMatchingRequestCache.clear();
}
+ /** Represents a fully resolved binding graph. */
+ static final class LegacyBindingGraph {
+ private final Resolver resolver;
+ private final ImmutableList<LegacyBindingGraph> resolvedSubgraphs;
+ private final ComponentNode componentNode;
+
+ LegacyBindingGraph(Resolver resolver, ImmutableList<LegacyBindingGraph> resolvedSubgraphs) {
+ this.resolver = resolver;
+ this.resolvedSubgraphs = resolvedSubgraphs;
+ this.componentNode =
+ ComponentNodeImpl.create(resolver.componentPath, resolver.componentDescriptor);
+ }
+
+ /** Returns the {@link ComponentNode} associated with this binding graph. */
+ ComponentNode componentNode() {
+ return componentNode;
+ }
+
+ /** Returns the {@link ComponentPath} associated with this binding graph. */
+ ComponentPath componentPath() {
+ return resolver.componentPath;
+ }
+
+ /** Returns the {@link ComponentDescriptor} associated with this binding graph. */
+ ComponentDescriptor componentDescriptor() {
+ return resolver.componentDescriptor;
+ }
+
+ /**
+ * Returns the {@link ResolvedBindings} in this graph or a parent graph that matches the given
+ * request.
+ *
+ * <p>An exception is thrown if there are no resolved bindings found for the request; however,
+ * this should never happen since all dependencies should have been resolved at this point.
+ */
+ ResolvedBindings resolvedBindings(BindingRequest request) {
+ return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
+ ? resolver.getResolvedMembersInjectionBindings(request.key())
+ : resolver.getResolvedContributionBindings(request.key());
+ }
+
+ /**
+ * Returns all {@link ResolvedBindings} for the given request.
+ *
+ * <p>Note that this only returns the bindings resolved in this component. Bindings resolved in
+ * parent components are not included.
+ */
+ Iterable<ResolvedBindings> resolvedBindings() {
+ // Don't return an immutable collection - this is only ever used for looping over all bindings
+ // in the graph. Copying is wasteful, especially if is a hashing collection, since the values
+ // should all, by definition, be distinct.
+ return Iterables.concat(
+ resolver.resolvedMembersInjectionBindings.values(),
+ resolver.resolvedContributionBindings.values());
+ }
+
+ /** Returns the resolved subgraphs. */
+ ImmutableList<LegacyBindingGraph> subgraphs() {
+ return resolvedSubgraphs;
+ }
+ }
+
private final class Resolver {
+ final ComponentPath componentPath;
final Optional<Resolver> parentResolver;
final ComponentDescriptor componentDescriptor;
final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
@@ -318,6 +372,7 @@ public final class BindingGraphFactory implements ClearableCache {
final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
Resolver(
+ ComponentPath componentPath,
Optional<Resolver> parentResolver,
ComponentDescriptor componentDescriptor,
ImmutableSetMultimap<Key, ContributionBinding> explicitBindings,
@@ -325,6 +380,7 @@ public final class BindingGraphFactory implements ClearableCache {
ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations,
ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations,
ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations) {
+ this.componentPath = componentPath;
this.parentResolver = parentResolver;
this.componentDescriptor = checkNotNull(componentDescriptor);
this.explicitBindings = checkNotNull(explicitBindings);
@@ -428,6 +484,7 @@ public final class BindingGraphFactory implements ClearableCache {
}
return ResolvedBindings.forContributionBindings(
+ componentPath,
requestKey,
Multimaps.index(bindings, binding -> getOwningComponent(requestKey, binding)),
multibindingDeclarations,
@@ -466,8 +523,8 @@ public final class BindingGraphFactory implements ClearableCache {
injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
return binding.isPresent()
? ResolvedBindings.forMembersInjectionBinding(
- requestKey, componentDescriptor, binding.get())
- : ResolvedBindings.noBindings(requestKey);
+ componentPath, requestKey, componentDescriptor, binding.get())
+ : ResolvedBindings.noBindings(componentPath, requestKey);
}
/**
@@ -577,8 +634,7 @@ public final class BindingGraphFactory implements ClearableCache {
* ResolvedBindings#owningComponent(ContributionBinding)}.
*/
private XTypeElement getOwningComponent(Key requestKey, ContributionBinding binding) {
- if (isResolvedInParent(requestKey, binding)
- && !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
+ if (isResolvedInParent(requestKey, binding) && !requiresResolution(binding)) {
ResolvedBindings parentResolvedBindings =
parentResolver.get().resolvedContributionBindings.get(requestKey);
return parentResolvedBindings.owningComponent(binding);
@@ -799,11 +855,17 @@ public final class BindingGraphFactory implements ClearableCache {
/* Resolve in the parent in case there are multibinding contributions or conflicts in some
* component between this one and the previously-resolved one. */
parentResolver.get().resolve(key);
- if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
- && getLocalExplicitBindings(key).isEmpty()) {
+ ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
+ // TODO(b/305748522): Allow caching for assisted injection bindings.
+ boolean isAssistedInjectionBinding =
+ previouslyResolvedBindings.bindings().stream()
+ .anyMatch(binding -> binding.kind() == BindingKind.ASSISTED_INJECTION);
+ if (!isAssistedInjectionBinding
+ && !requiresResolution(key)
+ && getLocalExplicitBindings(key).isEmpty()) {
/* Cache the inherited parent component's bindings in case resolving at the parent found
* bindings in some component between this one and the previously-resolved one. */
- resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get());
+ resolvedContributionBindings.put(key, previouslyResolvedBindings);
return;
}
}
@@ -830,26 +892,29 @@ public final class BindingGraphFactory implements ClearableCache {
}
}
- /**
- * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
- * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
- */
- Map<Key, ResolvedBindings> getResolvedContributionBindings() {
- Map<Key, ResolvedBindings> bindings = new LinkedHashMap<>();
- parentResolver.ifPresent(parent -> bindings.putAll(parent.getResolvedContributionBindings()));
- bindings.putAll(resolvedContributionBindings);
- return bindings;
+ private ResolvedBindings getResolvedContributionBindings(Key key) {
+ if (resolvedContributionBindings.containsKey(key)) {
+ return resolvedContributionBindings.get(key);
+ }
+ if (parentResolver.isPresent()) {
+ return parentResolver.get().getResolvedContributionBindings(key);
+ }
+ throw new AssertionError("No resolved bindings for key: " + key);
}
- /**
- * Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
- * resolvers, indexed by {@link ResolvedBindings#key()}.
- */
- ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
- return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
+ private ResolvedBindings getResolvedMembersInjectionBindings(Key key) {
+ return resolvedMembersInjectionBindings.get(key);
}
- private final class LocalDependencyChecker {
+ private boolean requiresResolution(Key key) {
+ return new LegacyRequiresResolutionChecker().requiresResolution(key);
+ }
+
+ private boolean requiresResolution(Binding binding) {
+ return new LegacyRequiresResolutionChecker().requiresResolution(binding);
+ }
+
+ private final class LegacyRequiresResolutionChecker {
private final Set<Object> cycleChecker = new HashSet<>();
/**
@@ -863,14 +928,14 @@ public final class BindingGraphFactory implements ClearableCache {
*
* @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
*/
- private boolean dependsOnLocalBindings(Key key) {
+ private boolean requiresResolution(Key key) {
// Don't recur infinitely if there are valid cycles in the dependency graph.
// http://b/23032377
if (!cycleChecker.add(key)) {
return false;
}
return reentrantComputeIfAbsent(
- keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
+ keyDependsOnLocalBindingsCache, key, this::requiresResolutionUncached);
}
/**
@@ -882,75 +947,89 @@ public final class BindingGraphFactory implements ClearableCache {
* <p>We don't care about non-reusable scoped dependencies because they will never depend on
* multibindings with contributions from subcomponents.
*/
- private boolean dependsOnLocalBindings(Binding binding) {
+ private boolean requiresResolution(Binding binding) {
if (!cycleChecker.add(binding)) {
return false;
}
return reentrantComputeIfAbsent(
- bindingDependsOnLocalBindingsCache, binding, this::dependsOnLocalBindingsUncached);
+ bindingDependsOnLocalBindingsCache, binding, this::requiresResolutionUncached);
}
- private boolean dependsOnLocalBindingsUncached(Key key) {
+ private boolean requiresResolutionUncached(Key key) {
checkArgument(
getPreviouslyResolvedBindings(key).isPresent(),
"no previously resolved bindings in %s for %s",
Resolver.this,
key);
ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
- if (hasLocalMultibindingContributions(key)
- || hasLocalOptionalBindingContribution(previouslyResolvedBindings)) {
+ if (hasLocalBindings(previouslyResolvedBindings)) {
return true;
}
for (Binding binding : previouslyResolvedBindings.bindings()) {
- if (dependsOnLocalBindings(binding)) {
+ if (requiresResolution(binding)) {
return true;
}
}
return false;
}
- private boolean dependsOnLocalBindingsUncached(Binding binding) {
+ private boolean requiresResolutionUncached(Binding binding) {
if ((!binding.scope().isPresent() || binding.scope().get().isReusable())
// TODO(beder): Figure out what happens with production subcomponents.
&& !binding.bindingType().equals(BindingType.PRODUCTION)) {
for (DependencyRequest dependency : binding.dependencies()) {
- if (dependsOnLocalBindings(dependency.key())) {
+ if (requiresResolution(dependency.key())) {
return true;
}
}
}
return false;
}
+ }
- /**
- * Returns {@code true} if there is at least one multibinding contribution declared within
- * this component's modules that matches the key.
- */
- private boolean hasLocalMultibindingContributions(Key requestKey) {
- return keysMatchingRequest(requestKey)
- .stream()
- .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
- }
+ private boolean hasLocalBindings(Binding binding) {
+ return hasLocalMultibindingContributions(binding.key())
+ || hasLocalOptionalBindingContribution(
+ binding.key(), ImmutableSet.of((ContributionBinding) binding));
+ }
- /**
- * Returns {@code true} if there is a contribution in this component for an {@code
- * Optional<Foo>} key that has not been contributed in a parent.
- */
- private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
- if (resolvedBindings
- .contributionBindings()
- .stream()
- .map(ContributionBinding::kind)
- .anyMatch(isEqual(OPTIONAL))) {
- return !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
- .isEmpty();
- } else {
- // If a parent contributes a @Provides Optional<Foo> binding and a child has a
- // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
- // Foo on its own
- return !getOptionalBindingDeclarations(resolvedBindings.key()).isEmpty();
- }
+ private boolean hasLocalBindings(ResolvedBindings resolvedBindings) {
+ return hasLocalMultibindingContributions(resolvedBindings.key())
+ || hasLocalOptionalBindingContribution(resolvedBindings);
+ }
+
+ /**
+ * Returns {@code true} if there is at least one multibinding contribution declared within
+ * this component's modules that matches the key.
+ */
+ private boolean hasLocalMultibindingContributions(Key requestKey) {
+ return keysMatchingRequest(requestKey)
+ .stream()
+ .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
+ }
+
+ /**
+ * Returns {@code true} if there is a contribution in this component for an {@code
+ * Optional<Foo>} key that has not been contributed in a parent.
+ */
+ private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
+ return hasLocalOptionalBindingContribution(
+ resolvedBindings.key(), resolvedBindings.contributionBindings());
+ }
+
+ private boolean hasLocalOptionalBindingContribution(
+ Key key, ImmutableSet<ContributionBinding> previousContributionBindings) {
+ if (previousContributionBindings.stream()
+ .map(ContributionBinding::kind)
+ .anyMatch(isEqual(OPTIONAL))) {
+ return !getLocalExplicitBindings(keyFactory.unwrapOptional(key).get())
+ .isEmpty();
+ } else {
+ // If a parent contributes a @Provides Optional<Foo> binding and a child has a
+ // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
+ // Foo on its own
+ return !getOptionalBindingDeclarations(key).isEmpty();
}
}
}
diff --git a/java/dagger/internal/codegen/binding/CancellationPolicy.java b/java/dagger/internal/codegen/binding/CancellationPolicy.java
new file mode 100644
index 000000000..7be42ffbb
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/CancellationPolicy.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+
+import androidx.room.compiler.processing.XAnnotation;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.xprocessing.XAnnotations;
+
+/**
+ * The cancellation policy for a {@link dagger.producers.ProductionComponent}.
+ *
+ * <p>@see dagger.producers.CancellationPolicy
+ */
+public enum CancellationPolicy {
+ PROPAGATE,
+ IGNORE;
+
+ static CancellationPolicy from(XAnnotation annotation) {
+ checkArgument(XAnnotations.getClassName(annotation).equals(TypeNames.CANCELLATION_POLICY));
+ return valueOf(getSimpleName(annotation.getAsEnum("fromSubcomponents")));
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
index 077f4546a..4b1e15ede 100644
--- a/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
+++ b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
@@ -18,6 +18,7 @@ package dagger.internal.codegen.binding;
import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import androidx.room.compiler.processing.XMethodElement;
import dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge;
import dagger.internal.codegen.model.DaggerExecutableElement;
@@ -26,8 +27,8 @@ public final class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge
private final DaggerExecutableElement factoryMethod;
- ChildFactoryMethodEdgeImpl(DaggerExecutableElement factoryMethod) {
- this.factoryMethod = factoryMethod;
+ ChildFactoryMethodEdgeImpl(XMethodElement factoryMethod) {
+ this.factoryMethod = DaggerExecutableElement.from(factoryMethod);
}
@Override
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
index 0bc22099a..254ca7ea5 100644
--- a/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
+++ b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
@@ -41,6 +41,7 @@ import dagger.internal.codegen.base.ComponentCreatorAnnotation;
import dagger.internal.codegen.base.ComponentCreatorKind;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DependencyRequest;
+import dagger.internal.codegen.xprocessing.XElements;
import java.util.List;
/**
@@ -161,7 +162,12 @@ public abstract class ComponentCreatorDescriptor {
for (XMethodElement method : getAllUnimplementedMethods(creator)) {
XMethodType resolvedMethodType = method.asMemberOf(creator.getType());
if (isSubtype(componentType, resolvedMethodType.getReturnType())) {
- verify(factoryMethod == null); // validation should have ensured there's only 1.
+ verify(
+ factoryMethod == null,
+ "Expected a single factory method for %s but found multiple: [%s, %s]",
+ XElements.toStableString(creator),
+ XElements.toStableString(factoryMethod),
+ XElements.toStableString(method));
factoryMethod = method;
} else {
XExecutableParameterElement parameter = getOnlyElement(method.getParameters());
@@ -171,7 +177,10 @@ public abstract class ComponentCreatorDescriptor {
method);
}
}
- verify(factoryMethod != null); // validation should have ensured this.
+ verify(
+ factoryMethod != null,
+ "Expected a single factory method for %s but found none.",
+ XElements.toStableString(creator));
ImmutableSetMultimap.Builder<ComponentRequirement, XExecutableParameterElement>
factoryParameters = ImmutableSetMultimap.builder();
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptor.java b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
index a105608b7..b0a0183b9 100644
--- a/java/dagger/internal/codegen/binding/ComponentDescriptor.java
+++ b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
@@ -21,14 +21,28 @@ import static androidx.room.compiler.processing.XTypeKt.isVoid;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotation;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.Scopes.productionScope;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.javapoet.TypeNames.isFutureType;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
-import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
@@ -44,16 +58,21 @@ import com.squareup.javapoet.TypeName;
import dagger.Component;
import dagger.Module;
import dagger.Subcomponent;
+import dagger.internal.codegen.base.ClearableCache;
import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.ModuleAnnotation;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.model.Scope;
-import dagger.internal.codegen.xprocessing.XAnnotations;
+import dagger.internal.codegen.xprocessing.XTypeElements;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* A component declaration.
@@ -68,52 +87,54 @@ import java.util.stream.Stream;
@CheckReturnValue
@AutoValue
public abstract class ComponentDescriptor {
+ /** The annotation that specifies that {@link #typeElement()} is a component. */
+ public abstract ComponentAnnotation annotation();
+
/**
- * The cancellation policy for a {@link dagger.producers.ProductionComponent}.
- *
- * <p>@see dagger.producers.CancellationPolicy
+ * The element that defines the component. This is the element to which the {@link #annotation()}
+ * was applied.
*/
- public enum CancellationPolicy {
- PROPAGATE,
- IGNORE;
+ public abstract XTypeElement typeElement();
- private static CancellationPolicy from(XAnnotation annotation) {
- checkArgument(XAnnotations.getClassName(annotation).equals(TypeNames.CANCELLATION_POLICY));
- return valueOf(getSimpleName(annotation.getAsEnum("fromSubcomponents")));
- }
- }
+ /**
+ * The set of component dependencies listed in {@link Component#dependencies} or {@link
+ * dagger.producers.ProductionComponent#dependencies()}.
+ */
+ public abstract ImmutableSet<ComponentRequirement> dependencies();
- /** Creates a {@link ComponentDescriptor}. */
- static ComponentDescriptor create(
- ComponentAnnotation componentAnnotation,
- XTypeElement component,
- ImmutableSet<ComponentRequirement> componentDependencies,
- ImmutableSet<ModuleDescriptor> transitiveModules,
- ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod,
- ImmutableSet<Scope> scopes,
- ImmutableSet<ComponentDescriptor> subcomponentsFromModules,
- ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod,
- ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod,
- ImmutableSet<ComponentMethodDescriptor> componentMethods,
- Optional<ComponentCreatorDescriptor> creator) {
- ComponentDescriptor descriptor =
- new AutoValue_ComponentDescriptor(
- componentAnnotation,
- component,
- componentDependencies,
- transitiveModules,
- dependenciesByDependencyMethod,
- scopes,
- subcomponentsFromModules,
- subcomponentsByFactoryMethod,
- subcomponentsByBuilderMethod,
- componentMethods,
- creator);
- return descriptor;
- }
+ /**
+ * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
+ * traversing {@link Module#includes()}.
+ */
+ public abstract ImmutableSet<ModuleDescriptor> modules();
- /** The annotation that specifies that {@link #typeElement()} is a component. */
- public abstract ComponentAnnotation annotation();
+ /** The scopes of the component. */
+ public abstract ImmutableSet<Scope> scopes();
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
+ * Module#subcomponents() module's subcomponents}.
+ */
+ abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * factory method.
+ */
+ public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByFactoryMethods();
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * builder method.
+ */
+ abstract ImmutableMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByBuilderEntryPoints();
+
+ public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
+
+ /** Returns a descriptor for the creator type for this component type, if the user defined one. */
+ public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
/** Returns {@code true} if this is a subcomponent. */
public final boolean isSubcomponent() {
@@ -136,18 +157,6 @@ public abstract class ComponentDescriptor {
return annotation().isRealComponent();
}
- /**
- * The element that defines the component. This is the element to which the {@link #annotation()}
- * was applied.
- */
- public abstract XTypeElement typeElement();
-
- /**
- * The set of component dependencies listed in {@link Component#dependencies} or {@link
- * dagger.producers.ProductionComponent#dependencies()}.
- */
- public abstract ImmutableSet<ComponentRequirement> dependencies();
-
/** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
return Stream.concat(
@@ -158,12 +167,6 @@ public abstract class ComponentDescriptor {
.collect(toImmutableSet());
}
- /**
- * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
- * traversing {@link Module#includes()}.
- */
- public abstract ImmutableSet<ModuleDescriptor> modules();
-
/** The types of the {@link #modules()}. */
public final ImmutableSet<XTypeElement> moduleTypes() {
return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
@@ -198,13 +201,21 @@ public abstract class ComponentDescriptor {
}
/**
- * This component's {@linkplain #dependencies() dependencies} keyed by each provision or
- * production method defined by that dependency. Note that the dependencies' types are not simply
- * the enclosing type of the method; a method may be declared by a supertype of the actual
- * dependency.
+ * Returns this component's dependencies keyed by its provision/production method.
+ *
+ * <p>Note that the dependencies' types are not simply the enclosing type of the method; a method
+ * may be declared by a supertype of the actual dependency.
*/
- public abstract ImmutableMap<XMethodElement, ComponentRequirement>
- dependenciesByDependencyMethod();
+ @Memoized
+ public ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod() {
+ ImmutableMap.Builder<XMethodElement, ComponentRequirement> builder = ImmutableMap.builder();
+ for (ComponentRequirement componentDependency : dependencies()) {
+ XTypeElements.getAllMethods(componentDependency.typeElement()).stream()
+ .filter(ComponentDescriptor::isComponentContributionMethod)
+ .forEach(method -> builder.put(method, componentDependency));
+ }
+ return builder.buildOrThrow();
+ }
/** The {@linkplain #dependencies() component dependency} that defines a method. */
public final ComponentRequirement getDependencyThatDefinesMethod(XElement method) {
@@ -216,9 +227,6 @@ public abstract class ComponentDescriptor {
return dependenciesByDependencyMethod().get(method);
}
- /** The scopes of the component. */
- public abstract ImmutableSet<Scope> scopes();
-
/**
* All {@link Subcomponent}s which are direct children of this component. This includes
* subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
@@ -233,19 +241,6 @@ public abstract class ComponentDescriptor {
.build();
}
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
- * Module#subcomponents() module's subcomponents}.
- */
- abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
-
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
- * factory method.
- */
- public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
- childComponentsDeclaredByFactoryMethods();
-
/** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
@Memoized
public ImmutableMap<XTypeElement, ComponentDescriptor> childComponentsByElement() {
@@ -259,13 +254,6 @@ public abstract class ComponentDescriptor {
childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
}
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
- * builder method.
- */
- abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
- childComponentsDeclaredByBuilderEntryPoints();
-
private final Supplier<ImmutableMap<XTypeElement, ComponentDescriptor>>
childComponentsByBuilderType =
Suppliers.memoize(
@@ -285,8 +273,6 @@ public abstract class ComponentDescriptor {
builderType.getQualifiedName());
}
- public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
-
/** Returns the first component method associated with this binding request, if one exists. */
public Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) {
return Optional.ofNullable(firstMatchingComponentMethods().get(request));
@@ -308,11 +294,6 @@ public abstract class ComponentDescriptor {
.collect(toImmutableSet());
}
- // TODO(gak): Consider making this non-optional and revising the
- // interaction between the spec & generation
- /** Returns a descriptor for the creator type for this component type, if the user defined one. */
- public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
-
/**
* Returns {@code true} for components that have a creator, either because the user {@linkplain
* #creatorDescriptor() specified one} or because it's a top-level component with an implicit
@@ -408,4 +389,204 @@ public abstract class ComponentDescriptor {
static boolean isComponentProductionMethod(XMethodElement method) {
return isComponentContributionMethod(method) && isFutureType(method.getReturnType());
}
+
+ /** A factory for creating a {@link ComponentDescriptor}. */
+ @Singleton
+ public static final class Factory implements ClearableCache {
+ private final XProcessingEnv processingEnv;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final ModuleDescriptor.Factory moduleDescriptorFactory;
+ private final InjectionAnnotations injectionAnnotations;
+ private final DaggerSuperficialValidation superficialValidation;
+ private final Map<XTypeElement, ComponentDescriptor> cache = new HashMap<>();
+
+ @Inject
+ Factory(
+ XProcessingEnv processingEnv,
+ DependencyRequestFactory dependencyRequestFactory,
+ ModuleDescriptor.Factory moduleDescriptorFactory,
+ InjectionAnnotations injectionAnnotations,
+ DaggerSuperficialValidation superficialValidation) {
+ this.processingEnv = processingEnv;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ this.moduleDescriptorFactory = moduleDescriptorFactory;
+ this.injectionAnnotations = injectionAnnotations;
+ this.superficialValidation = superficialValidation;
+ }
+
+ /** Returns a descriptor for a root component type. */
+ public ComponentDescriptor rootComponentDescriptor(XTypeElement typeElement) {
+ Optional<ComponentAnnotation> annotation =
+ rootComponentAnnotation(typeElement, superficialValidation);
+ checkArgument(annotation.isPresent(), "%s must have a component annotation", typeElement);
+ return create(typeElement, annotation.get());
+ }
+
+ /** Returns a descriptor for a subcomponent type. */
+ public ComponentDescriptor subcomponentDescriptor(XTypeElement typeElement) {
+ Optional<ComponentAnnotation> annotation =
+ subcomponentAnnotation(typeElement, superficialValidation);
+ checkArgument(annotation.isPresent(), "%s must have a subcomponent annotation", typeElement);
+ return create(typeElement, annotation.get());
+ }
+
+ /**
+ * Returns a descriptor for a fictional component based on a module type in order to validate
+ * its bindings.
+ */
+ public ComponentDescriptor moduleComponentDescriptor(XTypeElement typeElement) {
+ Optional<ModuleAnnotation> annotation = moduleAnnotation(typeElement, superficialValidation);
+ checkArgument(annotation.isPresent(), "%s must have a module annotation", typeElement);
+ return create(typeElement, ComponentAnnotation.fromModuleAnnotation(annotation.get()));
+ }
+
+ private ComponentDescriptor create(
+ XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
+ return reentrantComputeIfAbsent(
+ cache, typeElement, unused -> createUncached(typeElement, componentAnnotation));
+ }
+
+ private ComponentDescriptor createUncached(
+ XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
+ ImmutableSet<ComponentRequirement> componentDependencies =
+ componentAnnotation.dependencyTypes().stream()
+ .map(ComponentRequirement::forDependency)
+ .collect(toImmutableSet());
+
+ // Start with the component's modules. For fictional components built from a module, start
+ // with that module.
+ ImmutableSet<XTypeElement> modules =
+ componentAnnotation.isRealComponent()
+ ? componentAnnotation.modules()
+ : ImmutableSet.of(typeElement);
+
+ ImmutableSet<ModuleDescriptor> transitiveModules =
+ moduleDescriptorFactory.transitiveModules(modules);
+
+ ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
+ ImmutableSet.builder();
+ ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByFactoryMethod = ImmutableBiMap.builder();
+ ImmutableMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByBuilderMethod = ImmutableBiMap.builder();
+ if (componentAnnotation.isRealComponent()) {
+ for (XMethodElement componentMethod : getAllUnimplementedMethods(typeElement)) {
+ ComponentMethodDescriptor componentMethodDescriptor =
+ getDescriptorForComponentMethod(componentAnnotation, typeElement, componentMethod);
+ componentMethodsBuilder.add(componentMethodDescriptor);
+ componentMethodDescriptor
+ .subcomponent()
+ .ifPresent(
+ subcomponent -> {
+ // If the dependency request is present, that means the method returns the
+ // subcomponent factory.
+ if (componentMethodDescriptor.dependencyRequest().isPresent()) {
+ subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
+ } else {
+ subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
+ }
+ });
+ }
+ }
+
+ // Validation should have ensured that this set will have at most one element.
+ ImmutableSet<XTypeElement> enclosedCreators =
+ enclosedAnnotatedTypes(typeElement, creatorAnnotationsFor(componentAnnotation));
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ enclosedCreators.isEmpty()
+ ? Optional.empty()
+ : Optional.of(
+ ComponentCreatorDescriptor.create(
+ getOnlyElement(enclosedCreators), dependencyRequestFactory));
+
+ ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(typeElement);
+ if (componentAnnotation.isProduction()) {
+ scopes =
+ ImmutableSet.<Scope>builder()
+ .addAll(scopes).add(productionScope(processingEnv))
+ .build();
+ }
+
+ ImmutableSet<ComponentDescriptor> subcomponentsFromModules =
+ transitiveModules.stream()
+ .flatMap(transitiveModule -> transitiveModule.subcomponentDeclarations().stream())
+ .map(SubcomponentDeclaration::subcomponentType)
+ .map(this::subcomponentDescriptor)
+ .collect(toImmutableSet());
+
+ return new AutoValue_ComponentDescriptor(
+ componentAnnotation,
+ typeElement,
+ componentDependencies,
+ transitiveModules,
+ scopes,
+ subcomponentsFromModules,
+ subcomponentsByFactoryMethod.buildOrThrow(),
+ subcomponentsByBuilderMethod.buildOrThrow(),
+ componentMethodsBuilder.build(),
+ creatorDescriptor);
+ }
+
+ private ComponentMethodDescriptor getDescriptorForComponentMethod(
+ ComponentAnnotation componentAnnotation,
+ XTypeElement componentElement,
+ XMethodElement componentMethod) {
+ ComponentMethodDescriptor.Builder descriptor =
+ ComponentMethodDescriptor.builder(componentMethod);
+
+ XMethodType resolvedComponentMethod = componentMethod.asMemberOf(componentElement.getType());
+ XType returnType = resolvedComponentMethod.getReturnType();
+ if (isDeclared(returnType)
+ && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
+ XTypeElement returnTypeElement = returnType.getTypeElement();
+ if (returnTypeElement.hasAnyAnnotation(subcomponentAnnotations())) {
+ // It's a subcomponent factory method. There is no dependency request, and there could be
+ // any number of parameters. Just return the descriptor.
+ return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
+ }
+ if (isSubcomponentCreator(returnTypeElement)) {
+ descriptor.subcomponent(
+ subcomponentDescriptor(returnTypeElement.getEnclosingTypeElement()));
+ }
+ }
+
+ switch (componentMethod.getParameters().size()) {
+ case 0:
+ checkArgument(
+ !isVoid(returnType), "component method cannot be void: %s", componentMethod);
+ descriptor.dependencyRequest(
+ componentAnnotation.isProduction()
+ ? dependencyRequestFactory.forComponentProductionMethod(
+ componentMethod, resolvedComponentMethod)
+ : dependencyRequestFactory.forComponentProvisionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ case 1:
+ checkArgument(
+ isVoid(returnType)
+ // TODO(bcorso): Replace this with isSameType()?
+ || returnType
+ .getTypeName()
+ .equals(resolvedComponentMethod.getParameterTypes().get(0).getTypeName()),
+ "members injection method must return void or parameter type: %s",
+ componentMethod);
+ descriptor.dependencyRequest(
+ dependencyRequestFactory.forComponentMembersInjectionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "component method has too many parameters: " + componentMethod);
+ }
+
+ return descriptor.build();
+ }
+
+ @Override
+ public void clearCache() {
+ cache.clear();
+ }
+ }
}
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
deleted file mode 100644
index 1d0945078..000000000
--- a/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.binding;
-
-import static androidx.room.compiler.processing.XTypeKt.isVoid;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotation;
-import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
-import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
-import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
-import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
-import static dagger.internal.codegen.base.Scopes.productionScope;
-import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
-import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
-import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
-
-import androidx.room.compiler.processing.XMethodElement;
-import androidx.room.compiler.processing.XMethodType;
-import androidx.room.compiler.processing.XProcessingEnv;
-import androidx.room.compiler.processing.XType;
-import androidx.room.compiler.processing.XTypeElement;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.base.ComponentAnnotation;
-import dagger.internal.codegen.base.DaggerSuperficialValidation;
-import dagger.internal.codegen.base.ModuleAnnotation;
-import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.model.Scope;
-import dagger.internal.codegen.xprocessing.XTypeElements;
-import java.util.Optional;
-import javax.inject.Inject;
-
-/** A factory for {@link ComponentDescriptor}s. */
-public final class ComponentDescriptorFactory {
- private final XProcessingEnv processingEnv;
- private final DependencyRequestFactory dependencyRequestFactory;
- private final ModuleDescriptor.Factory moduleDescriptorFactory;
- private final InjectionAnnotations injectionAnnotations;
- private final DaggerSuperficialValidation superficialValidation;
-
- @Inject
- ComponentDescriptorFactory(
- XProcessingEnv processingEnv,
- DependencyRequestFactory dependencyRequestFactory,
- ModuleDescriptor.Factory moduleDescriptorFactory,
- InjectionAnnotations injectionAnnotations,
- DaggerSuperficialValidation superficialValidation) {
- this.processingEnv = processingEnv;
- this.dependencyRequestFactory = dependencyRequestFactory;
- this.moduleDescriptorFactory = moduleDescriptorFactory;
- this.injectionAnnotations = injectionAnnotations;
- this.superficialValidation = superficialValidation;
- }
-
- /** Returns a descriptor for a root component type. */
- public ComponentDescriptor rootComponentDescriptor(XTypeElement typeElement) {
- Optional<ComponentAnnotation> annotation =
- rootComponentAnnotation(typeElement, superficialValidation);
- checkArgument(annotation.isPresent(), "%s must have a component annotation", typeElement);
- return create(typeElement, annotation.get());
- }
-
- /** Returns a descriptor for a subcomponent type. */
- public ComponentDescriptor subcomponentDescriptor(XTypeElement typeElement) {
- Optional<ComponentAnnotation> annotation =
- subcomponentAnnotation(typeElement, superficialValidation);
- checkArgument(annotation.isPresent(), "%s must have a subcomponent annotation", typeElement);
- return create(typeElement, annotation.get());
- }
-
- /**
- * Returns a descriptor for a fictional component based on a module type in order to validate its
- * bindings.
- */
- public ComponentDescriptor moduleComponentDescriptor(XTypeElement typeElement) {
- Optional<ModuleAnnotation> annotation = moduleAnnotation(typeElement, superficialValidation);
- checkArgument(annotation.isPresent(), "%s must have a module annotation", typeElement);
- return create(typeElement, ComponentAnnotation.fromModuleAnnotation(annotation.get()));
- }
-
- private ComponentDescriptor create(
- XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
- ImmutableSet<ComponentRequirement> componentDependencies =
- componentAnnotation.dependencyTypes().stream()
- .map(ComponentRequirement::forDependency)
- .collect(toImmutableSet());
-
- ImmutableMap.Builder<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod =
- ImmutableMap.builder();
- for (ComponentRequirement componentDependency : componentDependencies) {
- XTypeElements.getAllMethods(componentDependency.typeElement()).stream()
- .filter(ComponentDescriptor::isComponentContributionMethod)
- .forEach(method -> dependenciesByDependencyMethod.put(method, componentDependency));
- }
-
- // Start with the component's modules. For fictional components built from a module, start with
- // that module.
- ImmutableSet<XTypeElement> modules =
- componentAnnotation.isRealComponent()
- ? componentAnnotation.modules()
- : ImmutableSet.of(typeElement);
-
- ImmutableSet<ModuleDescriptor> transitiveModules =
- moduleDescriptorFactory.transitiveModules(modules);
-
- ImmutableSet<ComponentDescriptor> subcomponentsFromModules =
- transitiveModules.stream()
- .flatMap(transitiveModule -> transitiveModule.subcomponentDeclarations().stream())
- .map(SubcomponentDeclaration::subcomponentType)
- .map(this::subcomponentDescriptor)
- .collect(toImmutableSet());
-
- ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
- ImmutableSet.builder();
- ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
- subcomponentsByFactoryMethod = ImmutableBiMap.builder();
- ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
- subcomponentsByBuilderMethod = ImmutableBiMap.builder();
- if (componentAnnotation.isRealComponent()) {
- for (XMethodElement componentMethod : getAllUnimplementedMethods(typeElement)) {
- ComponentMethodDescriptor componentMethodDescriptor =
- getDescriptorForComponentMethod(componentAnnotation, typeElement, componentMethod);
- componentMethodsBuilder.add(componentMethodDescriptor);
- componentMethodDescriptor
- .subcomponent()
- .ifPresent(
- subcomponent -> {
- // If the dependency request is present, that means the method returns the
- // subcomponent factory.
- if (componentMethodDescriptor.dependencyRequest().isPresent()) {
- subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
- } else {
- subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
- }
- });
- }
- }
-
- // Validation should have ensured that this set will have at most one element.
- ImmutableSet<XTypeElement> enclosedCreators =
- enclosedAnnotatedTypes(typeElement, creatorAnnotationsFor(componentAnnotation));
- Optional<ComponentCreatorDescriptor> creatorDescriptor =
- enclosedCreators.isEmpty()
- ? Optional.empty()
- : Optional.of(
- ComponentCreatorDescriptor.create(
- getOnlyElement(enclosedCreators), dependencyRequestFactory));
-
- ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(typeElement);
- if (componentAnnotation.isProduction()) {
- scopes =
- ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(processingEnv)).build();
- }
-
- return ComponentDescriptor.create(
- componentAnnotation,
- typeElement,
- componentDependencies,
- transitiveModules,
- dependenciesByDependencyMethod.build(),
- scopes,
- subcomponentsFromModules,
- subcomponentsByFactoryMethod.build(),
- subcomponentsByBuilderMethod.build(),
- componentMethodsBuilder.build(),
- creatorDescriptor);
- }
-
- private ComponentMethodDescriptor getDescriptorForComponentMethod(
- ComponentAnnotation componentAnnotation,
- XTypeElement componentElement,
- XMethodElement componentMethod) {
- ComponentMethodDescriptor.Builder descriptor =
- ComponentMethodDescriptor.builder(componentMethod);
-
- XMethodType resolvedComponentMethod = componentMethod.asMemberOf(componentElement.getType());
- XType returnType = resolvedComponentMethod.getReturnType();
- if (isDeclared(returnType) && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
- XTypeElement returnTypeElement = returnType.getTypeElement();
- if (returnTypeElement.hasAnyAnnotation(subcomponentAnnotations())) {
- // It's a subcomponent factory method. There is no dependency request, and there could be
- // any number of parameters. Just return the descriptor.
- return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
- }
- if (isSubcomponentCreator(returnTypeElement)) {
- descriptor.subcomponent(
- subcomponentDescriptor(returnTypeElement.getEnclosingTypeElement()));
- }
- }
-
- switch (componentMethod.getParameters().size()) {
- case 0:
- checkArgument(!isVoid(returnType), "component method cannot be void: %s", componentMethod);
- descriptor.dependencyRequest(
- componentAnnotation.isProduction()
- ? dependencyRequestFactory.forComponentProductionMethod(
- componentMethod, resolvedComponentMethod)
- : dependencyRequestFactory.forComponentProvisionMethod(
- componentMethod, resolvedComponentMethod));
- break;
-
- case 1:
- checkArgument(
- isVoid(returnType)
- // TODO(bcorso): Replace this with isSameType()?
- || returnType
- .getTypeName()
- .equals(resolvedComponentMethod.getParameterTypes().get(0).getTypeName()),
- "members injection method must return void or parameter type: %s",
- componentMethod);
- descriptor.dependencyRequest(
- dependencyRequestFactory.forComponentMembersInjectionMethod(
- componentMethod, resolvedComponentMethod));
- break;
-
- default:
- throw new IllegalArgumentException(
- "component method has too many parameters: " + componentMethod);
- }
-
- return descriptor.build();
- }
-}
diff --git a/java/dagger/internal/codegen/binding/ComponentRequirement.java b/java/dagger/internal/codegen/binding/ComponentRequirement.java
index df105773e..1779ec1d6 100644
--- a/java/dagger/internal/codegen/binding/ComponentRequirement.java
+++ b/java/dagger/internal/codegen/binding/ComponentRequirement.java
@@ -145,7 +145,7 @@ public abstract class ComponentRequirement {
* <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered
* {@code static}, requiring no module instance.
*/
- private boolean requiresModuleInstance() {
+ public boolean requiresModuleInstance() {
if (typeElement().isKotlinObject() || typeElement().isCompanionObject()) {
return false;
}
diff --git a/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
index 80591b121..853ee994b 100644
--- a/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
+++ b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
@@ -25,12 +25,10 @@ import static dagger.internal.codegen.base.RequestKinds.requestType;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XProcessingEnv;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import dagger.Provides;
import dagger.internal.codegen.base.Formatter;
import dagger.internal.codegen.model.DaggerAnnotation;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.xprocessing.XTypes;
-import dagger.producers.Produces;
import java.util.Optional;
import javax.inject.Inject;
@@ -43,8 +41,7 @@ import javax.inject.Inject;
* <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()}
* <dt>For component injection methods
* <dd>{@code SomeType is injected at\n ComponentType.method(foo)}
- * <dt>For parameters to {@link Provides @Provides}, {@link Produces @Produces}, or {@link
- * Inject @Inject} methods:
+ * <dt>For parameters to {@code @Provides}, {@code @Produces}, or {@code @Inject} methods:
* <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.method([…, ]param[, …])}
* <dt>For parameters to {@link Inject @Inject} constructors:
* <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType([…, ]param[, …])}
diff --git a/java/dagger/internal/codegen/binding/FrameworkField.java b/java/dagger/internal/codegen/binding/FrameworkField.java
index b1d1cf91b..a9f3bbfbd 100644
--- a/java/dagger/internal/codegen/binding/FrameworkField.java
+++ b/java/dagger/internal/codegen/binding/FrameworkField.java
@@ -24,12 +24,14 @@ import static dagger.internal.codegen.model.BindingKind.MEMBERS_INJECTOR;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import androidx.room.compiler.processing.XElement;
-import androidx.room.compiler.processing.XType;
import com.google.auto.value.AutoValue;
import com.google.common.base.CaseFormat;
+import com.google.common.base.Preconditions;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.javapoet.TypeNames;
import java.util.Optional;
/**
@@ -49,16 +51,17 @@ public abstract class FrameworkField {
/**
* Creates a framework field.
*
- * @param frameworkClassName the name of the framework class (e.g., {@link javax.inject.Provider})
- * @param valueTypeName the name of the type parameter of the framework class (e.g., {@code Foo}
- * for {@code Provider<Foo>}
- * @param fieldName the name of the field
+ * @param fieldType the type of the framework field (e.g., {@code Provider<Foo>}).
+ * @param fieldName the base name of the field. The name of the raw type of the field will be
+ * added as a suffix
*/
- public static FrameworkField create(
- ClassName frameworkClassName, TypeName valueTypeName, String fieldName) {
- String suffix = frameworkClassName.simpleName();
+ public static FrameworkField create(TypeName fieldType, String fieldName) {
+ Preconditions.checkState(
+ fieldType instanceof ClassName || fieldType instanceof ParameterizedTypeName,
+ "Can only create a field with a class name or parameterized type name");
+ String suffix = ((ClassName) TypeNames.rawTypeName(fieldType)).simpleName();
return new AutoValue_FrameworkField(
- ParameterizedTypeName.get(frameworkClassName, valueTypeName),
+ fieldType,
fieldName.endsWith(suffix) ? fieldName : fieldName + suffix);
}
@@ -71,15 +74,26 @@ public abstract class FrameworkField {
public static FrameworkField forBinding(
ContributionBinding binding, Optional<ClassName> frameworkClassName) {
return create(
- frameworkClassName.orElse(binding.frameworkType().frameworkClassName()),
- fieldValueType(binding).getTypeName(),
+ fieldType(binding, frameworkClassName.orElse(binding.frameworkType().frameworkClassName())),
frameworkFieldName(binding));
}
- private static XType fieldValueType(ContributionBinding binding) {
- return binding.contributionType().isMultibinding()
- ? binding.contributedType()
- : binding.key().type().xprocessing();
+ private static TypeName fieldType(ContributionBinding binding, ClassName frameworkClassName) {
+ if (binding.contributionType().isMultibinding()) {
+ return ParameterizedTypeName.get(frameworkClassName, binding.contributedType().getTypeName());
+ }
+
+ // If the binding key type is a Map<K, Provider<V>>, we need to change field type to a raw
+ // type. This is because it actually needs to be changed to Map<K, dagger.internal.Provider<V>>,
+ // but that gets into assignment issues when the field is passed to methods that expect
+ // Map<K, javax.inject.Provider<V>>. We could add casts everywhere, but it is easier to just
+ // make the field itself a raw type.
+ if (MapType.isMapOfProvider(binding.contributedType())) {
+ return frameworkClassName;
+ }
+
+ return ParameterizedTypeName.get(
+ frameworkClassName, binding.key().type().xprocessing().getTypeName());
}
private static String frameworkFieldName(ContributionBinding binding) {
@@ -104,7 +118,7 @@ public abstract class FrameworkField {
}
}
- public abstract ParameterizedTypeName type();
+ public abstract TypeName type();
public abstract String name();
}
diff --git a/java/dagger/internal/codegen/binding/FrameworkType.java b/java/dagger/internal/codegen/binding/FrameworkType.java
index ce3b149bc..c99594892 100644
--- a/java/dagger/internal/codegen/binding/FrameworkType.java
+++ b/java/dagger/internal/codegen/binding/FrameworkType.java
@@ -96,7 +96,8 @@ public enum FrameworkType {
case PROVIDER_OF_LAZY:
return Expression.create(
- from.type().rewrapType(TypeNames.LAZY).wrapType(TypeNames.PROVIDER), codeBlock);
+ from.type().rewrapType(TypeNames.LAZY).wrapType(TypeNames.DAGGER_PROVIDER),
+ codeBlock);
case FUTURE:
return Expression.create(from.type().rewrapType(TypeNames.LISTENABLE_FUTURE), codeBlock);
@@ -178,7 +179,7 @@ public enum FrameworkType {
public ClassName frameworkClassName() {
switch (this) {
case PROVIDER:
- return TypeNames.PROVIDER;
+ return TypeNames.DAGGER_PROVIDER;
case PRODUCER_NODE:
// TODO(cgdecker): Replace this with new class for representing internal producer nodes.
// Currently the new class is CancellableProducer, but it may be changed to ProducerNode and
diff --git a/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
index 0e17d9687..bd439bf93 100644
--- a/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
+++ b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
@@ -19,12 +19,10 @@ package dagger.internal.codegen.binding;
import static dagger.internal.codegen.binding.BindingType.PRODUCTION;
import dagger.internal.codegen.model.RequestKind;
-import dagger.producers.Producer;
-import javax.inject.Provider;
/**
* A mapper for associating a {@link RequestKind} to a {@link FrameworkType}, dependent on the type
- * of code to be generated (e.g., for {@link Provider} or {@link Producer}).
+ * of code to be generated (e.g., for {@code Provider} or {@code Producer}).
*/
public enum FrameworkTypeMapper {
FOR_PROVIDER() {
diff --git a/java/dagger/internal/codegen/binding/InjectionAnnotations.java b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
index a8adb15dd..d4e1f90b7 100644
--- a/java/dagger/internal/codegen/binding/InjectionAnnotations.java
+++ b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
@@ -22,12 +22,13 @@ import static androidx.room.compiler.processing.XElementKt.isMethod;
import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
import static androidx.room.compiler.processing.XElementKt.isTypeElement;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.binding.SourceFiles.factoryNameForElement;
import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.extension.DaggerCollectors.onlyElement;
import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XElements.asField;
import static dagger.internal.codegen.xprocessing.XElements.asMethod;
@@ -42,14 +43,17 @@ import androidx.room.compiler.processing.XExecutableElement;
import androidx.room.compiler.processing.XFieldElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.ElementFormatter;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
import dagger.internal.codegen.model.DaggerAnnotation;
import dagger.internal.codegen.model.Scope;
+import dagger.internal.codegen.xprocessing.XAnnotations;
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
@@ -100,18 +104,7 @@ public final class InjectionAnnotations {
public ImmutableSet<Scope> getScopes(XElement element) {
superficialValidation.validateTypeOf(element);
ImmutableSet<Scope> scopes =
- getScopesFromScopeMetadata(element)
- .orElseGet(
- () -> {
- // Validate the annotation types before we check for @Scope, otherwise the @Scope
- // annotation may appear to be missing (b/213880825).
- superficialValidation.validateAnnotationTypesOf(element);
- return element.getAllAnnotations().stream()
- .filter(InjectionAnnotations::hasScopeAnnotation)
- .map(DaggerAnnotation::from)
- .map(Scope::scope)
- .collect(toImmutableSet());
- });
+ getScopesWithMetadata(element).orElseGet(() -> getScopesWithoutMetadata(element));
// Fully validate each scope to ensure its values are also valid.
scopes.stream()
@@ -120,7 +113,18 @@ public final class InjectionAnnotations {
return scopes;
}
- private Optional<ImmutableSet<Scope>> getScopesFromScopeMetadata(XElement element) {
+ private ImmutableSet<Scope> getScopesWithoutMetadata(XElement element) {
+ // Validate the annotation types before we check for @Scope, otherwise the @Scope
+ // annotation may appear to be missing (b/213880825).
+ superficialValidation.validateAnnotationTypesOf(element);
+ return element.getAllAnnotations().stream()
+ .filter(InjectionAnnotations::hasScopeAnnotation)
+ .map(DaggerAnnotation::from)
+ .map(Scope::scope)
+ .collect(toImmutableSet());
+ }
+
+ private Optional<ImmutableSet<Scope>> getScopesWithMetadata(XElement element) {
Optional<XAnnotation> scopeMetadata = getScopeMetadata(element);
if (!scopeMetadata.isPresent()) {
return Optional.empty();
@@ -129,13 +133,20 @@ public final class InjectionAnnotations {
if (scopeName.isEmpty()) {
return Optional.of(ImmutableSet.of());
}
- XAnnotation scopeAnnotation =
+ ImmutableList<XAnnotation> scopeAnnotations =
element.getAllAnnotations().stream()
.filter(
annotation ->
scopeName.contentEquals(
annotation.getType().getTypeElement().getQualifiedName()))
- .collect(onlyElement());
+ .collect(toImmutableList());
+ checkState(
+ scopeAnnotations.size() == 1,
+ "Expected %s to have a scope annotation for %s but found: %s",
+ ElementFormatter.elementToString(element),
+ scopeName,
+ scopeAnnotations.stream().map(XAnnotations::toStableString).collect(toImmutableList()));
+ XAnnotation scopeAnnotation = getOnlyElement(scopeAnnotations);
// Do superficial validation before we convert to a Scope, otherwise the @Scope annotation may
// appear to be missing from the annotation if it's no longer on the classpath.
superficialValidation.validateAnnotationTypeOf(element, scopeAnnotation);
@@ -170,7 +181,7 @@ public final class InjectionAnnotations {
return Optional.empty();
}
- /*
+ /**
* Returns the qualifier on the given element if it exists.
*
* <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
@@ -203,16 +214,8 @@ public final class InjectionAnnotations {
public ImmutableSet<XAnnotation> getQualifiers(XElement element) {
superficialValidation.validateTypeOf(element);
ImmutableSet<XAnnotation> qualifiers =
- getQualifiersFromQualifierMetadata(element)
- .orElseGet(
- () -> {
- // Validate the annotation types before we check for @Qualifier, otherwise the
- // @Qualifier annotation may appear to be missing (b/213880825).
- superficialValidation.validateAnnotationTypesOf(element);
- return element.getAllAnnotations().stream()
- .filter(InjectionAnnotations::hasQualifierAnnotation)
- .collect(toImmutableSet());
- });
+ getQualifiersWithMetadata(element)
+ .orElseGet(() -> getQualifiersWithoutMetadata(element));
if (isField(element)) {
XFieldElement field = asField(element);
@@ -237,7 +240,16 @@ public final class InjectionAnnotations {
return qualifiers;
}
- private Optional<ImmutableSet<XAnnotation>> getQualifiersFromQualifierMetadata(XElement element) {
+ private ImmutableSet<XAnnotation> getQualifiersWithoutMetadata(XElement element) {
+ // Validate the annotation types before we check for @Qualifier, otherwise the
+ // @Qualifier annotation may appear to be missing (b/213880825).
+ superficialValidation.validateAnnotationTypesOf(element);
+ return element.getAllAnnotations().stream()
+ .filter(InjectionAnnotations::hasQualifierAnnotation)
+ .collect(toImmutableSet());
+ }
+
+ private Optional<ImmutableSet<XAnnotation>> getQualifiersWithMetadata(XElement element) {
Optional<XAnnotation> qualifierMetadata = getQualifierMetadata(element);
if (!qualifierMetadata.isPresent()) {
return Optional.empty();
diff --git a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
index 5ede20042..36e831bfd 100644
--- a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
+++ b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
@@ -68,10 +68,13 @@ final class InjectionSiteFactory {
XTypeElement typeElement = currentType.get().getTypeElement();
enclosingTypeElementOrder.put(typeElement, enclosingTypeElementOrder.size());
for (XElement enclosedElement : typeElement.getEnclosedElements()) {
- enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size());
injectionSiteVisitor
.visit(enclosedElement, currentType.get())
- .ifPresent(injectionSites::add);
+ .ifPresent(
+ injectionSite -> {
+ enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size());
+ injectionSites.add(injectionSite);
+ });
}
}
return ImmutableSortedSet.copyOf(
diff --git a/java/dagger/internal/codegen/binding/LegacyBindingGraph.java b/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
deleted file mode 100644
index b38697e8d..000000000
--- a/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.binding;
-
-import androidx.room.compiler.processing.XTypeElement;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimaps;
-import dagger.internal.codegen.model.Key;
-import dagger.internal.codegen.model.RequestKind;
-import java.util.Collection;
-import java.util.Map;
-
-// TODO(bcorso): Remove the LegacyBindingGraph after we've migrated to the new BindingGraph.
-/** The canonical representation of a full-resolved graph. */
-final class LegacyBindingGraph {
- private final ComponentDescriptor componentDescriptor;
- private final ImmutableMap<Key, ResolvedBindings> contributionBindings;
- private final ImmutableMap<Key, ResolvedBindings> membersInjectionBindings;
- private final ImmutableList<LegacyBindingGraph> subgraphs;
-
- LegacyBindingGraph(
- ComponentDescriptor componentDescriptor,
- ImmutableMap<Key, ResolvedBindings> contributionBindings,
- ImmutableMap<Key, ResolvedBindings> membersInjectionBindings,
- ImmutableList<LegacyBindingGraph> subgraphs) {
- this.componentDescriptor = componentDescriptor;
- this.contributionBindings = contributionBindings;
- this.membersInjectionBindings = membersInjectionBindings;
- this.subgraphs = checkForDuplicates(subgraphs);
- }
-
- ComponentDescriptor componentDescriptor() {
- return componentDescriptor;
- }
-
- ResolvedBindings resolvedBindings(BindingRequest request) {
- return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
- ? membersInjectionBindings.get(request.key())
- : contributionBindings.get(request.key());
- }
-
- Iterable<ResolvedBindings> resolvedBindings() {
- // Don't return an immutable collection - this is only ever used for looping over all bindings
- // in the graph. Copying is wasteful, especially if is a hashing collection, since the values
- // should all, by definition, be distinct.
- return Iterables.concat(membersInjectionBindings.values(), contributionBindings.values());
- }
-
- ImmutableList<LegacyBindingGraph> subgraphs() {
- return subgraphs;
- }
-
- private static ImmutableList<LegacyBindingGraph> checkForDuplicates(
- ImmutableList<LegacyBindingGraph> graphs) {
- Map<XTypeElement, Collection<LegacyBindingGraph>> duplicateGraphs =
- Maps.filterValues(
- Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
- overlapping -> overlapping.size() > 1);
- if (!duplicateGraphs.isEmpty()) {
- throw new IllegalArgumentException("Expected no duplicates: " + duplicateGraphs);
- }
- return graphs;
- }
-}
diff --git a/java/dagger/internal/codegen/binding/MapKeys.java b/java/dagger/internal/codegen/binding/MapKeys.java
index c156dbb06..61ef9f2b2 100644
--- a/java/dagger/internal/codegen/binding/MapKeys.java
+++ b/java/dagger/internal/codegen/binding/MapKeys.java
@@ -199,5 +199,26 @@ public final class MapKeys {
.build());
}
+ /**
+ * Returns if this binding is a map binding and uses @LazyClassKey for contributing class keys.
+ *
+ * <p>@LazyClassKey won't co-exist with @ClassKey in the graph, since the same binding type cannot
+ * use more than one @MapKey annotation type and Dagger validation will fail.
+ */
+ public static boolean useLazyClassKey(Binding binding, BindingGraph graph) {
+ if (!binding.dependencies().isEmpty()) {
+ ContributionBinding contributionBinding =
+ graph.contributionBinding(binding.dependencies().iterator().next().key());
+ return contributionBinding.mapKey().isPresent()
+ && contributionBinding
+ .mapKey()
+ .get()
+ .xprocessing()
+ .getClassName()
+ .equals(TypeNames.LAZY_CLASS_KEY);
+ }
+ return false;
+ }
+
private MapKeys() {}
}
diff --git a/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
index e98abcca2..d7fea80b7 100644
--- a/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
+++ b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
@@ -16,13 +16,14 @@
package dagger.internal.codegen.binding;
+import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
-import static java.util.stream.Collectors.joining;
import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XExecutableElement;
@@ -33,12 +34,13 @@ import androidx.room.compiler.processing.XMethodType;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.Formatter;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XTypes;
import java.util.Iterator;
-import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
@@ -46,6 +48,8 @@ import javax.inject.Inject;
public final class MethodSignatureFormatter extends Formatter<XExecutableElement> {
private static final ClassName JET_BRAINS_NOT_NULL =
ClassName.get("org.jetbrains.annotations", "NotNull");
+ private static final ClassName JET_BRAINS_NULLABLE =
+ ClassName.get("org.jetbrains.annotations", "Nullable");
private final InjectionAnnotations injectionAnnotations;
@@ -106,15 +110,9 @@ public final class MethodSignatureFormatter extends Formatter<XExecutableElement
XTypeElement container,
boolean includeReturnType) {
StringBuilder builder = new StringBuilder();
- List<XAnnotation> annotations = method.getAllAnnotations();
- if (!annotations.isEmpty()) {
- builder.append(
- annotations.stream()
- // Filter out @NotNull annotations added by KAPT to make error messages consistent
- .filter(annotation -> !annotation.getClassName().equals(JET_BRAINS_NOT_NULL))
- .map(MethodSignatureFormatter::formatAnnotation)
- .collect(joining(" ")))
- .append(" ");
+ ImmutableList<String> formattedAnnotations = formatedAnnotations(method);
+ if (!formattedAnnotations.isEmpty()) {
+ builder.append(String.join(" ", formattedAnnotations)).append(" ");
}
if (getSimpleName(method).contentEquals("<init>")) {
builder.append(container.getQualifiedName());
@@ -154,6 +152,34 @@ public final class MethodSignatureFormatter extends Formatter<XExecutableElement
return stripCommonTypePrefixes(XTypes.toStableString(type));
}
+ private static ImmutableList<String> formatedAnnotations(XExecutableElement executableElement) {
+ Nullability nullability = Nullability.of(executableElement);
+ ImmutableList<String> formattedAnnotations =
+ Streams.concat(
+ executableElement.getAllAnnotations().stream()
+ // Filter out @NotNull annotations added by KAPT to make error messages
+ // consistent
+ .filter(annotation -> !annotation.getClassName().equals(JET_BRAINS_NOT_NULL))
+ .map(MethodSignatureFormatter::formatAnnotation),
+ nullability.nullableAnnotations().stream()
+ // Filter out @NotNull annotations added by KAPT to make error messages
+ // consistent
+ .filter(annotation -> !annotation.equals(JET_BRAINS_NOT_NULL))
+ .map(annotation -> String.format("@%s", annotation.canonicalName())))
+ .distinct()
+ .collect(toImmutableList());
+ if (nullability.isKotlinTypeNullable()
+ && nullability.nullableAnnotations().stream().noneMatch(JET_BRAINS_NULLABLE::equals)
+ && getProcessingEnv(executableElement).findTypeElement(JET_BRAINS_NULLABLE) != null) {
+ formattedAnnotations =
+ ImmutableList.<String>builder()
+ .addAll(formattedAnnotations)
+ .add(String.format("@%s", JET_BRAINS_NULLABLE.canonicalName()))
+ .build();
+ }
+ return formattedAnnotations;
+ }
+
private static String formatAnnotation(XAnnotation annotation) {
return stripCommonTypePrefixes(XAnnotations.toString(annotation));
}
diff --git a/java/dagger/internal/codegen/binding/MonitoringModules.java b/java/dagger/internal/codegen/binding/MonitoringModules.java
new file mode 100644
index 000000000..fc6dc021c
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MonitoringModules.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.base.ClearableCache;
+import java.util.HashSet;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Keeps track of modules generated in the current round by {@link MonitoringModuleGenerator}. */
+@Singleton
+public final class MonitoringModules implements ClearableCache {
+ Set<ClassName> cache = new HashSet<>();
+
+ @Inject
+ MonitoringModules() {}
+
+ public void add(ClassName module) {
+ cache.add(module);
+ }
+
+ public boolean isEmpty() {
+ return cache.isEmpty();
+ }
+
+ @Override
+ public void clearCache() {
+ cache.clear();
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/Nullability.java b/java/dagger/internal/codegen/binding/Nullability.java
index 190227ba0..4231a8fb0 100644
--- a/java/dagger/internal/codegen/binding/Nullability.java
+++ b/java/dagger/internal/codegen/binding/Nullability.java
@@ -18,7 +18,7 @@ package dagger.internal.codegen.binding;
import static androidx.room.compiler.processing.XElementKt.isMethod;
import static androidx.room.compiler.processing.XElementKt.isVariableElement;
-import static dagger.internal.codegen.xprocessing.XAnnotations.getClassName;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XElements.asMethod;
import static dagger.internal.codegen.xprocessing.XElements.asVariable;
@@ -27,7 +27,10 @@ import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XNullability;
import androidx.room.compiler.processing.XType;
import com.google.auto.value.AutoValue;
-import java.util.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.xprocessing.XAnnotations;
+import java.util.stream.Stream;
/**
* Contains information about the nullability of an element.
@@ -42,16 +45,43 @@ import java.util.Optional;
@AutoValue
public abstract class Nullability {
/** A constant that can represent any non-null element. */
- public static final Nullability NOT_NULLABLE = new AutoValue_Nullability(false, Optional.empty());
+ public static final Nullability NOT_NULLABLE =
+ new AutoValue_Nullability(false, ImmutableSet.of());
public static Nullability of(XElement element) {
- Optional<XAnnotation> nullableAnnotation = getNullableAnnotation(element);
- boolean isNullable = isKotlinTypeNullable(element) || nullableAnnotation.isPresent();
- return isNullable ? new AutoValue_Nullability(isNullable, nullableAnnotation) : NOT_NULLABLE;
+ return new AutoValue_Nullability(
+ /* isKotlinTypeNullable= */ isKotlinTypeNullable(element),
+ /* nullableAnnotations= */ getNullableAnnotations(element));
}
+ private static ImmutableSet<ClassName> getNullableAnnotations(XElement element) {
+ return getNullableAnnotations(element.getAllAnnotations().stream(), ImmutableSet.of());
+ }
+
+ private static ImmutableSet<ClassName> getNullableAnnotations(
+ Stream<XAnnotation> annotations,
+ ImmutableSet<ClassName> filterSet) {
+ return annotations
+ .map(XAnnotations::getClassName)
+ .filter(annotation -> annotation.simpleName().contentEquals("Nullable"))
+ .filter(annotation -> !filterSet.contains(annotation))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns {@code true} if the element's type is a Kotlin nullable type, e.g. {@code Foo?}.
+ *
+ * <p>Note that this method ignores any {@code @Nullable} type annotations and only looks for
+ * explicit {@code ?} usages on kotlin types.
+ */
private static boolean isKotlinTypeNullable(XElement element) {
- if (isMethod(element)) {
+ if (element.getClosestMemberContainer().isFromJava()) {
+ // Note: Technically, it isn't possible for Java sources to have nullable types like in Kotlin
+ // sources, but for some reason KSP treats certain types as nullable if they have a
+ // specific @Nullable (TYPE_USE target) annotation. Thus, to avoid inconsistencies with KAPT,
+ // just return false if this element is from a java source.
+ return false;
+ } else if (isMethod(element)) {
return isKotlinTypeNullable(asMethod(element).getReturnType());
} else if (isVariableElement(element)) {
return isKotlinTypeNullable(asVariable(element).getType());
@@ -64,16 +94,13 @@ public abstract class Nullability {
return type.getNullability() == XNullability.NULLABLE;
}
- /** Returns the first type that specifies this' nullability, or empty if none. */
- private static Optional<XAnnotation> getNullableAnnotation(XElement element) {
- return element.getAllAnnotations().stream()
- .filter(annotation -> getClassName(annotation).simpleName().contentEquals("Nullable"))
- .findFirst();
- }
+ public abstract boolean isKotlinTypeNullable();
- public abstract boolean isNullable();
+ public abstract ImmutableSet<ClassName> nullableAnnotations();
- public abstract Optional<XAnnotation> nullableAnnotation();
+ public final boolean isNullable() {
+ return isKotlinTypeNullable() || !nullableAnnotations().isEmpty();
+ }
Nullability() {}
}
diff --git a/java/dagger/internal/codegen/binding/ResolvedBindings.java b/java/dagger/internal/codegen/binding/ResolvedBindings.java
index 8a354429a..406ae41f5 100644
--- a/java/dagger/internal/codegen/binding/ResolvedBindings.java
+++ b/java/dagger/internal/codegen/binding/ResolvedBindings.java
@@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
+import dagger.internal.codegen.model.ComponentPath;
import dagger.internal.codegen.model.Key;
/**
@@ -40,6 +41,9 @@ import dagger.internal.codegen.model.Key;
*/
@AutoValue
abstract class ResolvedBindings {
+ /** The component path for the resolved bindings. */
+ abstract ComponentPath componentPath();
+
/** The binding key for which the {@link #bindings()} have been resolved. */
abstract Key key();
@@ -127,12 +131,14 @@ abstract class ResolvedBindings {
/** Creates a {@link ResolvedBindings} for contribution bindings. */
static ResolvedBindings forContributionBindings(
+ ComponentPath componentPath,
Key key,
Multimap<XTypeElement, ContributionBinding> contributionBindings,
Iterable<MultibindingDeclaration> multibindings,
Iterable<SubcomponentDeclaration> subcomponentDeclarations,
Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
return new AutoValue_ResolvedBindings(
+ componentPath,
key,
ImmutableSetMultimap.copyOf(contributionBindings),
ImmutableMap.of(),
@@ -145,10 +151,12 @@ abstract class ResolvedBindings {
* Creates a {@link ResolvedBindings} for members injection bindings.
*/
static ResolvedBindings forMembersInjectionBinding(
+ ComponentPath componentPath,
Key key,
ComponentDescriptor owningComponent,
MembersInjectionBinding ownedMembersInjectionBinding) {
return new AutoValue_ResolvedBindings(
+ componentPath,
key,
ImmutableSetMultimap.of(),
ImmutableMap.of(owningComponent.typeElement(), ownedMembersInjectionBinding),
@@ -160,8 +168,9 @@ abstract class ResolvedBindings {
/**
* Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
*/
- static ResolvedBindings noBindings(Key key) {
+ static ResolvedBindings noBindings(ComponentPath componentPath, Key key) {
return new AutoValue_ResolvedBindings(
+ componentPath,
key,
ImmutableSetMultimap.of(),
ImmutableMap.of(),
diff --git a/java/dagger/internal/codegen/binding/SourceFiles.java b/java/dagger/internal/codegen/binding/SourceFiles.java
index 19d316f3d..e8a10753d 100644
--- a/java/dagger/internal/codegen/binding/SourceFiles.java
+++ b/java/dagger/internal/codegen/binding/SourceFiles.java
@@ -95,11 +95,23 @@ public final class SourceFiles {
return Maps.toMap(
binding.dependencies(),
- dependency ->
- FrameworkField.create(
- frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClassName(),
- dependency.key().type().xprocessing().getTypeName(),
- DependencyVariableNamer.name(dependency)));
+ dependency -> {
+ ClassName frameworkClassName =
+ frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClassName();
+ // Remap factory fields back to javax.inject.Provider to maintain backwards compatibility
+ // for now. In a future release, we should change this to Dagger Provider. This will still
+ // be a breaking change, but keeping compatibility for a while should reduce the
+ // likelihood of breakages as it would require components built at much older versions
+ // using factories built at newer versions to break.
+ if (frameworkClassName.equals(TypeNames.DAGGER_PROVIDER)) {
+ frameworkClassName = TypeNames.PROVIDER;
+ }
+ return FrameworkField.create(
+ ParameterizedTypeName.get(
+ frameworkClassName,
+ dependency.key().type().xprocessing().getTypeName()),
+ DependencyVariableNamer.name(dependency));
+ });
}
public CodeBlock frameworkTypeUsageStatement(
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
index 774ee5d94..fd9e42efb 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
@@ -20,12 +20,13 @@ import static androidx.room.compiler.processing.compat.XConverters.getProcessing
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.base.Formatter.INDENT;
import static dagger.internal.codegen.base.Keys.isValidImplicitProvisionKey;
import static dagger.internal.codegen.base.Keys.isValidMembersInjectionKey;
import static dagger.internal.codegen.base.RequestKinds.canBeSatisfiedByProductionBinding;
import static dagger.internal.codegen.binding.DependencyRequestFormatter.DOUBLE_INDENT;
import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import static dagger.internal.codegen.xprocessing.XTypes.isWildcard;
@@ -38,6 +39,7 @@ import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.WildcardTypeName;
+import dagger.internal.codegen.binding.ComponentNodeImpl;
import dagger.internal.codegen.binding.DependencyRequestFormatter;
import dagger.internal.codegen.binding.InjectBindingRegistry;
import dagger.internal.codegen.model.Binding;
@@ -47,7 +49,6 @@ import dagger.internal.codegen.model.BindingGraph.DependencyEdge;
import dagger.internal.codegen.model.BindingGraph.Edge;
import dagger.internal.codegen.model.BindingGraph.MissingBinding;
import dagger.internal.codegen.model.BindingGraph.Node;
-import dagger.internal.codegen.model.ComponentPath;
import dagger.internal.codegen.model.DaggerAnnotation;
import dagger.internal.codegen.model.DiagnosticReporter;
import dagger.internal.codegen.model.Key;
@@ -103,62 +104,20 @@ final class MissingBindingValidator extends ValidationBindingGraphPlugin {
prunedGraph
.missingBindings()
.forEach(
- missingBinding ->
- reportMissingBinding(missingBinding, prunedGraph, fullGraph, diagnosticReporter));
+ missingBinding -> reportMissingBinding(missingBinding, fullGraph, diagnosticReporter));
}
private void reportMissingBinding(
MissingBinding missingBinding,
- BindingGraph prunedGraph,
- BindingGraph fullGraph,
+ BindingGraph graph,
DiagnosticReporter diagnosticReporter) {
- ImmutableSet<Binding> wildcardAlternatives =
- getSimilarTypeBindings(fullGraph, missingBinding.key());
- String wildcardTypeErrorMessage = "";
- if (!wildcardAlternatives.isEmpty()) {
- wildcardTypeErrorMessage =
- String.format(
- "\nFound similar bindings:\n %s",
- String.join(
- "\n ",
- wildcardAlternatives.stream()
- .map(
- binding -> binding.key().type() + " in [" + binding.componentPath() + "]")
- .collect(toImmutableSet())))
- + "\n(In Kotlin source, a type like 'Set<Foo>' may"
- + " be translated as 'Set<? extends Foo>'. To avoid this implicit"
- + " conversion you can add '@JvmSuppressWildcards' on the associated type"
- + " argument, e.g. 'Set<@JvmSuppressWildcards Foo>'.)";
- }
-
- List<ComponentPath> alternativeComponents =
- wildcardAlternatives.isEmpty()
- ? fullGraph.bindings(missingBinding.key()).stream()
- .map(Binding::componentPath)
- .distinct()
- .collect(toImmutableList())
- : ImmutableList.of();
- // Print component name for each binding along the dependency path if the missing binding
- // exists in a different component than expected
- if (alternativeComponents.isEmpty()) {
- // TODO(b/266993189): the passed in diagnostic reporter is constructed with full graph, so it
- // doesn't print out full dependency path for a binding when invoking reportBinding on it.
- // Therefore, we manually constructed the binding dependency path and passed into
- // reportComponent.
- diagnosticReporter.reportComponent(
- ERROR,
- fullGraph.componentNode(missingBinding.componentPath()).get(),
- missingBindingErrorMessage(missingBinding, fullGraph)
- + wildcardTypeErrorMessage
- + "\n\nMissing binding usage:"
- + diagnosticMessageGeneratorFactory.create(prunedGraph).getMessage(missingBinding));
- } else {
- diagnosticReporter.reportComponent(
- ERROR,
- fullGraph.componentNode(missingBinding.componentPath()).get(),
- missingBindingErrorMessage(missingBinding, fullGraph)
- + wrongComponentErrorMessage(missingBinding, alternativeComponents, prunedGraph));
- }
+ diagnosticReporter.reportComponent(
+ ERROR,
+ graph.componentNode(missingBinding.componentPath()).get(),
+ missingBindingErrorMessage(missingBinding, graph)
+ + missingBindingDependencyTraceMessage(missingBinding, graph)
+ + alternativeBindingsMessage(missingBinding, graph)
+ + similarBindingsMessage(missingBinding, graph));
}
private static ImmutableSet<Binding> getSimilarTypeBindings(
@@ -222,50 +181,24 @@ final class MissingBindingValidator extends ValidationBindingGraphPlugin {
return errorMessage.toString();
}
- private String wrongComponentErrorMessage(
- MissingBinding missingBinding,
- List<ComponentPath> alternativeComponentPath,
- BindingGraph graph) {
+ private String missingBindingDependencyTraceMessage(
+ MissingBinding missingBinding, BindingGraph graph) {
ImmutableSet<DependencyEdge> entryPoints =
graph.entryPointEdgesDependingOnBinding(missingBinding);
DiagnosticMessageGenerator generator = diagnosticMessageGeneratorFactory.create(graph);
ImmutableList<DependencyEdge> dependencyTrace =
generator.dependencyTrace(missingBinding, entryPoints);
StringBuilder message =
- graph.isFullBindingGraph()
- ? new StringBuilder()
- : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
- // Check in which component the missing binding is requested. This can be different from the
- // component the missing binding is in because we'll try to search up the parent components for
- // a binding which makes missing bindings end up at the root component. This is different from
- // the place we are logically requesting the binding from. Note that this is related to the
- // particular dependency trace being shown and so is not necessarily stable.
- String missingComponentName =
- getComponentFromDependencyEdge(dependencyTrace.get(0), graph, false);
- boolean hasSameComponentName = false;
- for (ComponentPath component : alternativeComponentPath) {
- message.append("\nNote: A binding for ").append(missingBinding.key()).append(" exists in ");
- String currentComponentName = component.currentComponent().className().canonicalName();
- if (currentComponentName.contentEquals(missingComponentName)) {
- hasSameComponentName = true;
- message.append("[").append(component).append("]");
- } else {
- message.append(currentComponentName);
- }
- message.append(":");
- }
+ new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */).append("\n");
for (DependencyEdge edge : dependencyTrace) {
String line = dependencyRequestFormatter.format(edge.dependencyRequest());
if (line.isEmpty()) {
continue;
}
- // If we ran into a rare case where the component names collide and we need to show the full
- // path, only show the full path for the first dependency request. This is guaranteed to be
- // the component in question since the logic for checking for a collision uses the first
- // edge in the trace. Do not expand subsequent component paths to reduce spam.
- String componentName =
- String.format("[%s] ", getComponentFromDependencyEdge(edge, graph, hasSameComponentName));
- hasSameComponentName = false;
+ // We don't have to check for cases where component names collide since
+ // 1. We always show the full classname of the component, and
+ // 2. We always show the full component path at the end of the dependency trace (below).
+ String componentName = String.format("[%s] ", getComponentFromDependencyEdge(edge, graph));
message.append("\n").append(line.replace(DOUBLE_INDENT, DOUBLE_INDENT + componentName));
}
if (!dependencyTrace.isEmpty()) {
@@ -277,6 +210,71 @@ final class MissingBindingValidator extends ValidationBindingGraphPlugin {
return message.toString();
}
+ private String alternativeBindingsMessage(
+ MissingBinding missingBinding, BindingGraph graph) {
+ ImmutableSet<Binding> alternativeBindings = graph.bindings(missingBinding.key());
+ if (alternativeBindings.isEmpty()) {
+ return "";
+ }
+ StringBuilder message = new StringBuilder();
+ message.append("\n\nNote: ")
+ .append(missingBinding.key())
+ .append(" is provided in the following other components:");
+ for (Binding alternativeBinding : alternativeBindings) {
+ // Some alternative bindings appear multiple times because they were re-resolved in multiple
+ // components (e.g. due to multibinding contributions). To avoid the noise, we only report
+ // the binding where the module is contributed.
+ if (alternativeBinding.contributingModule().isPresent()
+ && !((ComponentNodeImpl) graph.componentNode(alternativeBinding.componentPath()).get())
+ .componentDescriptor()
+ .moduleTypes()
+ .contains(alternativeBinding.contributingModule().get().xprocessing())) {
+ continue;
+ }
+ message.append("\n").append(INDENT).append(asString(alternativeBinding));
+ }
+ return message.toString();
+ }
+
+ private String similarBindingsMessage(
+ MissingBinding missingBinding, BindingGraph graph) {
+ ImmutableSet<Binding> similarBindings =
+ getSimilarTypeBindings(graph, missingBinding.key());
+ if (similarBindings.isEmpty()) {
+ return "";
+ }
+ StringBuilder message =
+ new StringBuilder(
+ "\n\nNote: A similar binding is provided in the following other components:");
+ for (Binding similarBinding : similarBindings) {
+ message
+ .append("\n")
+ .append(INDENT)
+ .append(similarBinding.key())
+ .append(" is provided at:")
+ .append("\n")
+ .append(DOUBLE_INDENT)
+ .append(asString(similarBinding));
+ }
+ message.append("\n")
+ .append(
+ "(For Kotlin sources, you may need to use '@JvmSuppressWildcards' or '@JvmWildcard' if "
+ + "you need to explicitly control the wildcards at a particular usage site.)");
+ return message.toString();
+ }
+
+ private String asString(Binding binding) {
+ return String.format(
+ "[%s] %s",
+ binding.componentPath().currentComponent().xprocessing().getQualifiedName(),
+ binding.bindingElement().isPresent()
+ ? elementToString(
+ binding.bindingElement().get().xprocessing(),
+ /* elideMethodParameterTypes= */ true)
+ // For synthetic bindings just print the Binding#toString()
+ : binding);
+ }
+
private boolean allIncomingDependenciesCanUseProduction(
MissingBinding missingBinding, BindingGraph graph) {
return graph.network().inEdges(missingBinding).stream()
@@ -305,15 +303,11 @@ final class MissingBindingValidator extends ValidationBindingGraphPlugin {
.orElse(false);
}
- private static String getComponentFromDependencyEdge(
- DependencyEdge edge, BindingGraph graph, boolean completePath) {
- ComponentPath componentPath = graph.network().incidentNodes(edge).source().componentPath();
- return completePath
- ? componentPath.toString()
- : componentPath.currentComponent().className().canonicalName();
+ private static String getComponentFromDependencyEdge(DependencyEdge edge, BindingGraph graph) {
+ return source(edge, graph).componentPath().currentComponent().className().canonicalName();
}
- private Node source(Edge edge, BindingGraph graph) {
+ private static Node source(Edge edge, BindingGraph graph) {
return graph.network().incidentNodes(edge).source();
}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
index f767da851..30ebf09b2 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
@@ -30,6 +30,7 @@ import dagger.internal.codegen.model.BindingGraph;
import dagger.internal.codegen.model.DiagnosticReporter;
import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.validation.ValidationBindingGraphPlugin;
+import java.util.Optional;
import javax.inject.Inject;
/** Validates that there are not multiple set binding contributions to the same binding. */
@@ -59,7 +60,8 @@ final class SetMultibindingValidator extends ValidationBindingGraphPlugin {
Multimap<Key, Binding> dereferencedBindsTargets = HashMultimap.create();
for (Binding dep : bindingGraph.requestedBindings(binding)) {
if (dep.kind().equals(DELEGATE)) {
- dereferencedBindsTargets.put(dereferenceDelegateBinding(dep, bindingGraph), dep);
+ dereferenceDelegateBinding(dep, bindingGraph)
+ .ifPresent(dereferencedKey -> dereferencedBindsTargets.put(dereferencedKey, dep));
}
}
@@ -80,13 +82,19 @@ final class SetMultibindingValidator extends ValidationBindingGraphPlugin {
});
}
- /** Returns the delegate target of a delegate binding (going through other delegates as well). */
- private Key dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph) {
+ /**
+ * Returns the dereferenced key of a delegate binding (going through other delegates as well).
+ *
+ * <p>If the binding cannot be dereferenced (because it leads to a missing binding or duplicate
+ * bindings) then {@link Optional#empty()} is returned.
+ */
+ private Optional<Key> dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph) {
ImmutableSet<Binding> delegateSet = bindingGraph.requestedBindings(binding);
- if (delegateSet.isEmpty()) {
- // There may not be a delegate if the delegate is missing. In this case, we just take the
- // requested key and return that.
- return Iterables.getOnlyElement(binding.dependencies()).key();
+ if (delegateSet.size() != 1) {
+ // If there isn't exactly 1 delegate then it means either a MissingBinding or DuplicateBinding
+ // error will be reported. Just return nothing rather than trying to dereference further, as
+ // anything we report here will just be noise on top of the other error anyway.
+ return Optional.empty();
}
// If there is a binding, first we check if that is a delegate binding so we can dereference
// that binding if needed.
@@ -94,6 +102,6 @@ final class SetMultibindingValidator extends ValidationBindingGraphPlugin {
if (delegate.kind().equals(DELEGATE)) {
return dereferenceDelegateBinding(delegate, bindingGraph);
}
- return delegate.key();
+ return Optional.of(delegate.key());
}
}
diff --git a/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
index 69e859d4c..6e4b0a96f 100644
--- a/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
+++ b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/compileroption/CompilerOptions.java b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
index 42599e03b..73f7736f2 100644
--- a/java/dagger/internal/codegen/compileroption/CompilerOptions.java
+++ b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
@@ -24,16 +24,6 @@ public abstract class CompilerOptions {
public abstract boolean usesProducers();
/**
- * Returns true if the experimental Android mode is enabled.
- *
- * <p><b>Warning: Do Not use! This flag is for internal, experimental use only!</b>
- *
- * <p>Issues related to this flag will not be supported. This flag could break your build, cause
- * memory leaks in your app, or cause other unknown issues at runtime.
- */
- public abstract boolean experimentalMergedMode(XTypeElement element);
-
- /**
* Returns true if the fast initialization flag, {@code fastInit}, is enabled.
*
* <p>If enabled, the generated code will attempt to optimize for fast component initialization.
diff --git a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
index 356d55bf6..81380055a 100644
--- a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
+++ b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
@@ -19,7 +19,6 @@ package dagger.internal.codegen.compileroption;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Sets.immutableEnumSet;
import static dagger.internal.codegen.compileroption.FeatureStatus.DISABLED;
import static dagger.internal.codegen.compileroption.FeatureStatus.ENABLED;
@@ -107,37 +106,14 @@ public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions
}
@Override
- public boolean experimentalMergedMode(XTypeElement component) {
- boolean isExperimental = experimentalMergedModeInternal();
- if (isExperimental) {
- checkState(
- !fastInitInternal(component),
- "Both fast init and experimental merged mode were turned on, please specify exactly one"
- + " compilation mode.");
- }
- return isExperimental;
- }
-
- @Override
public boolean fastInit(XTypeElement component) {
- boolean isFastInit = fastInitInternal(component);
- if (isFastInit) {
- checkState(
- !experimentalMergedModeInternal(),
- "Both fast init and experimental merged mode were turned on, please specify exactly one"
- + " compilation mode.");
- }
- return isFastInit;
+ return fastInitInternal(component);
}
private boolean fastInitInternal(XTypeElement component) {
return isEnabled(FAST_INIT);
}
- private boolean experimentalMergedModeInternal() {
- return false;
- }
-
@Override
public boolean formatGeneratedSource() {
return isEnabled(FORMAT_GENERATED_SOURCE);
@@ -268,6 +244,16 @@ public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions
noLongerRecognized(FLOATING_BINDS_METHODS);
noLongerRecognized(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
noLongerRecognized(USE_GRADLE_INCREMENTAL_PROCESSING);
+ if (!isEnabled(IGNORE_PROVISION_KEY_WILDCARDS)) {
+ if (processingEnv.getBackend() == XProcessingEnv.Backend.KSP) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "When using KSP, you must also enable the '%s' compiler option (see %s).",
+ "dagger.ignoreProvisionKeyWildcards",
+ "https://dagger.dev/dev-guide/compiler-options#ignore-provision-key-wildcards"));
+ }
+ }
return this;
}
@@ -359,7 +345,7 @@ public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions
GENERATED_CLASS_EXTENDS_COMPONENT,
- IGNORE_PROVISION_KEY_WILDCARDS,
+ IGNORE_PROVISION_KEY_WILDCARDS(ENABLED),
VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES(ENABLED)
;
@@ -418,7 +404,7 @@ public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions
* How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
* constructor used in an ancestor component.
*/
- EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(WARNING, ERROR, NONE),
+ EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(ERROR, WARNING, NONE),
;
final ValidationType defaultType;
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java
index 179c411e4..a071eaeb2 100644
--- a/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java
@@ -16,9 +16,12 @@
package dagger.internal.codegen.componentgenerator;
+import androidx.room.compiler.processing.XProcessingEnv;
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.base.SourceFileHjarGenerator;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.ComponentDescriptor;
@@ -29,14 +32,23 @@ public interface ComponentGeneratorModule {
@Binds
abstract SourceFileGenerator<BindingGraph> componentGenerator(ComponentGenerator generator);
- // The HjarSourceFileGenerator wrapper first generates the entire TypeSpec before stripping out
+ // The SourceFileHjarGenerator wrapper first generates the entire TypeSpec before stripping out
// things that aren't needed for the hjar. However, this can be really expensive for the component
// because it is usually the most expensive file to generate, and most of its content is not
- // needed in the hjar. Thus, instead of wrapping the ComponentGenerator in HjarSourceFileGenerator
- // we provide a completely separate processing step, ComponentHjarProcessingStep, and generator,
- // ComponentHjarGenerator, for when generating hjars for components, which can avoid generating
- // the parts of the component that would have been stripped out by the HjarSourceFileGenerator.
- @Binds
- abstract SourceFileGenerator<ComponentDescriptor> componentHjarGenerator(
- ComponentHjarGenerator hjarGenerator);
+ // needed in the hjar. Thus, we provide a completely separate processing step,
+ // ComponentHjarProcessingStep and ComponentHjarGenerator, for when generating hjars for
+ // components, which can avoid generating the parts of the component that would have been stripped
+ // out by the HjarSourceFileGenerator anyway. Note that we still wrap ComponentHjarGenerator in
+ // SourceFileHjarGenerator because it adds in constructor and method bodies that are needed for
+ // Javac to compile correctly, e.g. super(...) calls in the constructor and return statements in
+ // methods.
+ @Provides
+ static SourceFileGenerator<ComponentDescriptor> componentHjarGenerator(
+ XProcessingEnv processingEnv,
+ ComponentHjarGenerator hjarGenerator) {
+ // Note: technically the ComponentHjarGenerator is already in hjar form, but the
+ // SourceFileHjarGenerator wrapper adds in proper method bodies, e.g. constructors that require
+ // super() calls or methods that require return statements.
+ return SourceFileHjarGenerator.wrap(hjarGenerator, processingEnv);
+ }
}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
index 62f11632e..4214cbc72 100644
--- a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
@@ -192,7 +192,14 @@ final class ComponentHjarGenerator extends SourceFileGenerator<ComponentDescript
&& isElementAccessibleFrom(
module.moduleElement(),
component.typeElement().getClassName().packageName()))
- .map(module -> ComponentRequirement.forModule(module.moduleElement().getType())));
+ .map(module -> ComponentRequirement.forModule(module.moduleElement().getType()))
+ // If the user hasn't defined an explicit creator/builder then we need to prune out the
+ // module requirements that don't require a module instance to match the non-hjar
+ // implementation.
+ .filter(
+ requirement ->
+ component.creatorDescriptor().isPresent()
+ || requirement.requiresModuleInstance()));
}
private boolean hasBindsInstanceMethods(ComponentDescriptor componentDescriptor) {
diff --git a/java/dagger/internal/codegen/javac/BUILD b/java/dagger/internal/codegen/javac/BUILD
index ad2f91be9..325dc99d0 100644
--- a/java/dagger/internal/codegen/javac/BUILD
+++ b/java/dagger/internal/codegen/javac/BUILD
@@ -23,24 +23,10 @@ java_library(
name = "javac",
srcs = glob(["*.java"]),
plugins = ["//java/dagger/internal/codegen:component-codegen"],
- exports = [
- ":javac-import",
- ],
deps = [
- ":javac-import",
"//java/dagger:core",
- "//java/dagger/internal/codegen/binding",
"//java/dagger/internal/codegen/compileroption",
- "//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/xprocessing",
"//third_party/java/guava/collect",
],
)
-
-load("@rules_java//java:defs.bzl", "java_import")
-
-# Replacement for @bazel_tools//third_party/java/jdk/langtools:javac, which seems to have gone away?
-java_import(
- name = "javac-import",
- jars = ["@bazel_tools//third_party/java/jdk/langtools:javac_jar"],
-)
diff --git a/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
index 6e778357d..e7c5a44d2 100644
--- a/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
+++ b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
@@ -37,11 +37,6 @@ final class JavacPluginCompilerOptions extends CompilerOptions {
}
@Override
- public boolean experimentalMergedMode(XTypeElement element) {
- return false;
- }
-
- @Override
public boolean fastInit(XTypeElement element) {
return false;
}
diff --git a/java/dagger/internal/codegen/javac/JavacPluginModule.java b/java/dagger/internal/codegen/javac/JavacPluginModule.java
index 519816440..f9e6da9bc 100644
--- a/java/dagger/internal/codegen/javac/JavacPluginModule.java
+++ b/java/dagger/internal/codegen/javac/JavacPluginModule.java
@@ -24,15 +24,13 @@ import com.sun.tools.javac.util.Context;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.internal.codegen.binding.BindingGraphFactory;
-import dagger.internal.codegen.binding.ComponentDescriptorFactory;
import dagger.internal.codegen.compileroption.CompilerOptions;
import javax.lang.model.util.Elements; // ALLOW_TYPES_ELEMENTS
import javax.lang.model.util.Types; // ALLOW_TYPES_ELEMENTS
/**
- * A module that provides a {@link BindingGraphFactory} and {@link ComponentDescriptorFactory} for
- * use in {@code javac} plugins. Requires a binding for the {@code javac} {@link Context}.
+ * A module that provides a {@link XMessager}, {@link XProcessingEnv}, and {@link CompilerOptions}
+ * for use in {@code javac} plugins. Requires a binding for the {@code javac} {@link Context}.
*/
@Module(includes = JavacPluginModule.BindsModule.class)
public final class JavacPluginModule {
diff --git a/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
index 4628802a4..50ec8b1c8 100644
--- a/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
+++ b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
@@ -30,7 +30,8 @@ public final class AnnotationSpecs {
RAWTYPES("rawtypes"),
UNCHECKED("unchecked"),
FUTURE_RETURN_VALUE_IGNORED("FutureReturnValueIgnored"),
- KOTLIN_INTERNAL("KotlinInternal", "KotlinInternalInJava")
+ KOTLIN_INTERNAL("KotlinInternal", "KotlinInternalInJava"),
+ CAST("cast")
;
private final ImmutableList<String> values;
diff --git a/java/dagger/internal/codegen/javapoet/CodeBlocks.java b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
index 912dc71b8..877a0a540 100644
--- a/java/dagger/internal/codegen/javapoet/CodeBlocks.java
+++ b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
@@ -18,7 +18,8 @@ package dagger.internal.codegen.javapoet;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
+import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
import static java.util.stream.StreamSupport.stream;
import static javax.lang.model.element.Modifier.PUBLIC;
@@ -87,7 +88,22 @@ public final class CodeBlocks {
return CodeBlock.of(
"$L",
anonymousClassBuilder("")
- .superclass(providerOf(providedType))
+ .superclass(daggerProviderOf(providedType))
+ .addMethod(
+ methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(providedType)
+ .addCode(body)
+ .build())
+ .build());
+ }
+
+ public static CodeBlock anonymousLazy(TypeName providedType, CodeBlock body) {
+ return CodeBlock.of(
+ "$L",
+ anonymousClassBuilder("")
+ .superclass(lazyOf(providedType))
.addMethod(
methodBuilder("get")
.addAnnotation(Override.class)
diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java
index 105394d42..ea89fc033 100644
--- a/java/dagger/internal/codegen/javapoet/TypeNames.java
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -53,6 +53,16 @@ public final class TypeNames {
public static final ClassName SUBCOMPONENT_FACTORY = SUBCOMPONENT.nestedClass("Factory");
// Dagger Internal classnames
+ public static final ClassName IDENTIFIER_NAME_STRING =
+ ClassName.get("dagger.internal", "IdentifierNameString");
+ public static final ClassName KEEP_FIELD_TYPE = ClassName.get("dagger.internal", "KeepFieldType");
+ public static final ClassName LAZY_CLASS_KEY =
+ ClassName.get("dagger.multibindings", "LazyClassKey");
+ public static final ClassName LAZY_CLASS_KEY_MAP =
+ ClassName.get("dagger.internal", "LazyClassKeyMap");
+ public static final ClassName LAZY_CLASS_KEY_MAP_FACTORY =
+ ClassName.get("dagger.internal", "LazyClassKeyMap", "Factory");
+
public static final ClassName DELEGATE_FACTORY =
ClassName.get("dagger.internal", "DelegateFactory");
public static final ClassName DOUBLE_CHECK = ClassName.get("dagger.internal", "DoubleCheck");
@@ -62,6 +72,7 @@ public final class TypeNames {
ClassName.get("dagger.internal", "InjectedFieldSignature");
public static final ClassName INSTANCE_FACTORY =
ClassName.get("dagger.internal", "InstanceFactory");
+ public static final ClassName MAP_BUILDER = ClassName.get("dagger.internal", "MapBuilder");
public static final ClassName MAP_FACTORY = ClassName.get("dagger.internal", "MapFactory");
public static final ClassName MAP_PROVIDER_FACTORY =
ClassName.get("dagger.internal", "MapProviderFactory");
@@ -69,6 +80,8 @@ public final class TypeNames {
public static final ClassName MEMBERS_INJECTORS =
ClassName.get("dagger.internal", "MembersInjectors");
public static final ClassName PROVIDER = ClassName.get("javax.inject", "Provider");
+ public static final ClassName DAGGER_PROVIDER = ClassName.get("dagger.internal", "Provider");
+ public static final ClassName DAGGER_PROVIDERS = ClassName.get("dagger.internal", "Providers");
public static final ClassName PROVIDER_OF_LAZY =
ClassName.get("dagger.internal", "ProviderOfLazy");
public static final ClassName SCOPE_METADATA = ClassName.get("dagger.internal", "ScopeMetadata");
@@ -81,6 +94,8 @@ public final class TypeNames {
// Dagger Producers classnames
public static final ClassName ABSTRACT_PRODUCER =
ClassName.get("dagger.producers.internal", "AbstractProducer");
+ public static final ClassName ABSTRACT_PRODUCES_METHOD_PRODUCER =
+ ClassName.get("dagger.producers.internal", "AbstractProducesMethodProducer");
public static final ClassName CANCELLATION_LISTENER =
ClassName.get("dagger.producers.internal", "CancellationListener");
public static final ClassName CANCELLATION_POLICY =
@@ -138,6 +153,8 @@ public final class TypeNames {
public static final ClassName ERROR = ClassName.get("java.lang", "Error");
public static final ClassName EXCEPTION = ClassName.get("java.lang", "Exception");
public static final ClassName RUNTIME_EXCEPTION = ClassName.get("java.lang", "RuntimeException");
+ public static final ClassName STRING = ClassName.get("java.lang", "String");
+
public static final ClassName MAP = ClassName.get("java.util", "Map");
public static final ClassName KOTLIN_METADATA = ClassName.get("kotlin", "Metadata");
public static final ClassName IMMUTABLE_MAP =
@@ -215,6 +232,10 @@ public final class TypeNames {
return ParameterizedTypeName.get(PROVIDER, typeName);
}
+ public static ParameterizedTypeName daggerProviderOf(TypeName typeName) {
+ return ParameterizedTypeName.get(DAGGER_PROVIDER, typeName);
+ }
+
public static ParameterizedTypeName setOf(TypeName elementType) {
return ParameterizedTypeName.get(SET, elementType);
}
diff --git a/java/dagger/internal/codegen/kythe/BUILD b/java/dagger/internal/codegen/kythe/BUILD
index 9b2e63b7a..217627d78 100644
--- a/java/dagger/internal/codegen/kythe/BUILD
+++ b/java/dagger/internal/codegen/kythe/BUILD
@@ -31,13 +31,13 @@ java_library(
"//java/dagger/internal/codegen/javac",
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/kotlin",
- "//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/model",
"//java/dagger/internal/codegen/validation",
"//java/dagger/internal/codegen/xprocessing",
"//third_party/java/auto:service",
"//third_party/java/error_prone:annotations",
"//third_party/java/guava/collect",
+ "//third_party/java/jsr330_inject",
],
)
diff --git a/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
index 3280b4f79..0b8a5024b 100644
--- a/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
+++ b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
@@ -40,7 +40,7 @@ import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingDeclaration;
import dagger.internal.codegen.binding.BindingGraphFactory;
import dagger.internal.codegen.binding.BindingNode;
-import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.binding.ComponentDescriptor;
import dagger.internal.codegen.binding.ModuleDescriptor;
import dagger.internal.codegen.javac.JavacPluginModule;
import dagger.internal.codegen.javapoet.TypeNames;
@@ -64,7 +64,7 @@ public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
// TODO(ronshapiro): use flogger
private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
private FactEmitter emitter;
- @Inject ComponentDescriptorFactory componentDescriptorFactory;
+ @Inject ComponentDescriptor.Factory componentDescriptorFactory;
@Inject BindingGraphFactory bindingGraphFactory;
@Inject XProcessingEnv xProcessingEnv;
diff --git a/java/dagger/internal/codegen/model/BUILD b/java/dagger/internal/codegen/model/BUILD
index e6c944ba0..f497f13c0 100644
--- a/java/dagger/internal/codegen/model/BUILD
+++ b/java/dagger/internal/codegen/model/BUILD
@@ -27,8 +27,6 @@ java_library(
"//java/dagger:core",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/xprocessing",
- "//java/dagger/producers",
- "//java/dagger/spi",
"//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/guava/base",
diff --git a/java/dagger/internal/codegen/model/DaggerAnnotation.java b/java/dagger/internal/codegen/model/DaggerAnnotation.java
index 4a598719a..23b18e4c8 100644
--- a/java/dagger/internal/codegen/model/DaggerAnnotation.java
+++ b/java/dagger/internal/codegen/model/DaggerAnnotation.java
@@ -49,7 +49,7 @@ public abstract class DaggerAnnotation {
return equivalenceWrapper().get();
}
- public AnnotationMirror java() {
+ public AnnotationMirror javac() {
return toJavac(xprocessing());
}
diff --git a/java/dagger/internal/codegen/model/DaggerElement.java b/java/dagger/internal/codegen/model/DaggerElement.java
index cf2ac52fb..2de01a18f 100644
--- a/java/dagger/internal/codegen/model/DaggerElement.java
+++ b/java/dagger/internal/codegen/model/DaggerElement.java
@@ -31,7 +31,7 @@ public abstract class DaggerElement {
public abstract XElement xprocessing();
- public Element java() {
+ public Element javac() {
return toJavac(xprocessing());
}
diff --git a/java/dagger/internal/codegen/model/DaggerExecutableElement.java b/java/dagger/internal/codegen/model/DaggerExecutableElement.java
index 484e446e2..42ce1fc97 100644
--- a/java/dagger/internal/codegen/model/DaggerExecutableElement.java
+++ b/java/dagger/internal/codegen/model/DaggerExecutableElement.java
@@ -32,7 +32,7 @@ public abstract class DaggerExecutableElement {
public abstract XExecutableElement xprocessing();
- public ExecutableElement java() {
+ public ExecutableElement javac() {
return toJavac(xprocessing());
}
diff --git a/java/dagger/internal/codegen/model/DaggerProcessingEnv.java b/java/dagger/internal/codegen/model/DaggerProcessingEnv.java
index 6645b90be..44308293a 100644
--- a/java/dagger/internal/codegen/model/DaggerProcessingEnv.java
+++ b/java/dagger/internal/codegen/model/DaggerProcessingEnv.java
@@ -39,7 +39,7 @@ public abstract class DaggerProcessingEnv {
return Backend.valueOf(xprocessing().getBackend().name());
}
- public ProcessingEnvironment java() {
+ public ProcessingEnvironment javac() {
return toJavac(xprocessing());
}
}
diff --git a/java/dagger/internal/codegen/model/DaggerType.java b/java/dagger/internal/codegen/model/DaggerType.java
index c606f15e0..b6f0beb72 100644
--- a/java/dagger/internal/codegen/model/DaggerType.java
+++ b/java/dagger/internal/codegen/model/DaggerType.java
@@ -39,7 +39,7 @@ public abstract class DaggerType {
return equivalenceWrapper().get();
}
- public TypeMirror java() {
+ public TypeMirror javac() {
return toJavac(xprocessing());
}
diff --git a/java/dagger/internal/codegen/model/DaggerTypeElement.java b/java/dagger/internal/codegen/model/DaggerTypeElement.java
index 77e0c4ffd..1bfab067d 100644
--- a/java/dagger/internal/codegen/model/DaggerTypeElement.java
+++ b/java/dagger/internal/codegen/model/DaggerTypeElement.java
@@ -32,7 +32,7 @@ public abstract class DaggerTypeElement {
public abstract XTypeElement xprocessing();
- public TypeElement java() {
+ public TypeElement javac() {
return toJavac(xprocessing());
}
diff --git a/java/dagger/internal/codegen/model/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/model/MoreAnnotationMirrors.java
index 9b45b891f..ade1f28e8 100644
--- a/java/dagger/internal/codegen/model/MoreAnnotationMirrors.java
+++ b/java/dagger/internal/codegen/model/MoreAnnotationMirrors.java
@@ -35,7 +35,7 @@ final class MoreAnnotationMirrors {
* defined in the annotation type.
*/
public static String toStableString(DaggerAnnotation qualifier) {
- return stableAnnotationMirrorToString(qualifier.java());
+ return stableAnnotationMirrorToString(qualifier.javac());
}
/**
diff --git a/java/dagger/internal/codegen/model/RequestKind.java b/java/dagger/internal/codegen/model/RequestKind.java
index 581a829b3..d21a04a41 100644
--- a/java/dagger/internal/codegen/model/RequestKind.java
+++ b/java/dagger/internal/codegen/model/RequestKind.java
@@ -19,11 +19,6 @@ package dagger.internal.codegen.model;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import dagger.Lazy;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-
/**
* Represents the different kinds of {@link javax.lang.model.type.TypeMirror types} that may be
* requested as dependencies for the same key. For example, {@code String}, {@code
@@ -35,13 +30,13 @@ public enum RequestKind {
/** A default request for an instance. E.g.: {@code FooType} */
INSTANCE,
- /** A request for a {@link Provider}. E.g.: {@code Provider<FooType>} */
+ /** A request for a {@code Provider}. E.g.: {@code Provider<FooType>} */
PROVIDER,
- /** A request for a {@link Lazy}. E.g.: {@code Lazy<FooType>} */
+ /** A request for a {@code Lazy}. E.g.: {@code Lazy<FooType>} */
LAZY,
- /** A request for a {@link Provider} of a {@link Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
+ /** A request for a {@code Provider} of a {@code Lazy}. E.g.: {@code Provider<Lazy<FooType>>}. */
PROVIDER_OF_LAZY,
/**
@@ -50,10 +45,10 @@ public enum RequestKind {
*/
MEMBERS_INJECTION,
- /** A request for a {@link Producer}. E.g.: {@code Producer<FooType>} */
+ /** A request for a {@code Producer}. E.g.: {@code Producer<FooType>} */
PRODUCER,
- /** A request for a {@link Produced}. E.g.: {@code Produced<FooType>} */
+ /** A request for a {@code Produced}. E.g.: {@code Produced<FooType>} */
PRODUCED,
/**
diff --git a/java/dagger/internal/codegen/processingstep/AssistedFactoryProcessingStep.java b/java/dagger/internal/codegen/processingstep/AssistedFactoryProcessingStep.java
index e95bdf927..ebd5d106d 100644
--- a/java/dagger/internal/codegen/processingstep/AssistedFactoryProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/AssistedFactoryProcessingStep.java
@@ -23,6 +23,7 @@ import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForB
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
import static dagger.internal.codegen.langmodel.Accessibility.accessibleTypeName;
import static dagger.internal.codegen.xprocessing.MethodSpecs.overriding;
@@ -311,9 +312,13 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<XTy
// use the parameter names of the @AssistedFactory method.
metadata.assistedInjectAssistedParameters().stream()
.map(metadata.assistedFactoryAssistedParametersMap()::get)
- .map(param -> CodeBlock.of("$L", getSimpleName(param)))
+ .map(param -> CodeBlock.of("$L", param.getJvmName()))
.collect(toParametersCodeBlock()))
.build())
+ // In a future release, we should delete this javax method. This will still be a breaking
+ // change, but keeping compatibility for a while should reduce the likelihood of breakages
+ // as it would require components built at much older versions using factories built at
+ // newer versions to break.
.addMethod(
MethodSpec.methodBuilder("create")
.addModifiers(PUBLIC, STATIC)
@@ -331,6 +336,27 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<XTy
: CodeBlock.of(""),
name,
delegateFactoryParam)
+ .build())
+ // Normally we would have called this just "create", but because of backwards
+ // compatibility we can't have two methods with the same name/arguments returning
+ // different Provider types.
+ .addMethod(
+ MethodSpec.methodBuilder("createFactoryProvider")
+ .addModifiers(PUBLIC, STATIC)
+ .addParameter(delegateFactoryParam)
+ .addTypeVariables(typeVariableNames(metadata.assistedInjectElement()))
+ .returns(daggerProviderOf(factory.getType().getTypeName()))
+ .addStatement(
+ "return $T.$Lcreate(new $T($N))",
+ INSTANCE_FACTORY,
+ // Java 7 type inference requires the method call provide the exact type here.
+ isPreJava8SourceVersion(processingEnv)
+ ? CodeBlock.of(
+ "<$T>",
+ accessibleTypeName(metadata.factoryType(), name, processingEnv))
+ : CodeBlock.of(""),
+ name,
+ delegateFactoryParam)
.build());
return ImmutableList.of(builder);
}
diff --git a/java/dagger/internal/codegen/processingstep/AssistedInjectProcessingStep.java b/java/dagger/internal/codegen/processingstep/AssistedInjectProcessingStep.java
index 8d73f4774..cfd0bec6e 100644
--- a/java/dagger/internal/codegen/processingstep/AssistedInjectProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/AssistedInjectProcessingStep.java
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter;
import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.validation.InjectValidator;
import dagger.internal.codegen.validation.ValidationReport;
import java.util.HashSet;
import java.util.Set;
@@ -34,10 +35,12 @@ import javax.inject.Inject;
/** An annotation processor for {@link dagger.assisted.AssistedInject}-annotated elements. */
final class AssistedInjectProcessingStep extends TypeCheckingProcessingStep<XConstructorElement> {
private final XMessager messager;
+ private final InjectValidator injectValidator;
@Inject
- AssistedInjectProcessingStep(XMessager messager) {
+ AssistedInjectProcessingStep(XMessager messager, InjectValidator injectValidator) {
this.messager = messager;
+ this.injectValidator = injectValidator;
}
@Override
@@ -48,7 +51,13 @@ final class AssistedInjectProcessingStep extends TypeCheckingProcessingStep<XCon
@Override
protected void process(
XConstructorElement assistedInjectElement, ImmutableSet<ClassName> annotations) {
- new AssistedInjectValidator().validate(assistedInjectElement).printMessagesTo(messager);
+ // The InjectValidator has already run and reported its errors in InjectProcessingStep, so no
+ // need to report its errors. However, the AssistedInjectValidator relies on the InjectValidator
+ // returning a clean report, so we check that first before running AssistedInjectValidator. This
+ // shouldn't be expensive since InjectValidator caches its results after validating.
+ if (injectValidator.validate(assistedInjectElement.getEnclosingElement()).isClean()) {
+ new AssistedInjectValidator().validate(assistedInjectElement).printMessagesTo(messager);
+ }
}
private final class AssistedInjectValidator {
diff --git a/java/dagger/internal/codegen/processingstep/ComponentHjarProcessingStep.java b/java/dagger/internal/codegen/processingstep/ComponentHjarProcessingStep.java
index 7a7e180da..864bd768d 100644
--- a/java/dagger/internal/codegen/processingstep/ComponentHjarProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/ComponentHjarProcessingStep.java
@@ -28,7 +28,6 @@ import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.ComponentDescriptor;
-import dagger.internal.codegen.binding.ComponentDescriptorFactory;
import dagger.internal.codegen.validation.ComponentCreatorValidator;
import dagger.internal.codegen.validation.ComponentValidator;
import dagger.internal.codegen.validation.ValidationReport;
@@ -52,7 +51,7 @@ final class ComponentHjarProcessingStep extends TypeCheckingProcessingStep<XType
private final XMessager messager;
private final ComponentValidator componentValidator;
private final ComponentCreatorValidator creatorValidator;
- private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final ComponentDescriptor.Factory componentDescriptorFactory;
private final SourceFileGenerator<ComponentDescriptor> componentGenerator;
@Inject
@@ -60,7 +59,7 @@ final class ComponentHjarProcessingStep extends TypeCheckingProcessingStep<XType
XMessager messager,
ComponentValidator componentValidator,
ComponentCreatorValidator creatorValidator,
- ComponentDescriptorFactory componentDescriptorFactory,
+ ComponentDescriptor.Factory componentDescriptorFactory,
SourceFileGenerator<ComponentDescriptor> componentGenerator) {
this.messager = messager;
this.componentValidator = componentValidator;
diff --git a/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java b/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
index 5b17c0061..168e8946f 100644
--- a/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/ComponentProcessingStep.java
@@ -34,7 +34,6 @@ import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingGraphFactory;
import dagger.internal.codegen.binding.ComponentDescriptor;
-import dagger.internal.codegen.binding.ComponentDescriptorFactory;
import dagger.internal.codegen.validation.BindingGraphValidator;
import dagger.internal.codegen.validation.ComponentCreatorValidator;
import dagger.internal.codegen.validation.ComponentDescriptorValidator;
@@ -52,7 +51,7 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<XTypeElem
private final ComponentValidator componentValidator;
private final ComponentCreatorValidator creatorValidator;
private final ComponentDescriptorValidator componentDescriptorValidator;
- private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final ComponentDescriptor.Factory componentDescriptorFactory;
private final BindingGraphFactory bindingGraphFactory;
private final SourceFileGenerator<BindingGraph> componentGenerator;
private final BindingGraphValidator bindingGraphValidator;
@@ -63,7 +62,7 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<XTypeElem
ComponentValidator componentValidator,
ComponentCreatorValidator creatorValidator,
ComponentDescriptorValidator componentDescriptorValidator,
- ComponentDescriptorFactory componentDescriptorFactory,
+ ComponentDescriptor.Factory componentDescriptorFactory,
BindingGraphFactory bindingGraphFactory,
SourceFileGenerator<BindingGraph> componentGenerator,
BindingGraphValidator bindingGraphValidator) {
@@ -132,7 +131,10 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<XTypeElem
return;
}
BindingGraph fullBindingGraph = bindingGraphFactory.create(subcomponentDescriptor, true);
- boolean isValid = bindingGraphValidator.isValid(fullBindingGraph.topLevelBindingGraph());
+ // In this case, we don't actually care about the return value. The important part here is that
+ // BindingGraphValidator#isValid() runs all of the SPI plugins and reports any errors.
+ // TODO(bcorso): Add a separate API with no return value for this particular case.
+ boolean unusedIsValid = bindingGraphValidator.isValid(fullBindingGraph.topLevelBindingGraph());
}
private void generateComponent(BindingGraph bindingGraph) {
diff --git a/java/dagger/internal/codegen/processingstep/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/processingstep/MonitoringModuleGenerator.java
index 00b033d96..3692144cf 100644
--- a/java/dagger/internal/codegen/processingstep/MonitoringModuleGenerator.java
+++ b/java/dagger/internal/codegen/processingstep/MonitoringModuleGenerator.java
@@ -35,6 +35,7 @@ import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.Module;
import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.binding.SourceFiles;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.multibindings.Multibinds;
@@ -42,10 +43,15 @@ import javax.inject.Inject;
/** Generates a monitoring module for use with production components. */
final class MonitoringModuleGenerator extends SourceFileGenerator<XTypeElement> {
+ private final MonitoringModules monitoringModules;
@Inject
- MonitoringModuleGenerator(XFiler filer, XProcessingEnv processingEnv) {
+ MonitoringModuleGenerator(
+ XFiler filer,
+ XProcessingEnv processingEnv,
+ MonitoringModules monitoringModules) {
super(filer, processingEnv);
+ this.monitoringModules = monitoringModules;
}
@Override
@@ -55,6 +61,7 @@ final class MonitoringModuleGenerator extends SourceFileGenerator<XTypeElement>
@Override
public ImmutableList<TypeSpec.Builder> topLevelTypes(XTypeElement componentElement) {
+ monitoringModules.add(SourceFiles.generatedMonitoringModuleName(componentElement));
return ImmutableList.of(
classBuilder(SourceFiles.generatedMonitoringModuleName(componentElement))
.addAnnotation(Module.class)
diff --git a/java/dagger/internal/codegen/processingstep/TypeCheckingProcessingStep.java b/java/dagger/internal/codegen/processingstep/TypeCheckingProcessingStep.java
index 5d6a64f3d..7ff47532a 100644
--- a/java/dagger/internal/codegen/processingstep/TypeCheckingProcessingStep.java
+++ b/java/dagger/internal/codegen/processingstep/TypeCheckingProcessingStep.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
+import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.ArrayList;
@@ -51,6 +52,7 @@ abstract class TypeCheckingProcessingStep<E extends XElement> implements XProces
@Inject XMessager messager;
@Inject CompilerOptions compilerOptions;
@Inject SuperficialValidator superficialValidator;
+ @Inject MonitoringModules monitoringModules;
@Override
public final ImmutableSet<String> annotations() {
@@ -70,6 +72,16 @@ abstract class TypeCheckingProcessingStep<E extends XElement> implements XProces
.forEach(
(element, annotations) -> {
try {
+ if (this instanceof ComponentProcessingStep && !monitoringModules.isEmpty()) {
+ // If there were any monitoring modules generated by Dagger in this round then we
+ // should just defer processing the components until the next round. This is an
+ // optimization to avoid unnecessary processing of components that will likely
+ // need to be reprocessed next round anyway due to the missing module. We should
+ // be guaranteed that there will be a next round since the monitoring modules were
+ // generated in this round.
+ deferredElements.add(element);
+ return;
+ }
// The XBasicAnnotationProcessor only validates the element itself. However, we
// validate the enclosing type here to keep the previous behavior of
// BasicAnnotationProcessor, since Dagger still relies on this behavior.
diff --git a/java/dagger/internal/codegen/validation/BUILD b/java/dagger/internal/codegen/validation/BUILD
index cc3670843..a24b7e0bf 100644
--- a/java/dagger/internal/codegen/validation/BUILD
+++ b/java/dagger/internal/codegen/validation/BUILD
@@ -36,7 +36,6 @@ java_library(
"//java/dagger/internal/codegen/model",
"//java/dagger/internal/codegen/xprocessing",
"//java/dagger/spi",
- "//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/checker_framework_annotations",
"//third_party/java/error_prone:annotations",
diff --git a/java/dagger/internal/codegen/validation/BindingMethodValidator.java b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
index 9598a7c35..731050649 100644
--- a/java/dagger/internal/codegen/validation/BindingMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
@@ -140,6 +140,7 @@ abstract class BindingMethodValidator extends BindingElementValidator<XMethodEle
@Override
protected final void checkAdditionalProperties() {
+ checkNotExtensionFunction();
checkEnclosingElement();
checkTypeParameters();
checkNotPrivate();
@@ -152,6 +153,12 @@ abstract class BindingMethodValidator extends BindingElementValidator<XMethodEle
/** Checks additional properties of the binding method. */
protected void checkAdditionalMethodProperties() {}
+ private void checkNotExtensionFunction() {
+ if (method.isExtensionFunction()) {
+ report.addError(bindingMethods("can not be an extension function"));
+ }
+ }
+
/**
* Adds an error if the method is not declared in a class or interface annotated with one of the
* {@link #enclosingElementAnnotations}.
diff --git a/java/dagger/internal/codegen/validation/ComponentValidator.java b/java/dagger/internal/codegen/validation/ComponentValidator.java
index b5034480c..b3469a14e 100644
--- a/java/dagger/internal/codegen/validation/ComponentValidator.java
+++ b/java/dagger/internal/codegen/validation/ComponentValidator.java
@@ -168,6 +168,7 @@ public final class ComponentValidator implements ClearableCache {
// the remaining checks will likely just output unhelpful noise in such cases.
return report.addError(invalidTypeError(), component).build();
}
+ validateFields();
validateUseOfCancellationPolicy();
validateIsAbstractType();
validateCreators();
@@ -208,6 +209,18 @@ public final class ComponentValidator implements ClearableCache {
componentKind().annotation().simpleName());
}
+ private void validateFields() {
+ component.getDeclaredMethods().stream()
+ .filter(method -> method.isKotlinPropertySetter() && method.isAbstract())
+ .forEach(
+ method ->
+ report.addError(
+ String.format(
+ "Cannot use 'abstract var' property in a component declaration to get a"
+ + " binding. Use 'val' or 'fun' instead: %s",
+ method.getPropertyName())));
+ }
+
private void validateCreators() {
ImmutableSet<XTypeElement> creators =
enclosedAnnotatedTypes(component, creatorAnnotationsFor(componentAnnotation()));
diff --git a/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
index fc83e31f2..24ad67691 100644
--- a/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
+++ b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
@@ -164,18 +164,11 @@ public final class DiagnosticMessageGenerator {
ImmutableList<DependencyEdge> dependencyTrace,
ImmutableSet<DependencyEdge> requests,
ImmutableSet<DependencyEdge> entryPoints) {
- StringBuilder message =
- graph.isFullBindingGraph()
- ? new StringBuilder()
- : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
-
- // Print the dependency trace unless it's a full binding graph
- if (!graph.isFullBindingGraph()) {
- dependencyTrace.forEach(
- edge -> dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
- if (!dependencyTrace.isEmpty()) {
- appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
- }
+ StringBuilder message = new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
+ dependencyTrace.forEach(
+ edge -> dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
+ if (!dependencyTrace.isEmpty()) {
+ appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
}
message.append(getRequestsNotInTrace(dependencyTrace, requests, entryPoints));
return message.toString();
@@ -190,25 +183,19 @@ public final class DiagnosticMessageGenerator {
ImmutableSet<XElement> requestsToPrint =
requests.stream()
// if printing entry points, skip entry points and the traced request
- .filter(
- request ->
- graph.isFullBindingGraph()
- || (!request.isEntryPoint() && !isTracedRequest(dependencyTrace, request)))
+ .filter(request -> !request.isEntryPoint())
+ .filter(request -> !isTracedRequest(dependencyTrace, request))
.map(request -> request.dependencyRequest().requestElement())
.flatMap(presentValues())
.map(DaggerElement::xprocessing)
.collect(toImmutableSet());
if (!requestsToPrint.isEmpty()) {
- message
- .append("\nIt is")
- .append(graph.isFullBindingGraph() ? " " : " also ")
- .append("requested at:");
+ message.append("\nIt is also requested at:");
elementFormatter.formatIndentedList(message, requestsToPrint, 1);
}
- // Print the remaining entry points, showing which component they're in, unless it's a full
- // binding graph
- if (!graph.isFullBindingGraph() && entryPoints.size() > 1) {
+ // Print the remaining entry points, showing which component they're in
+ if (entryPoints.size() > 1) {
message.append("\nThe following other entry points also depend on it:");
entryPointFormatter.formatIndentedList(
message,
@@ -253,9 +240,14 @@ public final class DiagnosticMessageGenerator {
}
};
- private static boolean isTracedRequest(
+ private boolean isTracedRequest(
ImmutableList<DependencyEdge> dependencyTrace, DependencyEdge request) {
- return !dependencyTrace.isEmpty() && request.equals(dependencyTrace.get(0));
+ return !dependencyTrace.isEmpty()
+ && request.dependencyRequest().equals(dependencyTrace.get(0).dependencyRequest())
+ // Comparing the dependency request is not enough since the request is just the key.
+ // Instead, we check that the target incident node is the same.
+ && graph.network().incidentNodes(request).target()
+ .equals(graph.network().incidentNodes(dependencyTrace.get(0)).target());
}
/**
diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
index cc2d6e940..16344d3f3 100644
--- a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
+++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen.validation;
import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.compat.XConverters.toKS;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
@@ -27,6 +28,7 @@ import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConst
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
import static dagger.internal.codegen.xprocessing.XTypes.erasedTypeName;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import static dagger.internal.codegen.xprocessing.XTypes.nonObjectSuperclass;
@@ -41,6 +43,7 @@ import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import com.google.devtools.ksp.symbol.Origin;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.squareup.javapoet.ClassName;
import dagger.Component;
@@ -82,7 +85,7 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
private final BindingFactory bindingFactory;
private final CompilerOptions compilerOptions;
- final class BindingsCollection<B extends Binding> {
+ private final class BindingsCollection<B extends Binding> {
private final ClassName factoryClass;
private final Map<Key, B> bindingsByKey = Maps.newLinkedHashMap();
private final Deque<B> bindingsRequiringGeneration = new ArrayDeque<>();
@@ -115,24 +118,40 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
}
/** Caches the binding and generates it if it needs generation. */
- void tryRegisterBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ void tryRegisterBinding(B binding, boolean isCalledFromInjectProcessor) {
+ if (processingEnv.getBackend() == XProcessingEnv.Backend.KSP) {
+ Origin origin =
+ toKS(closestEnclosingTypeElement(binding.bindingElement().get())).getOrigin();
+ // If the origin of the element is from a source file in the current compilation unit then
+ // we're guaranteed that the InjectProcessor should run over the element so only generate
+ // the Factory/MembersInjector if we're being called from the InjectProcessor.
+ //
+ // TODO(bcorso): generally, this isn't something we would need to keep track of manually.
+ // However, KSP incremental processing has a bug that will overwrite the cache for the
+ // element if we generate files for it, which can lead to missing generated files from
+ // other processors. See https://github.com/google/dagger/issues/4063 and
+ // https://github.com/google/dagger/issues/4054. Remove this once that bug is fixed.
+ if (!isCalledFromInjectProcessor && (origin == Origin.JAVA || origin == Origin.KOTLIN)) {
+ return;
+ }
+ }
tryToCacheBinding(binding);
@SuppressWarnings("unchecked")
B maybeUnresolved =
binding.unresolved().isPresent() ? (B) binding.unresolved().get() : binding;
- tryToGenerateBinding(maybeUnresolved, warnIfNotAlreadyGenerated);
+ tryToGenerateBinding(maybeUnresolved, isCalledFromInjectProcessor);
}
/**
* Tries to generate a binding, not generating if it already is generated. For resolved
* bindings, this will try to generate the unresolved version of the binding.
*/
- void tryToGenerateBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ void tryToGenerateBinding(B binding, boolean isCalledFromInjectProcessor) {
if (shouldGenerateBinding(binding)) {
bindingsRequiringGeneration.offer(binding);
if (compilerOptions.warnIfInjectionFactoryNotGeneratedUpstream()
- && warnIfNotAlreadyGenerated) {
+ && !isCalledFromInjectProcessor) {
messager.printMessage(
Kind.NOTE,
String.format(
@@ -147,6 +166,22 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
/** Returns true if the binding needs to be generated. */
private boolean shouldGenerateBinding(B binding) {
+ if (binding instanceof MembersInjectionBinding) {
+ MembersInjectionBinding membersInjectionBinding = (MembersInjectionBinding) binding;
+ // Empty members injection bindings are special and don't need source files.
+ if (membersInjectionBinding.injectionSites().isEmpty()) {
+ return false;
+ }
+ // Members injectors for classes with no local injection sites and no @Inject
+ // constructor are unused.
+ boolean hasInjectConstructor =
+ !(injectedConstructors(membersInjectionBinding.membersInjectedType()).isEmpty()
+ && assistedInjectedConstructors(
+ membersInjectionBinding.membersInjectedType()).isEmpty());
+ if (!membersInjectionBinding.hasLocalInjectionSites() && !hasInjectConstructor) {
+ return false;
+ }
+ }
return !binding.unresolved().isPresent()
&& !materializedBindingKeys.contains(binding.key())
&& !bindingsRequiringGeneration.contains(binding)
@@ -200,52 +235,20 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
membersInjectionBindings.generateBindings(membersInjectorGenerator);
}
- /**
- * Registers the binding for generation and later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {
- provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
- }
-
- /**
- * Registers the binding for generation and later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private void registerBinding(MembersInjectionBinding binding, boolean warnIfNotAlreadyGenerated) {
- /*
- * We generate MembersInjector classes for types with @Inject constructors only if they have any
- * injection sites.
- *
- * We generate MembersInjector classes for types without @Inject constructors only if they have
- * local (non-inherited) injection sites.
- *
- * Warn only when registering bindings post-hoc for those types.
- */
- if (warnIfNotAlreadyGenerated) {
- boolean hasInjectConstructor =
- !(injectedConstructors(binding.membersInjectedType()).isEmpty()
- && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty());
- warnIfNotAlreadyGenerated =
- hasInjectConstructor
- ? !binding.injectionSites().isEmpty()
- : binding.hasLocalInjectionSites();
- }
-
- membersInjectionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
- }
-
@Override
public Optional<ProvisionBinding> tryRegisterInjectConstructor(
XConstructorElement constructorElement) {
- return tryRegisterConstructor(constructorElement, Optional.empty(), false);
+ return tryRegisterConstructor(
+ constructorElement,
+ Optional.empty(),
+ /* isCalledFromInjectProcessor= */ true);
}
@CanIgnoreReturnValue
private Optional<ProvisionBinding> tryRegisterConstructor(
XConstructorElement constructorElement,
Optional<XType> resolvedType,
- boolean warnIfNotAlreadyGenerated) {
+ boolean isCalledFromInjectProcessor) {
XTypeElement typeElement = constructorElement.getEnclosingElement();
// Validating here shouldn't have a performance penalty because the validator caches its reports
@@ -263,9 +266,9 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
}
ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType);
- registerBinding(binding, warnIfNotAlreadyGenerated);
+ provisionBindings.tryRegisterBinding(binding, isCalledFromInjectProcessor);
if (!binding.injectionSites().isEmpty()) {
- tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated);
+ tryRegisterMembersInjectedType(typeElement, resolvedType, isCalledFromInjectProcessor);
}
return Optional.of(binding);
}
@@ -281,7 +284,9 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
fieldElement);
}
return tryRegisterMembersInjectedType(
- asTypeElement(fieldElement.getEnclosingElement()), Optional.empty(), false);
+ asTypeElement(fieldElement.getEnclosingElement()),
+ Optional.empty(),
+ /* isCalledFromInjectProcessor= */ true);
}
@Override
@@ -295,12 +300,16 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
methodElement);
}
return tryRegisterMembersInjectedType(
- asTypeElement(methodElement.getEnclosingElement()), Optional.empty(), false);
+ asTypeElement(methodElement.getEnclosingElement()),
+ Optional.empty(),
+ /* isCalledFromInjectProcessor= */ true);
}
@CanIgnoreReturnValue
private Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(
- XTypeElement typeElement, Optional<XType> resolvedType, boolean warnIfNotAlreadyGenerated) {
+ XTypeElement typeElement,
+ Optional<XType> resolvedType,
+ boolean isCalledFromInjectProcessor) {
// Validating here shouldn't have a performance penalty because the validator caches its reports
ValidationReport report = injectValidator.validateForMembersInjection(typeElement);
report.printMessagesTo(messager);
@@ -316,7 +325,7 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
}
MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType);
- registerBinding(binding, warnIfNotAlreadyGenerated);
+ membersInjectionBindings.tryRegisterBinding(binding, isCalledFromInjectProcessor);
for (Optional<XType> supertype = nonObjectSuperclass(type);
supertype.isPresent();
supertype = nonObjectSuperclass(supertype.get())) {
@@ -351,7 +360,12 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
assistedInjectedConstructors(element).stream())
// We're guaranteed that there's at most 1 @Inject constructors from above validation.
.collect(toOptional())
- .flatMap(constructor -> tryRegisterConstructor(constructor, Optional.of(type), true));
+ .flatMap(
+ constructor ->
+ tryRegisterConstructor(
+ constructor,
+ Optional.of(type),
+ /* isCalledFromInjectProcessor= */ false));
}
@CanIgnoreReturnValue
@@ -365,7 +379,9 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry {
return Optional.of(binding);
}
return tryRegisterMembersInjectedType(
- key.type().xprocessing().getTypeElement(), Optional.of(key.type().xprocessing()), true);
+ key.type().xprocessing().getTypeElement(),
+ Optional.of(key.type().xprocessing()),
+ /* isCalledFromInjectProcessor= */ false);
}
@Override
diff --git a/java/dagger/internal/codegen/validation/InjectValidator.java b/java/dagger/internal/codegen/validation/InjectValidator.java
index f223f6a9a..ac434d7d6 100644
--- a/java/dagger/internal/codegen/validation/InjectValidator.java
+++ b/java/dagger/internal/codegen/validation/InjectValidator.java
@@ -64,6 +64,7 @@ import javax.tools.Diagnostic.Kind;
*/
@Singleton
public final class InjectValidator implements ClearableCache {
+
private final XProcessingEnv processingEnv;
private final CompilerOptions compilerOptions;
private final DependencyRequestValidator dependencyRequestValidator;
@@ -298,6 +299,15 @@ public final class InjectValidator implements ClearableCache {
fieldElement);
}
+ if (fieldElement.isProtected()
+ && fieldElement.getEnclosingElement().isFromKotlin()
+ ) {
+ builder.addItem(
+ "Dagger injector does not have access to kotlin protected fields",
+ staticMemberDiagnosticKind(),
+ fieldElement);
+ }
+
validateDependencyRequest(builder, fieldElement);
return builder.build();
diff --git a/java/dagger/internal/codegen/validation/ModelBindingGraphConverter.java b/java/dagger/internal/codegen/validation/ModelBindingGraphConverter.java
index 314ed5fb4..ae53978d5 100644
--- a/java/dagger/internal/codegen/validation/ModelBindingGraphConverter.java
+++ b/java/dagger/internal/codegen/validation/ModelBindingGraphConverter.java
@@ -134,8 +134,8 @@ public final class ModelBindingGraphConverter {
}
private static Key toModel(dagger.internal.codegen.model.Key key) {
- return Key.builder(key.type().java())
- .qualifier(key.qualifier().map(DaggerAnnotation::java))
+ return Key.builder(key.type().javac())
+ .qualifier(key.qualifier().map(DaggerAnnotation::javac))
.multibindingContributionIdentifier(
key.multibindingContributionIdentifier().isPresent()
? Optional.of(toModel(key.multibindingContributionIdentifier().get()))
@@ -159,17 +159,17 @@ public final class ModelBindingGraphConverter {
.key(toModel(request.key()))
.isNullable(request.isNullable());
- request.requestElement().ifPresent(e -> builder.requestElement(e.java()));
+ request.requestElement().ifPresent(e -> builder.requestElement(e.javac()));
return builder.build();
}
private static Scope toModel(dagger.internal.codegen.model.Scope scope) {
- return Scope.scope(scope.scopeAnnotation().java());
+ return Scope.scope(scope.scopeAnnotation().javac());
}
private static ComponentPath toModel(dagger.internal.codegen.model.ComponentPath path) {
return ComponentPath.create(
- path.components().stream().map(DaggerTypeElement::java).collect(toImmutableList()));
+ path.components().stream().map(DaggerTypeElement::javac).collect(toImmutableList()));
}
private static dagger.internal.codegen.model.BindingGraph.ComponentNode toInternal(
@@ -232,8 +232,8 @@ public final class ModelBindingGraphConverter {
binding.dependencies().stream()
.map(ModelBindingGraphConverter::toModel)
.collect(toImmutableSet()),
- binding.bindingElement().map(DaggerElement::java),
- binding.contributingModule().map(DaggerTypeElement::java),
+ binding.bindingElement().map(DaggerElement::javac),
+ binding.contributingModule().map(DaggerTypeElement::javac),
binding.requiresModuleInstance(),
binding.scope().map(ModelBindingGraphConverter::toModel),
binding.isNullable(),
@@ -291,7 +291,7 @@ public final class ModelBindingGraphConverter {
static ChildFactoryMethodEdge create(
dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge childFactoryMethodEdge) {
return new AutoValue_ModelBindingGraphConverter_ChildFactoryMethodEdgeImpl(
- childFactoryMethodEdge.factoryMethod().java(), childFactoryMethodEdge);
+ childFactoryMethodEdge.factoryMethod().javac(), childFactoryMethodEdge);
}
abstract dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge delegate();
@@ -310,7 +310,7 @@ public final class ModelBindingGraphConverter {
subcomponentCreatorBindingEdge) {
return new AutoValue_ModelBindingGraphConverter_SubcomponentCreatorBindingEdgeImpl(
subcomponentCreatorBindingEdge.declaringModules().stream()
- .map(DaggerTypeElement::java)
+ .map(DaggerTypeElement::javac)
.collect(toImmutableSet()),
subcomponentCreatorBindingEdge);
}
diff --git a/java/dagger/internal/codegen/validation/ModuleValidator.java b/java/dagger/internal/codegen/validation/ModuleValidator.java
index a1d9b0d11..38bce2b44 100644
--- a/java/dagger/internal/codegen/validation/ModuleValidator.java
+++ b/java/dagger/internal/codegen/validation/ModuleValidator.java
@@ -58,7 +58,7 @@ import dagger.internal.codegen.base.ComponentCreatorAnnotation;
import dagger.internal.codegen.base.DaggerSuperficialValidation;
import dagger.internal.codegen.base.ModuleKind;
import dagger.internal.codegen.binding.BindingGraphFactory;
-import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.binding.ComponentDescriptor;
import dagger.internal.codegen.binding.InjectionAnnotations;
import dagger.internal.codegen.binding.MethodSignatureFormatter;
import dagger.internal.codegen.javapoet.TypeNames;
@@ -109,7 +109,7 @@ public final class ModuleValidator {
private final AnyBindingMethodValidator anyBindingMethodValidator;
private final MethodSignatureFormatter methodSignatureFormatter;
- private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final ComponentDescriptor.Factory componentDescriptorFactory;
private final BindingGraphFactory bindingGraphFactory;
private final BindingGraphValidator bindingGraphValidator;
private final InjectionAnnotations injectionAnnotations;
@@ -122,7 +122,7 @@ public final class ModuleValidator {
ModuleValidator(
AnyBindingMethodValidator anyBindingMethodValidator,
MethodSignatureFormatter methodSignatureFormatter,
- ComponentDescriptorFactory componentDescriptorFactory,
+ ComponentDescriptor.Factory componentDescriptorFactory,
BindingGraphFactory bindingGraphFactory,
BindingGraphValidator bindingGraphValidator,
InjectionAnnotations injectionAnnotations,
diff --git a/java/dagger/internal/codegen/validation/ProducesMethodValidator.java b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
index cf78b63c4..9b4c16c4e 100644
--- a/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
@@ -90,7 +90,7 @@ final class ProducesMethodValidator extends BindingMethodValidator {
// TODO(beder): Properly handle nullable with producer methods.
private void checkNullable() {
Nullability nullability = Nullability.of(method);
- if (nullability.nullableAnnotation().isPresent()) {
+ if (!nullability.nullableAnnotations().isEmpty()) {
report.addWarning("@Nullable on @Produces methods does not do anything");
}
}
diff --git a/java/dagger/internal/codegen/validation/SpiModelBindingGraphConverter.java b/java/dagger/internal/codegen/validation/SpiModelBindingGraphConverter.java
index 75d0f7f99..42e1adffb 100644
--- a/java/dagger/internal/codegen/validation/SpiModelBindingGraphConverter.java
+++ b/java/dagger/internal/codegen/validation/SpiModelBindingGraphConverter.java
@@ -16,9 +16,11 @@
package dagger.internal.codegen.validation;
+import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
import static androidx.room.compiler.processing.compat.XConverters.toJavac;
import static androidx.room.compiler.processing.compat.XConverters.toKS;
import static androidx.room.compiler.processing.compat.XConverters.toKSResolver;
+import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
@@ -31,6 +33,7 @@ import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.graph.EndpointPair;
@@ -38,7 +41,16 @@ import com.google.common.graph.ImmutableNetwork;
import com.google.common.graph.MutableNetwork;
import com.google.common.graph.Network;
import com.google.common.graph.NetworkBuilder;
+import com.google.devtools.ksp.processing.Resolver;
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment;
+import com.google.devtools.ksp.symbol.KSAnnotated;
+import com.google.devtools.ksp.symbol.KSAnnotation;
+import com.google.devtools.ksp.symbol.KSClassDeclaration;
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration;
+import com.google.devtools.ksp.symbol.KSType;
+import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
import dagger.spi.model.Binding;
import dagger.spi.model.BindingGraph;
import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
@@ -64,6 +76,12 @@ import dagger.spi.model.Key;
import dagger.spi.model.RequestKind;
import dagger.spi.model.Scope;
import java.util.Optional;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
/** A Utility class for converting to the {@link BindingGraph} used by external plugins. */
@@ -141,19 +159,20 @@ public final class SpiModelBindingGraphConverter {
}
}
- private static Key toSpiModel(dagger.internal.codegen.model.Key key, XProcessingEnv env) {
+ private static Key toSpiModel(dagger.internal.codegen.model.Key key) {
Key.Builder builder =
- Key.builder(toSpiModel(key.type().xprocessing(), env))
- .qualifier(key.qualifier().map(qualifier -> toSpiModel(qualifier.xprocessing(), env)));
+ Key.builder(toSpiModel(key.type().xprocessing()))
+ .qualifier(key.qualifier().map(qualifier -> toSpiModel(qualifier.xprocessing())));
if (key.multibindingContributionIdentifier().isPresent()) {
return builder
.multibindingContributionIdentifier(
toSpiModel(
- key.multibindingContributionIdentifier().get().contributingModule().xprocessing(),
- env),
+ key.multibindingContributionIdentifier()
+ .get()
+ .contributingModule()
+ .xprocessing()),
toSpiModel(
- key.multibindingContributionIdentifier().get().bindingMethod().xprocessing(),
- env))
+ key.multibindingContributionIdentifier().get().bindingMethod().xprocessing()))
.build();
}
return builder.build().withoutMultibindingContributionIdentifier();
@@ -169,98 +188,50 @@ public final class SpiModelBindingGraphConverter {
@SuppressWarnings("CheckReturnValue")
private static DependencyRequest toSpiModel(
- dagger.internal.codegen.model.DependencyRequest request, XProcessingEnv env) {
+ dagger.internal.codegen.model.DependencyRequest request) {
DependencyRequest.Builder builder =
DependencyRequest.builder()
.kind(toSpiModel(request.kind()))
- .key(toSpiModel(request.key(), env))
+ .key(toSpiModel(request.key()))
.isNullable(request.isNullable());
- request
- .requestElement()
- .ifPresent(e -> builder.requestElement(toSpiModel(e.xprocessing(), env)));
+ request.requestElement().ifPresent(e -> builder.requestElement(toSpiModel(e.xprocessing())));
return builder.build();
}
- private static Scope toSpiModel(dagger.internal.codegen.model.Scope scope, XProcessingEnv env) {
- return Scope.scope(toSpiModel(scope.scopeAnnotation().xprocessing(), env));
+ private static Scope toSpiModel(dagger.internal.codegen.model.Scope scope) {
+ return Scope.scope(toSpiModel(scope.scopeAnnotation().xprocessing()));
}
- private static ComponentPath toSpiModel(
- dagger.internal.codegen.model.ComponentPath path, XProcessingEnv env) {
+ private static ComponentPath toSpiModel(dagger.internal.codegen.model.ComponentPath path) {
return ComponentPath.create(
path.components().stream()
- .map(component -> toSpiModel(component.xprocessing(), env))
+ .map(component -> toSpiModel(component.xprocessing()))
.collect(toImmutableList()));
}
- private static DaggerTypeElement toSpiModel(XTypeElement typeElement, XProcessingEnv env) {
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerTypeElement.fromJavac(toJavac(typeElement));
- case KSP:
- return DaggerTypeElement.fromKsp(toKS(typeElement));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ private static DaggerTypeElement toSpiModel(XTypeElement typeElement) {
+ return DaggerTypeElementImpl.from(typeElement);
}
- private static DaggerType toSpiModel(XType type, XProcessingEnv env) {
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerType.fromJavac(toJavac(type));
- case KSP:
- return DaggerType.fromKsp(toKS(type));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ private static DaggerType toSpiModel(XType type) {
+ return DaggerTypeImpl.from(type);
}
- static DaggerAnnotation toSpiModel(XAnnotation annotation, XProcessingEnv env) {
- DaggerTypeElement typeElement = toSpiModel(annotation.getTypeElement(), env);
-
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerAnnotation.fromJavac(typeElement, toJavac(annotation));
- case KSP:
- return DaggerAnnotation.fromKsp(typeElement, toKS(annotation));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ static DaggerAnnotation toSpiModel(XAnnotation annotation) {
+ return DaggerAnnotationImpl.from(annotation);
}
- private static DaggerElement toSpiModel(XElement element, XProcessingEnv env) {
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerElement.fromJavac(toJavac(element));
- case KSP:
- return DaggerElement.fromKsp(XElements.toKSAnnotated(element));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ private static DaggerElement toSpiModel(XElement element) {
+ return DaggerElementImpl.from(element);
}
- private static DaggerExecutableElement toSpiModel(
- XExecutableElement executableElement, XProcessingEnv env) {
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerExecutableElement.fromJava(toJavac(executableElement));
- case KSP:
- return DaggerExecutableElement.fromKsp(toKS(executableElement));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ private static DaggerExecutableElement toSpiModel(XExecutableElement executableElement) {
+ return DaggerExecutableElementImpl.from(executableElement);
}
static DaggerProcessingEnv toSpiModel(XProcessingEnv env) {
- switch (env.getBackend()) {
- case JAVAC:
- return DaggerProcessingEnv.fromJavac(toJavac(env));
- case KSP:
- return DaggerProcessingEnv.fromKsp(toKS(env), toKSResolver(env));
- }
- throw new IllegalStateException(
- String.format("Backend %s is not supported yet.", env.getBackend()));
+ return DaggerProcessingEnvImpl.from(env);
}
private static dagger.internal.codegen.model.BindingGraph.ComponentNode toInternal(
@@ -295,14 +266,14 @@ public final class SpiModelBindingGraphConverter {
dagger.internal.codegen.model.BindingGraph.ComponentNode componentNode,
XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_ComponentNodeImpl(
- toSpiModel(componentNode.componentPath(), env),
+ toSpiModel(componentNode.componentPath()),
componentNode.isSubcomponent(),
componentNode.isRealComponent(),
componentNode.entryPoints().stream()
- .map(request -> SpiModelBindingGraphConverter.toSpiModel(request, env))
+ .map(SpiModelBindingGraphConverter::toSpiModel)
.collect(toImmutableSet()),
componentNode.scopes().stream()
- .map(request -> SpiModelBindingGraphConverter.toSpiModel(request, env))
+ .map(SpiModelBindingGraphConverter::toSpiModel)
.collect(toImmutableSet()),
componentNode);
}
@@ -319,15 +290,15 @@ public final class SpiModelBindingGraphConverter {
abstract static class BindingNodeImpl implements Binding {
static Binding create(dagger.internal.codegen.model.Binding binding, XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_BindingNodeImpl(
- toSpiModel(binding.key(), env),
- toSpiModel(binding.componentPath(), env),
+ toSpiModel(binding.key()),
+ toSpiModel(binding.componentPath()),
binding.dependencies().stream()
- .map(request -> SpiModelBindingGraphConverter.toSpiModel(request, env))
+ .map(SpiModelBindingGraphConverter::toSpiModel)
.collect(toImmutableSet()),
- binding.bindingElement().map(element -> toSpiModel(element.xprocessing(), env)),
- binding.contributingModule().map(module -> toSpiModel(module.xprocessing(), env)),
+ binding.bindingElement().map(element -> toSpiModel(element.xprocessing())),
+ binding.contributingModule().map(module -> toSpiModel(module.xprocessing())),
binding.requiresModuleInstance(),
- binding.scope().map(scope -> SpiModelBindingGraphConverter.toSpiModel(scope, env)),
+ binding.scope().map(SpiModelBindingGraphConverter::toSpiModel),
binding.isNullable(),
binding.isProduction(),
toSpiModel(binding.kind()),
@@ -348,8 +319,8 @@ public final class SpiModelBindingGraphConverter {
dagger.internal.codegen.model.BindingGraph.MissingBinding missingBinding,
XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_MissingBindingImpl(
- toSpiModel(missingBinding.componentPath(), env),
- toSpiModel(missingBinding.key(), env),
+ toSpiModel(missingBinding.componentPath()),
+ toSpiModel(missingBinding.key()),
missingBinding);
}
@@ -369,7 +340,7 @@ public final class SpiModelBindingGraphConverter {
dagger.internal.codegen.model.BindingGraph.DependencyEdge dependencyEdge,
XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_DependencyEdgeImpl(
- toSpiModel(dependencyEdge.dependencyRequest(), env),
+ toSpiModel(dependencyEdge.dependencyRequest()),
dependencyEdge.isEntryPoint(),
dependencyEdge);
}
@@ -388,8 +359,7 @@ public final class SpiModelBindingGraphConverter {
dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge childFactoryMethodEdge,
XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_ChildFactoryMethodEdgeImpl(
- toSpiModel(childFactoryMethodEdge.factoryMethod().xprocessing(), env),
- childFactoryMethodEdge);
+ toSpiModel(childFactoryMethodEdge.factoryMethod().xprocessing()), childFactoryMethodEdge);
}
abstract dagger.internal.codegen.model.BindingGraph.ChildFactoryMethodEdge internalDelegate();
@@ -409,7 +379,7 @@ public final class SpiModelBindingGraphConverter {
XProcessingEnv env) {
return new AutoValue_SpiModelBindingGraphConverter_SubcomponentCreatorBindingEdgeImpl(
subcomponentCreatorBindingEdge.declaringModules().stream()
- .map(module -> toSpiModel(module.xprocessing(), env))
+ .map(module -> toSpiModel(module.xprocessing()))
.collect(toImmutableSet()),
subcomponentCreatorBindingEdge);
}
@@ -458,6 +428,225 @@ public final class SpiModelBindingGraphConverter {
}
}
+ @AutoValue
+ abstract static class DaggerElementImpl extends DaggerElement {
+ public static DaggerElement from(XElement element) {
+ return new AutoValue_SpiModelBindingGraphConverter_DaggerElementImpl(element);
+ }
+
+ abstract XElement element();
+
+ @Override
+ public Element javac() {
+ checkIsJavac(backend());
+ return toJavac(element());
+ }
+
+ @Override
+ public KSAnnotated ksp() {
+ checkIsKsp(backend());
+ return toKS(element());
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(getProcessingEnv(element()));
+ }
+
+ @Override
+ public final String toString() {
+ return XElements.toStableString(element());
+ }
+ }
+
+ @AutoValue
+ abstract static class DaggerTypeElementImpl extends DaggerTypeElement {
+ public static DaggerTypeElement from(XTypeElement element) {
+ return new AutoValue_SpiModelBindingGraphConverter_DaggerTypeElementImpl(element);
+ }
+
+ abstract XTypeElement element();
+
+ @Override
+ public TypeElement javac() {
+ checkIsJavac(backend());
+ return toJavac(element());
+ }
+
+ @Override
+ public KSClassDeclaration ksp() {
+ checkIsKsp(backend());
+ return toKS(element());
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(getProcessingEnv(element()));
+ }
+
+ @Override
+ public final String toString() {
+ return XElements.toStableString(element());
+ }
+ }
+
+ @AutoValue
+ abstract static class DaggerTypeImpl extends DaggerType {
+ public static DaggerType from(XType type) {
+ return new AutoValue_SpiModelBindingGraphConverter_DaggerTypeImpl(
+ XTypes.equivalence().wrap(type));
+ }
+
+ abstract Equivalence.Wrapper<XType> type();
+
+ @Override
+ public TypeMirror javac() {
+ checkIsJavac(backend());
+ return toJavac(type().get());
+ }
+
+ @Override
+ public KSType ksp() {
+ checkIsKsp(backend());
+ return toKS(type().get());
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(getProcessingEnv(type().get()));
+ }
+
+ @Override
+ public final String toString() {
+ return XTypes.toStableString(type().get());
+ }
+ }
+
+ @AutoValue
+ abstract static class DaggerAnnotationImpl extends DaggerAnnotation {
+ public static DaggerAnnotation from(XAnnotation annotation) {
+ return new AutoValue_SpiModelBindingGraphConverter_DaggerAnnotationImpl(
+ XAnnotations.equivalence().wrap(annotation));
+ }
+
+ abstract Equivalence.Wrapper<XAnnotation> annotation();
+
+ @Override
+ public DaggerTypeElement annotationTypeElement() {
+ return DaggerTypeElementImpl.from(annotation().get().getTypeElement());
+ }
+
+ @Override
+ public AnnotationMirror javac() {
+ checkIsJavac(backend());
+ return toJavac(annotation().get());
+ }
+
+ @Override
+ public KSAnnotation ksp() {
+ checkIsKsp(backend());
+ return toKS(annotation().get());
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(getProcessingEnv(annotation().get()));
+ }
+
+ @Override
+ public final String toString() {
+ return XAnnotations.toStableString(annotation().get());
+ }
+ }
+
+ @AutoValue
+ abstract static class DaggerExecutableElementImpl extends DaggerExecutableElement {
+ public static DaggerExecutableElement from(XExecutableElement executableElement) {
+ return new AutoValue_SpiModelBindingGraphConverter_DaggerExecutableElementImpl(
+ executableElement);
+ }
+
+ abstract XExecutableElement executableElement();
+
+ @Override
+ public ExecutableElement javac() {
+ checkIsJavac(backend());
+ return toJavac(executableElement());
+ }
+
+ @Override
+ public KSFunctionDeclaration ksp() {
+ checkIsKsp(backend());
+ return toKS(executableElement());
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(getProcessingEnv(executableElement()));
+ }
+
+ @Override
+ public final String toString() {
+ return XElements.toStableString(executableElement());
+ }
+ }
+
+ private static class DaggerProcessingEnvImpl extends DaggerProcessingEnv {
+ private final XProcessingEnv env;
+
+ public static DaggerProcessingEnv from(XProcessingEnv env) {
+ return new DaggerProcessingEnvImpl(env);
+ }
+
+ DaggerProcessingEnvImpl(XProcessingEnv env) {
+ this.env = env;
+ }
+
+ @Override
+ public ProcessingEnvironment javac() {
+ checkIsJavac(backend());
+ return toJavac(env);
+ }
+
+ @Override
+ public SymbolProcessorEnvironment ksp() {
+ checkIsKsp(backend());
+ return toKS(env);
+ }
+
+ @Override
+ public Resolver resolver() {
+ return toKSResolver(env);
+ }
+
+ @Override
+ public DaggerProcessingEnv.Backend backend() {
+ return getBackend(env);
+ }
+ }
+
+ private static void checkIsJavac(DaggerProcessingEnv.Backend backend) {
+ checkState(
+ backend == DaggerProcessingEnv.Backend.JAVAC,
+ "Expected JAVAC backend but was: %s", backend);
+ }
+
+ private static void checkIsKsp(DaggerProcessingEnv.Backend backend) {
+ checkState(
+ backend == DaggerProcessingEnv.Backend.KSP,
+ "Expected KSP backend but was: %s", backend);
+ }
+
+ private static DaggerProcessingEnv.Backend getBackend(XProcessingEnv env) {
+ switch (env.getBackend()) {
+ case JAVAC:
+ return DaggerProcessingEnv.Backend.JAVAC;
+ case KSP:
+ return DaggerProcessingEnv.Backend.KSP;
+ }
+ throw new AssertionError(String.format("Unexpected backend %s", env.getBackend()));
+ }
+
private static final class DiagnosticReporterImpl extends DiagnosticReporter {
static DiagnosticReporterImpl create(
dagger.internal.codegen.model.DiagnosticReporter reporter) {
diff --git a/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java b/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java
index 42551a003..87ac24ae8 100644
--- a/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java
+++ b/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java
@@ -23,10 +23,10 @@ import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
import androidx.room.compiler.processing.XConstructorElement;
import androidx.room.compiler.processing.XConstructorType;
+import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XMethodType;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
-import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ParameterSpec;
import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
@@ -79,12 +79,12 @@ final class AssistedInjectionParameters {
}
private static ImmutableList<ParameterSpec> assistedParameterSpecs(
- List<? extends XVariableElement> paramElements,
+ List<XExecutableParameterElement> paramElements,
List<XType> paramTypes,
ShardImplementation shardImplementation) {
ImmutableList.Builder<ParameterSpec> assistedParameterSpecs = ImmutableList.builder();
for (int i = 0; i < paramElements.size(); i++) {
- XVariableElement paramElement = paramElements.get(i);
+ XExecutableParameterElement paramElement = paramElements.get(i);
XType paramType = paramTypes.get(i);
if (AssistedInjectionAnnotations.isAssistedParameter(paramElement)) {
assistedParameterSpecs.add(
diff --git a/java/dagger/internal/codegen/writing/BUILD b/java/dagger/internal/codegen/writing/BUILD
index c8f9b61c2..3adeb66cd 100644
--- a/java/dagger/internal/codegen/writing/BUILD
+++ b/java/dagger/internal/codegen/writing/BUILD
@@ -31,11 +31,9 @@ java_library(
"//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/javapoet",
- "//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/model",
"//java/dagger/internal/codegen/xprocessing",
- "//java/dagger/producers",
"//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/error_prone:annotations",
diff --git a/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
index 88d4d4123..f74b50a36 100644
--- a/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
+++ b/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
@@ -202,6 +202,12 @@ final class ComponentCreatorImplementationFactory {
case NEEDED:
return Optional.of(normalSetterMethod(requirement));
case UNNEEDED:
+ // If this is a generated Builder, then remove the setter methods for modules that don't
+ // require an instance.
+ if (!componentDescriptor().creatorDescriptor().isPresent()
+ && !requirement.requiresModuleInstance()) {
+ return Optional.empty();
+ }
// TODO(bcorso): Don't generate noop setters for any unneeded requirements.
// However, since this is a breaking change we can at least avoid trying
// to generate noop setters for impossible cases like when the requirement type
diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java
index 204d6faec..a41b1c793 100644
--- a/java/dagger/internal/codegen/writing/ComponentImplementation.java
+++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java
@@ -42,6 +42,7 @@ import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import static javax.tools.Diagnostic.Kind.ERROR;
+import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XMessager;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XProcessingEnv;
@@ -72,9 +73,9 @@ import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingNode;
import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.CancellationPolicy;
import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
import dagger.internal.codegen.binding.ComponentDescriptor;
-import dagger.internal.codegen.binding.ComponentDescriptor.CancellationPolicy;
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.binding.ComponentRequirement;
import dagger.internal.codegen.binding.KeyVariableNamer;
@@ -111,16 +112,11 @@ public final class ComponentImplementation {
/** Compiler Modes. */
public enum CompilerMode {
DEFAULT,
- FAST_INIT,
- EXPERIMENTAL_MERGED_MODE;
+ FAST_INIT;
public boolean isFastInit() {
return this == CompilerMode.FAST_INIT;
}
-
- public boolean isExperimentalMergedMode() {
- return this == CompilerMode.EXPERIMENTAL_MERGED_MODE;
- }
}
/** A type of field that this component can contain. */
@@ -311,11 +307,7 @@ public final class ComponentImplementation {
this.messager = messager;
XTypeElement typeElement = rootComponentImplementation().componentDescriptor().typeElement();
this.compilerMode =
- compilerOptions.fastInit(typeElement)
- ? CompilerMode.FAST_INIT
- : (compilerOptions.experimentalMergedMode(typeElement)
- ? CompilerMode.EXPERIMENTAL_MERGED_MODE
- : CompilerMode.DEFAULT);
+ compilerOptions.fastInit(typeElement) ? CompilerMode.FAST_INIT : CompilerMode.DEFAULT;
}
/**
@@ -464,7 +456,7 @@ public final class ComponentImplementation {
private final UniqueNameSet assistedParamNames = new UniqueNameSet();
private final List<CodeBlock> initializations = new ArrayList<>();
private final SwitchingProviders switchingProviders;
- private final ExperimentalSwitchingProviders experimentalSwitchingProviders;
+ private final LazyClassKeyProviders lazyClassKeyProviders;
private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>();
private final Map<XVariableElement, String> uniqueAssistedName = new LinkedHashMap<>();
private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
@@ -481,9 +473,7 @@ public final class ComponentImplementation {
private ShardImplementation(ClassName name) {
this.name = name;
this.switchingProviders = new SwitchingProviders(this, processingEnv);
- this.experimentalSwitchingProviders =
- new ExperimentalSwitchingProviders(this, componentRequestRepresentationsProvider);
-
+ this.lazyClassKeyProviders = new LazyClassKeyProviders(this);
if (graph.componentDescriptor().isProduction()) {
claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
}
@@ -517,9 +507,8 @@ public final class ComponentImplementation {
return switchingProviders;
}
- /** Returns the {@link ExperimentalSwitchingProviders} class for this shard. */
- public ExperimentalSwitchingProviders getExperimentalSwitchingProviders() {
- return experimentalSwitchingProviders;
+ public LazyClassKeyProviders getLazyClassKeyProviders() {
+ return lazyClassKeyProviders;
}
/** Returns the {@link ComponentImplementation} that owns this shard. */
@@ -665,12 +654,12 @@ public final class ComponentImplementation {
return assistedParamNames.getUniqueName(name);
}
- public String getUniqueFieldNameForAssistedParam(XVariableElement element) {
- if (uniqueAssistedName.containsKey(element)) {
- return uniqueAssistedName.get(element);
+ public String getUniqueFieldNameForAssistedParam(XExecutableParameterElement parameter) {
+ if (uniqueAssistedName.containsKey(parameter)) {
+ return uniqueAssistedName.get(parameter);
}
- String name = getUniqueAssistedParamName(getSimpleName(element));
- uniqueAssistedName.put(element, name);
+ String name = getUniqueAssistedParamName(parameter.getJvmName());
+ uniqueAssistedName.put(parameter, name);
return name;
}
diff --git a/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
index d7594cbc1..41c3c46de 100644
--- a/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
@@ -36,7 +36,6 @@ final class ComponentProvisionRequestRepresentation extends RequestRepresentatio
private final BindingGraph bindingGraph;
private final ComponentRequirementExpressions componentRequirementExpressions;
private final CompilerOptions compilerOptions;
- private final boolean isExperimentalMergedMode;
@AssistedInject
ComponentProvisionRequestRepresentation(
@@ -49,16 +48,11 @@ final class ComponentProvisionRequestRepresentation extends RequestRepresentatio
this.bindingGraph = bindingGraph;
this.componentRequirementExpressions = componentRequirementExpressions;
this.compilerOptions = compilerOptions;
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
}
@Override
Expression getDependencyExpression(ClassName requestingClass) {
- CodeBlock componentDependency =
- isExperimentalMergedMode
- ? CodeBlock.of("(($T) dependencies[0])", componentRequirement().type().getTypeName())
- : getComponentRequirementExpression(requestingClass);
+ CodeBlock componentDependency = getComponentRequirementExpression(requestingClass);
CodeBlock invocation =
CodeBlock.of(
"$L.$L()", componentDependency, asMethod(binding.bindingElement().get()).getJvmName());
diff --git a/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
index 124eba1ff..dd343168a 100644
--- a/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
+++ b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
@@ -19,7 +19,6 @@ package dagger.internal.codegen.writing;
import static androidx.room.compiler.processing.XTypeKt.isVoid;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
@@ -28,13 +27,17 @@ import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessibl
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.xprocessing.MethodSpecs.overriding;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XProcessingEnvs.isPreJava8SourceVersion;
import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XType;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.OptionalType;
import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingRequest;
@@ -69,11 +72,8 @@ public final class ComponentRequestRepresentations {
membersInjectionBindingRepresentationFactory;
private final ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory;
private final ProductionBindingRepresentation.Factory productionBindingRepresentationFactory;
- private final ExperimentalSwitchingProviderDependencyRepresentation.Factory
- experimentalSwitchingProviderDependencyRepresentationFactory;
private final Map<Binding, BindingRepresentation> representations = new HashMap<>();
- private final Map<Binding, ExperimentalSwitchingProviderDependencyRepresentation>
- experimentalSwitchingProviderDependencyRepresentations = new HashMap<>();
+ private final XProcessingEnv processingEnv;
@Inject
ComponentRequestRepresentations(
@@ -84,8 +84,7 @@ public final class ComponentRequestRepresentations {
MembersInjectionBindingRepresentation.Factory membersInjectionBindingRepresentationFactory,
ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory,
ProductionBindingRepresentation.Factory productionBindingRepresentationFactory,
- ExperimentalSwitchingProviderDependencyRepresentation.Factory
- experimentalSwitchingProviderDependencyRepresentationFactory) {
+ XProcessingEnv processingEnv) {
this.parent = parent;
this.graph = graph;
this.componentImplementation = componentImplementation;
@@ -93,9 +92,8 @@ public final class ComponentRequestRepresentations {
membersInjectionBindingRepresentationFactory;
this.provisionBindingRepresentationFactory = provisionBindingRepresentationFactory;
this.productionBindingRepresentationFactory = productionBindingRepresentationFactory;
- this.experimentalSwitchingProviderDependencyRepresentationFactory =
- experimentalSwitchingProviderDependencyRepresentationFactory;
this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.processingEnv = processingEnv;
}
/**
@@ -241,6 +239,15 @@ public final class ComponentRequestRepresentations {
componentMethod.methodElement()
.asMemberOf(componentImplementation.graph().componentTypeElement().getType())
.getReturnType();
+
+ // When compiling with -source 7, javac's type inference isn't strong enough to match things
+ // like Optional<javax.inject.Provider<T>> to Optional<dagger.internal.Provider<T>>.
+ if (isPreJava8SourceVersion(processingEnv)
+ && (MapType.isMapOfProvider(returnType)
+ || OptionalType.isOptionalProviderType(returnType))) {
+ return expression.castTo(returnType.getRawType());
+ }
+
return !isVoid(returnType) && !expression.type().isAssignableTo(returnType)
? expression.castTo(returnType)
: expression;
@@ -278,29 +285,4 @@ public final class ComponentRequestRepresentations {
}
throw new AssertionError();
}
-
- /**
- * Returns an {@link ExperimentalSwitchingProviderDependencyRepresentation} for the requested
- * binding to satisfy dependency requests on it from experimental switching providers. Cannot be
- * used for Members Injection requests.
- */
- ExperimentalSwitchingProviderDependencyRepresentation
- getExperimentalSwitchingProviderDependencyRepresentation(BindingRequest request) {
- checkState(
- componentImplementation.compilerMode().isExperimentalMergedMode(),
- "Compiler mode should be experimentalMergedMode!");
- Optional<Binding> localBinding = graph.localContributionBinding(request.key());
-
- if (localBinding.isPresent()) {
- return reentrantComputeIfAbsent(
- experimentalSwitchingProviderDependencyRepresentations,
- localBinding.get(),
- binding ->
- experimentalSwitchingProviderDependencyRepresentationFactory.create(
- (ProvisionBinding) binding));
- }
-
- checkArgument(parent.isPresent(), "no expression found for %s", request);
- return parent.get().getExperimentalSwitchingProviderDependencyRepresentation(request);
- }
}
diff --git a/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java b/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
index 673a659fd..82c01cfe2 100644
--- a/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
@@ -36,7 +36,9 @@ import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindsTypeChecker;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.RequestKind;
+import dagger.internal.codegen.xprocessing.XTypes;
/** A {@link dagger.internal.codegen.writing.RequestRepresentation} for {@code @Binds} methods. */
final class DelegateRequestRepresentation extends RequestRepresentation {
@@ -88,8 +90,14 @@ final class DelegateRequestRepresentation extends RequestRepresentation {
? delegateExpression.castTo(contributedType)
: delegateExpression;
default:
- return castToRawTypeIfNecessary(
- delegateExpression, requestType(requestKind, contributedType, processingEnv));
+ XType requestedType = requestType(requestKind, contributedType, processingEnv);
+ if (XTypes.isTypeOf(requestedType, TypeNames.PROVIDER)) {
+ // Even though the user may have requested a javax Provider, our generated code and
+ // factories only work in the Dagger Provider type, so swap to that one before doing
+ // a cast.
+ requestedType = XTypes.rewrapType(requestedType, TypeNames.DAGGER_PROVIDER);
+ }
+ return castToRawTypeIfNecessary(delegateExpression, requestedType);
}
}
diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
index 49cc024e9..c54b03cc4 100644
--- a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
@@ -24,7 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
import static dagger.internal.codegen.xprocessing.XElements.asMethod;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
@@ -47,7 +47,6 @@ import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.xprocessing.XAnnotations;
/**
* A {@link javax.inject.Provider} creation expression for a provision method on a component's
@@ -106,9 +105,8 @@ final class DependencyMethodProviderCreationExpression
binding
.nullability()
- .nullableAnnotation()
- .map(XAnnotations::getClassName)
- .ifPresent(getMethod::addAnnotation);
+ .nullableAnnotations()
+ .forEach(getMethod::addAnnotation);
// We need to use the componentShard here since the generated type is static and shards are
// not static classes so it can't be nested inside the shard.
@@ -123,7 +121,7 @@ final class DependencyMethodProviderCreationExpression
componentShard.addType(
COMPONENT_PROVISION_FACTORY,
classBuilder(factoryClassName)
- .addSuperinterface(providerOf(keyType))
+ .addSuperinterface(daggerProviderOf(keyType))
.addModifiers(PRIVATE, STATIC, FINAL)
.addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
.addMethod(
diff --git a/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java
index 4095a600d..f054e6a7e 100644
--- a/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java
@@ -24,11 +24,13 @@ import com.squareup.javapoet.ClassName;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.binding.BindsTypeChecker;
import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.binding.FrameworkType;
import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.BindingKind;
import dagger.internal.codegen.model.RequestKind;
@@ -59,30 +61,61 @@ final class DerivedFromFrameworkInstanceRequestRepresentation extends RequestRep
@Override
Expression getDependencyExpression(ClassName requestingClass) {
- Expression expression =
- frameworkType.to(
- requestKind,
- frameworkRequestRepresentation.getDependencyExpression(requestingClass),
- processingEnv);
- return requiresTypeCast(expression, requestingClass)
- ? expression.castTo(binding.contributedType())
- : expression;
+ return getDependencyExpressionFromFrameworkExpression(
+ frameworkRequestRepresentation.getDependencyExpression(requestingClass),
+ requestingClass);
}
@Override
Expression getDependencyExpressionForComponentMethod(
ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ return getDependencyExpressionFromFrameworkExpression(
+ frameworkRequestRepresentation
+ .getDependencyExpressionForComponentMethod(componentMethod, component),
+ component.name());
+ }
+
+ private Expression getDependencyExpressionFromFrameworkExpression(
+ Expression frameworkExpression, ClassName requestingClass) {
Expression expression =
frameworkType.to(
requestKind,
- frameworkRequestRepresentation.getDependencyExpressionForComponentMethod(
- componentMethod, component),
+ frameworkExpression,
processingEnv);
- return requiresTypeCast(expression, component.name())
+
+ // If it is a map type we need to do a raw type cast. This is because a user requested field
+ // type like dagger.internal.Provider<Map<K, javax.inject.Provider<V>>> isn't always assignable
+ // from something like dagger.internal.Provider<Map<K, dagger.internal.Provider<V>>> just due
+ // to variance issues.
+ if (MapType.isMapOfProvider(binding.contributedType())) {
+ return castMapOfProvider(expression, binding);
+ }
+
+ return requiresTypeCast(expression, requestingClass)
? expression.castTo(binding.contributedType())
: expression;
}
+ private Expression castMapOfProvider(Expression expression, ContributionBinding binding) {
+ switch (requestKind) {
+ case INSTANCE:
+ return expression.castTo(binding.contributedType());
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ return expression.castTo(processingEnv.requireType(TypeNames.DAGGER_PROVIDER).getRawType());
+ case LAZY:
+ return expression.castTo(processingEnv.requireType(TypeNames.LAZY).getRawType());
+ case PRODUCER:
+ case FUTURE:
+ return expression.castTo(processingEnv.requireType(TypeNames.PRODUCER).getRawType());
+ case PRODUCED:
+ return expression.castTo(processingEnv.requireType(TypeNames.PRODUCED).getRawType());
+
+ case MEMBERS_INJECTION: // fall through
+ }
+ throw new IllegalStateException("Unexpected request kind: " + requestKind);
+ }
+
private boolean requiresTypeCast(Expression expression, ClassName requestingClass) {
return binding.kind().equals(BindingKind.DELEGATE)
&& requestKind.equals(RequestKind.INSTANCE)
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
deleted file mode 100644
index 1ebbb240b..000000000
--- a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2021 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.writing;
-
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.xprocessing.XTypes.rewrapType;
-
-import androidx.room.compiler.processing.XProcessingEnv;
-import androidx.room.compiler.processing.XType;
-import com.squareup.javapoet.CodeBlock;
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedFactory;
-import dagger.assisted.AssistedInject;
-import dagger.internal.codegen.base.ContributionType;
-import dagger.internal.codegen.binding.BindsTypeChecker;
-import dagger.internal.codegen.binding.FrameworkType;
-import dagger.internal.codegen.binding.ProvisionBinding;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.javapoet.TypeNames;
-import dagger.internal.codegen.model.BindingKind;
-import dagger.internal.codegen.model.DependencyRequest;
-import dagger.internal.codegen.model.RequestKind;
-import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
-
-/**
- * Returns type casted expressions to satisfy dependency requests from experimental switching
- * providers.
- */
-final class ExperimentalSwitchingProviderDependencyRepresentation {
- private final ProvisionBinding binding;
- private final ShardImplementation shardImplementation;
- private final BindsTypeChecker bindsTypeChecker;
- private final XProcessingEnv processingEnv;
- private final XType type;
-
- @AssistedInject
- ExperimentalSwitchingProviderDependencyRepresentation(
- @Assisted ProvisionBinding binding,
- ComponentImplementation componentImplementation,
- BindsTypeChecker bindsTypeChecker,
- XProcessingEnv processingEnv) {
- this.binding = binding;
- this.shardImplementation = componentImplementation.shardImplementation(binding);
- this.processingEnv = processingEnv;
- this.bindsTypeChecker = bindsTypeChecker;
- this.type =
- isDelegateSetValuesBinding()
- // For convience we allow @Binds @ElementsIntoSet from Collection => Set so that List
- // can be contributed without converting to a Set first. Thus, here we rewrap the
- // contributed type from Set<T> => Collection<T> to reflect this.
- ? rewrapType(binding.contributedType(), TypeNames.COLLECTION)
- : binding.contributedType();
- }
-
- Expression getDependencyExpression(RequestKind requestKind, ProvisionBinding requestingBinding) {
- int index = findIndexOfDependency(requestingBinding);
- XType frameworkType =
- processingEnv.getDeclaredType(
- processingEnv.requireTypeElement(FrameworkType.PROVIDER.frameworkClassName()));
- Expression expression =
- FrameworkType.PROVIDER.to(
- requestKind,
- Expression.create(
- frameworkType,
- CodeBlock.of(
- "(($T) dependencies[$L])", frameworkType.getRawType().getTypeName(), index)),
- processingEnv);
- if (usesExplicitTypeCast(expression, requestKind)) {
- return expression.castTo(type);
- }
- if (usesRawTypeCast(requestKind)) {
- return expression.castTo(type.getRawType());
- }
- return expression;
- }
-
- private int findIndexOfDependency(ProvisionBinding requestingBinding) {
- return requestingBinding.dependencies().stream()
- .map(DependencyRequest::key)
- .collect(toImmutableList())
- .indexOf(binding.key())
- + (requestingBinding.requiresModuleInstance()
- && requestingBinding.contributingModule().isPresent()
- ? 1
- : 0);
- }
-
- private boolean isDelegateSetValuesBinding() {
- return binding.kind().equals(BindingKind.DELEGATE)
- && binding.contributionType().equals(ContributionType.SET_VALUES);
- }
-
- private boolean usesExplicitTypeCast(Expression expression, RequestKind requestKind) {
- // If the type is accessible, we can directly cast the expression use the type.
- return requestKind.equals(RequestKind.INSTANCE)
- && !bindsTypeChecker.isAssignable(expression.type(), type, binding.contributionType())
- && isTypeAccessibleFrom(type, shardImplementation.name().packageName());
- }
-
- private boolean usesRawTypeCast(RequestKind requestKind) {
- // If a type has inaccessible type arguments, then cast to raw type.
- return requestKind.equals(RequestKind.INSTANCE)
- && !isTypeAccessibleFrom(type, shardImplementation.name().packageName())
- && isRawTypeAccessible(type, shardImplementation.name().packageName());
- }
-
- @AssistedFactory
- static interface Factory {
- ExperimentalSwitchingProviderDependencyRepresentation create(ProvisionBinding binding);
- }
-}
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
deleted file mode 100644
index 586b5f53b..000000000
--- a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2022 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.writing;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getLast;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.codegen.binding.ProvisionBinding;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.model.Key;
-import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
-import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import javax.inject.Provider;
-
-/**
- * Keeps track of all provider expression requests for a component.
- *
- * <p>The provider expression request will be satisfied by a single generated {@code Provider} class
- * that can provide instances for all types by switching on an id.
- */
-final class ExperimentalSwitchingProviders {
- /**
- * Each switch size is fixed at 100 cases each and put in its own method. This is to limit the
- * size of the methods so that we don't reach the "huge" method size limit for Android that will
- * prevent it from being AOT compiled in some versions of Android (b/77652521). This generally
- * starts to happen around 1500 cases, but we are choosing 100 to be safe.
- */
- // TODO(bcorso): Include a proguard_spec in the Dagger library to prevent inlining these methods?
- // TODO(ronshapiro): Consider making this configurable via a flag.
- private static final int MAX_CASES_PER_SWITCH = 100;
-
- private static final long MAX_CASES_PER_CLASS = MAX_CASES_PER_SWITCH * MAX_CASES_PER_SWITCH;
- private static final TypeVariableName T = TypeVariableName.get("T");
-
- /**
- * Maps a {@link Key} to an instance of a {@link SwitchingProviderBuilder}. Each group of {@code
- * MAX_CASES_PER_CLASS} keys will share the same instance.
- */
- private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
- new LinkedHashMap<>();
-
- private final ShardImplementation shardImplementation;
- private final Provider<ComponentRequestRepresentations> componentRequestRepresentationsProvider;
-
- ExperimentalSwitchingProviders(
- ShardImplementation shardImplementation,
- Provider<ComponentRequestRepresentations> componentRequestRepresentationsProvider) {
- this.shardImplementation = checkNotNull(shardImplementation);
- this.componentRequestRepresentationsProvider =
- checkNotNull(componentRequestRepresentationsProvider);
- }
-
- /** Returns the framework instance creation expression for an inner switching provider class. */
- FrameworkInstanceCreationExpression newFrameworkInstanceCreationExpression(
- ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
- return new FrameworkInstanceCreationExpression() {
- @Override
- public CodeBlock creationExpression() {
- return switchingProviderBuilders
- .computeIfAbsent(binding.key(), key -> getSwitchingProviderBuilder())
- .getNewInstanceCodeBlock(binding, unscopedInstanceRequestRepresentation);
- }
- };
- }
-
- private SwitchingProviderBuilder getSwitchingProviderBuilder() {
- if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
- String name = shardImplementation.getUniqueClassName("SwitchingProvider");
- // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
- // exceeding component class constant pool limit.
- SwitchingProviderBuilder switchingProviderBuilder =
- new SwitchingProviderBuilder(shardImplementation.name().nestedClass(name));
- shardImplementation.addTypeSupplier(switchingProviderBuilder::build);
- return switchingProviderBuilder;
- }
- return getLast(switchingProviderBuilders.values());
- }
-
- // TODO(bcorso): Consider just merging this class with ExperimentalSwitchingProviders.
- private final class SwitchingProviderBuilder {
- // Keep the switch cases ordered by switch id. The switch Ids are assigned in pre-order
- // traversal, but the switch cases are assigned in post-order traversal of the binding graph.
- private final Map<Integer, CodeBlock> switchCases = new TreeMap<>();
- private final Map<Key, Integer> switchIds = new HashMap<>();
- private final ClassName switchingProviderType;
-
- SwitchingProviderBuilder(ClassName switchingProviderType) {
- this.switchingProviderType = checkNotNull(switchingProviderType);
- }
-
- private CodeBlock getNewInstanceCodeBlock(
- ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
- Key key = binding.key();
- if (!switchIds.containsKey(key)) {
- int switchId = switchIds.size();
- switchIds.put(key, switchId);
- switchCases.put(
- switchId, createSwitchCaseCodeBlock(key, unscopedInstanceRequestRepresentation));
- }
-
- CodeBlock switchingProviderDependencies;
- switch (binding.kind()) {
- // TODO(wanyingd): there might be a better way to get component requirement information
- // without using unscopedInstanceRequestRepresentation.
- case COMPONENT_PROVISION:
- switchingProviderDependencies =
- ((ComponentProvisionRequestRepresentation) unscopedInstanceRequestRepresentation)
- .getComponentRequirementExpression(shardImplementation.name());
- break;
- case SUBCOMPONENT_CREATOR:
- switchingProviderDependencies =
- ((SubcomponentCreatorRequestRepresentation) unscopedInstanceRequestRepresentation)
- .getDependencyExpressionArguments();
- break;
- case MULTIBOUND_SET:
- case MULTIBOUND_MAP:
- case OPTIONAL:
- case INJECTION:
- case PROVISION:
- case ASSISTED_FACTORY:
- // Arguments built in the order of module reference, provision dependencies and members
- // injection dependencies
- switchingProviderDependencies =
- componentRequestRepresentationsProvider.get().getCreateMethodArgumentsCodeBlock(
- binding, shardImplementation.name());
- break;
- default:
- throw new IllegalArgumentException("Unexpected binding kind: " + binding.kind());
- }
-
- return CodeBlock.of(
- "new $T<$L>($L)",
- switchingProviderType,
- // Add the type parameter explicitly when the binding is scoped because Java can't resolve
- // the type when wrapped. For example, the following will error:
- // fooProvider = DoubleCheck.provider(new SwitchingProvider<>(1));
- CodeBlock.of("$T", shardImplementation.accessibleTypeName(binding.contributedType())),
- switchingProviderDependencies.isEmpty()
- ? CodeBlock.of("$L", switchIds.get(key))
- : CodeBlock.of("$L, $L", switchIds.get(key), switchingProviderDependencies));
- }
-
- private CodeBlock createSwitchCaseCodeBlock(
- Key key, RequestRepresentation unscopedInstanceRequestRepresentation) {
- // TODO(bcorso): Try to delay calling getDependencyExpression() until we are writing out the
- // SwitchingProvider because calling it here makes FrameworkFieldInitializer think there's a
- // cycle when initializing ExperimentalSwitchingProviders which adds an unnecessary
- // DelegateFactory.
- CodeBlock instanceCodeBlock =
- unscopedInstanceRequestRepresentation
- .getDependencyExpression(switchingProviderType)
- .box()
- .codeBlock();
-
- return CodeBlock.builder()
- // TODO(bcorso): Is there something else more useful than the key?
- .add("case $L: // $L \n", switchIds.get(key), key)
- .addStatement("return ($T) $L", T, instanceCodeBlock)
- .build();
- }
-
- private TypeSpec build() {
- TypeSpec.Builder builder =
- classBuilder(switchingProviderType)
- .addModifiers(PRIVATE, FINAL, STATIC)
- .addTypeVariable(T)
- .addSuperinterface(providerOf(T))
- .addMethods(getMethods());
-
- // The SwitchingProvider constructor lists switch id first and then the dependency array.
- MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
- builder.addField(TypeName.INT, "id", PRIVATE, FINAL);
- constructor.addParameter(TypeName.INT, "id").addStatement("this.id = id");
- // Pass in provision dependencies and members injection dependencies.
- builder.addField(Object[].class, "dependencies", FINAL, PRIVATE);
- constructor
- .addParameter(Object[].class, "dependencies")
- .addStatement("this.dependencies = dependencies")
- .varargs(true);
-
- return builder.addMethod(constructor.build()).build();
- }
-
- private ImmutableList<MethodSpec> getMethods() {
- ImmutableList<CodeBlock> switchCodeBlockPartitions = switchCodeBlockPartitions();
- if (switchCodeBlockPartitions.size() == 1) {
- // There are less than MAX_CASES_PER_SWITCH cases, so no need for extra get methods.
- return ImmutableList.of(
- methodBuilder("get")
- .addModifiers(PUBLIC)
- .addAnnotation(suppressWarnings(UNCHECKED))
- .addAnnotation(Override.class)
- .returns(T)
- .addCode(getOnlyElement(switchCodeBlockPartitions))
- .build());
- }
-
- // This is the main public "get" method that will route to private getter methods.
- MethodSpec.Builder routerMethod =
- methodBuilder("get")
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .returns(T)
- .beginControlFlow("switch (id / $L)", MAX_CASES_PER_SWITCH);
-
- ImmutableList.Builder<MethodSpec> getMethods = ImmutableList.builder();
- for (int i = 0; i < switchCodeBlockPartitions.size(); i++) {
- MethodSpec method =
- methodBuilder("get" + i)
- .addModifiers(PRIVATE)
- .addAnnotation(suppressWarnings(UNCHECKED))
- .returns(T)
- .addCode(switchCodeBlockPartitions.get(i))
- .build();
- getMethods.add(method);
- routerMethod.addStatement("case $L: return $N()", i, method);
- }
-
- routerMethod.addStatement("default: throw new $T(id)", AssertionError.class).endControlFlow();
-
- return getMethods.add(routerMethod.build()).build();
- }
-
- private ImmutableList<CodeBlock> switchCodeBlockPartitions() {
- return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH)
- .stream()
- .map(
- partitionCases ->
- CodeBlock.builder()
- .beginControlFlow("switch (id)")
- .add(CodeBlocks.concat(partitionCases))
- .addStatement("default: throw new $T(id)", AssertionError.class)
- .endControlFlow()
- .build())
- .collect(toImmutableList());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/writing/FactoryGenerator.java b/java/dagger/internal/codegen/writing/FactoryGenerator.java
index 9ec13146d..32195ba6a 100644
--- a/java/dagger/internal/codegen/writing/FactoryGenerator.java
+++ b/java/dagger/internal/codegen/writing/FactoryGenerator.java
@@ -35,16 +35,15 @@ import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
import static dagger.internal.codegen.model.BindingKind.INJECTION;
import static dagger.internal.codegen.model.BindingKind.PROVISION;
import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
-import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XFiler;
import androidx.room.compiler.processing.XProcessingEnv;
-import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -71,7 +70,6 @@ import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.model.Scope;
import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
-import dagger.internal.codegen.xprocessing.XAnnotations;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -236,7 +234,7 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding
UniqueNameSet uniqueFieldNames = new UniqueNameSet();
ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
frameworkFields.values().forEach(field -> uniqueFieldNames.claim(field.name));
- ImmutableMap<XVariableElement, ParameterSpec> assistedParameters =
+ ImmutableMap<XExecutableParameterElement, ParameterSpec> assistedParameters =
assistedParameters(binding).stream()
.collect(
toImmutableMap(
@@ -244,13 +242,12 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding
parameter ->
ParameterSpec.builder(
parameter.getType().getTypeName(),
- uniqueFieldNames.getUniqueName(getSimpleName(parameter)))
+ uniqueFieldNames.getUniqueName(parameter.getJvmName()))
.build()));
TypeName providedTypeName = providedTypeName(binding);
MethodSpec.Builder getMethod =
methodBuilder("get")
.addModifiers(PUBLIC)
- .returns(providedTypeName)
.addParameters(assistedParameters.values());
if (factoryTypeName(binding).isPresent()) {
@@ -270,13 +267,14 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding
if (binding.kind().equals(PROVISION)) {
binding
.nullability()
- .nullableAnnotation()
- .map(XAnnotations::getClassName)
- .ifPresent(getMethod::addAnnotation);
+ .nullableAnnotations()
+ .forEach(getMethod::addAnnotation);
+ getMethod.returns(providedTypeName);
getMethod.addStatement("return $L", invokeNewInstance);
} else if (!binding.injectionSites().isEmpty()) {
CodeBlock instance = CodeBlock.of("instance");
getMethod
+ .returns(providedTypeName)
.addStatement("$T $L = $L", providedTypeName, instance, invokeNewInstance)
.addCode(
InjectionSiteMethod.invokeAll(
@@ -286,8 +284,11 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding
binding.key().type().xprocessing(),
sourceFiles.frameworkFieldUsages(binding.dependencies(), frameworkFields)::get))
.addStatement("return $L", instance);
+
} else {
- getMethod.addStatement("return $L", invokeNewInstance);
+ getMethod
+ .returns(providedTypeName)
+ .addStatement("return $L", invokeNewInstance);
}
return getMethod.build();
}
diff --git a/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
index 5a981773e..32f0dde04 100644
--- a/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
@@ -142,8 +142,9 @@ class FrameworkFieldInitializer implements FrameworkInstanceSupplier {
FrameworkField.forBinding(
binding, frameworkInstanceCreationExpression.alternativeFrameworkClass());
- TypeName fieldType =
- useRawType ? contributionBindingField.type().rawType : contributionBindingField.type();
+ TypeName fieldType = useRawType
+ ? TypeNames.rawTypeName(contributionBindingField.type())
+ : contributionBindingField.type();
if (binding.kind() == BindingKind.ASSISTED_INJECTION) {
// An assisted injection factory doesn't extend Provider, so we reference the generated
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java
index bb62d52fe..037df56d6 100644
--- a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java
@@ -47,7 +47,6 @@ final class FrameworkInstanceBindingRepresentation {
FrameworkInstanceBindingRepresentation(
@Assisted ProvisionBinding binding,
BindingGraph graph,
- @Assisted FrameworkInstanceSupplier providerField,
ComponentImplementation componentImplementation,
DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory,
DerivedFromFrameworkInstanceRequestRepresentation.Factory
@@ -65,7 +64,7 @@ final class FrameworkInstanceBindingRepresentation {
this.providerRequestRepresentation =
binding.kind().equals(DELEGATE) && !needsCaching(binding, graph)
? delegateRequestRepresentationFactory.create(binding, RequestKind.PROVIDER)
- : providerInstanceRequestRepresentationFactory.create(binding, providerField);
+ : providerInstanceRequestRepresentationFactory.create(binding);
this.producerFromProviderRepresentation =
producerNodeInstanceRequestRepresentationFactory.create(
binding,
@@ -108,7 +107,6 @@ final class FrameworkInstanceBindingRepresentation {
@AssistedFactory
static interface Factory {
- FrameworkInstanceBindingRepresentation create(
- ProvisionBinding binding, FrameworkInstanceSupplier providerField);
+ FrameworkInstanceBindingRepresentation create(ProvisionBinding binding);
}
}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
index 1353e887f..8e9d4c67d 100644
--- a/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
@@ -19,21 +19,17 @@ package dagger.internal.codegen.writing;
import static dagger.internal.codegen.model.BindingKind.DELEGATE;
import dagger.internal.codegen.binding.ContributionBinding;
-import dagger.internal.codegen.model.BindingKind;
import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
/** Generation mode for satisfying framework request to Provision Binding. */
enum FrameworkInstanceKind {
SWITCHING_PROVIDER,
- EXPERIMENTAL_SWITCHING_PROVIDER,
STATIC_FACTORY,
PROVIDER_FIELD;
public static FrameworkInstanceKind from(ContributionBinding binding, CompilerMode compilerMode) {
if (usesSwitchingProvider(binding, compilerMode)) {
- if (compilerMode.isExperimentalMergedMode()) {
- return EXPERIMENTAL_SWITCHING_PROVIDER;
- } else if (compilerMode.isFastInit()) {
+ if (compilerMode.isFastInit()) {
return SWITCHING_PROVIDER;
} else {
throw new IllegalStateException(
@@ -48,12 +44,7 @@ enum FrameworkInstanceKind {
private static boolean usesSwitchingProvider(
ContributionBinding binding, CompilerMode compilerMode) {
- if (!compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode()) {
- return false;
- }
- // TODO(wanyingd): remove this check once we allow inaccessible types in merged mode.
- if (compilerMode.isExperimentalMergedMode()
- && binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
+ if (!compilerMode.isFastInit()) {
return false;
}
switch (binding.kind()) {
@@ -100,10 +91,9 @@ enum FrameworkInstanceKind {
return true;
case PROVISION:
return !compilerMode.isFastInit()
- && !compilerMode.isExperimentalMergedMode()
&& !binding.requiresModuleInstance();
case INJECTION:
- return !compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode();
+ return !compilerMode.isFastInit();
default:
return false;
}
diff --git a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
deleted file mode 100644
index 4bafb5aff..000000000
--- a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.writing;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import androidx.room.compiler.processing.XElement;
-import com.google.common.collect.ImmutableList;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.base.SourceFileGenerator;
-import javax.lang.model.element.Modifier;
-
-/**
- * A source file generator that only writes the relevant code necessary for Bazel to create a
- * correct header (ABI) jar.
- */
-public final class HjarSourceFileGenerator<T> extends SourceFileGenerator<T> {
- private final SourceFileGenerator<T> delegate;
-
- private HjarSourceFileGenerator(SourceFileGenerator<T> delegate) {
- super(delegate);
- this.delegate = delegate;
- }
-
- public static <T> SourceFileGenerator<T> wrap(SourceFileGenerator<T> delegate) {
- return new HjarSourceFileGenerator<>(delegate);
- }
-
- @Override
- public XElement originatingElement(T input) {
- return delegate.originatingElement(input);
- }
-
- @Override
- public ImmutableList<TypeSpec.Builder> topLevelTypes(T input) {
- return delegate.topLevelTypes(input).stream()
- .map(completeType -> skeletonType(completeType.build()))
- .collect(toImmutableList());
- }
-
- private TypeSpec.Builder skeletonType(TypeSpec completeType) {
- TypeSpec.Builder skeleton =
- classBuilder(completeType.name)
- .addSuperinterfaces(completeType.superinterfaces)
- .addTypeVariables(completeType.typeVariables)
- .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
- .addAnnotations(completeType.annotations);
-
- if (!completeType.superclass.equals(ClassName.OBJECT)) {
- skeleton.superclass(completeType.superclass);
- }
-
- completeType.methodSpecs.stream()
- .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
- .map(this::skeletonMethod)
- .forEach(skeleton::addMethod);
-
- completeType.fieldSpecs.stream()
- .filter(field -> !field.modifiers.contains(PRIVATE))
- .map(this::skeletonField)
- .forEach(skeleton::addField);
-
- completeType.typeSpecs.stream()
- .map(type -> skeletonType(type).build())
- .forEach(skeleton::addType);
-
- return skeleton;
- }
-
- private MethodSpec skeletonMethod(MethodSpec completeMethod) {
- MethodSpec.Builder skeleton =
- completeMethod.isConstructor()
- ? constructorBuilder()
- : methodBuilder(completeMethod.name).returns(completeMethod.returnType);
-
- if (completeMethod.isConstructor()) {
- // Code in Turbine must (for technical reasons in javac) have a valid super() call for
- // constructors, otherwise javac will bark, and Turbine has no way to avoid this. So we retain
- // constructor method bodies if they do exist
- skeleton.addCode(completeMethod.code);
- }
-
- return skeleton
- .addModifiers(completeMethod.modifiers)
- .addTypeVariables(completeMethod.typeVariables)
- .addParameters(completeMethod.parameters)
- .addExceptions(completeMethod.exceptions)
- .varargs(completeMethod.varargs)
- .addAnnotations(completeMethod.annotations)
- .build();
- }
-
- private FieldSpec skeletonField(FieldSpec completeField) {
- return FieldSpec.builder(
- completeField.type,
- completeField.name,
- completeField.modifiers.toArray(new Modifier[0]))
- .addAnnotations(completeField.annotations)
- .build();
- }
-}
diff --git a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
index e43d8446d..3eaf92db0 100644
--- a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
+++ b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
@@ -26,10 +26,13 @@ import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XFiler;
import androidx.room.compiler.processing.XProcessingEnv;
import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.binding.MapKeys;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.model.DaggerAnnotation;
import javax.inject.Inject;
/**
@@ -56,11 +59,31 @@ public final class InaccessibleMapKeyProxyGenerator
public ImmutableList<TypeSpec.Builder> topLevelTypes(ContributionBinding binding) {
return MapKeys.mapKeyFactoryMethod(binding, processingEnv)
.map(
- method ->
- classBuilder(MapKeys.mapKeyProxyClassName(binding))
- .addModifiers(PUBLIC, FINAL)
- .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
- .addMethod(method))
+ method -> {
+ TypeSpec.Builder builder =
+ classBuilder(MapKeys.mapKeyProxyClassName(binding))
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(method);
+ // In proguard, we need to keep the classes referenced by @LazyClassKey, we do that by
+ // generating a field referencing the type, and then applying @KeepFieldType to the
+ // field. Here, we generate the field in the proxy class. For classes that are
+ // accessible from the dagger component, we generate fields in LazyClassKeyProvider.
+ // Note: the generated field should not be initialized to avoid class loading.
+ binding
+ .mapKey()
+ .map(DaggerAnnotation::xprocessing)
+ .filter(
+ mapKey ->
+ mapKey.getTypeElement().getClassName().equals(TypeNames.LAZY_CLASS_KEY))
+ .map(
+ mapKey ->
+ FieldSpec.builder(mapKey.getAsType("value").getTypeName(), "className")
+ .addAnnotation(TypeNames.KEEP_FIELD_TYPE)
+ .build())
+ .ifPresent(builder::addField);
+ return builder;
+ })
.map(ImmutableList::of)
.orElse(ImmutableList.of());
}
diff --git a/java/dagger/internal/codegen/writing/InjectionMethods.java b/java/dagger/internal/codegen/writing/InjectionMethods.java
index 75b950aa3..39c03dd7f 100644
--- a/java/dagger/internal/codegen/writing/InjectionMethods.java
+++ b/java/dagger/internal/codegen/writing/InjectionMethods.java
@@ -18,6 +18,7 @@ package dagger.internal.codegen.writing;
import static androidx.room.compiler.processing.XElementKt.isConstructor;
import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
import static androidx.room.compiler.processing.XTypeKt.isVoid;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
@@ -27,7 +28,6 @@ import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAss
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.binding.SourceFiles.protectAgainstKeywords;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
@@ -81,7 +81,6 @@ import dagger.internal.codegen.xprocessing.XAnnotations;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
-import javax.lang.model.SourceVersion;
/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
final class InjectionMethods {
@@ -145,7 +144,7 @@ final class InjectionMethods {
static CodeBlock invoke(
ProvisionBinding binding,
Function<DependencyRequest, CodeBlock> dependencyUsage,
- Function<XVariableElement, String> uniqueAssistedParameterName,
+ Function<XExecutableParameterElement, String> uniqueAssistedParameterName,
ClassName requestingClass,
Optional<CodeBlock> moduleReference,
CompilerOptions compilerOptions) {
@@ -162,7 +161,7 @@ final class InjectionMethods {
static ImmutableList<CodeBlock> invokeArguments(
ProvisionBinding binding,
Function<DependencyRequest, CodeBlock> dependencyUsage,
- Function<XVariableElement, String> uniqueAssistedParameterName) {
+ Function<XExecutableParameterElement, String> uniqueAssistedParameterName) {
ImmutableMap<XExecutableParameterElement, DependencyRequest> dependencyRequestMap =
binding.provisionDependencies().stream()
.collect(
@@ -406,10 +405,10 @@ final class InjectionMethods {
if (isVoid(method.getReturnType())) {
return builder.addStatement("$L", invocation).build();
} else {
- Nullability.of(method)
- .nullableAnnotation()
- .map(XAnnotations::getClassName)
- .ifPresent(builder::addAnnotation);
+ Nullability nullability = Nullability.of(method);
+ nullability
+ .nullableAnnotations()
+ .forEach(builder::addAnnotation);
return builder
.returns(method.getReturnType().getTypeName())
.addStatement("return $L", invocation)
@@ -463,7 +462,11 @@ final class InjectionMethods {
return parameters.stream()
.map(
parameter -> {
- String name = parameterNameSet.getUniqueName(validJavaName(getSimpleName(parameter)));
+ String name =
+ parameterNameSet.getUniqueName(
+ isMethodParameter(parameter)
+ ? asMethodParameter(parameter).getJvmName()
+ : getSimpleName(parameter));
boolean useObject = !isRawTypePubliclyAccessible(parameter.getType());
return copyParameter(methodBuilder, parameter.getType(), name, useObject);
})
@@ -487,19 +490,4 @@ final class InjectionMethods {
// If we had to cast the instance add an extra parenthesis incase we're calling a method on it.
return useObject ? CodeBlock.of("($L)", instance) : instance;
}
-
- private static String validJavaName(CharSequence name) {
- if (SourceVersion.isIdentifier(name)) {
- return protectAgainstKeywords(name.toString());
- }
-
- StringBuilder newName = new StringBuilder(name.length());
- char firstChar = name.charAt(0);
- if (!Character.isJavaIdentifierStart(firstChar)) {
- newName.append('_');
- }
-
- name.chars().forEach(c -> newName.append(Character.isJavaIdentifierPart(c) ? c : '_'));
- return newName.toString();
- }
}
diff --git a/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
index d32531bef..27e017dba 100644
--- a/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
@@ -18,8 +18,15 @@ package dagger.internal.codegen.writing;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.model.BindingKind.ASSISTED_FACTORY;
import static dagger.internal.codegen.model.BindingKind.INJECTION;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
+import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
@@ -29,6 +36,7 @@ import dagger.internal.codegen.javapoet.CodeBlocks;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import java.util.Optional;
import javax.inject.Provider;
/**
@@ -42,32 +50,64 @@ final class InjectionOrProvisionProviderCreationExpression
private final ContributionBinding binding;
private final ShardImplementation shardImplementation;
private final ComponentRequestRepresentations componentRequestRepresentations;
+ private final XProcessingEnv processingEnv;
@AssistedInject
InjectionOrProvisionProviderCreationExpression(
@Assisted ContributionBinding binding,
ComponentImplementation componentImplementation,
- ComponentRequestRepresentations componentRequestRepresentations) {
+ ComponentRequestRepresentations componentRequestRepresentations,
+ XProcessingEnv processingEnv) {
this.binding = checkNotNull(binding);
this.shardImplementation = componentImplementation.shardImplementation(binding);
this.componentRequestRepresentations = componentRequestRepresentations;
+ this.processingEnv = processingEnv;
}
@Override
public CodeBlock creationExpression() {
+ ClassName factoryImpl = generatedClassNameForBinding(binding);
CodeBlock createFactory =
CodeBlock.of(
- "$T.create($L)",
- generatedClassNameForBinding(binding),
+ "$T.$L($L)",
+ factoryImpl,
+ // A different name is used for assisted factories due to backwards compatibility
+ // issues when migrating from the javax Provider.
+ binding.kind().equals(ASSISTED_FACTORY) ? "createFactoryProvider" : "create",
componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
binding, shardImplementation.name()));
+ // If this is for an AssistedFactory, then we may need to change the call in case we're building
+ // against a library built at an older version of Dagger before the changes to make factories
+ // return a Dagger Provider instead of a javax.inject.Provider.
+ if (binding.kind().equals(ASSISTED_FACTORY)) {
+ XTypeElement factoryType = processingEnv.findTypeElement(factoryImpl);
+ // If we can't find the factory, then assume it is being generated this run, which means
+ // it should be the newer version and not need wrapping. If it is missing for some other
+ // reason, then that likely means there will just be some other compilation failure.
+ if (factoryType != null) {
+ Optional<XMethodElement> createMethod = factoryType.getDeclaredMethods().stream()
+ .filter(method -> method.isStatic()
+ && getSimpleName(method).equals("createFactoryProvider"))
+ .collect(toOptional());
+ // Only convert it if the newer method doesn't exist.
+ if (createMethod.isEmpty()) {
+ createFactory = CodeBlock.of(
+ "$T.asDaggerProvider($T.create($L))",
+ TypeNames.DAGGER_PROVIDERS,
+ factoryImpl,
+ componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+ binding, shardImplementation.name()));
+ }
+ }
+ }
+
// When scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the
// type properly, so cast to a raw framework type before scoping.
if (binding.kind().equals(INJECTION)
&& binding.unresolved().isPresent()
&& binding.scope().isPresent()) {
- return CodeBlocks.cast(createFactory, TypeNames.PROVIDER);
+ return CodeBlocks.cast(createFactory, TypeNames.DAGGER_PROVIDER);
} else {
return createFactory;
}
diff --git a/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java b/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java
new file mode 100644
index 000000000..2c2581172
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/LazyClassKeyProviders.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static dagger.internal.codegen.base.MapKeyAccessibility.isMapKeyAccessibleFrom;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import androidx.room.compiler.processing.XAnnotation;
+import com.google.common.base.Preconditions;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.model.Key;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Keeps track of all providers for DaggerMap keys. */
+public final class LazyClassKeyProviders {
+ public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider";
+ private final ClassName mapKeyProviderType;
+ private final Map<Key, FieldSpec> entries = new HashMap<>();
+ private final Map<Key, FieldSpec> keepClassNamesFields = new HashMap<>();
+ private final UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+ private final ShardImplementation shardImplementation;
+ private boolean providerAdded = false;
+
+ LazyClassKeyProviders(ShardImplementation shardImplementation) {
+ String name = shardImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME);
+ mapKeyProviderType = shardImplementation.name().nestedClass(name);
+ this.shardImplementation = shardImplementation;
+ }
+
+ /** Returns a reference to a field in LazyClassKeyProvider that corresponds to this binding. */
+ CodeBlock getMapKeyExpression(Key key) {
+ // This is for avoid generating empty LazyClassKeyProvider in codegen tests
+ if (!providerAdded) {
+ shardImplementation.addTypeSupplier(this::build);
+ providerAdded = true;
+ }
+ if (!entries.containsKey(key)) {
+ addField(key);
+ }
+ return CodeBlock.of("$T.$N", mapKeyProviderType, entries.get(key));
+ }
+
+ private void addField(Key key) {
+ Preconditions.checkArgument(
+ key.multibindingContributionIdentifier().isPresent()
+ && key.multibindingContributionIdentifier()
+ .get()
+ .bindingMethod()
+ .xprocessing()
+ .hasAnnotation(TypeNames.LAZY_CLASS_KEY));
+ XAnnotation lazyClassKeyAnnotation =
+ key.multibindingContributionIdentifier()
+ .get()
+ .bindingMethod()
+ .xprocessing()
+ .getAnnotation(TypeNames.LAZY_CLASS_KEY);
+ ClassName lazyClassKey =
+ lazyClassKeyAnnotation.getAsType("value").getTypeElement().getClassName();
+ entries.put(
+ key,
+ FieldSpec.builder(
+ TypeNames.STRING,
+ uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
+ // TODO(b/217435141): Leave the field as non-final. We will apply @IdentifierNameString
+ // on the field, which doesn't work well with static final fields.
+ .addModifiers(STATIC)
+ .initializer("$S", lazyClassKey.reflectionName())
+ .build());
+ // To be able to apply -includedescriptorclasses rule to keep the class names referenced by
+ // LazyClassKey, we need to generate fields that uses those classes as type in
+ // LazyClassKeyProvider. For types that are not accessible from the generated component, we
+ // generate fields in the proxy class.
+ // Note: the generated field should not be initialized to avoid class loading.
+ if (isMapKeyAccessibleFrom(lazyClassKeyAnnotation, shardImplementation.name().packageName())) {
+ keepClassNamesFields.put(
+ key,
+ FieldSpec.builder(
+ lazyClassKey,
+ uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
+ .addAnnotation(TypeNames.KEEP_FIELD_TYPE)
+ .build());
+ }
+ }
+
+ private TypeSpec build() {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(mapKeyProviderType)
+ .addAnnotation(TypeNames.IDENTIFIER_NAME_STRING)
+ .addModifiers(PRIVATE, STATIC, FINAL)
+ .addFields(entries.values())
+ .addFields(keepClassNamesFields.values());
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
index 1f857dbf1..0ea382d1f 100644
--- a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
@@ -22,14 +22,16 @@ import static dagger.internal.codegen.binding.SourceFiles.mapFactoryClassName;
import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
import androidx.room.compiler.processing.XProcessingEnv;
-import androidx.room.compiler.processing.XType;
+import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.MapKeys;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DependencyRequest;
import java.util.stream.Stream;
@@ -41,6 +43,8 @@ final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpr
private final ComponentImplementation componentImplementation;
private final BindingGraph graph;
private final ContributionBinding binding;
+ private final boolean useLazyClassKey;
+ private final LazyClassKeyProviders lazyClassKeyProviders;
@AssistedInject
MapFactoryCreationExpression(
@@ -54,22 +58,31 @@ final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpr
this.binding = checkNotNull(binding);
this.componentImplementation = componentImplementation;
this.graph = graph;
+ this.useLazyClassKey = MapKeys.useLazyClassKey(binding, graph);
+ this.lazyClassKeyProviders =
+ componentImplementation.shardImplementation(binding).getLazyClassKeyProviders();
}
@Override
public CodeBlock creationExpression() {
- CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
+ ClassName mapFactoryClassName = mapFactoryClassName(binding);
+ CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName);
+ TypeName valueTypeName = TypeName.OBJECT;
if (!useRawType()) {
MapType mapType = MapType.from(binding.key());
// TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
// mapType.unwrappedValueType() method that doesn't require a framework type
- XType valueType =
+ valueTypeName =
Stream.of(TypeNames.PROVIDER, TypeNames.PRODUCER, TypeNames.PRODUCED)
.filter(mapType::valuesAreTypeOf)
.map(mapType::unwrappedValueType)
.collect(toOptional())
- .orElseGet(mapType::valueType);
- builder.add("<$T, $T>", mapType.keyType().getTypeName(), valueType.getTypeName());
+ .orElseGet(mapType::valueType)
+ .getTypeName();
+ builder.add(
+ "<$T, $T>",
+ useLazyClassKey ? TypeNames.STRING : mapType.keyType().getTypeName(),
+ valueTypeName);
}
builder.add("builder($L)", binding.dependencies().size());
@@ -78,12 +91,20 @@ final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpr
ContributionBinding contributionBinding = graph.contributionBinding(dependency.key());
builder.add(
".put($L, $L)",
- getMapKeyExpression(contributionBinding, componentImplementation.name(), processingEnv),
+ useLazyClassKey
+ ? lazyClassKeyProviders.getMapKeyExpression(dependency.key())
+ : getMapKeyExpression(
+ contributionBinding, componentImplementation.name(), processingEnv),
multibindingDependencyExpression(dependency));
}
- builder.add(".build()");
- return builder.build();
+ return useLazyClassKey
+ ? CodeBlock.of(
+ "$T.<$T>of($L)",
+ TypeNames.LAZY_CLASS_KEY_MAP_FACTORY,
+ valueTypeName,
+ builder.add(".build()").build())
+ : builder.add(".build()").build();
}
@AssistedFactory
diff --git a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
index bb49ebe99..f631d34ce 100644
--- a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
@@ -38,6 +38,7 @@ import dagger.internal.MapBuilder;
import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.MapKeys;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.javapoet.TypeNames;
@@ -54,7 +55,8 @@ final class MapRequestRepresentation extends RequestRepresentation {
private final ProvisionBinding binding;
private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
private final ComponentRequestRepresentations componentRequestRepresentations;
- private final boolean isExperimentalMergedMode;
+ private final boolean useLazyClassKey;
+ private final LazyClassKeyProviders lazyClassKeyProviders;
@AssistedInject
MapRequestRepresentation(
@@ -70,12 +72,29 @@ final class MapRequestRepresentation extends RequestRepresentation {
this.componentRequestRepresentations = componentRequestRepresentations;
this.dependencies =
Maps.toMap(binding.dependencies(), dep -> graph.contributionBinding(dep.key()));
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
+ this.useLazyClassKey = MapKeys.useLazyClassKey(binding, graph);
+ this.lazyClassKeyProviders =
+ componentImplementation.shardImplementation(binding).getLazyClassKeyProviders();
}
@Override
Expression getDependencyExpression(ClassName requestingClass) {
+ MapType mapType = MapType.from(binding.key());
+ Expression dependencyExpression = getUnderlyingMapExpression(requestingClass);
+ // LazyClassKey is backed with a string map, therefore needs to be wrapped.
+ if (useLazyClassKey) {
+ return Expression.create(
+ dependencyExpression.type(),
+ CodeBlock.of(
+ "$T.<$T>of($L)",
+ TypeNames.LAZY_CLASS_KEY_MAP,
+ mapType.valueType().getTypeName(),
+ dependencyExpression.codeBlock()));
+ }
+ return dependencyExpression;
+ }
+
+ private Expression getUnderlyingMapExpression(ClassName requestingClass) {
// TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
boolean isImmutableMapAvailable = isImmutableMapAvailable();
// TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
@@ -87,9 +106,7 @@ final class MapRequestRepresentation extends RequestRepresentation {
.add(maybeTypeParameters(requestingClass))
.add(
"of($L)",
- dependencies
- .keySet()
- .stream()
+ dependencies.keySet().stream()
.map(dependency -> keyAndValueExpression(dependency, requestingClass))
.collect(toParametersCodeBlock()))
.build());
@@ -104,10 +121,10 @@ final class MapRequestRepresentation extends RequestRepresentation {
"singletonMap($L)",
keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
default:
- CodeBlock.Builder instantiation = CodeBlock.builder();
- instantiation
- .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
- .add(maybeTypeParameters(requestingClass));
+ CodeBlock.Builder instantiation =
+ CodeBlock.builder()
+ .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
+ .add(maybeTypeParameters(requestingClass));
if (isImmutableMapBuilderWithExpectedSizeAvailable()) {
instantiation.add("builderWithExpectedSize($L)", dependencies.size());
} else if (isImmutableMapAvailable) {
@@ -135,16 +152,12 @@ final class MapRequestRepresentation extends RequestRepresentation {
private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
return CodeBlock.of(
"$L, $L",
- getMapKeyExpression(dependencies.get(dependency), requestingClass, processingEnv),
- isExperimentalMergedMode
- ? componentRequestRepresentations
- .getExperimentalSwitchingProviderDependencyRepresentation(
- bindingRequest(dependency))
- .getDependencyExpression(dependency.kind(), binding)
- .codeBlock()
- : componentRequestRepresentations
- .getDependencyExpression(bindingRequest(dependency), requestingClass)
- .codeBlock());
+ useLazyClassKey
+ ? lazyClassKeyProviders.getMapKeyExpression(dependency.key())
+ : getMapKeyExpression(dependencies.get(dependency), requestingClass, processingEnv),
+ componentRequestRepresentations
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock());
}
private Expression collectionsStaticFactoryInvocation(
@@ -163,7 +176,9 @@ final class MapRequestRepresentation extends RequestRepresentation {
MapType mapType = MapType.from(binding.key());
return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
? CodeBlock.of(
- "<$T, $T>", mapType.keyType().getTypeName(), mapType.valueType().getTypeName())
+ "<$T, $T>",
+ useLazyClassKey ? TypeNames.STRING : mapType.keyType().getTypeName(),
+ mapType.valueType().getTypeName())
: CodeBlock.of("");
}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
index bac7490b4..a00e3676f 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
@@ -16,15 +16,12 @@
package dagger.internal.codegen.writing;
-import static com.google.common.base.Preconditions.checkState;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XType;
@@ -52,7 +49,6 @@ import javax.inject.Inject;
@PerComponentImplementation
final class MembersInjectionMethods {
private final Map<Key, Expression> injectMethodExpressions = new LinkedHashMap<>();
- private final Map<Key, Expression> experimentalInjectMethodExpressions = new LinkedHashMap<>();
private final ComponentImplementation componentImplementation;
private final ComponentRequestRepresentations bindingExpressions;
private final BindingGraph graph;
@@ -81,7 +77,7 @@ final class MembersInjectionMethods {
: graph.localContributionBinding(key).get();
Expression expression =
reentrantComputeIfAbsent(
- injectMethodExpressions, key, k -> injectMethodExpression(binding, false));
+ injectMethodExpressions, key, k -> injectMethodExpression(binding));
ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
return Expression.create(
expression.type(),
@@ -94,32 +90,11 @@ final class MembersInjectionMethods {
instance));
}
- /**
- * Returns the members injection {@link Expression} for the given {@link Key}, creating it if
- * necessary.
- */
- Expression getInjectExpressionExperimental(
- ProvisionBinding provisionBinding, CodeBlock instance, ClassName requestingClass) {
- checkState(
- componentImplementation.compilerMode().isExperimentalMergedMode(),
- "Compiler mode should be experimentalMergedMode!");
- Expression expression =
- reentrantComputeIfAbsent(
- experimentalInjectMethodExpressions,
- provisionBinding.key(),
- k -> injectMethodExpression(provisionBinding, true));
- return Expression.create(
- expression.type(), CodeBlock.of("$L($L, dependencies)", expression.codeBlock(), instance));
- }
-
- private Expression injectMethodExpression(Binding binding, boolean useStaticInjectionMethod) {
+ private Expression injectMethodExpression(Binding binding) {
// TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
// exceeding component class constant pool limit.
// Add to Component Shard so that is can be accessible from Switching Providers.
- ShardImplementation shardImplementation =
- useStaticInjectionMethod
- ? componentImplementation.getComponentShard()
- : componentImplementation.shardImplementation(binding);
+ ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
XType keyType = binding.key().type().xprocessing();
XType membersInjectedType =
isTypeAccessibleFrom(keyType, shardImplementation.name().packageName())
@@ -132,16 +107,10 @@ final class MembersInjectionMethods {
ParameterSpec parameter =
ParameterSpec.builder(membersInjectedType.getTypeName(), "instance").build();
MethodSpec.Builder methodBuilder =
- useStaticInjectionMethod
- ? methodBuilder(methodName)
- .addModifiers(PRIVATE, STATIC)
- .returns(membersInjectedType.getTypeName())
- .addParameter(parameter)
- .addParameter(Object[].class, "dependencies")
- : methodBuilder(methodName)
- .addModifiers(PRIVATE)
- .returns(membersInjectedType.getTypeName())
- .addParameter(parameter);
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .returns(membersInjectedType.getTypeName())
+ .addParameter(parameter);
XTypeElement canIgnoreReturnValue =
processingEnv.findTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
if (canIgnoreReturnValue != null) {
@@ -155,23 +124,14 @@ final class MembersInjectionMethods {
instance,
membersInjectedType,
request ->
- (useStaticInjectionMethod
- ? bindingExpressions
- .getExperimentalSwitchingProviderDependencyRepresentation(
- bindingRequest(request))
- .getDependencyExpression(request.kind(), (ProvisionBinding) binding)
- : bindingExpressions.getDependencyArgumentExpression(
- request, shardImplementation.name()))
+ bindingExpressions
+ .getDependencyArgumentExpression(request, shardImplementation.name())
.codeBlock()));
methodBuilder.addStatement("return $L", instance);
MethodSpec method = methodBuilder.build();
shardImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
- return Expression.create(
- membersInjectedType,
- useStaticInjectionMethod
- ? CodeBlock.of("$T.$N", shardImplementation.name(), method)
- : CodeBlock.of("$N", method));
+ return Expression.create(membersInjectedType, CodeBlock.of("$N", method));
}
private static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java b/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java
index 79b8802b9..f8f31a36e 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java
@@ -17,7 +17,6 @@
package dagger.internal.codegen.writing;
import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XMethodElement;
@@ -56,7 +55,7 @@ final class MembersInjectionRequestRepresentation extends RequestRepresentation
XMethodElement methodElement = componentMethod.methodElement();
XExecutableParameterElement parameter = getOnlyElement(methodElement.getParameters());
return membersInjectionMethods.getInjectExpression(
- binding.key(), CodeBlock.of("$L", getSimpleName(parameter)), component.name());
+ binding.key(), CodeBlock.of("$L", parameter.getJvmName()), component.name());
}
// TODO(bcorso): Consider making this a method on all RequestRepresentations.
diff --git a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
index 82d12ddb5..36a991591 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
@@ -20,8 +20,6 @@ import static com.google.common.base.Preconditions.checkState;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors;
-import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors;
import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
@@ -90,19 +88,6 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI
@Override
public ImmutableList<TypeSpec.Builder> topLevelTypes(MembersInjectionBinding binding) {
- // Empty members injection bindings are special and don't need source files.
- if (binding.injectionSites().isEmpty()) {
- return ImmutableList.of();
- }
-
- // Members injectors for classes with no local injection sites and no @Inject
- // constructor are unused.
- if (!binding.hasLocalInjectionSites()
- && injectedConstructors(binding.membersInjectedType()).isEmpty()
- && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty()) {
- return ImmutableList.of();
- }
-
// We don't want to write out resolved bindings -- we want to write out the generic version.
checkState(
@@ -162,7 +147,9 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI
dependency.key().type().xprocessing(), generatedTypeName.packageName());
String fieldName = fieldNames.getUniqueName(bindingField.name());
- TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
+ TypeName fieldType = useRawFrameworkType
+ ? TypeNames.rawTypeName(bindingField.type())
+ : bindingField.type();
FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName);
diff --git a/java/dagger/internal/codegen/writing/OptionalFactories.java b/java/dagger/internal/codegen/writing/OptionalFactories.java
index deb9dbfa8..3c327e8bc 100644
--- a/java/dagger/internal/codegen/writing/OptionalFactories.java
+++ b/java/dagger/internal/codegen/writing/OptionalFactories.java
@@ -28,8 +28,8 @@ import static dagger.internal.codegen.base.RequestKinds.requestTypeName;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
@@ -62,15 +62,12 @@ import dagger.internal.codegen.binding.FrameworkType;
import dagger.internal.codegen.javapoet.AnnotationSpecs;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.RequestKind;
-import dagger.producers.Producer;
-import dagger.producers.internal.Producers;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Provider;
/** The nested class and static methods required by the component to implement optional bindings. */
// TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional.
@@ -150,15 +147,15 @@ final class OptionalFactories {
"absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name())))
.addModifiers(PRIVATE, STATIC)
.addTypeVariable(typeVariable)
- .returns(providerOf(optionalKind.of(typeVariable)))
+ .returns(daggerProviderOf(optionalKind.of(typeVariable)))
.addJavadoc(
"Returns a {@link $T} that returns {@code $L}.",
- TypeNames.PROVIDER,
+ TypeNames.DAGGER_PROVIDER,
optionalKind.absentValueExpression())
.addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
.addStatement(
"$1T provider = ($1T) $2N",
- providerOf(optionalKind.of(typeVariable)),
+ daggerProviderOf(optionalKind.of(typeVariable)),
perGeneratedFileCache.absentOptionalProviderFields.computeIfAbsent(
optionalKind,
kind -> {
@@ -176,7 +173,7 @@ final class OptionalFactories {
*/
private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
return FieldSpec.builder(
- TypeNames.PROVIDER,
+ TypeNames.DAGGER_PROVIDER,
String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
PRIVATE,
STATIC,
@@ -185,7 +182,7 @@ final class OptionalFactories {
.initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
.addJavadoc(
"A {@link $T} that returns {@code $L}.",
- TypeNames.PROVIDER,
+ TypeNames.DAGGER_PROVIDER,
optionalKind.absentValueExpression())
.build();
}
@@ -193,7 +190,7 @@ final class OptionalFactories {
/** Information about the type of a factory for present bindings. */
@AutoValue
abstract static class PresentFactorySpec {
- /** Whether the factory is a {@link Provider} or a {@link Producer}. */
+ /** Whether the factory is a {@code Provider} or a {@code Producer}. */
abstract FrameworkType frameworkType();
/** What kind of {@code Optional} is returned. */
@@ -302,7 +299,7 @@ final class OptionalFactories {
* {@code Producer<Optional<Produced<T>>>}.
* </ul>
*
- * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the
+ * @param delegateFactory an expression for a {@code Provider} or {@code Producer} of the
* underlying type
*/
CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
@@ -415,7 +412,9 @@ final class OptionalFactories {
spec.optionalKind(),
spec.valueType(),
CodeBlock.of(
- "$T.createFutureProduced($N.get())", Producers.class, delegateField)))
+ "$T.createFutureProduced($N.get())",
+ TypeNames.PRODUCERS,
+ delegateField)))
.build();
default:
diff --git a/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
index b77531456..8c9219197 100644
--- a/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
@@ -33,13 +33,13 @@ import dagger.internal.codegen.base.OptionalType.OptionalKind;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.model.DependencyRequest;
+import dagger.internal.codegen.model.RequestKind;
/** A binding expression for optional bindings. */
final class OptionalRequestRepresentation extends RequestRepresentation {
private final ProvisionBinding binding;
private final ComponentRequestRepresentations componentRequestRepresentations;
private final XProcessingEnv processingEnv;
- private final boolean isExperimentalMergedMode;
@AssistedInject
OptionalRequestRepresentation(
@@ -50,8 +50,6 @@ final class OptionalRequestRepresentation extends RequestRepresentation {
this.binding = binding;
this.componentRequestRepresentations = componentRequestRepresentations;
this.processingEnv = processingEnv;
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
}
@Override
@@ -78,18 +76,15 @@ final class OptionalRequestRepresentation extends RequestRepresentation {
DependencyRequest dependency = getOnlyElement(binding.dependencies());
CodeBlock dependencyExpression =
- isExperimentalMergedMode
- ? componentRequestRepresentations
- .getExperimentalSwitchingProviderDependencyRepresentation(
- bindingRequest(dependency))
- .getDependencyExpression(dependency.kind(), binding)
- .codeBlock()
- : componentRequestRepresentations
- .getDependencyExpression(bindingRequest(dependency), requestingClass)
- .codeBlock();
+ componentRequestRepresentations
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock();
- return isTypeAccessibleFrom(
- dependency.key().type().xprocessing(), requestingClass.packageName())
+ boolean needsObjectExpression = !isTypeAccessibleFrom(
+ dependency.key().type().xprocessing(), requestingClass.packageName())
+ || (isPreJava8SourceVersion(processingEnv) && dependency.kind() == RequestKind.PROVIDER);
+
+ return !needsObjectExpression
? Expression.create(
binding.key().type().xprocessing(),
optionalKind.presentExpression(dependencyExpression))
diff --git a/java/dagger/internal/codegen/writing/ProducerEntryPointView.java b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
index 648e2537f..34d54584d 100644
--- a/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
+++ b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
@@ -31,14 +31,11 @@ import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
-import dagger.producers.Producer;
-import dagger.producers.internal.CancellationListener;
-import dagger.producers.internal.Producers;
import java.util.Optional;
/**
- * A factory of {@linkplain Producers#entryPointViewOf(Producer, CancellationListener) entry point
- * views} of {@link Producer}s.
+ * A factory of {@code Producers#entryPointViewOf(Producer, CancellationListener)} of
+ * {@code Producer}s.
*/
final class ProducerEntryPointView {
private final ShardImplementation shardImplementation;
@@ -50,9 +47,8 @@ final class ProducerEntryPointView {
}
/**
- * Returns an expression for an {@linkplain Producers#entryPointViewOf(Producer,
- * CancellationListener) entry point view} of a producer if the component method returns a {@link
- * Producer} or {@link com.google.common.util.concurrent.ListenableFuture}.
+ * Returns an expression for an {@code Producers#entryPointViewOf(Producer, CancellationListener)}
+ * of a producer if the component method returns a {@code Producer} or {@code ListenableFuture}.
*
* <p>This is intended to be a replacement implementation for {@link
* dagger.internal.codegen.writing.RequestRepresentation#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor,
@@ -97,7 +93,7 @@ final class ProducerEntryPointView {
CodeBlock.of(
"this.$N = $T.entryPointViewOf($L, $L);",
field,
- Producers.class,
+ TypeNames.PRODUCERS,
producerExpression.getDependencyExpression(shardImplementation.name()).codeBlock(),
// Always pass in the componentShard reference here rather than the owning shard for
// this key because this needs to be the root CancellationListener.
diff --git a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
index e6c88a9c7..1d8ab02f7 100644
--- a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
+++ b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
@@ -77,16 +77,13 @@ import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.DependencyRequest;
import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.model.RequestKind;
-import dagger.producers.Producer;
-import dagger.producers.internal.AbstractProducesMethodProducer;
-import dagger.producers.internal.Producers;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import javax.inject.Inject;
-/** Generates {@link Producer} implementations from {@link ProductionBinding} instances. */
+/** Generates {@code Producer} implementations from {@link ProductionBinding} instances. */
public final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
private final CompilerOptions compilerOptions;
private final KeyFactory keyFactory;
@@ -163,7 +160,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti
addFieldAndConstructorParameter(
factoryBuilder, constructorBuilder, fieldName, bindingField.type());
fieldsBuilder.put(dependency, field);
- frameworkFieldAssignments.add(fieldAssignment(field, bindingField.type()));
+ frameworkFieldAssignments.add(fieldAssignment(field, bindingField));
}
}
ImmutableMap<DependencyRequest, FieldSpec> fields = fieldsBuilder.build();
@@ -219,7 +216,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti
factoryBuilder
.superclass(
ParameterizedTypeName.get(
- ClassName.get(AbstractProducesMethodProducer.class),
+ TypeNames.ABSTRACT_PRODUCES_METHOD_PRODUCER,
futureTransform.applyArgType(),
providedTypeName))
.addMethod(constructor)
@@ -260,11 +257,12 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti
return field;
}
- private static CodeBlock fieldAssignment(FieldSpec field, ParameterizedTypeName type) {
+ private static CodeBlock fieldAssignment(FieldSpec field, FrameworkField frameworkField) {
CodeBlock.Builder statement = CodeBlock.builder();
- if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
+ if (frameworkField.type() != null
+ && TypeNames.rawTypeName(frameworkField.type()).equals(TypeNames.PRODUCER)) {
statement.addStatement(
- "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
+ "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, TypeNames.PRODUCERS);
} else {
statement.addStatement("this.$1N = $1N", field);
}
@@ -275,7 +273,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti
MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type) {
if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
constructorBuilder.addStatement(
- "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
+ "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, TypeNames.PRODUCERS);
} else {
constructorBuilder.addStatement("this.$1N = $1N", field);
}
diff --git a/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
index 05dd50b27..9d0895673 100644
--- a/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
@@ -26,10 +26,9 @@ import dagger.internal.codegen.binding.FrameworkType;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.producers.Producer;
import java.util.Optional;
-/** An {@link Producer} creation expression for provision bindings. */
+/** An {@code Producer} creation expression for provision bindings. */
final class ProducerFromProviderCreationExpression implements FrameworkInstanceCreationExpression {
private final RequestRepresentation providerRequestRepresentation;
private final ClassName requestingClass;
diff --git a/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java b/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
index 813010cfd..cdcc38933 100644
--- a/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
@@ -26,9 +26,9 @@ import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescri
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.binding.FrameworkType;
import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
-import dagger.producers.internal.Producers;
/** Binding expression for producer node instances. */
final class ProducerNodeInstanceRequestRepresentation
@@ -61,7 +61,7 @@ final class ProducerNodeInstanceRequestRepresentation
key,
CodeBlock.of(
"$T.cancel($L, $N);",
- Producers.class,
+ TypeNames.PRODUCERS,
result.codeBlock(),
ComponentImplementation.MAY_INTERRUPT_IF_RUNNING_PARAM));
return result;
diff --git a/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java b/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
index 9bd8f3d8d..f031a72a6 100644
--- a/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
@@ -20,18 +20,29 @@ import androidx.room.compiler.processing.XProcessingEnv;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
-import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.ProvisionBinding;
/** Binding expression for provider instances. */
final class ProviderInstanceRequestRepresentation extends FrameworkInstanceRequestRepresentation {
@AssistedInject
ProviderInstanceRequestRepresentation(
- @Assisted ContributionBinding binding,
- @Assisted FrameworkInstanceSupplier frameworkInstanceSupplier,
+ @Assisted ProvisionBinding binding,
+ SwitchingProviderInstanceSupplier.Factory switchingProviderInstanceSupplierFactory,
+ StaticFactoryInstanceSupplier.Factory staticFactoryInstanceSupplierFactory,
+ ProviderInstanceSupplier.Factory providerInstanceSupplierFactory,
+ ComponentImplementation componentImplementation,
XProcessingEnv processingEnv) {
- super(binding, frameworkInstanceSupplier, processingEnv);
+ super(
+ binding,
+ frameworkInstanceSupplier(
+ binding,
+ switchingProviderInstanceSupplierFactory,
+ staticFactoryInstanceSupplierFactory,
+ providerInstanceSupplierFactory,
+ componentImplementation),
+ processingEnv);
}
@Override
@@ -39,9 +50,27 @@ final class ProviderInstanceRequestRepresentation extends FrameworkInstanceReque
return FrameworkType.PROVIDER;
}
+ private static FrameworkInstanceSupplier frameworkInstanceSupplier(
+ ProvisionBinding binding,
+ SwitchingProviderInstanceSupplier.Factory switchingProviderInstanceSupplierFactory,
+ StaticFactoryInstanceSupplier.Factory staticFactoryInstanceSupplierFactory,
+ ProviderInstanceSupplier.Factory providerInstanceSupplierFactory,
+ ComponentImplementation componentImplementation) {
+ FrameworkInstanceKind frameworkInstanceKind =
+ FrameworkInstanceKind.from(binding, componentImplementation.compilerMode());
+ switch (frameworkInstanceKind) {
+ case SWITCHING_PROVIDER:
+ return switchingProviderInstanceSupplierFactory.create(binding);
+ case STATIC_FACTORY:
+ return staticFactoryInstanceSupplierFactory.create(binding);
+ case PROVIDER_FIELD:
+ return providerInstanceSupplierFactory.create(binding);
+ }
+ throw new AssertionError("Unexpected FrameworkInstanceKind: " + frameworkInstanceKind);
+ }
+
@AssistedFactory
static interface Factory {
- ProviderInstanceRequestRepresentation create(
- ContributionBinding binding, FrameworkInstanceSupplier frameworkInstanceSupplier);
+ ProviderInstanceRequestRepresentation create(ProvisionBinding binding);
}
}
diff --git a/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
index 4ae7f3759..0f9b6f3dd 100644
--- a/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
@@ -25,7 +25,6 @@ import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindingRequest;
import dagger.internal.codegen.binding.ProvisionBinding;
-import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
@@ -43,34 +42,17 @@ final class ProvisionBindingRepresentation implements BindingRepresentation {
@AssistedInject
ProvisionBindingRepresentation(
@Assisted ProvisionBinding binding,
- BindingGraph graph,
- ComponentImplementation componentImplementation,
DirectInstanceBindingRepresentation.Factory directInstanceBindingRepresentationFactory,
FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory,
- SwitchingProviderInstanceSupplier.Factory switchingProviderInstanceSupplierFactory,
- ProviderInstanceSupplier.Factory providerInstanceSupplierFactory,
- StaticFactoryInstanceSupplier.Factory staticFactoryInstanceSupplierFactory,
- CompilerOptions compilerOptions) {
+ BindingGraph graph,
+ ComponentImplementation componentImplementation) {
this.binding = binding;
this.graph = graph;
this.compilerMode = componentImplementation.compilerMode();
this.directInstanceBindingRepresentation =
directInstanceBindingRepresentationFactory.create(binding);
- FrameworkInstanceSupplier frameworkInstanceSupplier = null;
- switch (FrameworkInstanceKind.from(binding, compilerMode)) {
- case SWITCHING_PROVIDER:
- case EXPERIMENTAL_SWITCHING_PROVIDER:
- frameworkInstanceSupplier = switchingProviderInstanceSupplierFactory.create(binding);
- break;
- case STATIC_FACTORY:
- frameworkInstanceSupplier = staticFactoryInstanceSupplierFactory.create(binding);
- break;
- case PROVIDER_FIELD:
- frameworkInstanceSupplier = providerInstanceSupplierFactory.create(binding);
- break;
- }
this.frameworkInstanceBindingRepresentation =
- frameworkInstanceBindingRepresentationFactory.create(binding, frameworkInstanceSupplier);
+ frameworkInstanceBindingRepresentationFactory.create(binding);
}
@Override
@@ -81,9 +63,6 @@ final class ProvisionBindingRepresentation implements BindingRepresentation {
}
private boolean usesDirectInstanceExpression(RequestKind requestKind) {
- if (compilerMode.isExperimentalMergedMode()) {
- return false;
- }
if (requestKind != RequestKind.INSTANCE && requestKind != RequestKind.FUTURE) {
return false;
}
diff --git a/java/dagger/internal/codegen/writing/SetRequestRepresentation.java b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
index f5d77ad48..68a340a1b 100644
--- a/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
@@ -47,7 +47,6 @@ final class SetRequestRepresentation extends RequestRepresentation {
private final BindingGraph graph;
private final ComponentRequestRepresentations componentRequestRepresentations;
private final XProcessingEnv processingEnv;
- private final boolean isExperimentalMergedMode;
@AssistedInject
SetRequestRepresentation(
@@ -60,8 +59,6 @@ final class SetRequestRepresentation extends RequestRepresentation {
this.graph = graph;
this.componentRequestRepresentations = componentRequestRepresentations;
this.processingEnv = processingEnv;
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
}
@Override
@@ -139,14 +136,7 @@ final class SetRequestRepresentation extends RequestRepresentation {
DependencyRequest dependency, ClassName requestingClass) {
RequestRepresentation bindingExpression =
componentRequestRepresentations.getRequestRepresentation(bindingRequest(dependency));
- CodeBlock expression =
- isExperimentalMergedMode
- ? componentRequestRepresentations
- .getExperimentalSwitchingProviderDependencyRepresentation(
- bindingRequest(dependency))
- .getDependencyExpression(dependency.kind(), binding)
- .codeBlock()
- : bindingExpression.getDependencyExpression(requestingClass).codeBlock();
+ CodeBlock expression = bindingExpression.getDependencyExpression(requestingClass).codeBlock();
// TODO(b/211774331): Type casting should be Set after contributions to Set multibinding are
// limited to be Set.
diff --git a/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java b/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
index 4026f1681..79613fd40 100644
--- a/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
@@ -19,7 +19,6 @@ package dagger.internal.codegen.writing;
import static androidx.room.compiler.processing.XElementKt.isConstructor;
import static androidx.room.compiler.processing.XElementKt.isMethod;
import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
@@ -58,7 +57,6 @@ final class SimpleMethodRequestRepresentation extends RequestRepresentation {
private final MembersInjectionMethods membersInjectionMethods;
private final ComponentRequirementExpressions componentRequirementExpressions;
private final ShardImplementation shardImplementation;
- private final boolean isExperimentalMergedMode;
@AssistedInject
SimpleMethodRequestRepresentation(
@@ -80,8 +78,6 @@ final class SimpleMethodRequestRepresentation extends RequestRepresentation {
this.membersInjectionMethods = membersInjectionMethods;
this.componentRequirementExpressions = componentRequirementExpressions;
this.shardImplementation = componentImplementation.shardImplementation(binding);
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
}
@Override
@@ -146,12 +142,8 @@ final class SimpleMethodRequestRepresentation extends RequestRepresentation {
}
private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
- return isExperimentalMergedMode
- ? componentRequestRepresentations
- .getExperimentalSwitchingProviderDependencyRepresentation(bindingRequest(dependency))
- .getDependencyExpression(dependency.kind(), provisionBinding)
- : componentRequestRepresentations.getDependencyArgumentExpression(
- dependency, requestingClass);
+ return componentRequestRepresentations.getDependencyArgumentExpression(
+ dependency, requestingClass);
}
private Expression injectMembers(CodeBlock instance, ClassName requestingClass) {
@@ -167,11 +159,8 @@ final class SimpleMethodRequestRepresentation extends RequestRepresentation {
instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
}
}
- return isExperimentalMergedMode
- ? membersInjectionMethods.getInjectExpressionExperimental(
- provisionBinding, instance, requestingClass)
- : membersInjectionMethods.getInjectExpression(
- provisionBinding.key(), instance, requestingClass);
+ return membersInjectionMethods.getInjectExpression(
+ provisionBinding.key(), instance, requestingClass);
}
private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
@@ -180,11 +169,7 @@ final class SimpleMethodRequestRepresentation extends RequestRepresentation {
.contributingModule()
.map(XTypeElement::getType)
.map(ComponentRequirement::forModule)
- .map(
- module ->
- isExperimentalMergedMode
- ? CodeBlock.of("(($T) dependencies[0])", module.type().getTypeName())
- : componentRequirementExpressions.getExpression(module, requestingClass))
+ .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
: Optional.empty();
}
diff --git a/java/dagger/internal/codegen/writing/StaticMemberSelects.java b/java/dagger/internal/codegen/writing/StaticMemberSelects.java
index 3fea6173f..a1ea63c03 100644
--- a/java/dagger/internal/codegen/writing/StaticMemberSelects.java
+++ b/java/dagger/internal/codegen/writing/StaticMemberSelects.java
@@ -22,11 +22,11 @@ import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementType
import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName;
import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.DAGGER_PROVIDER;
import static dagger.internal.codegen.javapoet.TypeNames.FACTORY;
import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER;
import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
@@ -52,7 +52,7 @@ final class StaticMemberSelects {
? new ParameterizedStaticMethod(
PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER)
: new ParameterizedStaticMethod(
- MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
+ MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), DAGGER_PROVIDER);
}
/**
diff --git a/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
index eeadabee9..c71c70af6 100644
--- a/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
@@ -20,29 +20,23 @@ import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
-import java.util.ArrayList;
-import java.util.List;
/** A binding expression for a subcomponent creator that just invokes the constructor. */
final class SubcomponentCreatorRequestRepresentation extends RequestRepresentation {
private final ShardImplementation shardImplementation;
private final ContributionBinding binding;
- private final boolean isExperimentalMergedMode;
@AssistedInject
SubcomponentCreatorRequestRepresentation(
@Assisted ContributionBinding binding, ComponentImplementation componentImplementation) {
this.binding = binding;
this.shardImplementation = componentImplementation.shardImplementation(binding);
- this.isExperimentalMergedMode =
- componentImplementation.compilerMode().isExperimentalMergedMode();
}
@Override
@@ -51,20 +45,9 @@ final class SubcomponentCreatorRequestRepresentation extends RequestRepresentati
binding.key().type().xprocessing(),
"new $T($L)",
shardImplementation.getSubcomponentCreatorSimpleName(binding.key()),
- isExperimentalMergedMode
- ? getDependenciesExperimental()
- : shardImplementation.componentFieldsByImplementation().values().stream()
- .map(field -> CodeBlock.of("$N", field))
- .collect(toParametersCodeBlock()));
- }
-
- private CodeBlock getDependenciesExperimental() {
- List<CodeBlock> expressions = new ArrayList<>();
- int index = 0;
- for (FieldSpec field : shardImplementation.componentFieldsByImplementation().values()) {
- expressions.add(CodeBlock.of("($T) dependencies[$L]", field.type, index++));
- }
- return expressions.stream().collect(toParametersCodeBlock());
+ shardImplementation.componentFieldsByImplementation().values().stream()
+ .map(field -> CodeBlock.of("$N", field))
+ .collect(toParametersCodeBlock()));
}
CodeBlock getDependencyExpressionArguments() {
diff --git a/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
index 7bea206f6..6f8ca7b3f 100644
--- a/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
+++ b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
@@ -46,11 +46,9 @@ final class SwitchingProviderInstanceSupplier implements FrameworkInstanceSuppli
unscopedDirectInstanceRequestRepresentationFactory) {
ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
- componentImplementation.compilerMode().isExperimentalMergedMode()
- ? shardImplementation.getExperimentalSwitchingProviders()
- .newFrameworkInstanceCreationExpression(
- binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding))
- : shardImplementation.getSwitchingProviders().newFrameworkInstanceCreationExpression(
+ shardImplementation
+ .getSwitchingProviders()
+ .newFrameworkInstanceCreationExpression(
binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding));
this.frameworkInstanceSupplier =
new FrameworkFieldInitializer(
diff --git a/java/dagger/internal/codegen/writing/SwitchingProviders.java b/java/dagger/internal/codegen/writing/SwitchingProviders.java
index e6c3bbf52..736f51529 100644
--- a/java/dagger/internal/codegen/writing/SwitchingProviders.java
+++ b/java/dagger/internal/codegen/writing/SwitchingProviders.java
@@ -24,7 +24,7 @@ import static com.squareup.javapoet.TypeSpec.classBuilder;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
@@ -172,7 +172,7 @@ final class SwitchingProviders {
classBuilder(switchingProviderType)
.addModifiers(PRIVATE, FINAL, STATIC)
.addTypeVariable(T)
- .addSuperinterface(providerOf(T))
+ .addSuperinterface(daggerProviderOf(T))
.addMethods(getMethods());
// The SwitchingProvider constructor lists all component parameters first and switch id last.
diff --git a/java/dagger/internal/codegen/xprocessing/BUILD b/java/dagger/internal/codegen/xprocessing/BUILD
index 262be175a..da465e1d0 100644
--- a/java/dagger/internal/codegen/xprocessing/BUILD
+++ b/java/dagger/internal/codegen/xprocessing/BUILD
@@ -30,6 +30,7 @@ java_library(
deps = [
":xprocessing-lib",
"//java/dagger/internal/codegen/extension",
+ "//java/dagger/spi",
"//third_party/java/auto:common",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
diff --git a/java/dagger/internal/codegen/xprocessing/DaggerElements.java b/java/dagger/internal/codegen/xprocessing/DaggerElements.java
new file mode 100644
index 000000000..84daeb0a9
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/DaggerElements.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.xprocessing;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.compat.XConverters;
+import com.google.devtools.ksp.symbol.KSClassDeclaration;
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration;
+import com.google.devtools.ksp.symbol.KSPropertyDeclaration;
+import com.google.devtools.ksp.symbol.KSValueParameter;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DaggerProcessingEnv;
+import dagger.spi.model.DaggerType;
+
+/** Convert Dagger model types to XProcessing types. */
+public final class DaggerElements {
+ public static XElement toXProcessing(
+ DaggerElement element, DaggerProcessingEnv daggerProcessingEnv) {
+ XProcessingEnv processingEnv = toXProcessing(daggerProcessingEnv);
+ switch (element.backend()) {
+ case JAVAC:
+ return XConverters.toXProcessing(element.javac(), processingEnv);
+ case KSP:
+ if (element.ksp() instanceof KSFunctionDeclaration) {
+ return XConverters.toXProcessing((KSFunctionDeclaration) element.ksp(), processingEnv);
+ } else if (element.ksp() instanceof KSClassDeclaration) {
+ return XConverters.toXProcessing((KSClassDeclaration) element.ksp(), processingEnv);
+ } else if (element.ksp() instanceof KSValueParameter) {
+ return XConverters.toXProcessing((KSValueParameter) element.ksp(), processingEnv);
+ } else if (element.ksp() instanceof KSPropertyDeclaration) {
+ return XConverters.toXProcessing((KSPropertyDeclaration) element.ksp(), processingEnv);
+ }
+ throw new IllegalStateException(
+ String.format("Unsupported ksp declaration %s.", element.ksp()));
+ }
+ throw new IllegalStateException(
+ String.format("Backend %s not supported yet.", element.backend()));
+ }
+
+ public static XType toXProcessing(DaggerType type, DaggerProcessingEnv daggerProcessingEnv) {
+ XProcessingEnv processingEnv = toXProcessing(daggerProcessingEnv);
+ switch (type.backend()) {
+ case JAVAC:
+ return XConverters.toXProcessing(type.javac(), processingEnv);
+ case KSP:
+ return XConverters.toXProcessing(type.ksp(), processingEnv);
+ }
+ throw new IllegalStateException(String.format("Backend %s not supported yet.", type.backend()));
+ }
+
+ public static XProcessingEnv toXProcessing(DaggerProcessingEnv processingEnv) {
+ switch (processingEnv.backend()) {
+ case JAVAC:
+ return XProcessingEnv.create(processingEnv.javac());
+ case KSP:
+ return XProcessingEnv.create(processingEnv.ksp(), processingEnv.resolver());
+ }
+ throw new IllegalStateException(
+ String.format("Backend %s not supported yet.", processingEnv.backend()));
+ }
+
+ private DaggerElements() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/JavaPoetExt.java b/java/dagger/internal/codegen/xprocessing/JavaPoetExt.java
index b28d9c008..bf8e94f2a 100644
--- a/java/dagger/internal/codegen/xprocessing/JavaPoetExt.java
+++ b/java/dagger/internal/codegen/xprocessing/JavaPoetExt.java
@@ -50,8 +50,7 @@ public final class JavaPoetExt {
}
public static ParameterSpec toParameterSpec(XExecutableParameterElement param) {
- return ParameterSpec.builder(param.getType().getTypeName(), XElements.getSimpleName(param))
- .build();
+ return ParameterSpec.builder(param.getType().getTypeName(), param.getJvmName()).build();
}
private JavaPoetExt() {}
diff --git a/java/dagger/internal/codegen/xprocessing/MethodSpecs.java b/java/dagger/internal/codegen/xprocessing/MethodSpecs.java
index fa1f26541..cc8c2f459 100644
--- a/java/dagger/internal/codegen/xprocessing/MethodSpecs.java
+++ b/java/dagger/internal/codegen/xprocessing/MethodSpecs.java
@@ -16,7 +16,6 @@
package dagger.internal.codegen.xprocessing;
-import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static javax.lang.model.element.Modifier.PROTECTED;
import static javax.lang.model.element.Modifier.PUBLIC;
@@ -47,7 +46,7 @@ public final class MethodSpecs {
builder.addModifiers(PROTECTED);
}
for (int i = 0; i < methodType.getParameterTypes().size(); i++) {
- String parameterName = getSimpleName(method.getParameters().get(i));
+ String parameterName = method.getParameters().get(i).getJvmName();
TypeName parameterType = methodType.getParameterTypes().get(i).getTypeName();
builder.addParameter(ParameterSpec.builder(parameterType, parameterName).build());
}
diff --git a/java/dagger/internal/codegen/xprocessing/XElements.java b/java/dagger/internal/codegen/xprocessing/XElements.java
index 552be65f9..b63d4d93d 100644
--- a/java/dagger/internal/codegen/xprocessing/XElements.java
+++ b/java/dagger/internal/codegen/xprocessing/XElements.java
@@ -47,6 +47,7 @@ import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XTypeParameterElement;
import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.ksp.symbol.KSAnnotated;
import com.squareup.javapoet.ClassName;
@@ -54,6 +55,7 @@ import java.util.Collection;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
// TODO(bcorso): Consider moving these methods into XProcessing library.
/** A utility class for {@link XElement} helper methods. */
@@ -383,5 +385,37 @@ public final class XElements {
return element.getClosestMemberContainer().asClassName().getPackageName();
}
+ public static boolean isFinal(XExecutableElement element) {
+ if (element.isFinal()) {
+ return true;
+ }
+ if (getProcessingEnv(element).getBackend() == XProcessingEnv.Backend.KSP) {
+ if (toKS(element).getModifiers().contains(com.google.devtools.ksp.symbol.Modifier.FINAL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static ImmutableList<Modifier> getModifiers(XExecutableElement element) {
+ ImmutableList.Builder<Modifier> builder = ImmutableList.builder();
+ if (isFinal(element)) {
+ builder.add(Modifier.FINAL);
+ } else if (element.isAbstract()) {
+ builder.add(Modifier.ABSTRACT);
+ }
+ if (element.isStatic()) {
+ builder.add(Modifier.STATIC);
+ }
+ if (element.isPublic()) {
+ builder.add(Modifier.PUBLIC);
+ } else if (element.isPrivate()) {
+ builder.add(Modifier.PRIVATE);
+ } else if (element.isProtected()) {
+ builder.add(Modifier.PROTECTED);
+ }
+ return builder.build();
+ }
+
private XElements() {}
}
diff --git a/java/dagger/internal/codegen/xprocessing/XMethodElements.java b/java/dagger/internal/codegen/xprocessing/XMethodElements.java
index 3066d797e..cbff642c3 100644
--- a/java/dagger/internal/codegen/xprocessing/XMethodElements.java
+++ b/java/dagger/internal/codegen/xprocessing/XMethodElements.java
@@ -16,11 +16,8 @@
package dagger.internal.codegen.xprocessing;
-import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
-import static androidx.room.compiler.processing.compat.XConverters.toJavac;
import androidx.room.compiler.processing.XMethodElement;
-import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
// TODO(bcorso): Consider moving these methods into XProcessing library.
@@ -40,19 +37,5 @@ public final class XMethodElements {
return !method.getExecutableType().getTypeVariableNames().isEmpty();
}
- // TODO(b/278628663): Replace with XMethodElement#isDefault.
- public static boolean isDefault(XMethodElement method) {
- XProcessingEnv processingEnv = getProcessingEnv(method);
- switch (processingEnv.getBackend()) {
- case JAVAC:
- return toJavac(method).isDefault();
- case KSP:
- throw new AssertionError(
- "XMethodElement#isDefault() is not supported on KSP yet: "
- + XElements.toStableString(method));
- }
- throw new AssertionError(String.format("Unsupported backend %s", processingEnv.getBackend()));
- }
-
private XMethodElements() {}
}
diff --git a/java/dagger/internal/codegen/xprocessing/XTypeElements.java b/java/dagger/internal/codegen/xprocessing/XTypeElements.java
index 6d9c56486..7330b8f6a 100644
--- a/java/dagger/internal/codegen/xprocessing/XTypeElements.java
+++ b/java/dagger/internal/codegen/xprocessing/XTypeElements.java
@@ -16,21 +16,16 @@
package dagger.internal.codegen.xprocessing;
-import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static kotlin.streams.jdk8.StreamsKt.asStream;
import androidx.room.compiler.processing.XHasModifiers;
import androidx.room.compiler.processing.XMethodElement;
-import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XTypeParameterElement;
-import androidx.room.compiler.processing.compat.XConverters;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.devtools.ksp.symbol.Origin;
-import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeVariableName;
// TODO(bcorso): Consider moving these methods into XProcessing library.
@@ -142,20 +137,5 @@ public final class XTypeElements {
return visibilities.build();
}
- /** Returns true if the source of the given type element is Kotlin. */
- public static boolean isKotlinSource(XTypeElement typeElement) {
- XProcessingEnv processingEnv = getProcessingEnv(typeElement);
- switch (processingEnv.getBackend()) {
- case KSP:
- // If this is KSP, then we should be able to check the origin of the declaration.
- Origin origin = XConverters.toKS(typeElement).getOrigin();
- return origin == Origin.KOTLIN || origin == Origin.KOTLIN_LIB;
- case JAVAC:
- // If this is KAPT, then the java stubs should have kotlin metadata.
- return typeElement.hasAnnotation(ClassName.get("kotlin", "Metadata"));
- }
- throw new AssertionError("Unhandled backend kind: " + processingEnv.getBackend());
- }
-
private XTypeElements() {}
}
diff --git a/java/dagger/internal/codegen/xprocessing/XTypes.java b/java/dagger/internal/codegen/xprocessing/XTypes.java
index 654ce697a..7f4b77aac 100644
--- a/java/dagger/internal/codegen/xprocessing/XTypes.java
+++ b/java/dagger/internal/codegen/xprocessing/XTypes.java
@@ -388,12 +388,46 @@ public final class XTypes {
* {@link Optional} is returned if there is no non-{@link Object} superclass.
*/
public static Optional<XType> nonObjectSuperclass(XType type) {
- return isDeclared(type)
- ? type.getSuperTypes().stream()
- .filter(supertype -> !supertype.getTypeName().equals(TypeName.OBJECT))
- .filter(supertype -> isDeclared(supertype) && supertype.getTypeElement().isClass())
- .collect(toOptional())
- : Optional.empty();
+ if (!isDeclared(type)) {
+ return Optional.empty();
+ }
+ // We compare elements (rather than TypeName) here because its more efficient on the heap.
+ XTypeElement objectElement = objectElement(getProcessingEnv(type));
+ XTypeElement typeElement = type.getTypeElement();
+ if (!typeElement.isClass() || typeElement.equals(objectElement)) {
+ return Optional.empty();
+ }
+ XType superClass = typeElement.getSuperClass();
+ if (!isDeclared(superClass)) {
+ return Optional.empty();
+ }
+ XTypeElement superClassElement = superClass.getTypeElement();
+ if (!superClassElement.isClass() || superClassElement.equals(objectElement)) {
+ return Optional.empty();
+ }
+ // TODO(b/310954522): XType#getSuperTypes() is less efficient (especially on the heap) as it
+ // requires creating XType for not just superclass but all super interfaces as well, so we go
+ // through a bit of effort here to avoid that call unless its absolutely necessary since
+ // nonObjectSuperclass is called quite a bit via InjectionSiteFactory. However, we should
+ // eventually optimize this on the XProcessing side instead, e.g. maybe separating
+ // XType#getSuperClass() into a separate method.
+ return superClass.getTypeArguments().isEmpty()
+ ? Optional.of(superClass)
+ : type.getSuperTypes().stream()
+ .filter(XTypes::isDeclared)
+ .filter(supertype -> supertype.getTypeElement().isClass())
+ .filter(supertype -> !supertype.getTypeElement().equals(objectElement))
+ .collect(toOptional());
+ }
+
+ private static XTypeElement objectElement(XProcessingEnv processingEnv) {
+ switch (processingEnv.getBackend()) {
+ case JAVAC:
+ return processingEnv.requireTypeElement(TypeName.OBJECT);
+ case KSP:
+ return processingEnv.requireTypeElement("kotlin.Any");
+ }
+ throw new AssertionError("Unexpected backend: " + processingEnv.getBackend());
}
/**
diff --git a/java/dagger/internal/codegen/xprocessing/xprocessing-testing.jar b/java/dagger/internal/codegen/xprocessing/xprocessing-testing.jar
index 7811751e0..4d0d7fd00 100644
--- a/java/dagger/internal/codegen/xprocessing/xprocessing-testing.jar
+++ b/java/dagger/internal/codegen/xprocessing/xprocessing-testing.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/xprocessing/xprocessing.jar b/java/dagger/internal/codegen/xprocessing/xprocessing.jar
index 1c41e2053..24590f84d 100644
--- a/java/dagger/internal/codegen/xprocessing/xprocessing.jar
+++ b/java/dagger/internal/codegen/xprocessing/xprocessing.jar
Binary files differ
diff --git a/java/dagger/lint/DaggerKotlinIssueDetector.kt b/java/dagger/lint/DaggerKotlinIssueDetector.kt
index 8ca862309..6a4a33ca6 100644
--- a/java/dagger/lint/DaggerKotlinIssueDetector.kt
+++ b/java/dagger/lint/DaggerKotlinIssueDetector.kt
@@ -204,7 +204,8 @@ class DaggerKotlinIssueDetector : Detector(), SourceCodeScanner {
) {
val containingClass = node.containingClass?.toUElement(UClass::class.java) ?: return
if (containingClass.isObject()) {
- val annotation = node.findAnnotation(JVM_STATIC_ANNOTATION)!!
+ val annotation = node.findAnnotation(JVM_STATIC_ANNOTATION)
+ ?: node.javaPsi.modifierList.findAnnotation(JVM_STATIC_ANNOTATION)!!
context.report(
ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT,
context.getLocation(annotation),
diff --git a/java/dagger/model/BUILD b/java/dagger/model/BUILD
index aaa830601..1a672dc03 100644
--- a/java/dagger/model/BUILD
+++ b/java/dagger/model/BUILD
@@ -44,7 +44,6 @@ java_library(
deps = [
"//java/dagger:core",
"//java/dagger/internal/codegen/extension",
- "//java/dagger/producers",
"//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/error_prone:annotations",
diff --git a/java/dagger/model/BindingGraph.java b/java/dagger/model/BindingGraph.java
index 1ccba4326..d09bf02c1 100644
--- a/java/dagger/model/BindingGraph.java
+++ b/java/dagger/model/BindingGraph.java
@@ -43,14 +43,13 @@ import javax.lang.model.element.TypeElement;
* <p>A {@link BindingGraph} represents one of the following:
*
* <ul>
- * <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
- * dagger.producers.ProductionComponent}
- * <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
- * dagger.producers.ProductionSubcomponent} (only when the value of {@code
- * -Adagger.fullBindingGraphValidation} is not {@code NONE})
- * <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
- * including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
- * dagger.producers.ProducerModule#subcomponents()} ()}
+ * <li>an entire component hierarchy rooted at a {@code Component} or {@code ProductionComponent}
+ * <li>a partial component hierarchy rooted at a {@code Subcomponent} or
+ * {@code ProductionSubcomponent} (only when the value of
+ * {@code -Adagger.fullBindingGraphValidation} is not {@code NONE})
+ * <li>the bindings installed by a {@code Module} or {@code ProducerModule},
+ * including all subcomponents generated by {@code Module#subcomponents()} and
+ * {@code ProducerModule#subcomponents()}
* </ul>
*
* In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
diff --git a/java/dagger/model/BindingKind.java b/java/dagger/model/BindingKind.java
index 1bfb08098..46d11309a 100644
--- a/java/dagger/model/BindingKind.java
+++ b/java/dagger/model/BindingKind.java
@@ -34,8 +34,7 @@ public enum BindingKind {
ASSISTED_FACTORY,
/**
- * An implicit binding for a {@link dagger.Component}- or {@link
- * dagger.producers.ProductionComponent}-annotated type.
+ * An implicit binding for a {@code Component}- or {@code ProductionComponent}-annotated type.
*/
COMPONENT,
@@ -65,14 +64,13 @@ public enum BindingKind {
/** A binding for a {@link dagger.BindsInstance}-annotated builder method. */
BOUND_INSTANCE,
- /** A binding for a {@link dagger.producers.Produces}-annotated method. */
+ /** A binding for a {@code Produces}-annotated method. */
PRODUCTION,
/**
- * A binding for a production method on a production component's {@linkplain
- * dagger.producers.ProductionComponent#dependencies()} dependency} that returns a {@link
- * com.google.common.util.concurrent.ListenableFuture} or {@link
- * com.google.common.util.concurrent.FluentFuture}. Methods on production component dependencies
+ * A binding for a production method on a production component's
+ * {@code ProductionComponent#dependencies()} that returns a {@code ListenableFuture} or
+ * {@code FluentFuture}. Methods on production component dependencies
* that don't return a future are considered {@linkplain #COMPONENT_PROVISION component provision
* bindings}.
*/
diff --git a/java/dagger/model/ComponentPath.java b/java/dagger/model/ComponentPath.java
index 5a74c7a51..9dfdbf93e 100644
--- a/java/dagger/model/ComponentPath.java
+++ b/java/dagger/model/ComponentPath.java
@@ -40,8 +40,7 @@ public abstract class ComponentPath {
public abstract ImmutableList<TypeElement> components();
/**
- * Returns the root {@link dagger.Component}- or {@link
- * dagger.producers.ProductionComponent}-annotated type
+ * Returns the root {@code Component}- or {@code ProductionComponent}-annotated type
*/
public final TypeElement rootComponent() {
return components().get(0);
diff --git a/java/dagger/model/RequestKind.java b/java/dagger/model/RequestKind.java
index 74a434633..6a19b0a08 100644
--- a/java/dagger/model/RequestKind.java
+++ b/java/dagger/model/RequestKind.java
@@ -19,11 +19,6 @@ package dagger.model;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import dagger.Lazy;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-
/**
* Represents the different kinds of {@link javax.lang.model.type.TypeMirror types} that may be
* requested as dependencies for the same key. For example, {@code String}, {@code
@@ -35,13 +30,13 @@ public enum RequestKind {
/** A default request for an instance. E.g.: {@code FooType} */
INSTANCE,
- /** A request for a {@link Provider}. E.g.: {@code Provider<FooType>} */
+ /** A request for a {@code Provider}. E.g.: {@code Provider<FooType>} */
PROVIDER,
- /** A request for a {@link Lazy}. E.g.: {@code Lazy<FooType>} */
+ /** A request for a {@code Lazy}. E.g.: {@code Lazy<FooType>} */
LAZY,
- /** A request for a {@link Provider} of a {@link Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
+ /** A request for a {@code Provider} of a {@code Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
PROVIDER_OF_LAZY,
/**
@@ -50,15 +45,15 @@ public enum RequestKind {
*/
MEMBERS_INJECTION,
- /** A request for a {@link Producer}. E.g.: {@code Producer<FooType>} */
+ /** A request for a {@code Producer}. E.g.: {@code Producer<FooType>} */
PRODUCER,
- /** A request for a {@link Produced}. E.g.: {@code Produced<FooType>} */
+ /** A request for a {@code Produced}. E.g.: {@code Produced<FooType>} */
PRODUCED,
/**
- * A request for a {@link com.google.common.util.concurrent.ListenableFuture}. E.g.: {@code
- * ListenableFuture<FooType>}. These can only be requested by component interfaces.
+ * A request for a {@code ListenableFuture}. E.g.: {@code ListenableFuture<FooType>}. These can
+ * only be requested by component interfaces.
*/
FUTURE,
;
diff --git a/java/dagger/model/Scope.java b/java/dagger/model/Scope.java
index c7ebfa811..fe64c86d4 100644
--- a/java/dagger/model/Scope.java
+++ b/java/dagger/model/Scope.java
@@ -26,7 +26,6 @@ import com.google.auto.value.AutoValue;
import com.google.common.base.Equivalence;
import com.squareup.javapoet.ClassName;
import dagger.Reusable;
-import dagger.producers.ProductionScope;
import javax.inject.Singleton;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
@@ -88,7 +87,7 @@ public abstract class Scope {
return isScope(REUSABLE);
}
- /** Returns {@code true} if this scope is the {@link ProductionScope @ProductionScope} scope. */
+ /** Returns {@code true} if this scope is the {@code @ProductionScope} scope. */
public final boolean isProductionScope() {
return isScope(PRODUCTION_SCOPE);
}
diff --git a/java/dagger/multibindings/LazyClassKey.java b/java/dagger/multibindings/LazyClassKey.java
new file mode 100644
index 000000000..da46984e3
--- /dev/null
+++ b/java/dagger/multibindings/LazyClassKey.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.multibindings;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.MapKey;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A {@link MapKey} annotation for maps with {@code Class<?>} keys.
+ *
+ * <p>The difference from {@link ClassKey} is that dagger generates a string representation for the
+ * class to use under the hood, which prevents loading unused classes at runtime.
+ */
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
+@Retention(RUNTIME)
+@Documented
+@MapKey
+public @interface LazyClassKey {
+ Class<?> value();
+}
diff --git a/java/dagger/producers/internal/AbstractMapProducer.java b/java/dagger/producers/internal/AbstractMapProducer.java
index c60f0cb07..360f1eb8e 100644
--- a/java/dagger/producers/internal/AbstractMapProducer.java
+++ b/java/dagger/producers/internal/AbstractMapProducer.java
@@ -17,12 +17,13 @@
package dagger.producers.internal;
import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.Providers.asDaggerProvider;
import static dagger.producers.internal.Producers.producerFromProvider;
import com.google.common.collect.ImmutableMap;
+import dagger.internal.Provider;
import dagger.producers.Producer;
import java.util.Map;
-import javax.inject.Provider;
/**
* An {@code abstract} {@link Producer} implementation used to implement {@link Map} bindings.
@@ -76,6 +77,15 @@ abstract class AbstractMapProducer<K, V, V2> extends AbstractProducer<Map<K, V2>
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ Builder<K, V, V2> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
/** Adds contributions from a super-implementation of a component into this builder. */
Builder<K, V, V2> putAll(Producer<Map<K, V2>> mapOfProducers) {
if (mapOfProducers instanceof DelegateProducer) {
diff --git a/java/dagger/producers/internal/AbstractProducesMethodProducer.java b/java/dagger/producers/internal/AbstractProducesMethodProducer.java
index 0cf36ca53..95b8f8340 100644
--- a/java/dagger/producers/internal/AbstractProducesMethodProducer.java
+++ b/java/dagger/producers/internal/AbstractProducesMethodProducer.java
@@ -17,15 +17,16 @@
package dagger.producers.internal;
import static dagger.internal.Preconditions.checkNotNull;
+import static dagger.internal.Providers.asDaggerProvider;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Provider;
import dagger.producers.monitoring.ProducerMonitor;
import dagger.producers.monitoring.ProducerToken;
import dagger.producers.monitoring.ProductionComponentMonitor;
import java.util.concurrent.Executor;
-import javax.inject.Provider;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
/**
@@ -54,6 +55,18 @@ public abstract class AbstractProducesMethodProducer<D, T> extends AbstractProdu
this.executorProvider = checkNotNull(executorProvider);
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ protected AbstractProducesMethodProducer(
+ javax.inject.Provider<ProductionComponentMonitor> monitorProvider,
+ @NullableDecl ProducerToken token,
+ javax.inject.Provider<Executor> executorProvider) {
+ this(asDaggerProvider(monitorProvider), token, asDaggerProvider(executorProvider));
+ }
+
@Override
protected final ListenableFuture<T> compute() {
monitor = monitorProvider.get().producerMonitorFor(token);
diff --git a/java/dagger/producers/internal/DelegateProducer.java b/java/dagger/producers/internal/DelegateProducer.java
index 6cc7547c3..de29234ac 100644
--- a/java/dagger/producers/internal/DelegateProducer.java
+++ b/java/dagger/producers/internal/DelegateProducer.java
@@ -20,8 +20,8 @@ import static dagger.internal.Preconditions.checkNotNull;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.internal.DoubleCheck;
+import dagger.internal.Provider;
import dagger.producers.Producer;
-import javax.inject.Provider;
/**
* A DelegateProducer that is used to stitch Producer indirection during initialization across
diff --git a/java/dagger/producers/internal/MapOfProducedProducer.java b/java/dagger/producers/internal/MapOfProducedProducer.java
index bd9f1bfcc..3db774094 100644
--- a/java/dagger/producers/internal/MapOfProducedProducer.java
+++ b/java/dagger/producers/internal/MapOfProducedProducer.java
@@ -18,6 +18,7 @@ package dagger.producers.internal;
import static com.google.common.util.concurrent.Futures.transform;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static dagger.internal.Providers.asDaggerProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
@@ -25,11 +26,11 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Provider;
import dagger.producers.Produced;
import dagger.producers.Producer;
import java.util.List;
import java.util.Map;
-import javax.inject.Provider;
/**
* A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
@@ -108,6 +109,15 @@ public final class MapOfProducedProducer<K, V> extends AbstractMapProducer<K, V,
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
@Override
public Builder<K, V> putAll(Producer<Map<K, Produced<V>>> mapOfProducedProducer) {
super.putAll(mapOfProducedProducer);
diff --git a/java/dagger/producers/internal/MapOfProducerProducer.java b/java/dagger/producers/internal/MapOfProducerProducer.java
index 064cf7499..145ea6db6 100644
--- a/java/dagger/producers/internal/MapOfProducerProducer.java
+++ b/java/dagger/producers/internal/MapOfProducerProducer.java
@@ -16,6 +16,7 @@
package dagger.producers.internal;
+import static dagger.internal.Providers.asDaggerProvider;
import static dagger.producers.internal.Producers.entryPointViewOf;
import static dagger.producers.internal.Producers.nonCancellationPropagatingViewOf;
@@ -24,9 +25,9 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Provider;
import dagger.producers.Producer;
import java.util.Map;
-import javax.inject.Provider;
/**
* A {@link Producer} implementation used to implement {@link Map} bindings. This factory returns an
@@ -65,6 +66,15 @@ public final class MapOfProducerProducer<K, V> extends AbstractMapProducer<K, V,
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
@Override
public Builder<K, V> putAll(Producer<Map<K, Producer<V>>> mapOfProducerProducer) {
super.putAll(mapOfProducerProducer);
diff --git a/java/dagger/producers/internal/MapProducer.java b/java/dagger/producers/internal/MapProducer.java
index 8caeb45cb..c832ef402 100644
--- a/java/dagger/producers/internal/MapProducer.java
+++ b/java/dagger/producers/internal/MapProducer.java
@@ -17,18 +17,19 @@
package dagger.producers.internal;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static dagger.internal.Providers.asDaggerProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Provider;
import dagger.producers.Producer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import javax.inject.Provider;
/**
* A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
@@ -62,6 +63,15 @@ public final class MapProducer<K, V> extends AbstractMapProducer<K, V, V> {
return this;
}
+ /**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public Builder<K, V> put(K key, javax.inject.Provider<V> providerOfValue) {
+ return put(key, asDaggerProvider(providerOfValue));
+ }
+
@Override
public Builder<K, V> putAll(Producer<Map<K, V>> mapProducer) {
super.putAll(mapProducer);
diff --git a/java/dagger/producers/internal/Producers.java b/java/dagger/producers/internal/Producers.java
index 54e4d5ee3..9385ee326 100644
--- a/java/dagger/producers/internal/Producers.java
+++ b/java/dagger/producers/internal/Producers.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.catchingAsync;
import static com.google.common.util.concurrent.Futures.transform;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static dagger.internal.Providers.asDaggerProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
@@ -27,12 +28,12 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.Provider;
import dagger.producers.Produced;
import dagger.producers.Producer;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.inject.Provider;
/**
* Utility methods for use in generated producer code.
@@ -135,6 +136,15 @@ public final class Producers {
}
/**
+ * Legacy javax version of the method to support libraries compiled with an older version of
+ * Dagger. Do not use directly.
+ */
+ @Deprecated
+ public static <T> Producer<T> producerFromProvider(final javax.inject.Provider<T> provider) {
+ return producerFromProvider(asDaggerProvider(provider));
+ }
+
+ /**
* Returns a producer that succeeds with the given value.
*
* @deprecated Prefer the non-internal version of this method: {@link
diff --git a/java/dagger/proguard.pro b/java/dagger/proguard.pro
new file mode 100644
index 000000000..1bfc94749
--- /dev/null
+++ b/java/dagger/proguard.pro
@@ -0,0 +1,3 @@
+-keepclasseswithmembers,includedescriptorclasses class * {
+ @dagger.internal.KeepFieldType <fields>;
+} \ No newline at end of file
diff --git a/java/dagger/r8.pro b/java/dagger/r8.pro
new file mode 100644
index 000000000..17e0ffe6d
--- /dev/null
+++ b/java/dagger/r8.pro
@@ -0,0 +1,3 @@
+-identifiernamestring @dagger.internal.IdentifierNameString class ** {
+ static java.lang.String *;
+} \ No newline at end of file
diff --git a/java/dagger/spi/BUILD b/java/dagger/spi/BUILD
index 13428c1a4..8084e6396 100644
--- a/java/dagger/spi/BUILD
+++ b/java/dagger/spi/BUILD
@@ -63,7 +63,6 @@ gen_maven_artifact(
],
artifact_target_maven_deps = [
"com.google.code.findbugs:jsr305",
- "com.google.dagger:dagger-producers",
"com.google.dagger:dagger",
"com.google.devtools.ksp:symbol-processing-api",
"com.google.guava:failureaccess",
diff --git a/java/dagger/spi/model/BUILD b/java/dagger/spi/model/BUILD
index e9346ddf6..e5af4e50b 100644
--- a/java/dagger/spi/model/BUILD
+++ b/java/dagger/spi/model/BUILD
@@ -40,7 +40,6 @@ kt_jvm_library(
deps = [
"//java/dagger:core",
"//java/dagger/internal/codegen/extension",
- "//java/dagger/producers",
"//third_party/java/auto:common",
"//third_party/java/auto:value",
"//third_party/java/error_prone:annotations",
@@ -48,7 +47,6 @@ kt_jvm_library(
"//third_party/java/guava/collect",
"//third_party/java/guava/graph",
"//third_party/java/javapoet",
- "//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"@maven//:com_google_devtools_ksp_symbol_processing_api",
],
diff --git a/java/dagger/spi/model/BindingGraph.java b/java/dagger/spi/model/BindingGraph.java
index aab7c2a4b..7f433a1e0 100644
--- a/java/dagger/spi/model/BindingGraph.java
+++ b/java/dagger/spi/model/BindingGraph.java
@@ -41,14 +41,14 @@ import java.util.stream.Stream;
* <p>A {@link BindingGraph} represents one of the following:
*
* <ul>
- * <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
- * dagger.producers.ProductionComponent}
- * <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
- * dagger.producers.ProductionSubcomponent} (only when the value of {@code
- * -Adagger.fullBindingGraphValidation} is not {@code NONE})
- * <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
- * including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
- * dagger.producers.ProducerModule#subcomponents()} ()}
+ * <li>an entire component hierarchy rooted at a {@link dagger.Component} or
+ * {@code ProductionComponent}
+ * <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or
+ * {@code ProductionSubcomponent} (only when the value of
+ * {@code -Adagger.fullBindingGraphValidation} is not {@code NONE})
+ * <li>the bindings installed by a {@link Module} or {@code ProducerModule},
+ * including all subcomponents generated by {@link Module#subcomponents()} ()} and
+ * {@code ProducerModule#subcomponents()} ()}
* </ul>
*
* In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
diff --git a/java/dagger/spi/model/BindingKind.java b/java/dagger/spi/model/BindingKind.java
index b854b5300..eb1e83501 100644
--- a/java/dagger/spi/model/BindingKind.java
+++ b/java/dagger/spi/model/BindingKind.java
@@ -34,8 +34,8 @@ public enum BindingKind {
ASSISTED_FACTORY,
/**
- * An implicit binding for a {@link dagger.Component}- or {@link
- * dagger.producers.ProductionComponent}-annotated type.
+ * An implicit binding for a {@link dagger.Component}- or {@code ProductionComponent}-annotated
+ * type.
*/
COMPONENT,
@@ -65,16 +65,14 @@ public enum BindingKind {
/** A binding for a {@link dagger.BindsInstance}-annotated builder method. */
BOUND_INSTANCE,
- /** A binding for a {@link dagger.producers.Produces}-annotated method. */
+ /** A binding for a {@code Produces}-annotated method. */
PRODUCTION,
/**
- * A binding for a production method on a production component's {@linkplain
- * dagger.producers.ProductionComponent#dependencies()} dependency} that returns a {@link
- * com.google.common.util.concurrent.ListenableFuture} or {@link
- * com.google.common.util.concurrent.FluentFuture}. Methods on production component dependencies
- * that don't return a future are considered {@linkplain #COMPONENT_PROVISION component provision
- * bindings}.
+ * A binding for a production method on a production component's
+ * {@code ProductionComponent#dependencies()} that returns a {@code ListenableFuture} or
+ * {@code FluentFuture}. Methods on production component dependencies
+ * that don't return a future are considered component provision bindings.
*/
COMPONENT_PRODUCTION,
diff --git a/java/dagger/spi/model/ComponentPath.java b/java/dagger/spi/model/ComponentPath.java
index 63a5f6b90..11c811e26 100644
--- a/java/dagger/spi/model/ComponentPath.java
+++ b/java/dagger/spi/model/ComponentPath.java
@@ -39,8 +39,7 @@ public abstract class ComponentPath {
public abstract ImmutableList<DaggerTypeElement> components();
/**
- * Returns the root {@link dagger.Component}- or {@link
- * dagger.producers.ProductionComponent}-annotated type
+ * Returns the root {@code Component}- or {@code ProductionComponent}-annotated type
*/
public final DaggerTypeElement rootComponent() {
return components().get(0);
@@ -89,7 +88,7 @@ public abstract class ComponentPath {
@Override
public final String toString() {
- return components().stream().map(DaggerTypeElement::qualifiedName).collect(joining(" → "));
+ return components().stream().map(Key::qualifiedName).collect(joining(" → "));
}
@Memoized
diff --git a/java/dagger/spi/model/DaggerAnnotation.java b/java/dagger/spi/model/DaggerAnnotation.java
index 2ec66c010..5b590033f 100644
--- a/java/dagger/spi/model/DaggerAnnotation.java
+++ b/java/dagger/spi/model/DaggerAnnotation.java
@@ -16,55 +16,29 @@
package dagger.spi.model;
-import com.google.auto.common.AnnotationMirrors;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.symbol.KSAnnotation;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.lang.model.element.AnnotationMirror;
/** Wrapper type for an annotation. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerAnnotation {
- public static DaggerAnnotation fromJavac(
- DaggerTypeElement annotationTypeElement, AnnotationMirror annotation) {
- return new AutoValue_DaggerAnnotation(annotationTypeElement, annotation, null);
- }
-
- public static DaggerAnnotation fromKsp(
- DaggerTypeElement annotationTypeElement, KSAnnotation ksp) {
- return new AutoValue_DaggerAnnotation(annotationTypeElement, null, ksp);
- }
-
public abstract DaggerTypeElement annotationTypeElement();
/**
- * java representation for the annotation, returns {@code null} if the annotation isn't a java
- * element.
+ * Returns the Javac representation for the annotation.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
*/
- @Nullable
- public abstract AnnotationMirror java();
+ public abstract AnnotationMirror javac();
- /** KSP declaration for the annotation, returns {@code null} not using KSP. */
- @Nullable
+ /**
+ * Returns the KSP representation for the annotation.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract KSAnnotation ksp();
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
-
- @Override
- public final String toString() {
- switch (backend()) {
- case JAVAC:
- return AnnotationMirrors.toString(java());
- case KSP:
- return ksp().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
+ /** Returns the backend used in this compilation. */
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/DaggerElement.java b/java/dagger/spi/model/DaggerElement.java
index 0f6a2cf35..0c0c53ebf 100644
--- a/java/dagger/spi/model/DaggerElement.java
+++ b/java/dagger/spi/model/DaggerElement.java
@@ -16,49 +16,27 @@
package dagger.spi.model;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.symbol.KSAnnotated;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.lang.model.element.Element;
/** Wrapper type for an element. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerElement {
- public static DaggerElement fromJavac(Element element) {
- return new AutoValue_DaggerElement(element, null);
- }
-
- public static DaggerElement fromKsp(KSAnnotated ksp) {
- return new AutoValue_DaggerElement(null, ksp);
- }
-
/**
- * Java representation for the element, returns {@code null} not using java annotation processor.
+ * Returns the Javac representation for the element.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
*/
- @Nullable
- public abstract Element java();
+ public abstract Element javac();
- /** KSP declaration for the element, returns {@code null} not using KSP. */
- @Nullable
+ /**
+ * Returns the KSP representation for the element.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract KSAnnotated ksp();
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
-
- @Override
- public final String toString() {
- switch (backend()) {
- case JAVAC:
- return java().toString();
- case KSP:
- return ksp().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
+ /** Returns the backend used in this compilation. */
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/DaggerExecutableElement.java b/java/dagger/spi/model/DaggerExecutableElement.java
index 7df6a1e38..afbd8e001 100644
--- a/java/dagger/spi/model/DaggerExecutableElement.java
+++ b/java/dagger/spi/model/DaggerExecutableElement.java
@@ -16,59 +16,27 @@
package dagger.spi.model;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.symbol.KSFunctionDeclaration;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.lang.model.element.ExecutableElement;
/** Wrapper type for an executable element. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerExecutableElement {
- public static DaggerExecutableElement fromJava(ExecutableElement executableElement) {
- return new AutoValue_DaggerExecutableElement(executableElement, null);
- }
-
- public static DaggerExecutableElement fromKsp(KSFunctionDeclaration declaration) {
- return new AutoValue_DaggerExecutableElement(null, declaration);
- }
-
/**
- * Java representation for the element, returns {@code null} not using java annotation processor.
+ * Returns the Javac representation for the executable element.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
*/
- @Nullable
- public abstract ExecutableElement java();
+ public abstract ExecutableElement javac();
- /** KSP declaration for the element, returns {@code null} not using KSP. */
- @Nullable
+ /**
+ * Returns the KSP representation for the executable element.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract KSFunctionDeclaration ksp();
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
-
- @Override
- public final String toString() {
- switch (backend()) {
- case JAVAC:
- return java().toString();
- case KSP:
- return ksp().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
-
- String simpleName() {
- switch (backend()) {
- case JAVAC:
- return java().getSimpleName().toString();
- case KSP:
- return ksp().getSimpleName().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
+ /** Returns the backend used in this compilation. */
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/DaggerProcessingEnv.java b/java/dagger/spi/model/DaggerProcessingEnv.java
index 8c652c89d..ab2e33e5f 100644
--- a/java/dagger/spi/model/DaggerProcessingEnv.java
+++ b/java/dagger/spi/model/DaggerProcessingEnv.java
@@ -16,50 +16,41 @@
package dagger.spi.model;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.processing.Resolver;
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.annotation.processing.ProcessingEnvironment;
/** Wrapper type for an element. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerProcessingEnv {
/** Represents a type of backend used for compilation. */
- public enum Backend { JAVAC, KSP }
-
- public static boolean isJavac(Backend backend) {
- return backend.equals(Backend.JAVAC);
- }
-
- public static DaggerProcessingEnv fromJavac(ProcessingEnvironment env) {
- return new AutoValue_DaggerProcessingEnv(env, null, null);
- }
-
- public static DaggerProcessingEnv fromKsp(SymbolProcessorEnvironment env, Resolver resolver) {
- return new AutoValue_DaggerProcessingEnv(null, env, resolver);
+ public enum Backend {
+ JAVAC,
+ KSP
}
/**
- * Java representation for the processing environment, returns {@code null} not using java
- * annotation processor.
+ * Returns the Javac representation for the processing environment.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
*/
- @Nullable
- public abstract ProcessingEnvironment java();
+ public abstract ProcessingEnvironment javac();
- @Nullable
+ /**
+ * Returns the KSP representation for the processing environment.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract SymbolProcessorEnvironment ksp();
- @Nullable
+ /**
+ * Returns the KSP representation for the resolver.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract Resolver resolver();
/** Returns the backend used in this compilation. */
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/DaggerType.java b/java/dagger/spi/model/DaggerType.java
index 249d49492..463350606 100644
--- a/java/dagger/spi/model/DaggerType.java
+++ b/java/dagger/spi/model/DaggerType.java
@@ -16,47 +16,27 @@
package dagger.spi.model;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.symbol.KSType;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.lang.model.type.TypeMirror;
/** Wrapper type for a type. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerType {
- public static DaggerType fromJavac(TypeMirror type) {
- return new AutoValue_DaggerType(type, null);
- }
-
- public static DaggerType fromKsp(KSType type) {
- return new AutoValue_DaggerType(null, type);
- }
-
- /** Java representation for the type, returns {@code null} not using java annotation processor. */
- @Nullable
- public abstract TypeMirror java();
-
- /** KSP declaration for the type, returns {@code null} not using KSP. */
- @Nullable
+ /**
+ * Returns the Javac representation for the type.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
+ */
+ public abstract TypeMirror javac();
+
+ /**
+ * Returns the KSP representation for the type.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract KSType ksp();
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
-
- @Override
- public final String toString() {
- switch (backend()) {
- case JAVAC:
- return java().toString();
- case KSP:
- return ksp().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
+ /** Returns the backend used in this compilation. */
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/DaggerTypeElement.java b/java/dagger/spi/model/DaggerTypeElement.java
index 2f70a54b8..935770d11 100644
--- a/java/dagger/spi/model/DaggerTypeElement.java
+++ b/java/dagger/spi/model/DaggerTypeElement.java
@@ -16,78 +16,27 @@
package dagger.spi.model;
-import com.google.auto.common.MoreElements;
-import com.google.auto.value.AutoValue;
import com.google.devtools.ksp.symbol.KSClassDeclaration;
-import javax.annotation.Nullable;
+import com.google.errorprone.annotations.DoNotMock;
import javax.lang.model.element.TypeElement;
/** Wrapper type for a type element. */
-@AutoValue
+@DoNotMock("Only use real implementations created by Dagger")
public abstract class DaggerTypeElement {
- public static DaggerTypeElement fromJavac(@Nullable TypeElement element) {
- return new AutoValue_DaggerTypeElement(element, null);
- }
-
- public static DaggerTypeElement fromKsp(@Nullable KSClassDeclaration declaration) {
- return new AutoValue_DaggerTypeElement(null, declaration);
- }
-
- /** Java representation for the type, returns {@code null} not using java annotation processor. */
- @Nullable
- public abstract TypeElement java();
-
- /** KSP declaration for the element, returns {@code null} not using KSP. */
- @Nullable
+ /**
+ * Returns the Javac representation for the type element.
+ *
+ * @throws IllegalStateException if the current backend isn't Javac.
+ */
+ public abstract TypeElement javac();
+
+ /**
+ * Returns the KSP representation for the type element.
+ *
+ * @throws IllegalStateException if the current backend isn't KSP.
+ */
public abstract KSClassDeclaration ksp();
- public final boolean hasAnnotation(String annotationName) {
- switch (backend()) {
- case JAVAC:
- return MoreElements.isAnnotationPresent(java(), annotationName);
- case KSP:
- return KspUtilsKt.hasAnnotation(ksp(), annotationName);
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
-
- public String packageName() {
- switch (backend()) {
- case JAVAC:
- return MoreElements.getPackage(java()).getQualifiedName().toString();
- case KSP:
- return KspUtilsKt.getNormalizedPackageName(ksp());
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
-
- public String qualifiedName() {
- switch (backend()) {
- case JAVAC:
- return java().getQualifiedName().toString();
- case KSP:
- return ksp().getQualifiedName().asString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
-
- public DaggerProcessingEnv.Backend backend() {
- if (java() != null) {
- return DaggerProcessingEnv.Backend.JAVAC;
- } else if (ksp() != null) {
- return DaggerProcessingEnv.Backend.KSP;
- }
- throw new AssertionError("Unexpected backend");
- }
-
- @Override
- public final String toString() {
- switch (backend()) {
- case JAVAC:
- return java().toString();
- case KSP:
- return ksp().toString();
- }
- throw new IllegalStateException(String.format("Backend %s not supported yet.", backend()));
- }
+ /** Returns the backend used in this compilation. */
+ public abstract DaggerProcessingEnv.Backend backend();
}
diff --git a/java/dagger/spi/model/Key.java b/java/dagger/spi/model/Key.java
index d871e6d12..d4478f6a6 100644
--- a/java/dagger/spi/model/Key.java
+++ b/java/dagger/spi/model/Key.java
@@ -130,7 +130,7 @@ public abstract class Key {
private static MultibindingContributionIdentifier create(
DaggerTypeElement contributingModule, DaggerExecutableElement bindingMethod) {
return new AutoValue_Key_MultibindingContributionIdentifier(
- contributingModule.qualifiedName(), bindingMethod.simpleName());
+ qualifiedName(contributingModule), simpleName(bindingMethod));
}
/** Returns the module containing the multibinding method. */
@@ -150,4 +150,24 @@ public abstract class Key {
return String.format("%s#%s", contributingModule(), bindingMethod());
}
}
+
+ static String qualifiedName(DaggerTypeElement element) {
+ switch (element.backend()) {
+ case JAVAC:
+ return element.javac().getQualifiedName().toString();
+ case KSP:
+ return element.ksp().getQualifiedName().asString();
+ }
+ throw new IllegalStateException("Unknown backend: " + element.backend());
+ }
+
+ private static String simpleName(DaggerExecutableElement element) {
+ switch (element.backend()) {
+ case JAVAC:
+ return element.javac().getSimpleName().toString();
+ case KSP:
+ return element.ksp().getSimpleName().asString();
+ }
+ throw new IllegalStateException("Unknown backend: " + element.backend());
+ }
}
diff --git a/java/dagger/spi/model/MoreAnnotationMirrors.java b/java/dagger/spi/model/MoreAnnotationMirrors.java
index 44c0363c9..a5c04c6af 100644
--- a/java/dagger/spi/model/MoreAnnotationMirrors.java
+++ b/java/dagger/spi/model/MoreAnnotationMirrors.java
@@ -35,7 +35,7 @@ final class MoreAnnotationMirrors {
* defined in the annotation type.
*/
public static String toStableString(DaggerAnnotation qualifier) {
- return stableAnnotationMirrorToString(qualifier.java());
+ return stableAnnotationMirrorToString(qualifier.javac());
}
/**
diff --git a/java/dagger/spi/model/RequestKind.java b/java/dagger/spi/model/RequestKind.java
index 62f46cd65..bcbe26d4f 100644
--- a/java/dagger/spi/model/RequestKind.java
+++ b/java/dagger/spi/model/RequestKind.java
@@ -19,11 +19,6 @@ package dagger.spi.model;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import dagger.Lazy;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-
/**
* Represents the different kinds of {@link javax.lang.model.type.TypeMirror types} that may be
* requested as dependencies for the same key. For example, {@code String}, {@code
@@ -35,13 +30,13 @@ public enum RequestKind {
/** A default request for an instance. E.g.: {@code FooType} */
INSTANCE,
- /** A request for a {@link Provider}. E.g.: {@code Provider<FooType>} */
+ /** A request for a {@code Provider}. E.g.: {@code Provider<FooType>} */
PROVIDER,
- /** A request for a {@link Lazy}. E.g.: {@code Lazy<FooType>} */
+ /** A request for a {@code Lazy}. E.g.: {@code Lazy<FooType>} */
LAZY,
- /** A request for a {@link Provider} of a {@link Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
+ /** A request for a {@code Provider} of a {@code Lazy}. E.g.: {@code Provider<Lazy<FooType>>}. */
PROVIDER_OF_LAZY,
/**
@@ -50,15 +45,15 @@ public enum RequestKind {
*/
MEMBERS_INJECTION,
- /** A request for a {@link Producer}. E.g.: {@code Producer<FooType>} */
+ /** A request for a {@code Producer}. E.g.: {@code Producer<FooType>} */
PRODUCER,
- /** A request for a {@link Produced}. E.g.: {@code Produced<FooType>} */
+ /** A request for a {@code Produced}. E.g.: {@code Produced<FooType>} */
PRODUCED,
/**
- * A request for a {@link com.google.common.util.concurrent.ListenableFuture}. E.g.: {@code
- * ListenableFuture<FooType>}. These can only be requested by component interfaces.
+ * A request for a {@code ListenableFuture}. E.g.: {@code ListenableFuture<FooType>}. These can
+ * only be requested by component interfaces.
*/
FUTURE,
;
diff --git a/java/dagger/spi/model/Scope.java b/java/dagger/spi/model/Scope.java
index d1505b969..2d86101f1 100644
--- a/java/dagger/spi/model/Scope.java
+++ b/java/dagger/spi/model/Scope.java
@@ -18,6 +18,7 @@ package dagger.spi.model;
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.auto.common.MoreElements;
import com.google.auto.value.AutoValue;
/** A representation of a {@link javax.inject.Scope}. */
@@ -42,8 +43,16 @@ public abstract class Scope {
* Returns {@code true} if {@code scopeAnnotationType} is a {@link javax.inject.Scope} annotation.
*/
public static boolean isScope(DaggerTypeElement scopeAnnotationType) {
- return scopeAnnotationType.hasAnnotation(SCOPE)
- || scopeAnnotationType.hasAnnotation(SCOPE_JAVAX);
+ switch (scopeAnnotationType.backend()) {
+ case JAVAC:
+ return MoreElements.isAnnotationPresent(scopeAnnotationType.javac(), SCOPE)
+ || MoreElements.isAnnotationPresent(scopeAnnotationType.javac(), SCOPE_JAVAX);
+ case KSP:
+ return KspUtilsKt.hasAnnotation(scopeAnnotationType.ksp(), SCOPE)
+ || KspUtilsKt.hasAnnotation(scopeAnnotationType.ksp(), SCOPE_JAVAX);
+ }
+ throw new IllegalStateException(
+ String.format("Backend %s not supported yet.", scopeAnnotationType.backend()));
}
private boolean isScope(String annotationName) {
@@ -71,8 +80,7 @@ public abstract class Scope {
}
/**
- * Returns {@code true} if this scope is the {@link
- * dagger.producers.ProductionScope @ProductionScope} scope.
+ * Returns {@code true} if this scope is the {@code @ProductionScope} scope.
*/
public final boolean isProductionScope() {
return isScope(PRODUCTION_SCOPE);
diff --git a/java/dagger/spi/model/testing/BindingGraphSubject.java b/java/dagger/spi/model/testing/BindingGraphSubject.java
index e2067318b..4d53d9367 100644
--- a/java/dagger/spi/model/testing/BindingGraphSubject.java
+++ b/java/dagger/spi/model/testing/BindingGraphSubject.java
@@ -112,7 +112,7 @@ public final class BindingGraphSubject extends Subject {
private static String formattedType(DaggerType type) {
switch (type.backend()) {
case JAVAC:
- return type.java().toString();
+ return type.javac().toString();
case KSP:
return type.ksp().getDeclaration().getQualifiedName().asString();
}
diff --git a/java/dagger/testing/compile/BUILD b/java/dagger/testing/compile/BUILD
index 585939867..a0e09a814 100644
--- a/java/dagger/testing/compile/BUILD
+++ b/java/dagger/testing/compile/BUILD
@@ -26,6 +26,9 @@ java_library(
"CompilerProcessors.java",
"CompilerTests.java",
],
+ exports = [
+ "//java/dagger/internal/codegen/xprocessing:xprocessing-testing",
+ ],
deps = [
"//java/dagger/internal/codegen:processor",
"//java/dagger/internal/codegen/extension",
diff --git a/java/dagger/testing/compile/CompilerTests.java b/java/dagger/testing/compile/CompilerTests.java
index 55574201e..ac74a618e 100644
--- a/java/dagger/testing/compile/CompilerTests.java
+++ b/java/dagger/testing/compile/CompilerTests.java
@@ -21,6 +21,7 @@ import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.collect.Streams.stream;
import static com.google.testing.compile.Compiler.javac;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static java.util.stream.Collectors.toMap;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XProcessingEnvConfig;
@@ -39,6 +40,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
+import com.google.devtools.ksp.processing.SymbolProcessorProvider;
import com.google.testing.compile.Compiler;
import dagger.internal.codegen.ComponentProcessor;
import dagger.internal.codegen.KspComponentProcessor;
@@ -46,11 +48,13 @@ import dagger.spi.model.BindingGraphPlugin;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
+import javax.annotation.processing.Processor;
import org.junit.rules.TemporaryFolder;
/** A helper class for working with java compiler tests. */
@@ -150,6 +154,8 @@ public final class CompilerTests {
// Set default values
return builder
.processorOptions(DEFAULT_PROCESSOR_OPTIONS)
+ .additionalJavacProcessors(ImmutableList.of())
+ .additionalKspProcessors(ImmutableList.of())
.processingStepSuppliers(ImmutableSet.of())
.bindingGraphPluginSuppliers(ImmutableSet.of());
}
@@ -160,6 +166,12 @@ public final class CompilerTests {
/** Returns the annotation processor options */
abstract ImmutableMap<String, String> processorOptions();
+ /** Returns the extra Javac processors. */
+ abstract ImmutableCollection<Processor> additionalJavacProcessors();
+
+ /** Returns the extra KSP processors. */
+ abstract ImmutableCollection<SymbolProcessorProvider> additionalKspProcessors();
+
/** Returns the processing steps suppliers. */
abstract ImmutableCollection<Supplier<XProcessingStep>> processingStepSuppliers();
@@ -192,6 +204,16 @@ public final class CompilerTests {
return toBuilder().processorOptions(newProcessorOptions).build();
}
+ /** Returns a new {@link HiltCompiler} instance with the additional Javac processors. */
+ public DaggerCompiler withAdditionalJavacProcessors(Processor... processors) {
+ return toBuilder().additionalJavacProcessors(ImmutableList.copyOf(processors)).build();
+ }
+
+ /** Returns a new {@link HiltCompiler} instance with the additional KSP processors. */
+ public DaggerCompiler withAdditionalKspProcessors(SymbolProcessorProvider... processors) {
+ return toBuilder().additionalKspProcessors(ImmutableList.copyOf(processors)).build();
+ }
+
/** Returns a new {@link Compiler} instance with the given processing steps. */
public DaggerCompiler withProcessingSteps(Supplier<XProcessingStep>... suppliers) {
return toBuilder().processingStepSuppliers(ImmutableList.copyOf(suppliers)).build();
@@ -210,23 +232,43 @@ public final class CompilerTests {
/* kotlincArguments= */ ImmutableList.of(
"-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"),
/* config= */ PROCESSING_ENV_CONFIG,
- /* javacProcessors= */ ImmutableList.of(
- ComponentProcessor.withTestPlugins(bindingGraphPlugins()),
- new CompilerProcessors.JavacProcessor(processingSteps())),
- /* symbolProcessorProviders= */ ImmutableList.of(
- KspComponentProcessor.Provider.withTestPlugins(bindingGraphPlugins()),
- new CompilerProcessors.KspProcessor.Provider(processingSteps())),
+ /* javacProcessors= */ mergeProcessors(
+ ImmutableList.of(
+ ComponentProcessor.withTestPlugins(bindingGraphPlugins()),
+ new CompilerProcessors.JavacProcessor(processingSteps())),
+ additionalJavacProcessors()),
+ /* symbolProcessorProviders= */ mergeProcessors(
+ ImmutableList.of(
+ KspComponentProcessor.Provider.withTestPlugins(bindingGraphPlugins()),
+ new CompilerProcessors.KspProcessor.Provider(processingSteps())),
+ additionalKspProcessors()),
result -> {
onCompilationResult.accept(result);
return null;
});
}
+ private static <T> ImmutableList<T> mergeProcessors(
+ Collection<T> defaultProcessors, Collection<T> extraProcessors) {
+ Map<Class<?>, T> processors =
+ defaultProcessors.stream()
+ .collect(toMap(Object::getClass, (T e) -> e, (p1, p2) -> p2, HashMap::new));
+ // Adds extra processors, and allows overriding any processors of the same class.
+ extraProcessors.forEach(processor -> processors.put(processor.getClass(), processor));
+ return ImmutableList.copyOf(processors.values());
+ }
+
/** Used to build a {@link DaggerCompiler}. */
@AutoValue.Builder
public abstract static class Builder {
abstract Builder sources(ImmutableCollection<Source> sources);
abstract Builder processorOptions(Map<String, String> processorOptions);
+
+ abstract Builder additionalJavacProcessors(ImmutableCollection<Processor> processors);
+
+ abstract Builder additionalKspProcessors(
+ ImmutableCollection<SymbolProcessorProvider> processors);
+
abstract Builder processingStepSuppliers(
ImmutableCollection<Supplier<XProcessingStep>> processingStepSuppliers);
abstract Builder bindingGraphPluginSuppliers(