AutoValue and Immutable Collection Conversion: NestDigest and its Dependent Classes. #java11 #desugar #refactor - NestDigest and its Dependent Classes have been settled down to have clear boundary between reading and writing. - Followup of https://github.com/bazelbuild/bazel/commit/5dbaff6b836281bd22c8c1c92f9b95a52173c05f PiperOrigin-RevId: 295281029
diff --git a/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecordTest.java b/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecordTest.java index 85b4553..c0dd084 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecordTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecordTest.java
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord.ClassMemberRecordBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -25,9 +26,9 @@ /** Tests for {@link ClassMemberRecord}. */ @RunWith(JUnit4.class) -public class ClassMemberRecordTest { +public final class ClassMemberRecordTest { - private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create(); + private final ClassMemberRecordBuilder classMemberRecord = ClassMemberRecord.builder(); @Test public void trackFieldUse() { @@ -38,7 +39,7 @@ classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_GETFIELD); classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_PUTFIELD); - assertThat(classMemberRecord.findAllMemberUseKind(classMemberKey)) + assertThat(classMemberRecord.build().findAllMemberUseKind(classMemberKey)) .containsExactly( MemberUseKind.GETFIELD, MemberUseKind.PUTFIELD, @@ -53,7 +54,7 @@ classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKESPECIAL); classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_NEWINVOKESPECIAL); - assertThat(classMemberRecord.findAllMemberUseKind(classMemberKey)) + assertThat(classMemberRecord.build().findAllMemberUseKind(classMemberKey)) .containsExactly(MemberUseKind.INVOKESPECIAL, MemberUseKind.H_NEWINVOKESPECIAL); } @@ -72,7 +73,7 @@ classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_NEWINVOKESPECIAL); classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_INVOKEINTERFACE); - assertThat(classMemberRecord.findAllMemberUseKind(classMemberKey)) + assertThat(classMemberRecord.build().findAllMemberUseKind(classMemberKey)) .containsExactly( MemberUseKind.INVOKEVIRTUAL, MemberUseKind.INVOKESPECIAL, @@ -93,9 +94,10 @@ classMemberRecord.logMemberDecl( classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.findOwnerAccessCode(classMemberKey)) + ClassMemberRecord readOnlyClassMemberRecord = classMemberRecord.build(); + assertThat(readOnlyClassMemberRecord.findOwnerAccessCode(classMemberKey)) .isEqualTo(Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER); - assertThat(classMemberRecord.findMemberAccessCode(classMemberKey)) + assertThat(readOnlyClassMemberRecord.findMemberAccessCode(classMemberKey)) .isEqualTo(Opcodes.ACC_PRIVATE); } @@ -107,15 +109,16 @@ classMemberKey, Opcodes.ACC_DEPRECATED | Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_DEPRECATED | Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.findOwnerAccessCode(classMemberKey)) + ClassMemberRecord rawClassMemberRecord = classMemberRecord.build(); + assertThat(rawClassMemberRecord.findOwnerAccessCode(classMemberKey)) .isEqualTo(Opcodes.ACC_DEPRECATED | Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER); - assertThat(classMemberRecord.findMemberAccessCode(classMemberKey)) + assertThat(rawClassMemberRecord.findMemberAccessCode(classMemberKey)) .isEqualTo(Opcodes.ACC_DEPRECATED | Opcodes.ACC_PRIVATE); } @Test public void mergeRecord_trackingReasons() { - ClassMemberRecord otherClassMemberRecord = ClassMemberRecord.create(); + ClassMemberRecordBuilder otherClassMemberRecord = ClassMemberRecord.builder(); MethodKey method1 = MethodKey.create(ClassName.create("package/path/OwnerClass"), "method1", "(II)I"); @@ -131,23 +134,24 @@ otherClassMemberRecord.logMemberUse(method2, Opcodes.INVOKESPECIAL); otherClassMemberRecord.logMemberUse(method3, Opcodes.INVOKEVIRTUAL); - classMemberRecord.mergeFrom(otherClassMemberRecord); + ClassMemberRecord mergedRecord = + classMemberRecord.mergeFrom(otherClassMemberRecord.build()).build(); - assertThat(classMemberRecord.hasDeclReason(method1)).isTrue(); - assertThat(classMemberRecord.findOwnerAccessCode(method1)).isEqualTo(Opcodes.ACC_SUPER); - assertThat(classMemberRecord.findMemberAccessCode(method1)).isEqualTo(Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.findAllMemberUseKind(method1)).isEmpty(); + assertThat(mergedRecord.hasDeclReason(method1)).isTrue(); + assertThat(mergedRecord.findOwnerAccessCode(method1)).isEqualTo(Opcodes.ACC_SUPER); + assertThat(mergedRecord.findMemberAccessCode(method1)).isEqualTo(Opcodes.ACC_PRIVATE); + assertThat(mergedRecord.findAllMemberUseKind(method1)).isEmpty(); - assertThat(classMemberRecord.hasDeclReason(method2)).isTrue(); - assertThat(classMemberRecord.findOwnerAccessCode(method2)).isEqualTo(Opcodes.ACC_SUPER); - assertThat(classMemberRecord.findMemberAccessCode(method2)).isEqualTo(Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.findAllMemberUseKind(method2)) + assertThat(mergedRecord.hasDeclReason(method2)).isTrue(); + assertThat(mergedRecord.findOwnerAccessCode(method2)).isEqualTo(Opcodes.ACC_SUPER); + assertThat(mergedRecord.findMemberAccessCode(method2)).isEqualTo(Opcodes.ACC_PRIVATE); + assertThat(mergedRecord.findAllMemberUseKind(method2)) .containsExactly(MemberUseKind.INVOKEVIRTUAL, MemberUseKind.INVOKESPECIAL); - assertThat(classMemberRecord.hasDeclReason(method3)).isFalse(); - assertThat(classMemberRecord.findOwnerAccessCode(method3)).isEqualTo(0); - assertThat(classMemberRecord.findMemberAccessCode(method3)).isEqualTo(0); - assertThat(classMemberRecord.findAllMemberUseKind(method3)) + assertThat(mergedRecord.hasDeclReason(method3)).isFalse(); + assertThat(mergedRecord.findOwnerAccessCode(method3)).isEqualTo(0); + assertThat(mergedRecord.findMemberAccessCode(method3)).isEqualTo(0); + assertThat(mergedRecord.findAllMemberUseKind(method3)) .containsExactly(MemberUseKind.INVOKEVIRTUAL); } @@ -157,10 +161,12 @@ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I"); classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKEVIRTUAL); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); + ClassMemberRecord rawClassMemberRecord = classMemberRecord.build(); + assertThat(rawClassMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); - classMemberRecord.filterUsedMemberWithTrackedDeclaration(); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isFalse(); + ClassMemberRecord filteredRecord = + rawClassMemberRecord.filterUsedMemberWithTrackedDeclaration(); + assertThat(filteredRecord.hasTrackingReason(classMemberKey)).isFalse(); } @Test @@ -170,10 +176,12 @@ classMemberRecord.logMemberDecl( classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); + ClassMemberRecord rawClassMemberRecord = classMemberRecord.build(); + assertThat(rawClassMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); - classMemberRecord.filterUsedMemberWithTrackedDeclaration(); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isFalse(); + ClassMemberRecord filteredRecord = + rawClassMemberRecord.filterUsedMemberWithTrackedDeclaration(); + assertThat(filteredRecord.hasTrackingReason(classMemberKey)).isFalse(); } @Test @@ -183,9 +191,11 @@ classMemberRecord.logMemberDecl( classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, Opcodes.ACC_PRIVATE); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); + ClassMemberRecord rawClassMemberRecord = classMemberRecord.build(); + assertThat(rawClassMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); - classMemberRecord.filterUsedMemberWithTrackedDeclaration(); - assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isTrue(); + ClassMemberRecord filteredRecord = + rawClassMemberRecord.filterUsedMemberWithTrackedDeclaration(); + assertThat(filteredRecord.hasTrackingReason(classMemberKey)).isTrue(); } }
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java index 24c3343..21c4f33 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestAnalyzerTest.java
@@ -19,8 +19,6 @@ import com.google.common.collect.ImmutableList; import com.google.devtools.build.android.desugar.io.FileContentProvider; -import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; -import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; import com.google.devtools.build.android.desugar.testing.junit.DesugarRule; import com.google.devtools.build.android.desugar.testing.junit.DesugarRunner; import com.google.devtools.build.android.desugar.testing.junit.JarEntryRecord; @@ -51,15 +49,10 @@ .enableIterativeTransformation(0) .build(); - private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create(); - private final ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create(); - private final NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord); - @Test public void emptyInputFiles() throws IOException { - NestAnalyzer nestAnalyzer = - new NestAnalyzer(ImmutableList.of(), nestDigest, classMemberRecord, classAttributeRecord); - nestAnalyzer.analyze(); + NestDigest nestDigest = NestAnalyzer.analyzeNests(ImmutableList.of()); + assertThat(nestDigest.getAllCompanionClassNames()).isEmpty(); } @@ -72,19 +65,15 @@ throws IOException { JarFile jarFile = analyzedTarget.jarFile(); - NestAnalyzer nestAnalyzer = - new NestAnalyzer( + + NestDigest nestDigest = + NestAnalyzer.analyzeNests( jarFile.stream() .map( entry -> new FileContentProvider<>( entry.getName(), () -> getJarEntryInputStream(jarFile, entry))) - .collect(toImmutableList()), - nestDigest, - classMemberRecord, - classAttributeRecord); - - nestAnalyzer.analyze(); + .collect(toImmutableList())); assertThat(nestDigest.getAllCompanionClassNames()) .containsExactly(
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java index d789b3b..0f95132 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java
@@ -17,8 +17,10 @@ import static com.google.common.truth.Truth.assertThat; import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord.ClassAttributeRecordBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassAttributes; import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord.ClassMemberRecordBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassName; import com.google.devtools.build.android.desugar.langmodel.MethodKey; import org.junit.Test; @@ -28,11 +30,10 @@ /** The tests for {@link NestDigest}. */ @RunWith(JUnit4.class) -public class NestDigestTest { +public final class NestDigestTest { - private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create(); - private final ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create(); - private final NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord); + private final ClassMemberRecordBuilder classMemberRecord = ClassMemberRecord.builder(); + private final ClassAttributeRecordBuilder classAttributeRecord = ClassAttributeRecord.builder(); @Test public void prepareCompanionClassWriters_noCompanionClassesGenerated() { @@ -41,7 +42,11 @@ /* ownerAccess= */ Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, /* memberDeclAccess= */ Opcodes.ACC_PRIVATE); - nestDigest.prepareCompanionClasses(); + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); assertThat(nestDigest.getAllCompanionClassNames()).isEmpty(); } @@ -53,18 +58,22 @@ classMemberRecord.logMemberDecl( constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClass$NestedClass")) .setNestHost(ClassName.create("package/path/OwnerClass")) .build()); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClass")) .addNestMember(ClassName.create("package/path/OwnerClass$NestedClass")) .build()); - nestDigest.prepareCompanionClasses(); + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); assertThat(nestDigest.getAllCompanionClassNames()) .containsExactly("package/path/OwnerClass$NestCC"); @@ -95,29 +104,33 @@ MethodKey.create(ClassName.create("package/path/OwnerClassB"), "<init>", "()V"), Opcodes.INVOKESPECIAL); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClassA$NestedClass")) .setNestHost(ClassName.create("package/path/OwnerClassA")) .build()); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClassB$NestedClass")) .setNestHost(ClassName.create("package/path/OwnerClassB")) .build()); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClassA")) .addNestMember(ClassName.create("package/path/OwnerClassA$NestedClass")) .build()); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create("package/path/OwnerClassB")) .addNestMember(ClassName.create("package/path/OwnerClassB$NestedClass")) .build()); - nestDigest.prepareCompanionClasses(); + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); assertThat(nestDigest.getAllCompanionClassNames()) .containsExactly("package/path/OwnerClassA$NestCC", "package/path/OwnerClassB$NestCC"); @@ -132,18 +145,22 @@ constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE); classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create(constructor.ownerName() + "$NestClass")) .setNestHost(ClassName.create(constructor.ownerName())) .build()); - classAttributeRecord.setClassAttributes( + classAttributeRecord.addClassAttributes( ClassAttributes.builder() .setClassBinaryName(ClassName.create(constructor.ownerName())) .addNestMember(ClassName.create(constructor.ownerName() + "$NestClass")) .build()); - nestDigest.prepareCompanionClasses(); + NestDigest nestDigest = + NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); assertThat(nestDigest.getAllCompanionClassNames()) .containsExactly("package/path/$Owner$Class$$NestCC");
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java index 625eb1c..9eff3bc 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -40,8 +40,6 @@ import com.google.devtools.build.android.desugar.io.InputFileProvider; import com.google.devtools.build.android.desugar.io.OutputFileProvider; import com.google.devtools.build.android.desugar.io.ThrowingClassLoader; -import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; -import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; import com.google.devtools.build.android.desugar.langmodel.ClassMemberUseCounter; import com.google.devtools.build.android.desugar.nest.NestAnalyzer; import com.google.devtools.build.android.desugar.nest.NestDesugaring; @@ -655,12 +653,10 @@ ClassVsInterface interfaceCache, ImmutableSet.Builder<String> interfaceLambdaMethodCollector) throws IOException { - ClassMemberRecord classMemberRecord = ClassMemberRecord.create(); - ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create(); + ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents = inputFiles.toInputFileStreams(); - NestDigest nestDigest = - NestAnalyzer.analyzeNests(inputFileContents, classMemberRecord, classAttributeRecord); + NestDigest nestDigest = NestAnalyzer.analyzeNests(inputFileContents); // Apply core library type name remapping to the digest instance produced by the nest analyzer, // since the analysis-oriented nest analyzer visits core library classes without name remapping // as those transformation-oriented visitors. @@ -1142,6 +1138,7 @@ return 0; } + @SuppressWarnings("CatchAndPrintStackTrace") static void verifyLambdaDumpDirectoryRegistered(Path dumpDirectory) throws IOException { try { Class<?> klass = Class.forName("java.lang.invoke.InnerClassLambdaMetafactory"); @@ -1161,7 +1158,7 @@ } catch (ReflectiveOperationException e) { // We do not want to crash Desugar, if we cannot load or access these classes or fields. // We aim to provide better diagnostics. If we cannot, just let it go. - e.printStackTrace(System.err); // To silence error-prone's complaint. + e.printStackTrace(System.err); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributeRecord.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributeRecord.java index 95b21e0..b54b2fe 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributeRecord.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributeRecord.java
@@ -18,58 +18,59 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; /** Tracks {@link ClassAttributes} for all classes under investigation. */ -public final class ClassAttributeRecord implements TypeMappable<ClassAttributeRecord> { +@AutoValue +public abstract class ClassAttributeRecord implements TypeMappable<ClassAttributeRecord> { - private final Map<ClassName, ClassAttributes> record; + public abstract ImmutableMap<ClassName, ClassAttributes> record(); - public static ClassAttributeRecord create() { - return new ClassAttributeRecord(new HashMap<>()); + public static ClassAttributeRecordBuilder builder() { + return new AutoValue_ClassAttributeRecord.Builder(); } - private ClassAttributeRecord(Map<ClassName, ClassAttributes> record) { - this.record = record; - } - - public ClassAttributes setClassAttributes(ClassAttributes classAttributes) { - ClassName classBinaryName = classAttributes.classBinaryName(); - if (record.containsKey(classBinaryName)) { - throw new IllegalStateException( - String.format( - "Expected the ClassAttributes of a class to be put into this record only once during" - + " {@link ClassVisitor#visitEnd}: Pre-existing: (%s), Now (%s)", - record.get(classBinaryName), classAttributes)); - } - return record.put(classBinaryName, classAttributes); - } - - public Optional<ClassName> getNestHost(ClassName className) { - ClassAttributes classAttributes = record.get(className); + public final Optional<ClassName> getNestHost(ClassName className) { + ClassAttributes classAttributes = record().get(className); checkNotNull( classAttributes, "Expected recorded ClassAttributes for (%s). Available record: %s", className, - record.keySet()); + record().keySet()); return classAttributes.nestHost(); } - public ImmutableSet<ClassName> getNestMembers(ClassName className) { - ClassAttributes classAttributes = record.get(className); + public final ImmutableSet<ClassName> getNestMembers(ClassName className) { + ClassAttributes classAttributes = record().get(className); checkNotNull( classAttributes, "Expected recorded ClassAttributes for (%s). Available record: %s", className, - record.keySet()); + record().keySet()); return classAttributes.nestMembers(); } @Override - public ClassAttributeRecord acceptTypeMapper(TypeMapper typeMapper) { - return new ClassAttributeRecord(typeMapper.mapMutable(record)); + public final ClassAttributeRecord acceptTypeMapper(TypeMapper typeMapper) { + return ClassAttributeRecord.builder().setRecord(typeMapper.map(record())).build(); + } + + /** The builder for {@link ClassAttributeRecord}. */ + @AutoValue.Builder + public abstract static class ClassAttributeRecordBuilder { + + abstract ImmutableMap.Builder<ClassName, ClassAttributes> recordBuilder(); + + abstract ClassAttributeRecordBuilder setRecord(ImmutableMap<ClassName, ClassAttributes> record); + + public final ClassAttributeRecordBuilder addClassAttributes(ClassAttributes classAttributes) { + recordBuilder().put(classAttributes.classBinaryName(), classAttributes); + return this; + } + + public abstract ClassAttributeRecord build(); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java index 28786ae..0608a91 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberRecord.java
@@ -17,8 +17,13 @@ package com.google.devtools.build.android.desugar.langmodel; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableMap.toImmutableMap; +import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberTrackReason.ClassMemberTrackReasonBuilder; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Predicate; @@ -27,105 +32,123 @@ * A record that tracks the declarations and usages of a class member, including fields, * constructors and methods. */ -public final class ClassMemberRecord implements TypeMappable<ClassMemberRecord> { +@AutoValue +public abstract class ClassMemberRecord implements TypeMappable<ClassMemberRecord> { /** Tracks a class member with a reason. */ - private final Map<ClassMemberKey<?>, ClassMemberTrackReason> reasons; + abstract ImmutableMap<ClassMemberKey<?>, ClassMemberTrackReason> reasons(); - /** The factory method of this class. */ - public static ClassMemberRecord create() { - return new ClassMemberRecord(new LinkedHashMap<>()); + /** Creates a builder instance for this class. */ + public static ClassMemberRecordBuilder builder() { + return new AutoValue_ClassMemberRecord.Builder(); } /** Gets class members with both tracked declarations and tracked usage. */ - public boolean filterUsedMemberWithTrackedDeclaration() { - return reasons - .values() - .removeIf( - reason -> { - // keep interface members and class members with use references. - return !(reason.hasInterfaceDeclReason() - || (reason.hasDeclReason() && reason.hasMemberUseReason())); - }); - } - - private ClassMemberRecord(Map<ClassMemberKey<?>, ClassMemberTrackReason> reasons) { - this.reasons = reasons; + public final ClassMemberRecord filterUsedMemberWithTrackedDeclaration() { + return builder() + .setReasons( + reasons().entrySet().stream() + .filter( + entry -> { + ClassMemberTrackReason reason = entry.getValue(); + // keep interface members and class members with use references. + return reason.hasInterfaceDeclReason() + || (reason.hasDeclReason() && reason.hasMemberUseReason()); + }) + .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))) + .autoInternalBuild(); } /** Find all member keys that represent a constructor. */ - public ImmutableList<ClassMemberKey<?>> findAllConstructorMemberKeys() { + public final ImmutableList<ClassMemberKey<?>> findAllConstructorMemberKeys() { return findAllMatchedMemberKeys(ClassMemberKey::isConstructor); } /** Find all member keys based on the given member key predicate. */ - ImmutableList<ClassMemberKey<?>> findAllMatchedMemberKeys( + private ImmutableList<ClassMemberKey<?>> findAllMatchedMemberKeys( Predicate<ClassMemberKey<?>> predicate) { - return reasons.keySet().stream().filter(predicate).collect(toImmutableList()); + return reasons().keySet().stream().filter(predicate).collect(toImmutableList()); } - /** Whether this record has any reason to desugar a nest. */ - public boolean hasAnyReason() { - return !reasons.isEmpty(); + public final boolean hasTrackingReason(ClassMemberKey<?> classMemberKey) { + return reasons().containsKey(classMemberKey); } - public boolean hasTrackingReason(ClassMemberKey<?> classMemberKey) { - return reasons.containsKey(classMemberKey); - } - - boolean hasDeclReason(ClassMemberKey<?> classMemberKey) { - return hasTrackingReason(classMemberKey) && reasons.get(classMemberKey).hasDeclReason(); + final boolean hasDeclReason(ClassMemberKey<?> classMemberKey) { + return hasTrackingReason(classMemberKey) && reasons().get(classMemberKey).hasDeclReason(); } /** Find the original access code for the owner of the class member. */ - int findOwnerAccessCode(ClassMemberKey<?> memberKey) { - if (reasons.containsKey(memberKey)) { - return reasons.get(memberKey).getOwnerAccess(); + final int findOwnerAccessCode(ClassMemberKey<?> memberKey) { + if (reasons().containsKey(memberKey)) { + return reasons().get(memberKey).ownerAccess(); } throw new IllegalStateException(String.format("ClassMemberKey Not Found: %s", memberKey)); } /** Find the original access code for the class member declaration. */ - int findMemberAccessCode(ClassMemberKey<?> memberKey) { - if (reasons.containsKey(memberKey)) { - return reasons.get(memberKey).getMemberAccess(); + final int findMemberAccessCode(ClassMemberKey<?> memberKey) { + if (reasons().containsKey(memberKey)) { + return reasons().get(memberKey).memberAccess(); } throw new IllegalStateException(String.format("ClassMemberKey Not Found: %s", memberKey)); } /** Find all invocation codes of a class member. */ - public ImmutableList<MemberUseKind> findAllMemberUseKind(ClassMemberKey<?> memberKey) { - if (reasons.containsKey(memberKey)) { - return ImmutableList.copyOf(reasons.get(memberKey).getUseAccesses()); - } - return ImmutableList.of(); - } - - /** Logs the declaration of a class member. */ - public ClassMemberTrackReason logMemberDecl( - ClassMemberKey<?> memberKey, int ownerAccess, int memberDeclAccess) { - return reasons - .computeIfAbsent(memberKey, classMemberKey -> new ClassMemberTrackReason()) - .setDeclAccess(ownerAccess, memberDeclAccess); - } - - /** Logs the use of a class member, including field access and method invocations. */ - public ClassMemberTrackReason logMemberUse(ClassMemberKey<?> memberKey, int invokeOpcode) { - return reasons - .computeIfAbsent(memberKey, classMemberKey -> new ClassMemberTrackReason()) - .addUseAccess(invokeOpcode); - } - - /** Merge an another member record into this record. */ - public void mergeFrom(ClassMemberRecord otherClassMemberRecord) { - otherClassMemberRecord.reasons.forEach( - (classMemberKey, classMemberTrackReason) -> - reasons.merge( - classMemberKey, classMemberTrackReason, ClassMemberTrackReason::mergeFrom)); + public final ImmutableList<MemberUseKind> findAllMemberUseKind(ClassMemberKey<?> memberKey) { + return reasons().containsKey(memberKey) + ? reasons().get(memberKey).useAccesses().asList() + : ImmutableList.of(); } @Override public ClassMemberRecord acceptTypeMapper(TypeMapper typeMapper) { - return new ClassMemberRecord(typeMapper.mapKey(reasons)); + return builder().setReasons(typeMapper.mapKey(reasons())).autoInternalBuild(); + } + + /** The builder for {@link ClassMemberRecord}. */ + @AutoValue.Builder + public abstract static class ClassMemberRecordBuilder { + + private final Map<ClassMemberKey<?>, ClassMemberTrackReasonBuilder> reasonsCollector = + new LinkedHashMap<>(); + + abstract ClassMemberRecordBuilder setReasons( + Map<ClassMemberKey<?>, ClassMemberTrackReason> value); + + /** Logs the declaration of a class member. */ + public final ClassMemberTrackReasonBuilder logMemberDecl( + ClassMemberKey<?> memberKey, int ownerAccess, int memberDeclAccess) { + return getTrackReason(memberKey).setDeclAccess(ownerAccess, memberDeclAccess); + } + + /** Logs the use of a class member, including field access and method invocations. */ + public final ClassMemberTrackReasonBuilder logMemberUse( + ClassMemberKey<?> memberKey, int invokeOpcode) { + return getTrackReason(memberKey).addUseAccess(invokeOpcode); + } + + /** Merges an another member record into this record builder. */ + public final ClassMemberRecordBuilder mergeFrom(ClassMemberRecord otherClassMemberRecord) { + otherClassMemberRecord + .reasons() + .forEach( + (otherMemberKey, otherMemberTrackReason) -> + getTrackReason(otherMemberKey).mergeFrom(otherMemberTrackReason)); + return this; + } + + private ClassMemberTrackReasonBuilder getTrackReason(ClassMemberKey<?> memberKey) { + return reasonsCollector.computeIfAbsent( + memberKey, classMemberKey -> ClassMemberTrackReason.builder()); + } + + public final ClassMemberRecord build() { + return setReasons( + Maps.transformValues(reasonsCollector, ClassMemberTrackReasonBuilder::build)) + .autoInternalBuild(); + } + + abstract ClassMemberRecord autoInternalBuild(); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberTrackReason.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberTrackReason.java index 223682f..8c06f32 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberTrackReason.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberTrackReason.java
@@ -16,98 +16,79 @@ package com.google.devtools.build.android.desugar.langmodel; -import com.google.common.hash.Hashing; -import java.util.EnumSet; +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; import org.objectweb.asm.Opcodes; /** * Used to track the declaration and invocation information of a class member, including fields, * constructors and methods. */ -public final class ClassMemberTrackReason { +@AutoValue +abstract class ClassMemberTrackReason { - private boolean hasDeclReason; - private int ownerAccess; - private int memberAccess; - private final EnumSet<MemberUseKind> useAccesses = EnumSet.noneOf(MemberUseKind.class); + abstract boolean hasDeclReason(); - ClassMemberTrackReason setDeclAccess(int ownerAccess, int memberAccess) { - this.ownerAccess = ownerAccess; - this.memberAccess = memberAccess; - this.hasDeclReason = true; - return this; + abstract int ownerAccess(); + + abstract int memberAccess(); + + abstract ImmutableSet<MemberUseKind> useAccesses(); + + public static ClassMemberTrackReasonBuilder builder() { + return new AutoValue_ClassMemberTrackReason.Builder() + .setHasDeclReason(false) + .setOwnerAccess(0) + .setMemberAccess(0); } - ClassMemberTrackReason addUseAccess(int invokeOpcode) { - this.useAccesses.add(MemberUseKind.fromValue(invokeOpcode)); - return this; + abstract ClassMemberTrackReasonBuilder toBuilder(); + + final boolean hasInterfaceDeclReason() { + return hasDeclReason() && (ownerAccess() & Opcodes.ACC_INTERFACE) != 0; } - boolean hasDeclReason() { - return hasDeclReason; + final boolean hasMemberUseReason() { + return !useAccesses().isEmpty(); } - boolean hasInterfaceDeclReason() { - return hasDeclReason && (ownerAccess & Opcodes.ACC_INTERFACE) != 0; - } + /** The builder for {@link ClassMemberTrackReason}. */ + @AutoValue.Builder + abstract static class ClassMemberTrackReasonBuilder { - boolean hasMemberUseReason() { - return !useAccesses.isEmpty(); - } + abstract ClassMemberTrackReasonBuilder setHasDeclReason(boolean value); - int getOwnerAccess() { - return ownerAccess; - } + abstract ClassMemberTrackReasonBuilder setOwnerAccess(int value); - int getMemberAccess() { - return memberAccess; - } + abstract ClassMemberTrackReasonBuilder setMemberAccess(int value); - EnumSet<MemberUseKind> getUseAccesses() { - return useAccesses; - } + abstract ClassMemberTrackReasonBuilder setUseAccesses(Collection<MemberUseKind> value); - ClassMemberTrackReason mergeFrom(ClassMemberTrackReason otherClassMemberTrackReason) { - if (!hasDeclReason() && otherClassMemberTrackReason.hasDeclReason()) { - ownerAccess = otherClassMemberTrackReason.getOwnerAccess(); - memberAccess = otherClassMemberTrackReason.getMemberAccess(); - hasDeclReason = true; + abstract ImmutableSet.Builder<MemberUseKind> useAccessesBuilder(); + + final ClassMemberTrackReasonBuilder setDeclAccess(int ownerAccess, int memberAccess) { + return setHasDeclReason(true).setOwnerAccess(ownerAccess).setMemberAccess(memberAccess); } - useAccesses.addAll(otherClassMemberTrackReason.getUseAccesses()); - return this; - } - @Override - public int hashCode() { - return Hashing.sha256() - .newHasher() - .putBoolean(hasDeclReason) - .putInt(ownerAccess) - .putInt(memberAccess) - .putInt(useAccesses.hashCode()) - .hash() - .asInt(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; + final ClassMemberTrackReasonBuilder addUseAccess(int invokeOpcode) { + useAccessesBuilder().add(MemberUseKind.fromValue(invokeOpcode)); + return this; } - if (obj instanceof ClassMemberTrackReason) { - ClassMemberTrackReason other = (ClassMemberTrackReason) obj; - return this.hasDeclReason == other.hasDeclReason - && this.ownerAccess == other.ownerAccess - && this.memberAccess == other.memberAccess - && this.useAccesses.equals(other.useAccesses); - } - return false; - } - @Override - public String toString() { - return String.format( - "%s{hasDeclReason=%s, ownerAccess=%d, memberAccess=%d, useAccesses=%s}", - getClass().getSimpleName(), hasDeclReason, ownerAccess, memberAccess, useAccesses); + final ClassMemberTrackReasonBuilder addAllUseAccesses(Collection<MemberUseKind> values) { + useAccessesBuilder().addAll(values); + return this; + } + + final ClassMemberTrackReasonBuilder mergeFrom(ClassMemberTrackReason otherReason) { + if (otherReason.hasDeclReason()) { + setDeclAccess(otherReason.ownerAccess(), otherReason.memberAccess()); + } + addAllUseAccesses(otherReason.useAccesses()); + return this; + } + + abstract ClassMemberTrackReason build(); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java index 25ecee2..4ed92bd 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java
@@ -20,14 +20,11 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toMap; import com.google.common.collect.ConcurrentHashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import java.util.HashMap; -import java.util.Map; import java.util.function.Function; import org.objectweb.asm.commons.Remapper; @@ -73,32 +70,10 @@ e -> e.getKey().acceptTypeMapper(this), e -> e.getValue().acceptTypeMapper(this))); } - // TODO(b/149403079): Remove this method once the map collection used by referencing classes have - // been migrated to immutable. - public <K extends TypeMappable<? extends K>, V extends TypeMappable<V>> Map<K, V> mapMutable( - Map<K, V> mappableTypes) { - return mappableTypes.entrySet().stream() - .collect( - toMap( - e -> e.getKey().acceptTypeMapper(this), - e -> e.getValue().acceptTypeMapper(this), - (prev, next) -> next, - HashMap::new)); - } - public <K extends TypeMappable<? extends K>, V> ImmutableMap<K, V> mapKey( ImmutableMap<K, V> mappableTypes) { return mappableTypes.entrySet().stream() .collect(toImmutableMap(e -> e.getKey().acceptTypeMapper(this), e -> e.getValue())); } - public <K extends TypeMappable<? extends K>, V> Map<K, V> mapKey(Map<K, V> mappableTypes) { - return mappableTypes.entrySet().stream() - .collect( - toMap( - e -> e.getKey().acceptTypeMapper(this), - e -> e.getValue(), - (prev, next) -> next, - HashMap::new)); - } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/CrossMateMainCollector.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/CrossMateMainCollector.java index 24d7b65..937abb6 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/CrossMateMainCollector.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/CrossMateMainCollector.java
@@ -14,10 +14,11 @@ package com.google.devtools.build.android.desugar.nest; -import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord.ClassAttributeRecordBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassAttributes; import com.google.devtools.build.android.desugar.langmodel.ClassAttributes.ClassAttributesBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord.ClassMemberRecordBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassName; import com.google.devtools.build.android.desugar.langmodel.FieldKey; import com.google.devtools.build.android.desugar.langmodel.LangModelHelper; @@ -34,15 +35,15 @@ final class CrossMateMainCollector extends ClassVisitor { /** The project-wise class member records. */ - private final ClassMemberRecord memberRecord; + private final ClassMemberRecordBuilder memberRecord; - private final ClassAttributeRecord classAttributeRecord; + private final ClassAttributeRecordBuilder classAttributeRecord; /** * An class member record to stage member record candidates, merging into the project-wise member * record during the {@link #visitEnd()} where eligible conditions are specified. */ - private final ClassMemberRecord stagingMemberRecord = ClassMemberRecord.create(); + private final ClassMemberRecordBuilder stagingMemberRecord = ClassMemberRecord.builder(); private final ClassAttributesBuilder classAttributesBuilder = ClassAttributes.builder(); @@ -51,7 +52,7 @@ private boolean isInNest; public CrossMateMainCollector( - ClassMemberRecord memberRecord, ClassAttributeRecord classAttributeRecord) { + ClassMemberRecordBuilder memberRecord, ClassAttributeRecordBuilder classAttributeRecord) { super(Opcodes.ASM7); this.memberRecord = memberRecord; this.classAttributeRecord = classAttributeRecord; @@ -129,9 +130,9 @@ @Override public void visitEnd() { if (isInNest || (classAccessCode & Opcodes.ACC_INTERFACE) != 0) { - memberRecord.mergeFrom(stagingMemberRecord); + memberRecord.mergeFrom(stagingMemberRecord.build()); } - classAttributeRecord.setClassAttributes(classAttributesBuilder.build()); + classAttributeRecord.addClassAttributes(classAttributesBuilder.build()); super.visitEnd(); } @@ -150,10 +151,12 @@ * * <p>@see CrossMateMainCollector#stagingMemberRecord for more details. */ - private final ClassMemberRecord memberRecord; + private final ClassMemberRecordBuilder memberRecord; CrossMateRefCollector( - MethodVisitor methodVisitor, MethodKey enclosingMethodKey, ClassMemberRecord memberRecord) { + MethodVisitor methodVisitor, + MethodKey enclosingMethodKey, + ClassMemberRecordBuilder memberRecord) { super(Opcodes.ASM7, methodVisitor); this.enclosingMethodKey = enclosingMethodKey; this.memberRecord = memberRecord;
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestAnalyzer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestAnalyzer.java index 14a0e77..204dfea 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestAnalyzer.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestAnalyzer.java
@@ -16,11 +16,12 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.devtools.build.android.desugar.io.FileContentProvider; import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord.ClassAttributeRecordBuilder; import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord; +import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord.ClassMemberRecordBuilder; import java.io.IOException; import java.io.InputStream; import org.objectweb.asm.ClassReader; @@ -32,9 +33,8 @@ public class NestAnalyzer { private final ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents; - private final NestDigest nestDigest; - private final ClassMemberRecord classMemberRecord; - private final ClassAttributeRecord classAttributeRecord; + private final ClassMemberRecordBuilder classMemberRecord; + private final ClassAttributeRecordBuilder classAttributeRecord; /** * Perform a nest-based analysis of input classes, including tracking private member access @@ -43,32 +43,25 @@ * @return A manager class for nest companions. */ public static NestDigest analyzeNests( - ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents, - ClassMemberRecord classMemberRecord, - ClassAttributeRecord classAttributeRecord) + ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents) throws IOException { - NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord); NestAnalyzer nestAnalyzer = - new NestAnalyzer(inputFileContents, nestDigest, classMemberRecord, classAttributeRecord); - nestAnalyzer.analyze(); - return nestDigest; + new NestAnalyzer( + inputFileContents, ClassAttributeRecord.builder(), ClassMemberRecord.builder()); + return nestAnalyzer.analyze(); } - @VisibleForTesting - NestAnalyzer( + private NestAnalyzer( ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents, - NestDigest nestDigest, - ClassMemberRecord classMemberRecord, - ClassAttributeRecord classAttributeRecord) { + ClassAttributeRecordBuilder classAttributeRecord, + ClassMemberRecordBuilder classMemberRecord) { this.inputFileContents = checkNotNull(inputFileContents); - this.nestDigest = checkNotNull(nestDigest); - this.classMemberRecord = checkNotNull(classMemberRecord); + this.classMemberRecord = classMemberRecord; this.classAttributeRecord = classAttributeRecord; } /** Performs class member declaration and usage analysis of files. */ - @VisibleForTesting - void analyze() throws IOException { + private NestDigest analyze() throws IOException { for (FileContentProvider<? extends InputStream> inputClassFile : inputFileContents) { if (inputClassFile.isClassFile()) { try (InputStream inputStream = inputClassFile.get()) { @@ -79,7 +72,10 @@ } } } - classMemberRecord.filterUsedMemberWithTrackedDeclaration(); - nestDigest.prepareCompanionClasses(); + + return NestDigest.builder() + .setClassMemberRecord(classMemberRecord.build().filterUsedMemberWithTrackedDeclaration()) + .setClassAttributeRecord(classAttributeRecord.build()) + .build(); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDigest.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDigest.java index 9d983f2..d39c49e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDigest.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDigest.java
@@ -16,8 +16,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.devtools.build.android.desugar.langmodel.LangModelConstants.NEST_COMPANION_CLASS_SIMPLE_NAME; +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.ImmutableMap; import com.google.common.collect.Streams; @@ -30,7 +33,6 @@ import com.google.devtools.build.android.desugar.langmodel.TypeMappable; import com.google.devtools.build.android.desugar.langmodel.TypeMapper; import java.io.ByteArrayInputStream; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -38,60 +40,50 @@ import org.objectweb.asm.ClassWriter; /** Manages the creation and IO stream for nest-companion classes. */ -public class NestDigest implements TypeMappable<NestDigest> { +@AutoValue +public abstract class NestDigest implements TypeMappable<NestDigest> { - private final ClassMemberRecord classMemberRecord; - private final ClassAttributeRecord classAttributeRecord; - private final Map<ClassName, ClassName> nestCompanionToHostMap; + public abstract ClassMemberRecord classMemberRecord(); + + public abstract ClassAttributeRecord classAttributeRecord(); + + @Memoized + ImmutableList<ClassName> nestHostsWithCompanion() { + return classMemberRecord().findAllConstructorMemberKeys().stream() + .map( + constructor -> nestHost(constructor.owner(), classAttributeRecord(), ImmutableMap.of())) + .flatMap(Streams::stream) + .distinct() + .collect(toImmutableList()); + } + + @Memoized + ImmutableMap<ClassName, ClassName> nestCompanionToHostMap() { + return nestHostsWithCompanion().stream() + .collect( + toImmutableMap( + nestHost -> nestHost.innerClass(NEST_COMPANION_CLASS_SIMPLE_NAME), + nestHost -> nestHost)); + } /** * A map from the class binary names of nest hosts to the associated class writer of the nest's * companion. */ - private ImmutableMap<ClassName, ClassWriter> companionWriters; - - public static NestDigest create( - ClassMemberRecord classMemberRecord, ClassAttributeRecord classAttributeRecord) { - return new NestDigest( - classMemberRecord, classAttributeRecord, new HashMap<>(), /* companionWriters= */ null); + @Memoized + ImmutableMap<ClassName, ClassWriter> companionWriters() { + return nestHostsWithCompanion().stream() + .collect( + toImmutableMap( + nestHost -> nestHost, nestHost -> new ClassWriter(ClassWriter.COMPUTE_MAXS))); } - private NestDigest( - ClassMemberRecord classMemberRecord, - ClassAttributeRecord classAttributeRecord, - Map<ClassName, ClassName> nestCompanionToHostMap, - ImmutableMap<ClassName, ClassWriter> companionWriters) { - this.classMemberRecord = classMemberRecord; - this.classAttributeRecord = classAttributeRecord; - this.nestCompanionToHostMap = nestCompanionToHostMap; - this.companionWriters = companionWriters; - } - - /** - * Generates the nest companion class writers. The nest companion classes will be generated as the - * last placeholder class type for the synthetic constructor, whose originating constructor has - * any invocation in other classes in nest. - */ - void prepareCompanionClasses() { - ImmutableList<ClassName> nestHostsWithCompanion = - classMemberRecord.findAllConstructorMemberKeys().stream() - .map( - constructor -> - nestHost(constructor.owner(), classAttributeRecord, nestCompanionToHostMap)) - .flatMap(Streams::stream) - .distinct() - .collect(toImmutableList()); - ImmutableMap.Builder<ClassName, ClassWriter> companionWriterBuilders = ImmutableMap.builder(); - for (ClassName nestHost : nestHostsWithCompanion) { - ClassName nestCompanion = nestHost.innerClass(NEST_COMPANION_CLASS_SIMPLE_NAME); - nestCompanionToHostMap.put(nestCompanion, nestHost); - companionWriterBuilders.put(nestHost, new ClassWriter(ClassWriter.COMPUTE_MAXS)); - } - companionWriters = companionWriterBuilders.build(); + public static NestDigestBuilder builder() { + return new AutoValue_NestDigest.Builder(); } public boolean hasAnyTrackingReason(ClassMemberKey<?> classMemberKey) { - return classMemberRecord.hasTrackingReason(classMemberKey); + return classMemberRecord().hasTrackingReason(classMemberKey); } public boolean hasAnyUse(ClassMemberKey<?> classMemberKey, MemberUseKind useKind) { @@ -99,7 +91,7 @@ } public ImmutableList<MemberUseKind> findAllMemberUseKinds(ClassMemberKey<?> classMemberKey) { - return classMemberRecord.findAllMemberUseKind(classMemberKey); + return classMemberRecord().findAllMemberUseKind(classMemberKey); } /** @@ -110,8 +102,8 @@ */ public Optional<ClassName> nestHost(ClassName className) { // Ensures prepareCompanionClasses has been executed. - checkNotNull(companionWriters); - return nestHost(className, classAttributeRecord, nestCompanionToHostMap); + checkNotNull(companionWriters()); + return nestHost(className, classAttributeRecord(), nestCompanionToHostMap()); } /** @@ -163,7 +155,7 @@ */ @Nullable public ClassWriter getCompanionClassWriter(ClassName className) { - return nestHost(className).map(nestHost -> companionWriters.get(nestHost)).orElse(null); + return nestHost(className).map(nestHost -> companionWriters().get(nestHost)).orElse(null); } /** Gets all nest companion classes required to be generated. */ @@ -172,7 +164,7 @@ } public ImmutableList<ClassName> getAllCompanionClasses() { - return companionWriters.keySet().stream().map(this::nestCompanion).collect(toImmutableList()); + return companionWriters().keySet().stream().map(this::nestCompanion).collect(toImmutableList()); } /** Gets all nest companion files required to be generated. */ @@ -194,16 +186,26 @@ checkNotNull( companionClassWriter, "Expected companion class (%s) to be present in (%s)", - companionWriters); + companionWriters()); return new ByteArrayInputStream(companionClassWriter.toByteArray()); } @Override public NestDigest acceptTypeMapper(TypeMapper typeMapper) { - return new NestDigest( - classMemberRecord.acceptTypeMapper(typeMapper), - classAttributeRecord.acceptTypeMapper(typeMapper), - typeMapper.mapMutable(nestCompanionToHostMap), - typeMapper.mapKey(companionWriters)); + return NestDigest.builder() + .setClassMemberRecord(classMemberRecord().acceptTypeMapper(typeMapper)) + .setClassAttributeRecord(classAttributeRecord().acceptTypeMapper(typeMapper)) + .build(); + } + + /** The builder class for {@link NestDigest}. */ + @AutoValue.Builder + public abstract static class NestDigestBuilder { + + public abstract NestDigestBuilder setClassMemberRecord(ClassMemberRecord value); + + public abstract NestDigestBuilder setClassAttributeRecord(ClassAttributeRecord value); + + public abstract NestDigest build(); } }