From da0f251bb49f38cded330bac77b250517ea7a220 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 19 Aug 2026 10:53:29 -0700 Subject: [PATCH] Add `-Adagger.casesPerSwitchingProviderSwitch` compiler option RELNOTES=n/a PiperOrigin-RevId: 967298430 --- .../compileroption/CompilerOptions.java | 11 +++++ .../ProcessingEnvironmentCompilerOptions.java | 11 +++++ .../codegen/writing/SwitchingProviders.java | 26 ++++------ .../codegen/SwitchingProviderTest.java | 49 +++++++++++++++++++ 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/dagger-compiler/main/java/dagger/internal/codegen/compileroption/CompilerOptions.java b/dagger-compiler/main/java/dagger/internal/codegen/compileroption/CompilerOptions.java index 788b692aa70..b0ab8170697 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/compileroption/CompilerOptions.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/compileroption/CompilerOptions.java @@ -144,6 +144,17 @@ public int keysPerComponentShard(XTypeElement component) { return 3500; } + /** + * 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? + public int casesPerSwitchingProviderSwitch() { + return 100; + } + /** * This option enables a fix to an issue where Dagger previously would erroneously allow * multibinding contributions in a component to have dependencies on child components. This will diff --git a/dagger-compiler/main/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java b/dagger-compiler/main/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java index 81ce3bab245..3320e08505f 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java @@ -78,6 +78,8 @@ public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions { // EnumOption doesn't support integer inputs so just doing this as a 1-off for now. private static final String KEYS_PER_COMPONENT_SHARD = "dagger.keysPerComponentShard"; + private static final String CASES_PER_SWITCHING_PROVIDER_SWITCH = + "dagger.casesPerSwitchingProviderSwitch"; private final XProcessingEnv processingEnv; @@ -233,6 +235,14 @@ public int keysPerComponentShard(XTypeElement component) { return super.keysPerComponentShard(component); } + @Override + public int casesPerSwitchingProviderSwitch() { + if (options.containsKey(CASES_PER_SWITCHING_PROVIDER_SWITCH)) { + return Integer.parseInt(options.get(CASES_PER_SWITCHING_PROVIDER_SWITCH)); + } + return super.casesPerSwitchingProviderSwitch(); + } + private boolean isEnabled(KeyOnlyOption keyOnlyOption) { return options.containsKey(keyOnlyOption.toString()); } @@ -467,6 +477,7 @@ public static ImmutableSet supportedOptions() { .flatMap(CommandLineOption::allNames) .collect(toImmutableSet())) .add(KEYS_PER_COMPONENT_SHARD) + .add(CASES_PER_SWITCHING_PROVIDER_SWITCH) .build(); } diff --git a/dagger-compiler/main/java/dagger/internal/codegen/writing/SwitchingProviders.java b/dagger-compiler/main/java/dagger/internal/codegen/writing/SwitchingProviders.java index de0147a99d8..6761dbdfaa9 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/writing/SwitchingProviders.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/writing/SwitchingProviders.java @@ -66,21 +66,9 @@ * that can provide instances for all types by switching on an id. */ final class SwitchingProviders { - /** - * 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; - /** * 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. + * maxCasesPerClass} keys will share the same instance. */ private final Map switchingProviderBuilders = new LinkedHashMap<>(); @@ -89,6 +77,8 @@ final class SwitchingProviders { private final CompilerOptions compilerOptions; private final XProcessingEnv processingEnv; private final XTypeName typeVariable; + private final int maxCasesPerSwitch; + private final long maxCasesPerClass; SwitchingProviders( ShardImplementation shardImplementation, @@ -97,6 +87,8 @@ final class SwitchingProviders { this.shardImplementation = checkNotNull(shardImplementation); this.compilerOptions = checkNotNull(compilerOptions); this.processingEnv = checkNotNull(processingEnv); + this.maxCasesPerSwitch = compilerOptions.casesPerSwitchingProviderSwitch(); + this.maxCasesPerClass = (long) maxCasesPerSwitch * maxCasesPerSwitch; this.typeVariable = XTypeName.getTypeVariableName( "T", @@ -121,7 +113,7 @@ public XCodeBlock creationExpression() { } private SwitchingProviderBuilder getSwitchingProviderBuilder() { - if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) { + if (switchingProviderBuilders.size() % maxCasesPerClass == 0) { String name = shardImplementation.getUniqueClassName("SwitchingProvider"); SwitchingProviderBuilder switchingProviderBuilder = new SwitchingProviderBuilder(shardImplementation.name().nestedClass(name)); @@ -217,7 +209,7 @@ private XTypeSpec build() { private ImmutableList getMethods() { ImmutableList switchCodeBlockPartitions = switchCodeBlockPartitions(); if (switchCodeBlockPartitions.size() == 1) { - // The case amount does not exceed MAX_CASES_PER_SWITCH, so no need for extra get methods. + // The case amount does not exceed maxCasesPerSwitch, so no need for extra get methods. return ImmutableList.of( methodBuilder("get") .isOverride(true) @@ -234,7 +226,7 @@ private ImmutableList getMethods() { .isOverride(true) .addModifiers(PUBLIC) .returns(typeVariable) - .beginControlFlow("switch (id / %L)", MAX_CASES_PER_SWITCH); + .beginControlFlow("switch (id / %L)", maxCasesPerSwitch); ImmutableList.Builder getMethods = ImmutableList.builder(); for (int i = 0; i < switchCodeBlockPartitions.size(); i++) { @@ -258,7 +250,7 @@ private ImmutableList getMethods() { } private ImmutableList switchCodeBlockPartitions() { - return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH) + return Lists.partition(ImmutableList.copyOf(switchCases.values()), maxCasesPerSwitch) .stream() .map( partitionCases -> diff --git a/javatests/dagger/internal/codegen/SwitchingProviderTest.java b/javatests/dagger/internal/codegen/SwitchingProviderTest.java index dfd78db33c7..31c7327d60a 100644 --- a/javatests/dagger/internal/codegen/SwitchingProviderTest.java +++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java @@ -18,6 +18,7 @@ import androidx.room3.compiler.processing.util.Source; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import dagger.testing.compile.CompilerTests; import dagger.testing.golden.GoldenFileRule; import org.junit.Rule; @@ -304,4 +305,52 @@ public void optionals() throws Exception { subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } + + @Test + public void customCasesPerSwitchingProviderSwitch() throws Exception { + ImmutableList.Builder sources = ImmutableList.builder(); + StringBuilder entryPoints = new StringBuilder(); + for (int i = 0; i <= 10; i++) { + String bindingName = "Binding" + i; + sources.add( + CompilerTests.javaSource( + "test." + bindingName, + "package test;", + "", + "import javax.inject.Inject;", + "", + "final class " + bindingName + " {", + " @Inject", + " " + bindingName + "() {}", + "}")); + entryPoints.append(String.format(" Provider<%1$s> get%1$sProvider();\n", bindingName)); + } + + sources.add( + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "", + "import dagger.Component;", + "import javax.inject.Provider;", + "", + "@Component", + "interface TestComponent {", + entryPoints.toString(), + "}")); + + ImmutableMap options = + ImmutableMap.builder() + .putAll(compilerMode.processorOptions()) + .put("dagger.casesPerSwitchingProviderSwitch", "5") + .buildOrThrow(); + + CompilerTests.daggerCompiler(sources.build()) + .withProcessingOptions(options) + .compile( + subject -> { + subject.hasErrorCount(0); + subject.hasWarningCount(0); + }); + } }