Skip to content

Commit b8f26b5

Browse files
alasiadsGoogle Java Core Libraries
authored andcommitted
AutoValue support for very large classes.
This change makes it so that if the AutoValue class defines a Builder method, the builder will be passed to the constructor. Effectively working around the java 255 method-arg limit. This is instead of defining a constructor where every field in the autovalue is an argument in it. This change will only apply to autovalues that don't have extensions: in which case the constructor is private and we can be assured that updating it shouldn't be problematic for current cases. RELNOTES=Support for AutoValues with +255 properties. PiperOrigin-RevId: 582096324
1 parent 99d964c commit b8f26b5

File tree

5 files changed

+71
-65
lines changed

5 files changed

+71
-65
lines changed

value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,10 @@ abstract class AutoValueOrBuilderTemplateVars extends AutoValueishTemplateVars {
153153
* subclass, followed by a space if they are not empty.
154154
*/
155155
String builderClassModifiers = "";
156+
157+
/**
158+
* Set if the code should generate a constructor that takes a Builder as an argument instead of
159+
* the usual per-field constructor.
160+
*/
161+
Boolean shouldGenerateBuilderConstructor = false;
156162
}

value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import javax.annotation.processing.ProcessingEnvironment;
4646
import javax.annotation.processing.Processor;
4747
import javax.annotation.processing.SupportedAnnotationTypes;
48+
import javax.lang.model.SourceVersion;
4849
import javax.lang.model.element.AnnotationMirror;
4950
import javax.lang.model.element.ExecutableElement;
5051
import javax.lang.model.element.TypeElement;
@@ -289,6 +290,10 @@ void processType(TypeElement type) {
289290
vars.subclass = TypeSimplifier.simpleNameOf(subclass);
290291
vars.finalSubclass = finalSubclass;
291292
vars.isFinal = (subclassDepth == 0);
293+
vars.shouldGenerateBuilderConstructor =
294+
applicableExtensions.isEmpty()
295+
&& builder.isPresent()
296+
&& processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) > 0;
292297
vars.modifiers = vars.isFinal ? "final " : "abstract ";
293298
vars.builderClassModifiers =
294299
consumedBuilderMethods.isEmpty()

value/src/main/java/com/google/auto/value/processor/autovalue.vm

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,34 +59,42 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes {
5959

6060
## Constructor
6161

62+
#if ($shouldGenerateBuilderConstructor)
63+
@SuppressWarnings("nullness") // Null checks happen during "build" method.
64+
#end
6265
#if ($isFinal && $builderTypeName != "")
6366
private ##
6467
#end
68+
#if ($shouldGenerateBuilderConstructor)
69+
$subclass(${builderName}${actualTypes} builder) {
70+
#foreach ($p in $props)
71+
this.$p = builder.$p;
72+
#end
73+
}
74+
#else
6575
$subclass(
66-
#foreach ($p in $props)
67-
68-
${p.nullableAnnotation}$p.type $p #if ($foreach.hasNext) , #end
69-
#end ) {
70-
#foreach ($p in $props)
71-
#if (!$p.kind.primitive && !$p.nullable && ($builderTypeName == "" || !$isFinal))
72-
## We don't need a null check if the type is primitive or @Nullable. We also don't need it
73-
## if there is a builder, since the build() method will check for us. However, if there is a
74-
## builder but there are also extensions (!$isFinal) then we can't omit the null check because
75-
## the constructor is called from the extension code.
76+
#foreach ($p in $props)
77+
${p.nullableAnnotation}$p.type $p #if ($foreach.hasNext) , #end
78+
#end) {
79+
#foreach ($p in $props)
80+
#if (!$p.kind.primitive && !$p.nullable && ($builderTypeName == "" || !$isFinal))
81+
## We don't need a null check if the type is primitive or @Nullable. We also don't need it
82+
## if there is a builder, since the build() method will check for us. However, if there is a
83+
## builder but there are also extensions (!$isFinal) then we can't omit the null check because
84+
## the constructor is called from the extension code.
7685

77-
#if ($identifiers)
86+
#if ($identifiers)
7887
if ($p == null) {
7988
throw new NullPointerException("Null $p.name");
8089
}
81-
#else
90+
#else
8291
`java.util.Objects`.requireNonNull($p);
92+
#end
8393
#end
84-
85-
#end
86-
8794
this.$p = $p;
88-
#end
95+
#end
8996
}
97+
#end
9098

9199
## Property getters
92100

value/src/main/java/com/google/auto/value/processor/builder.vm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,15 @@ ${builderClassModifiers}class ${builderName}${builderFormalTypes} ##
274274

275275
#end
276276

277-
#if ($builtType != "void") return #end ${build}(
278-
#foreach ($p in $props)
279-
280-
this.$p #if ($foreach.hasNext) , #end
281-
#end
282-
$builderRequiredProperties.defaultedBitmaskParameters );
277+
#if ($builtType != "void") return #end
278+
#if ($shouldGenerateBuilderConstructor)
279+
${build}(this);
280+
#else
281+
${build}(
282+
#foreach ($p in $props)
283+
this.$p #if ($foreach.hasNext) , #end
284+
#end
285+
$builderRequiredProperties.defaultedBitmaskParameters );
286+
#end
283287
}
284288
}

value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public class AutoValueCompilationTest {
6161
private boolean typeAnnotationsWork =
6262
Double.parseDouble(JAVA_SPECIFICATION_VERSION.value()) >= 9.0;
6363

64+
// Sadly we can't rely on JDK 8 to handle accessing the private methods of the builder.
65+
// So skip the test unless we are on at least JDK 9.
66+
private boolean constructorUsesBuilderFields =
67+
Double.parseDouble(JAVA_SPECIFICATION_VERSION.value()) >= 9.0;
68+
6469
@Test
6570
public void simpleSuccess() {
6671
// Positive test case that ensures we generate the expected code for at least one case.
@@ -1066,6 +1071,7 @@ public void nullablePrimitive() {
10661071

10671072
@Test
10681073
public void correctBuilder() {
1074+
assume().that(constructorUsesBuilderFields).isTrue();
10691075
JavaFileObject javaFileObject =
10701076
JavaFileObjects.forSourceLines(
10711077
"foo.bar.Baz",
@@ -1170,21 +1176,15 @@ public void correctBuilder() {
11701176
" private final Optional<String> anOptionalString;",
11711177
" private final NestedAutoValue<T> aNestedAutoValue;",
11721178
"",
1173-
" private AutoValue_Baz(",
1174-
" int anInt,",
1175-
" byte[] aByteArray,",
1176-
" @Nullable int[] aNullableIntArray,",
1177-
" List<T> aList,",
1178-
" ImmutableMap<T, String> anImmutableMap,",
1179-
" Optional<String> anOptionalString,",
1180-
" NestedAutoValue<T> aNestedAutoValue) {",
1181-
" this.anInt = anInt;",
1182-
" this.aByteArray = aByteArray;",
1183-
" this.aNullableIntArray = aNullableIntArray;",
1184-
" this.aList = aList;",
1185-
" this.anImmutableMap = anImmutableMap;",
1186-
" this.anOptionalString = anOptionalString;",
1187-
" this.aNestedAutoValue = aNestedAutoValue;",
1179+
" @SuppressWarnings(\"nullness\") // Null checks happen during \"build\" method.",
1180+
" private AutoValue_Baz(Builder<T> builder) {",
1181+
" this.anInt = builder.anInt;",
1182+
" this.aByteArray = builder.aByteArray;",
1183+
" this.aNullableIntArray = builder.aNullableIntArray;",
1184+
" this.aList = builder.aList;",
1185+
" this.anImmutableMap = builder.anImmutableMap;",
1186+
" this.anOptionalString = builder.anOptionalString;",
1187+
" this.aNestedAutoValue = builder.aNestedAutoValue;",
11881188
" }",
11891189
"",
11901190
" @Override public int anInt() {",
@@ -1439,14 +1439,7 @@ public void correctBuilder() {
14391439
" }",
14401440
" throw new IllegalStateException(\"Missing required properties:\" + missing);",
14411441
" }",
1442-
" return new AutoValue_Baz<T>(",
1443-
" this.anInt,",
1444-
" this.aByteArray,",
1445-
" this.aNullableIntArray,",
1446-
" this.aList,",
1447-
" this.anImmutableMap,",
1448-
" this.anOptionalString,",
1449-
" this.aNestedAutoValue);",
1442+
" return new AutoValue_Baz<T>(this);",
14501443
" }",
14511444
" }",
14521445
"}");
@@ -1465,6 +1458,7 @@ public void correctBuilder() {
14651458
@Test
14661459
public void builderWithNullableTypeAnnotation() {
14671460
assume().that(typeAnnotationsWork).isTrue();
1461+
assume().that(constructorUsesBuilderFields).isTrue();
14681462
JavaFileObject javaFileObject =
14691463
JavaFileObjects.forSourceLines(
14701464
"foo.bar.Baz",
@@ -1531,19 +1525,14 @@ public void builderWithNullableTypeAnnotation() {
15311525
" private final ImmutableMap<T, String> anImmutableMap;",
15321526
" private final Optional<String> anOptionalString;",
15331527
"",
1534-
" private AutoValue_Baz(",
1535-
" int anInt,",
1536-
" byte[] aByteArray,",
1537-
" int @Nullable [] aNullableIntArray,",
1538-
" List<T> aList,",
1539-
" ImmutableMap<T, String> anImmutableMap,",
1540-
" Optional<String> anOptionalString) {",
1541-
" this.anInt = anInt;",
1542-
" this.aByteArray = aByteArray;",
1543-
" this.aNullableIntArray = aNullableIntArray;",
1544-
" this.aList = aList;",
1545-
" this.anImmutableMap = anImmutableMap;",
1546-
" this.anOptionalString = anOptionalString;",
1528+
" @SuppressWarnings(\"nullness\") // Null checks happen during \"build\" method.",
1529+
" private AutoValue_Baz(Builder<T> builder) {",
1530+
" this.anInt = builder.anInt;",
1531+
" this.aByteArray = builder.aByteArray;",
1532+
" this.aNullableIntArray = builder.aNullableIntArray;",
1533+
" this.aList = builder.aList;",
1534+
" this.anImmutableMap = builder.anImmutableMap;",
1535+
" this.anOptionalString = builder.anOptionalString;",
15471536
" }",
15481537
"",
15491538
" @Override public int anInt() {",
@@ -1733,13 +1722,7 @@ public void builderWithNullableTypeAnnotation() {
17331722
" }",
17341723
" throw new IllegalStateException(\"Missing required properties:\" + missing);",
17351724
" }",
1736-
" return new AutoValue_Baz<T>(",
1737-
" this.anInt,",
1738-
" this.aByteArray,",
1739-
" this.aNullableIntArray,",
1740-
" this.aList,",
1741-
" this.anImmutableMap,",
1742-
" this.anOptionalString);",
1725+
" return new AutoValue_Baz<T>(this);",
17431726
" }",
17441727
" }",
17451728
"}");

0 commit comments

Comments
 (0)