Add Support of Core Library Type Desugaring for Classes with Nest-based Access.
#java11 #desugar
Rationale:
- Context: Nest desugaring takes two passes of input class files. During the first pass, a nest analyzer collects class attributes without core library type renaming (i.e. __desugar__/ prefixing). During the second pass, core library types are renamed (by CoreLibrarySupport-enabled class visitors) before running nest-based desugaring (by NestDesugaring).
- This causes breakages that class attributes for core types cannot be found during NestDesugaring visiting. Potential Real issues:
1) Java 11 core library with inter-mate private access cannot be desugared.
2) IllegalStateExceptions/NullPointerExceptions even for prior Java 11, since ClassAttributes in langmode/ is not specific to NestHost/NestMembers, it is expected ClassAttributes is present for every input class during second pass to ensure state integrity. An blind null check would just hide the incorrect state and make wrong assumptions.
- Key Changes:
- Add support type remapping for the nest summary object (NestDigest), including its recursively dependent data classes.
- Rename NestCompanions to NestDigest; Move ClassMemberRecord APIs used by NestDesugaring to NestDigest; Remove the NestDesugaring's direct dependency on ClassMemberRecord, and let NestDesugaring only query with NestDigest for nest attribute info.
- Create ClassName class in replacement of String-represented class binary names to get better name anchoring, conversions among different names and centralized checking.
- Share and feed the core library renaming logic into NestDigest.
- Add javadesugar/ package in par with java/ for easy testing.
PiperOrigin-RevId: 293876896
diff --git a/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKeyTest.java b/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKeyTest.java
index f8094d7..61c4685 100644
--- a/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKeyTest.java
+++ b/src/test/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKeyTest.java
@@ -29,129 +29,90 @@
@Test
public void fieldKey_bridgeOfInstanceRead() {
FieldKey fieldKey =
- FieldKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceFieldOfLongType",
- /* descriptor= */ "J");
+ FieldKey.create(ClassName.create("a/b/Charlie"), "instanceFieldOfLongType", "J");
assertThat(fieldKey.bridgeOfInstanceRead())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceFieldOfLongType$bridge_getter",
- /* descriptor= */ "(La/b/Charlie;)J"));
+ ClassName.create("a/b/Charlie"),
+ "instanceFieldOfLongType$bridge_getter",
+ "(La/b/Charlie;)J"));
}
@Test
public void fieldKey_bridgeOfInstanceWrite() {
+
FieldKey fieldKey =
- FieldKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceFieldOfLongType",
- /* descriptor= */ "J");
+ FieldKey.create(ClassName.create("a/b/Charlie"), "instanceFieldOfLongType", "J");
assertThat(fieldKey.bridgeOfInstanceWrite())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceFieldOfLongType$bridge_setter",
- /* descriptor= */ "(La/b/Charlie;J)J"));
+ ClassName.create("a/b/Charlie"),
+ "instanceFieldOfLongType$bridge_setter",
+ "(La/b/Charlie;J)J"));
}
@Test
public void fieldKey_bridgeOfStaticRead() {
FieldKey fieldKey =
- FieldKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "staticFieldOfLongType",
- /* descriptor= */ "J");
+ FieldKey.create(ClassName.create("a/b/Charlie"), "staticFieldOfLongType", "J");
assertThat(fieldKey.bridgeOfStaticRead())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "staticFieldOfLongType$bridge_getter",
- /* descriptor= */ "()J"));
+ ClassName.create("a/b/Charlie"), "staticFieldOfLongType$bridge_getter", "()J"));
}
@Test
public void fieldKey_bridgeOfStaticWrite() {
FieldKey fieldKey =
- FieldKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "staticFieldOfLongType",
- /* descriptor= */ "J");
+ FieldKey.create(ClassName.create("a/b/Charlie"), "staticFieldOfLongType", "J");
assertThat(fieldKey.bridgeOfStaticWrite())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "staticFieldOfLongType$bridge_setter",
- /* descriptor= */ "(J)J"));
+ ClassName.create("a/b/Charlie"), "staticFieldOfLongType$bridge_setter", "(J)J"));
}
@Test
public void methodKey_bridgeOfClassInstanceMethod() {
- MethodKey methodKey =
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie", /* name= */ "twoLongSum", /* descriptor= */ "(JJ)J");
+ MethodKey methodKey = MethodKey.create(ClassName.create("a/b/Charlie"), "twoLongSum", "(JJ)J");
assertThat(methodKey.bridgeOfClassInstanceMethod())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "twoLongSum$bridge",
- /* descriptor= */ "(La/b/Charlie;JJ)J"));
+ ClassName.create("a/b/Charlie"), "twoLongSum$bridge", "(La/b/Charlie;JJ)J"));
}
@Test
public void methodKey_bridgeOfClassStaticMethod() {
- MethodKey methodKey =
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie", /* name= */ "twoLongSum", /* descriptor= */ "(JJ)J");
+ MethodKey methodKey = MethodKey.create(ClassName.create("a/b/Charlie"), "twoLongSum", "(JJ)J");
assertThat(methodKey.bridgeOfClassStaticMethod())
- .isEqualTo(
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "twoLongSum$bridge",
- /* descriptor= */ "(JJ)J"));
+ .isEqualTo(MethodKey.create(ClassName.create("a/b/Charlie"), "twoLongSum$bridge", "(JJ)J"));
}
@Test
public void methodKey_bridgeOfConstructor() {
- MethodKey methodKey =
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie", /* name= */ "<init>", /* descriptor= */ "(JJ)V");
- assertThat(methodKey.bridgeOfConstructor("a/b/Charlie$NestCC"))
+ MethodKey methodKey = MethodKey.create(ClassName.create("a/b/Charlie"), "<init>", "(JJ)V");
+ assertThat(methodKey.bridgeOfConstructor(ClassName.create("a/b/Charlie$NestCC")))
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "<init>",
- /* descriptor= */ "(JJLa/b/Charlie$NestCC;)V"));
+ ClassName.create("a/b/Charlie"), "<init>", "(JJLa/b/Charlie$NestCC;)V"));
}
@Test
public void methodKey_substituteOfInterfaceInstanceMethod() {
MethodKey methodKey =
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceInstanceMethod",
- /* descriptor= */ "(JJ)J");
+ MethodKey.create(ClassName.create("a/b/Charlie"), "instanceInstanceMethod", "(JJ)J");
assertThat(methodKey.substituteOfInterfaceInstanceMethod())
.isEqualTo(
MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceInstanceMethod",
- /* descriptor= */ "(La/b/Charlie;JJ)J"));
+ ClassName.create("a/b/Charlie"), "instanceInstanceMethod", "(La/b/Charlie;JJ)J"));
}
@Test
public void methodKey_substituteOfInterfaceStaticMethod() {
+
MethodKey methodKey =
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceStaticMethod",
- /* descriptor= */ "(JJ)J");
+ MethodKey.create(ClassName.create("a/b/Charlie"), "instanceStaticMethod", "(JJ)J");
assertThat(methodKey.substituteOfInterfaceStaticMethod())
.isEqualTo(
- MethodKey.create(
- /* ownerClass= */ "a/b/Charlie",
- /* name= */ "instanceStaticMethod",
- /* descriptor= */ "(JJ)J"));
+ MethodKey.create(ClassName.create("a/b/Charlie"), "instanceStaticMethod", "(JJ)J"));
}
}
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 7e01e1e..85b4553 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
@@ -31,8 +31,8 @@
@Test
public void trackFieldUse() {
- ClassMemberKey classMemberKey =
- FieldKey.create("package/path/OwnerClass", "fieldOfPrimitiveLong", "J");
+ FieldKey classMemberKey =
+ FieldKey.create(ClassName.create("package/path/OwnerClass"), "fieldOfPrimitiveLong", "J");
classMemberRecord.logMemberUse(classMemberKey, Opcodes.GETFIELD);
classMemberRecord.logMemberUse(classMemberKey, Opcodes.PUTFIELD);
classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_GETFIELD);
@@ -48,7 +48,8 @@
@Test
public void trackConstructorUse() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "<init>", "()V");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "<init>", "()V");
classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKESPECIAL);
classMemberRecord.logMemberUse(classMemberKey, Opcodes.H_NEWINVOKESPECIAL);
@@ -58,7 +59,8 @@
@Test
public void trackMethodUse() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKEVIRTUAL);
classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKESPECIAL);
classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKESTATIC);
@@ -86,7 +88,8 @@
@Test
public void trackMemberDeclaration() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberDecl(
classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
@@ -98,7 +101,8 @@
@Test
public void trackMemberDeclaration_withDeprecatedAnnotation() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberDecl(
classMemberKey,
Opcodes.ACC_DEPRECATED | Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
@@ -113,9 +117,12 @@
public void mergeRecord_trackingReasons() {
ClassMemberRecord otherClassMemberRecord = ClassMemberRecord.create();
- ClassMemberKey method1 = MethodKey.create("package/path/OwnerClass", "method1", "(II)I");
- ClassMemberKey method2 = MethodKey.create("package/path/OwnerClass", "method2", "(II)I");
- ClassMemberKey method3 = MethodKey.create("package/path/OwnerClass", "method3", "(II)I");
+ MethodKey method1 =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method1", "(II)I");
+ MethodKey method2 =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method2", "(II)I");
+ MethodKey method3 =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method3", "(II)I");
classMemberRecord.logMemberDecl(method1, Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
classMemberRecord.logMemberUse(method2, Opcodes.INVOKEVIRTUAL);
@@ -146,7 +153,8 @@
@Test
public void filterUsedMemberWithTrackedDeclaration_noMemberDeclaration() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberUse(classMemberKey, Opcodes.INVOKEVIRTUAL);
assertThat(classMemberRecord.hasTrackingReason(classMemberKey)).isTrue();
@@ -157,7 +165,8 @@
@Test
public void filterUsedMemberWithTrackedDeclaration_noMemberUse() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberDecl(
classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
@@ -169,7 +178,8 @@
@Test
public void filterUsedMemberWithTrackedDeclaration_interfaceMemberWithoutUse_shouldTrack() {
- ClassMemberKey classMemberKey = MethodKey.create("package/path/OwnerClass", "method", "(II)I");
+ MethodKey classMemberKey =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I");
classMemberRecord.logMemberDecl(
classMemberKey, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, Opcodes.ACC_PRIVATE);
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD
index 99f9ffd..8751692 100644
--- a/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD
+++ b/src/test/java/com/google/devtools/build/android/desugar/nest/BUILD
@@ -61,10 +61,10 @@
)
java_test(
- name = "NestCompanionsTest",
+ name = "NestDigestTest",
size = "small",
- srcs = ["NestCompanionsTest.java"],
- test_class = "com.google.devtools.build.android.desugar.nest.NestCompanionsTest",
+ srcs = ["NestDigestTest.java"],
+ test_class = "com.google.devtools.build.android.desugar.nest.NestDigestTest",
deps = [
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
"//src/tools/android/java/com/google/devtools/build/android/desugar/nest",
@@ -218,6 +218,7 @@
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/complexcase:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/constructor:srcs",
+ "//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/classfileformat:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/field:srcs",
"//src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/interfacemethod:srcs",
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 b240c67..24c3343 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
@@ -53,16 +53,14 @@
private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create();
private final ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create();
- private final NestCompanions nestCompanions =
- NestCompanions.create(classMemberRecord, classAttributeRecord);
+ private final NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord);
@Test
public void emptyInputFiles() throws IOException {
NestAnalyzer nestAnalyzer =
- new NestAnalyzer(
- ImmutableList.of(), nestCompanions, classMemberRecord, classAttributeRecord);
+ new NestAnalyzer(ImmutableList.of(), nestDigest, classMemberRecord, classAttributeRecord);
nestAnalyzer.analyze();
- assertThat(nestCompanions.getAllCompanionClasses()).isEmpty();
+ assertThat(nestDigest.getAllCompanionClassNames()).isEmpty();
}
@Test
@@ -82,13 +80,13 @@
new FileContentProvider<>(
entry.getName(), () -> getJarEntryInputStream(jarFile, entry)))
.collect(toImmutableList()),
- nestCompanions,
+ nestDigest,
classMemberRecord,
classAttributeRecord);
nestAnalyzer.analyze();
- assertThat(nestCompanions.getAllCompanionClasses())
+ assertThat(nestDigest.getAllCompanionClassNames())
.containsExactly(
"com/google/devtools/build/android/desugar/nest/testsrc/nestanalyzer/AnalyzedTarget$NestCC");
}
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestCompanionsTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestCompanionsTest.java
deleted file mode 100644
index 3ee867b..0000000
--- a/src/test/java/com/google/devtools/build/android/desugar/nest/NestCompanionsTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2019 The Bazel Authors. All rights reserved.
-//
-// 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 com.google.devtools.build.android.desugar.nest;
-
-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.ClassAttributes;
-import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
-import com.google.devtools.build.android.desugar.langmodel.MethodKey;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.objectweb.asm.Opcodes;
-
-/** The tests for {@link NestCompanions}. */
-@RunWith(JUnit4.class)
-public class NestCompanionsTest {
-
- private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create();
- private final ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create();
- private final NestCompanions nestCompanions =
- NestCompanions.create(classMemberRecord, classAttributeRecord);
-
- @Test
- public void prepareCompanionClassWriters_noCompanionClassesGenerated() {
- classMemberRecord.logMemberDecl(
- MethodKey.create("package/path/OwnerClass", "method", "(II)I"),
- /* ownerAccess= */ Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE,
- /* memberDeclAccess= */ Opcodes.ACC_PRIVATE);
-
- nestCompanions.prepareCompanionClasses();
-
- assertThat(nestCompanions.getAllCompanionClasses()).isEmpty();
- }
-
- @Test
- public void prepareCompanionClassWriters_companionClassesGenerated() {
- MethodKey constructor = MethodKey.create("package/path/OwnerClass", "<init>", "()V");
- classMemberRecord.logMemberDecl(
- constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
- classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL);
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClass$NestedClass")
- .setNestHost("package/path/OwnerClass")
- .build());
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClass")
- .addNestMember("package/path/OwnerClass$NestedClass")
- .build());
-
- nestCompanions.prepareCompanionClasses();
-
- assertThat(nestCompanions.getAllCompanionClasses())
- .containsExactly("package/path/OwnerClass$NestCC");
- }
-
- @Test
- public void preparCompanionClassWriters_multipleCompanionClassesGenerated() {
- classMemberRecord.logMemberDecl(
- MethodKey.create("package/path/OwnerClassA", "<init>", "()V"),
- Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
- Opcodes.ACC_PRIVATE);
- classMemberRecord.logMemberDecl(
- MethodKey.create("package/path/OwnerClassA", "method", "(II)I"),
- Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
- Opcodes.ACC_PRIVATE);
- classMemberRecord.logMemberDecl(
- MethodKey.create("package/path/OwnerClassB", "<init>", "()V"),
- Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
- Opcodes.ACC_PRIVATE);
-
- classMemberRecord.logMemberUse(
- MethodKey.create("package/path/OwnerClassA", "<init>", "()V"), Opcodes.INVOKESPECIAL);
- classMemberRecord.logMemberUse(
- MethodKey.create("package/path/OwnerClassA", "method", "(II)I"), Opcodes.INVOKESPECIAL);
- classMemberRecord.logMemberUse(
- MethodKey.create("package/path/OwnerClassB", "<init>", "()V"), Opcodes.INVOKESPECIAL);
-
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClassA$NestedClass")
- .setNestHost("package/path/OwnerClassA")
- .build());
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClassB$NestedClass")
- .setNestHost("package/path/OwnerClassB")
- .build());
-
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClassA")
- .addNestMember("package/path/OwnerClassA$NestedClass")
- .build());
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName("package/path/OwnerClassB")
- .addNestMember("package/path/OwnerClassB$NestedClass")
- .build());
-
- nestCompanions.prepareCompanionClasses();
-
- assertThat(nestCompanions.getAllCompanionClasses())
- .containsExactly("package/path/OwnerClassA$NestCC", "package/path/OwnerClassB$NestCC");
- }
-
- @Test
- public void prepareCompanionClassWriters_classNameWithDollarSign() {
- MethodKey constructor = MethodKey.create("package/path/$Owner$Class$", "<init>", "()V");
-
- classMemberRecord.logMemberDecl(
- constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
- classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL);
-
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName(constructor.owner() + "$NestClass")
- .setNestHost(constructor.owner())
- .build());
- classAttributeRecord.setClassAttributes(
- ClassAttributes.builder()
- .setClassBinaryName(constructor.owner())
- .addNestMember(constructor.owner() + "$NestClass")
- .build());
-
- nestCompanions.prepareCompanionClasses();
-
- assertThat(nestCompanions.getAllCompanionClasses())
- .containsExactly("package/path/$Owner$Class$$NestCC");
- }
-}
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java
new file mode 100644
index 0000000..fbd3b5e
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDesugaringCoreLibTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * 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 com.google.devtools.build.android.desugar.nest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Splitter;
+import com.google.devtools.build.android.desugar.testing.junit.AsmNode;
+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.JdkSuppress;
+import com.google.devtools.build.android.desugar.testing.junit.JdkVersion;
+import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.objectweb.asm.tree.ClassNode;
+
+/** Tests for accessing private constructors from another class within a nest. */
+@RunWith(DesugarRunner.class)
+@JdkSuppress(minJdkVersion = JdkVersion.V11)
+public class NestDesugaringCoreLibTest {
+
+ private static final Lookup lookup = MethodHandles.lookup();
+
+ @Rule
+ public final DesugarRule desugarRule =
+ DesugarRule.builder(this, lookup)
+ .addSourceInputs(getInputSourceFilesFromJvmOption("input_srcs"))
+ .addJavacOptions("-source 11", "-target 11")
+ .addCommandOptions("desugar_nest_based_private_access", "true")
+ .addCommandOptions("allow_empty_bootclasspath", "true")
+ .addCommandOptions("core_library", "true")
+ .addCommandOptions("desugar_supported_core_libs", "true")
+ .addCommandOptions("rewrite_core_library_prefix", "javadesugar/testing/")
+ .build();
+
+ private static Path[] getInputSourceFilesFromJvmOption(String jvmOptionKey) {
+ return Splitter.on(" ").trimResults().splitToList(System.getProperty(jvmOptionKey)).stream()
+ .map(Paths::get)
+ .toArray(Path[]::new);
+ }
+
+ @Test
+ public void inputClassFileMajorVersions(
+ @AsmNode(className = "javadesugar.testing.TestCoreType$MateA", round = 0) ClassNode before,
+ @AsmNode(className = "jd$.testing.TestCoreType$MateA", round = 1) ClassNode after) {
+ assertThat(before.version).isEqualTo(JdkVersion.V11);
+ assertThat(after.version).isEqualTo(JdkVersion.V1_7);
+ }
+
+ @Test
+ public void invokeInterMatePrivateStaticMethodOfCoreLibType(
+ @RuntimeMethodHandle(className = "jd$.testing.TestCoreType", memberName = "twoSum")
+ MethodHandle twoSum)
+ throws Throwable {
+ long result = (long) twoSum.invoke(1L, 2L);
+ assertThat(result).isEqualTo(3L);
+ }
+
+ @Test
+ public void invokeInterMatePrivateInstanceMethodOfCoreLibType(
+ @RuntimeMethodHandle(className = "jd$.testing.TestCoreType", memberName = "twoSumWithBase")
+ MethodHandle twoSum)
+ throws Throwable {
+ long result = (long) twoSum.invoke(1000L, 1L, 2L);
+ assertThat(result).isEqualTo(1003L);
+ }
+}
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
new file mode 100644
index 0000000..d789b3b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/nest/NestDigestTest.java
@@ -0,0 +1,151 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// 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 com.google.devtools.build.android.desugar.nest;
+
+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.ClassAttributes;
+import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
+import com.google.devtools.build.android.desugar.langmodel.MethodKey;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.objectweb.asm.Opcodes;
+
+/** The tests for {@link NestDigest}. */
+@RunWith(JUnit4.class)
+public class NestDigestTest {
+
+ private final ClassMemberRecord classMemberRecord = ClassMemberRecord.create();
+ private final ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create();
+ private final NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord);
+
+ @Test
+ public void prepareCompanionClassWriters_noCompanionClassesGenerated() {
+ classMemberRecord.logMemberDecl(
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "method", "(II)I"),
+ /* ownerAccess= */ Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE,
+ /* memberDeclAccess= */ Opcodes.ACC_PRIVATE);
+
+ nestDigest.prepareCompanionClasses();
+
+ assertThat(nestDigest.getAllCompanionClassNames()).isEmpty();
+ }
+
+ @Test
+ public void prepareCompanionClassWriters_companionClassesGenerated() {
+ MethodKey constructor =
+ MethodKey.create(ClassName.create("package/path/OwnerClass"), "<init>", "()V");
+ classMemberRecord.logMemberDecl(
+ constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
+ classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL);
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClass$NestedClass"))
+ .setNestHost(ClassName.create("package/path/OwnerClass"))
+ .build());
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClass"))
+ .addNestMember(ClassName.create("package/path/OwnerClass$NestedClass"))
+ .build());
+
+ nestDigest.prepareCompanionClasses();
+
+ assertThat(nestDigest.getAllCompanionClassNames())
+ .containsExactly("package/path/OwnerClass$NestCC");
+ }
+
+ @Test
+ public void preparCompanionClassWriters_multipleCompanionClassesGenerated() {
+ classMemberRecord.logMemberDecl(
+ MethodKey.create(ClassName.create("package/path/OwnerClassA"), "<init>", "()V"),
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+ Opcodes.ACC_PRIVATE);
+ classMemberRecord.logMemberDecl(
+ MethodKey.create(ClassName.create("package/path/OwnerClassA"), "method", "(II)I"),
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+ Opcodes.ACC_PRIVATE);
+ classMemberRecord.logMemberDecl(
+ MethodKey.create(ClassName.create("package/path/OwnerClassB"), "<init>", "()V"),
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+ Opcodes.ACC_PRIVATE);
+
+ classMemberRecord.logMemberUse(
+ MethodKey.create(ClassName.create("package/path/OwnerClassA"), "<init>", "()V"),
+ Opcodes.INVOKESPECIAL);
+ classMemberRecord.logMemberUse(
+ MethodKey.create(ClassName.create("package/path/OwnerClassA"), "method", "(II)I"),
+ Opcodes.INVOKESPECIAL);
+ classMemberRecord.logMemberUse(
+ MethodKey.create(ClassName.create("package/path/OwnerClassB"), "<init>", "()V"),
+ Opcodes.INVOKESPECIAL);
+
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClassA$NestedClass"))
+ .setNestHost(ClassName.create("package/path/OwnerClassA"))
+ .build());
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClassB$NestedClass"))
+ .setNestHost(ClassName.create("package/path/OwnerClassB"))
+ .build());
+
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClassA"))
+ .addNestMember(ClassName.create("package/path/OwnerClassA$NestedClass"))
+ .build());
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create("package/path/OwnerClassB"))
+ .addNestMember(ClassName.create("package/path/OwnerClassB$NestedClass"))
+ .build());
+
+ nestDigest.prepareCompanionClasses();
+
+ assertThat(nestDigest.getAllCompanionClassNames())
+ .containsExactly("package/path/OwnerClassA$NestCC", "package/path/OwnerClassB$NestCC");
+ }
+
+ @Test
+ public void prepareCompanionClassWriters_classNameWithDollarSign() {
+ MethodKey constructor =
+ MethodKey.create(ClassName.create("package/path/$Owner$Class$"), "<init>", "()V");
+
+ classMemberRecord.logMemberDecl(
+ constructor, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, Opcodes.ACC_PRIVATE);
+ classMemberRecord.logMemberUse(constructor, Opcodes.INVOKESPECIAL);
+
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create(constructor.ownerName() + "$NestClass"))
+ .setNestHost(ClassName.create(constructor.ownerName()))
+ .build());
+ classAttributeRecord.setClassAttributes(
+ ClassAttributes.builder()
+ .setClassBinaryName(ClassName.create(constructor.ownerName()))
+ .addNestMember(ClassName.create(constructor.ownerName() + "$NestClass"))
+ .build());
+
+ nestDigest.prepareCompanionClasses();
+
+ assertThat(nestDigest.getAllCompanionClassNames())
+ .containsExactly("package/path/$Owner$Class$$NestCC");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD
new file mode 100644
index 0000000..300fa54
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/BUILD
@@ -0,0 +1,17 @@
+package(
+ default_testonly = 1,
+ default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+filegroup(
+ name = "testing",
+ srcs = glob(["*.java"]),
+)
+
+filegroup(
+ name = "srcs",
+ testonly = 0,
+ srcs = glob(["*"]),
+)
diff --git a/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java
new file mode 100644
index 0000000..6f42dd1
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/android/desugar/nest/testsrc/simpleunit/core/javadesugar/testing/TestCoreType.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * 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 javadesugar.testing;
+
+/**
+ * A fake core library class for testing core type desugaring. Related flags include,
+ *
+ * <p>--core_library, --desugar_supported_core_libs, --rewrite_core_library_prefix
+ */
+public class TestCoreType {
+
+ /** Invocation entry point for testing to invoke private static methods in anther mate. */
+ public static long twoSum(long x, long y) {
+ return MateA.twoSum(x, y);
+ }
+
+ /** Invocation entry point for testing to invoke private instance methods in anther mate. */
+ public static long twoSumWithBase(long base, long x, long y) {
+ return new MateA(base).twoSumWithBase(x, y);
+ }
+
+ private TestCoreType() {}
+
+ private static class MateA {
+
+ private final long base;
+
+ private MateA(long base) {
+ this.base = base;
+ }
+
+ private static long twoSum(long x, long y) {
+ return x + y;
+ }
+
+ private long twoSumWithBase(long x, long y) {
+ return base + x + y;
+ }
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
index f58d5a0..aae5b5a 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java
@@ -94,7 +94,10 @@
this.rewriter = rewriter;
this.targetLoader = targetLoader;
checkArgument(
- renamedPrefixes.stream().allMatch(prefix -> prefix.startsWith("java/")), renamedPrefixes);
+ renamedPrefixes.stream()
+ .allMatch(prefix -> prefix.startsWith("java/") || prefix.startsWith("javadesugar/")),
+ "Unexpected renamedPrefixes: Actual (%s).",
+ renamedPrefixes);
this.renamedPrefixes = ImmutableSet.copyOf(renamedPrefixes);
this.excludeFromEmulation = ImmutableSet.copyOf(excludeFromEmulation);
@@ -122,7 +125,8 @@
"Original renamed, no need to move it: %s",
move);
checkArgument(
- !pair.get(1).startsWith("java/") || isRenamedCoreLibrary(pair.get(1)),
+ !(pair.get(1).startsWith("java/") || pair.get(1).startsWith("javadesugar/"))
+ || isRenamedCoreLibrary(pair.get(1)),
"Core library target not renamed: %s",
move);
checkArgument(
@@ -178,20 +182,26 @@
public boolean isRenamedCoreLibrary(String internalName) {
String unprefixedName = rewriter.unprefix(internalName);
- if (!unprefixedName.startsWith("java/") || renamedPrefixes.isEmpty()) {
+ if (!(unprefixedName.startsWith("java/") || unprefixedName.startsWith("javadesugar/"))
+ || renamedPrefixes.isEmpty()) {
return false; // shortcut
}
// Rename any classes desugar might generate under java/ (for emulated interfaces) as well as
// configured prefixes
return looksGenerated(unprefixedName)
- || renamedPrefixes.stream().anyMatch(prefix -> unprefixedName.startsWith(prefix));
+ || renamedPrefixes.stream().anyMatch(unprefixedName::startsWith);
}
public String renameCoreLibrary(String internalName) {
internalName = rewriter.unprefix(internalName);
- return (internalName.startsWith("java/"))
- ? "j$/" + internalName.substring(/* cut away "java/" prefix */ 5)
- : internalName;
+ if (internalName.startsWith("java/")) {
+ return "j$/" + internalName.substring(/* cut away "java/" prefix */ 5);
+ }
+ if (internalName.startsWith("javadesugar/")) {
+ return "jd$/" + internalName.substring(/* cut away "javadesugar/" prefix */ 12);
+ }
+
+ return internalName;
}
public Remapper getRemapper() {
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 e5de759..d033715 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
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.io.Resources;
@@ -41,8 +42,8 @@
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.NestCompanions;
import com.google.devtools.build.android.desugar.nest.NestDesugaring;
+import com.google.devtools.build.android.desugar.nest.NestDigest;
import com.google.devtools.build.android.desugar.strconcat.IndyStringConcatDesugaring;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest;
import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse;
@@ -53,7 +54,6 @@
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor;
-import java.io.ByteArrayInputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
@@ -70,6 +70,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -397,7 +398,8 @@
private final CoreLibraryRewriter rewriter;
private final LambdaClassMaker lambdas;
private final GeneratedClassStore store = new GeneratedClassStore();
- private final ClassMemberUseCounter classMemberUseCounter = new ClassMemberUseCounter();
+ private final ClassMemberUseCounter classMemberUseCounter =
+ new ClassMemberUseCounter(new ConcurrentHashMap<>());
private final Set<String> visitedExceptionTypes = new LinkedHashSet<>();
/** The counter to record the times of try-with-resources desugaring is invoked. */
private final AtomicInteger numOfTryWithResourcesInvoked = new AtomicInteger();
@@ -494,7 +496,7 @@
ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
ClassVsInterface interfaceCache = new ClassVsInterface(classpathReader);
- CoreLibrarySupport coreLibrarySupport =
+ final CoreLibrarySupport coreLibrarySupport =
options.desugarCoreLibs
? new CoreLibrarySupport(
rewriter,
@@ -656,9 +658,14 @@
ClassAttributeRecord classAttributeRecord = ClassAttributeRecord.create();
ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents =
inputFiles.toInputFileStreams();
- NestCompanions nestCompanions =
+ NestDigest nestDigest =
NestAnalyzer.analyzeNests(inputFileContents, classMemberRecord, classAttributeRecord);
- for (FileContentProvider<? extends InputStream> inputFileProvider : 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.
+ nestDigest = nestDigest.acceptTypeMapper(rewriter.getPrefixer());
+ for (FileContentProvider<? extends InputStream> inputFileProvider :
+ Iterables.concat(inputFileContents, nestDigest.getCompanionFileProviders())) {
String inputFilename = inputFileProvider.getBinaryPathName();
if ("module-info.class".equals(inputFilename)
|| (inputFilename.endsWith("/module-info.class")
@@ -691,8 +698,7 @@
interfaceLambdaMethodCollector,
writer,
reader,
- classMemberRecord,
- nestCompanions);
+ nestDigest);
if (writer == visitor) {
// Just copy the input if there are no rewritings
outputFileProvider.write(inputFilename, reader.b);
@@ -728,15 +734,6 @@
}
}
}
-
- for (FileContentProvider<ByteArrayInputStream> companionFileProvider :
- nestCompanions.getCompanionFileProviders()) {
- try (ByteArrayInputStream companionInputStream = companionFileProvider.get()) {
- outputFileProvider.write(
- companionFileProvider.getBinaryPathName(),
- ByteStreams.toByteArray(companionInputStream));
- }
- }
}
/**
@@ -985,8 +982,7 @@
ImmutableSet.Builder<String> interfaceLambdaMethodCollector,
UnprefixingClassWriter writer,
ClassReader input,
- ClassMemberRecord classMemberRecord,
- NestCompanions nestCompanions) {
+ NestDigest nestDigest) {
ClassVisitor visitor = checkNotNull(writer);
if (coreLibrarySupport != null) {
@@ -1065,7 +1061,7 @@
}
if (options.desugarNestBasedPrivateAccess) {
- visitor = new NestDesugaring(visitor, nestCompanions, classMemberRecord);
+ visitor = new NestDesugaring(visitor, nestDigest);
}
if (options.desugarIndifyStringConcat) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
index e10343f..ae739c2 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
@@ -11,6 +11,7 @@
"//src/tools/android/java/com/google/devtools/build/android/desugar:__subpackages__",
],
deps = [
+ "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
"//third_party:asm",
"//third_party:asm-commons",
"//third_party:asm-tree",
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
index a63bc05..71e6ed2 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java
@@ -13,6 +13,8 @@
// limitations under the License.
package com.google.devtools.build.android.desugar.io;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
+import com.google.devtools.build.android.desugar.langmodel.TypeMapper;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
@@ -26,10 +28,13 @@
/** Utility class to prefix or unprefix class names of core library classes */
public class CoreLibraryRewriter {
+
private final String prefix;
+ private final TypeMapper prefixer;
public CoreLibraryRewriter(String prefix) {
this.prefix = prefix;
+ this.prefixer = new TypeMapper(this::prefix);
}
/**
@@ -40,7 +45,7 @@
if (prefix.isEmpty()) {
return new ClassReader(content);
} else {
- return new PrefixingClassReader(content, prefix);
+ return new PrefixingClassReader(content, prefixer);
}
}
@@ -53,7 +58,11 @@
}
static boolean shouldPrefix(String typeName) {
- return (typeName.startsWith("java/") || typeName.startsWith("sun/")) && !except(typeName);
+ return (typeName.startsWith("java/")
+ || typeName.startsWith("sun/")
+ || typeName.startsWith("javadesugar/") // Testing-only fake package prefix.
+ )
+ && !except(typeName);
}
private static boolean except(String typeName) {
@@ -90,6 +99,17 @@
return prefix;
}
+ public TypeMapper getPrefixer() {
+ return prefixer;
+ }
+
+ private ClassName prefix(ClassName className) {
+ if (shouldPrefix(className.binaryName())) {
+ return className.prependPrefix(prefix);
+ }
+ return className;
+ }
+
/** Removes prefix from class names */
public String unprefix(String typeName) {
if (prefix.isEmpty() || !typeName.startsWith(prefix)) {
@@ -100,54 +120,39 @@
/** ClassReader that prefixes core library class names as they are read */
private static class PrefixingClassReader extends ClassReader {
- private final String prefix;
- PrefixingClassReader(InputStream content, String prefix) throws IOException {
+ private final TypeMapper prefixer;
+
+ PrefixingClassReader(InputStream content, TypeMapper prefixer) throws IOException {
super(content);
- this.prefix = prefix;
+ this.prefixer = prefixer;
}
@Override
public void accept(ClassVisitor cv, Attribute[] attrs, int flags) {
- cv =
- new ClassRemapper(
- cv,
- new Remapper() {
- @Override
- public String map(String typeName) {
- return prefix(typeName);
- }
- });
+ cv = new ClassRemapper(cv, prefixer);
super.accept(cv, attrs, flags);
}
@Override
public String getClassName() {
- return prefix(super.getClassName());
+ return prefixer.map(super.getClassName());
}
@Override
public String getSuperName() {
String result = super.getSuperName();
- return result != null ? prefix(result) : null;
+ return result != null ? prefixer.map(result) : null;
}
@Override
public String[] getInterfaces() {
String[] result = super.getInterfaces();
for (int i = 0, len = result.length; i < len; ++i) {
- result[i] = prefix(result[i]);
+ result[i] = prefixer.map(result[i]);
}
return result;
}
-
- /** Prefixes core library class names with prefix. */
- private String prefix(String typeName) {
- if (shouldPrefix(typeName)) {
- return prefix + typeName;
- }
- return typeName;
- }
}
/**
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 4fe3db0..30564d9 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
@@ -16,24 +16,29 @@
package com.google.devtools.build.android.desugar.langmodel;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.stream.Collectors.toMap;
+
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 class ClassAttributeRecord {
+public final class ClassAttributeRecord implements TypeMappable<ClassAttributeRecord> {
- private final Map<String, ClassAttributes> record = new HashMap<>();
+ private final Map<ClassName, ClassAttributes> record;
public static ClassAttributeRecord create() {
- return new ClassAttributeRecord();
+ return new ClassAttributeRecord(new HashMap<>());
}
- private ClassAttributeRecord() {}
+ private ClassAttributeRecord(Map<ClassName, ClassAttributes> record) {
+ this.record = record;
+ }
public ClassAttributes setClassAttributes(ClassAttributes classAttributes) {
- String classBinaryName = classAttributes.classBinaryName();
+ ClassName classBinaryName = classAttributes.classBinaryName();
if (record.containsKey(classBinaryName)) {
throw new IllegalStateException(
String.format(
@@ -44,19 +49,36 @@
return record.put(classBinaryName, classAttributes);
}
- public Optional<String> getNestHost(String className) {
+ public Optional<ClassName> getNestHost(ClassName className) {
ClassAttributes classAttributes = record.get(className);
- if (classAttributes != null) {
- return classAttributes.nestHost();
- }
- return Optional.empty();
+ checkNotNull(
+ classAttributes,
+ "Expected recorded ClassAttributes for (%s). Available record: %s",
+ className,
+ record.keySet());
+ return classAttributes.nestHost();
}
- public ImmutableSet<String> getNestMembers(String className) {
+ public ImmutableSet<ClassName> getNestMembers(ClassName className) {
ClassAttributes classAttributes = record.get(className);
- if (classAttributes != null) {
- return classAttributes.nestMembers();
- }
- return ImmutableSet.of();
+ checkNotNull(
+ classAttributes,
+ "Expected recorded ClassAttributes for (%s). Available record: %s",
+ className,
+ record.keySet());
+ return classAttributes.nestMembers();
+ }
+
+ @Override
+ public ClassAttributeRecord acceptTypeMapper(TypeMapper typeMapper) {
+ return new ClassAttributeRecord(
+ this.record.values().stream()
+ .map(attr -> attr.acceptTypeMapper(typeMapper))
+ .collect(
+ toMap(
+ ClassAttributes::classBinaryName,
+ attr -> attr,
+ (prev, next) -> next,
+ HashMap::new)));
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributes.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributes.java
index 429215e..30b7132 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributes.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassAttributes.java
@@ -26,13 +26,13 @@
* <p>https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7
*/
@AutoValue
-public abstract class ClassAttributes {
+public abstract class ClassAttributes implements TypeMappable<ClassAttributes> {
- public abstract String classBinaryName();
+ public abstract ClassName classBinaryName();
- public abstract Optional<String> nestHost();
+ public abstract Optional<ClassName> nestHost();
- public abstract ImmutableSet<String> nestMembers();
+ public abstract ImmutableSet<ClassName> nestMembers();
// Include other class attributes as necessary.
@@ -40,17 +40,29 @@
return new AutoValue_ClassAttributes.Builder();
}
+ @Override
+ public ClassAttributes acceptTypeMapper(TypeMapper typeMapper) {
+ ClassAttributesBuilder mappedBuilder = builder();
+ mappedBuilder.setClassBinaryName(classBinaryName().acceptTypeMapper(typeMapper));
+ if (nestHost().isPresent()) {
+ mappedBuilder.setNestHost(nestHost().get().acceptTypeMapper(typeMapper));
+ }
+ nestMembers().stream().map(typeMapper::map).forEach(mappedBuilder::addNestMember);
+ mappedBuilder.setClassBinaryName(classBinaryName().acceptTypeMapper(typeMapper));
+ return mappedBuilder.build();
+ }
+
/** The builder of {@link ClassAttributes}. */
@AutoValue.Builder
public abstract static class ClassAttributesBuilder {
- public abstract ClassAttributesBuilder setClassBinaryName(String classBinaryName);
+ public abstract ClassAttributesBuilder setClassBinaryName(ClassName classBinaryName);
- public abstract ClassAttributesBuilder setNestHost(String nestHost);
+ public abstract ClassAttributesBuilder setNestHost(ClassName nestHost);
- abstract ImmutableSet.Builder<String> nestMembersBuilder();
+ abstract ImmutableSet.Builder<ClassName> nestMembersBuilder();
- public ClassAttributesBuilder addNestMember(String nestMember) {
+ public ClassAttributesBuilder addNestMember(ClassName nestMember) {
nestMembersBuilder().add(nestMember);
return this;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
index 078bfef..b80ca57 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberKey.java
@@ -17,13 +17,13 @@
package com.google.devtools.build.android.desugar.langmodel;
/** The key that indexes a class member, including fields, constructors and methods. */
-public abstract class ClassMemberKey {
+public abstract class ClassMemberKey<T extends ClassMemberKey<T>> implements TypeMappable<T> {
/**
* The class or interface that owns the class member, i.e. the immediate enclosing class of the
* declaration site of a field, constructor or method.
*/
- public abstract String owner();
+ public abstract ClassName owner();
/** The simple name of the class member. */
public abstract String name();
@@ -31,6 +31,11 @@
/** The descriptor of the class member. */
public abstract String descriptor();
+ /** The binary name of {@link #owner()} */
+ public final String ownerName() {
+ return owner().binaryName();
+ }
+
/** Whether member key represents a constructor. */
public final boolean isConstructor() {
return "<init>".equals(name());
@@ -40,4 +45,8 @@
final String nameWithSuffix(String suffix) {
return name() + '$' + suffix;
}
+
+ /** Produces a new class member key by mapping this key instance. */
+ @Override
+ public abstract T acceptTypeMapper(TypeMapper typeMapper);
}
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 b56353b..d6bd18c 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,6 +17,7 @@
package com.google.devtools.build.android.desugar.langmodel;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.stream.Collectors.toMap;
import com.google.common.collect.ImmutableList;
import java.util.LinkedHashMap;
@@ -27,10 +28,10 @@
* A record that tracks the declarations and usages of a class member, including fields,
* constructors and methods.
*/
-public final class ClassMemberRecord {
+public final class ClassMemberRecord implements TypeMappable<ClassMemberRecord> {
/** Tracks a class member with a reason. */
- private final Map<ClassMemberKey, ClassMemberTrackReason> reasons;
+ private final Map<ClassMemberKey<?>, ClassMemberTrackReason> reasons;
/** The factory method of this class. */
public static ClassMemberRecord create() {
@@ -49,7 +50,7 @@
});
}
- private ClassMemberRecord(Map<ClassMemberKey, ClassMemberTrackReason> reasons) {
+ private ClassMemberRecord(Map<ClassMemberKey<?>, ClassMemberTrackReason> reasons) {
this.reasons = reasons;
}
@@ -68,16 +69,16 @@
return !reasons.isEmpty();
}
- public boolean hasTrackingReason(ClassMemberKey classMemberKey) {
+ public boolean hasTrackingReason(ClassMemberKey<?> classMemberKey) {
return reasons.containsKey(classMemberKey);
}
- boolean hasDeclReason(ClassMemberKey classMemberKey) {
+ 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) {
+ int findOwnerAccessCode(ClassMemberKey<?> memberKey) {
if (reasons.containsKey(memberKey)) {
return reasons.get(memberKey).getOwnerAccess();
}
@@ -85,7 +86,7 @@
}
/** Find the original access code for the class member declaration. */
- int findMemberAccessCode(ClassMemberKey memberKey) {
+ int findMemberAccessCode(ClassMemberKey<?> memberKey) {
if (reasons.containsKey(memberKey)) {
return reasons.get(memberKey).getMemberAccess();
}
@@ -93,7 +94,7 @@
}
/** Find all invocation codes of a class member. */
- public ImmutableList<MemberUseKind> findAllMemberUseKind(ClassMemberKey memberKey) {
+ public ImmutableList<MemberUseKind> findAllMemberUseKind(ClassMemberKey<?> memberKey) {
if (reasons.containsKey(memberKey)) {
return ImmutableList.copyOf(reasons.get(memberKey).getUseAccesses());
}
@@ -102,14 +103,14 @@
/** Logs the declaration of a class member. */
public ClassMemberTrackReason logMemberDecl(
- ClassMemberKey memberKey, int ownerAccess, int memberDeclAccess) {
+ 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) {
+ public ClassMemberTrackReason logMemberUse(ClassMemberKey<?> memberKey, int invokeOpcode) {
return reasons
.computeIfAbsent(memberKey, classMemberKey -> new ClassMemberTrackReason())
.addUseAccess(invokeOpcode);
@@ -122,4 +123,11 @@
reasons.merge(
classMemberKey, classMemberTrackReason, ClassMemberTrackReason::mergeFrom));
}
+
+ @Override
+ public ClassMemberRecord acceptTypeMapper(TypeMapper typeMapper) {
+ return new ClassMemberRecord(
+ reasons.keySet().stream()
+ .collect(toMap(key -> key.acceptTypeMapper(typeMapper), reasons::get)));
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUse.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUse.java
index 75c9b6a..e9ea744 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUse.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUse.java
@@ -24,24 +24,29 @@
* access.
*/
@AutoValue
-public abstract class ClassMemberUse {
+public abstract class ClassMemberUse implements TypeMappable<ClassMemberUse> {
- public abstract ClassMemberKey method();
+ public abstract ClassMemberKey<?> method();
public abstract MemberUseKind useKind();
- public static ClassMemberUse create(ClassMemberKey memberKey, MemberUseKind memberUseKind) {
+ public static ClassMemberUse create(ClassMemberKey<?> memberKey, MemberUseKind memberUseKind) {
return new AutoValue_ClassMemberUse(memberKey, memberUseKind);
}
// Performs the current member use on the given class visitor.
public final void acceptClassMethodInsn(MethodVisitor mv) {
- ClassMemberKey method = method();
+ ClassMemberKey<?> method = method();
mv.visitMethodInsn(
useKind().getOpcode(),
- method.owner(),
+ method.ownerName(),
method.name(),
method.descriptor(),
/* isInterface= */ false);
}
+
+ @Override
+ public ClassMemberUse acceptTypeMapper(TypeMapper typeMapper) {
+ return create(method().acceptTypeMapper(typeMapper), useKind());
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUseCounter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUseCounter.java
index b59cd83..ae4051b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUseCounter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassMemberUseCounter.java
@@ -16,15 +16,19 @@
package com.google.devtools.build.android.desugar.langmodel;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.LongAdder;
+import java.util.stream.Collectors;
/** The counter used to track a class member use. */
-public final class ClassMemberUseCounter {
+public final class ClassMemberUseCounter implements TypeMappable<ClassMemberUseCounter> {
/** Tracks a class member with its associated count. */
- private final ConcurrentHashMap<ClassMemberUse, LongAdder> memberUseCounter =
- new ConcurrentHashMap<>();
+ private final ConcurrentMap<ClassMemberUse, LongAdder> memberUseCounter;
+
+ public ClassMemberUseCounter(ConcurrentMap<ClassMemberUse, LongAdder> memberUseCounter) {
+ this.memberUseCounter = memberUseCounter;
+ }
/** Increases the member use count by one when an member access is encountered. */
public void incrementMemberUseCount(ClassMemberUse classMemberUse) {
@@ -35,4 +39,13 @@
public long getMemberUseCount(ClassMemberUse memberKey) {
return memberUseCounter.getOrDefault(memberKey, new LongAdder()).longValue();
}
+
+ @Override
+ public ClassMemberUseCounter acceptTypeMapper(TypeMapper typeMapper) {
+ return new ClassMemberUseCounter(
+ memberUseCounter.keySet().stream()
+ .collect(
+ Collectors.toConcurrentMap(
+ memberUse -> memberUse.acceptTypeMapper(typeMapper), memberUseCounter::get)));
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
new file mode 100644
index 0000000..fb47b5f
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/ClassName.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * 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 com.google.devtools.build.android.desugar.langmodel;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.value.AutoValue;
+import org.objectweb.asm.Type;
+
+/**
+ * Represents the identifiable name of a Java class or interface with convenient conversions among
+ * different names.
+ */
+@AutoValue
+public abstract class ClassName implements TypeMappable<ClassName> {
+
+ /**
+ * The textual binary name used to index the class name, as defined at,
+ * https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.2.1
+ */
+ public abstract String binaryName();
+
+ public static ClassName create(String binaryName) {
+ checkState(
+ !binaryName.contains("."),
+ "Expected a binary/internal class name ('/'-delimited) instead of a qualified name."
+ + " Actual: (%s)",
+ binaryName);
+ return new AutoValue_ClassName(binaryName);
+ }
+
+ public static ClassName create(Class<?> clazz) {
+ return create(Type.getInternalName(clazz));
+ }
+
+ public final Type toAsmObjectType() {
+ return Type.getObjectType(binaryName());
+ }
+
+ public final String qualifiedName() {
+ return binaryName().replace('/', '.');
+ }
+
+ public ClassName innerClass(String innerClassSimpleName) {
+ return ClassName.create(binaryName() + '$' + innerClassSimpleName);
+ }
+
+ public final String simpleName() {
+ String binaryName = binaryName();
+ int i = binaryName.lastIndexOf('/');
+ return i < 0 ? binaryName : binaryName.substring(i + 1);
+ }
+
+ public final String classFilePathName() {
+ return binaryName() + ".class";
+ }
+
+ public final ClassName prependPrefix(String prefix) {
+ return ClassName.create(prefix + binaryName());
+ }
+
+ @Override
+ public ClassName acceptTypeMapper(TypeMapper typeMapper) {
+ return typeMapper.map(this);
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/FieldKey.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/FieldKey.java
index c954ab2..0ed43e2 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/FieldKey.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/FieldKey.java
@@ -23,24 +23,22 @@
/** The key to index a class or interface field. */
@AutoValue
-public abstract class FieldKey extends ClassMemberKey {
+public abstract class FieldKey extends ClassMemberKey<FieldKey> {
/** The factory method for {@link FieldKey}. */
- public static FieldKey create(String ownerClass, String name, String descriptor) {
- checkState(
- !ownerClass.contains("."),
- "Expected a binary/internal class name ('/'-delimited) instead of a qualified name."
- + " Actual: (%s#%s:%s)",
- ownerClass,
- name,
- descriptor);
+ public static FieldKey create(ClassName owner, String name, String descriptor) {
checkState(
!descriptor.startsWith("("),
"Expected a type descriptor for field instead of a method descriptor. Actual: (%s#%s:%s)",
- ownerClass,
+ owner,
name,
descriptor);
- return new AutoValue_FieldKey(ownerClass, name, descriptor);
+ return new AutoValue_FieldKey(owner, name, descriptor);
+ }
+
+ @Override
+ public FieldKey acceptTypeMapper(TypeMapper typeMapper) {
+ return FieldKey.create(typeMapper.map(owner()), name(), typeMapper.mapDesc(descriptor()));
}
/**
@@ -82,7 +80,7 @@
return MethodKey.create(
owner(),
nameWithSuffix("bridge_getter"),
- Type.getMethodDescriptor(getFieldType(), Type.getObjectType(owner())));
+ Type.getMethodDescriptor(getFieldType(), Type.getObjectType(ownerName())));
}
/**
@@ -102,7 +100,7 @@
return MethodKey.create(
owner(),
nameWithSuffix("bridge_setter"),
- Type.getMethodDescriptor(getFieldType(), Type.getObjectType(owner()), getFieldType()));
+ Type.getMethodDescriptor(getFieldType(), Type.getObjectType(ownerName()), getFieldType()));
}
public Type getFieldType() {
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/LangModelHelper.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/LangModelHelper.java
index 731744d..d339ed5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/LangModelHelper.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/LangModelHelper.java
@@ -142,9 +142,9 @@
* enclosing method.
*/
public static boolean isCrossMateRefInNest(
- ClassMemberKey referencedMember, MethodKey enclosingMethod) {
- String enclosingClassName = enclosingMethod.owner();
- String referencedMemberName = referencedMember.owner();
+ ClassMemberKey<?> referencedMember, MethodKey enclosingMethod) {
+ String enclosingClassName = enclosingMethod.ownerName();
+ String referencedMemberName = referencedMember.ownerName();
return (isEligibleAsInnerClass(enclosingClassName)
|| isEligibleAsInnerClass(referencedMemberName))
&& !referencedMemberName.equals(enclosingClassName);
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodDeclInfo.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodDeclInfo.java
index 7f64d26..920b120 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodDeclInfo.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodDeclInfo.java
@@ -22,7 +22,7 @@
/** A unit data object represents a class or interface declaration. */
// TODO(deltazulu): Consider @AutoValue-ize this class. (String[] as attribute is not supported).
-public final class MethodDeclInfo {
+public final class MethodDeclInfo implements TypeMappable<MethodDeclInfo> {
public final MethodKey methodKey;
public final int ownerAccess;
public final int memberAccess;
@@ -63,4 +63,10 @@
: visitor.visitClassInstanceMethod(this, param);
}
}
+
+ @Override
+ public MethodDeclInfo acceptTypeMapper(TypeMapper typeMapper) {
+ return new MethodDeclInfo(
+ methodKey.acceptTypeMapper(typeMapper), ownerAccess, memberAccess, signature, exceptions);
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
index 8c6d15e..3e153f2 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/MethodKey.java
@@ -24,17 +24,10 @@
/** The key to index a class or interface method or constructor. */
@AutoValue
-public abstract class MethodKey extends ClassMemberKey {
+public abstract class MethodKey extends ClassMemberKey<MethodKey> {
/** The factory method for {@link MethodKey}. */
- public static MethodKey create(String ownerClass, String name, String descriptor) {
- checkState(
- !ownerClass.contains("."),
- "Expected a binary/internal class name ('/'-delimited) instead of a qualified name."
- + " Actual: (%s#%s:%s)",
- ownerClass,
- name,
- descriptor);
+ public static MethodKey create(ClassName ownerClass, String name, String descriptor) {
checkState(
descriptor.isEmpty() // Allows empty descriptor for non-overloaded methods.
|| descriptor.startsWith("("),
@@ -56,37 +49,36 @@
}
/** The synthetic constructor for a private constructor. */
- public final MethodKey bridgeOfConstructor(String nestCompanion) {
+ public final MethodKey bridgeOfConstructor(ClassName nestCompanion) {
checkState(isConstructor(), "Expect to use for a constructor but is %s", this);
- Type companionClassType = Type.getObjectType(nestCompanion);
+ Type companionClassType = nestCompanion.toAsmObjectType();
Type[] argumentTypes = getArgumentTypes();
Type[] bridgeConstructorArgTypes = Arrays.copyOf(argumentTypes, argumentTypes.length + 1);
bridgeConstructorArgTypes[argumentTypes.length] = companionClassType;
- return MethodKey.create(
+ return create(
owner(), name(), Type.getMethodDescriptor(getReturnType(), bridgeConstructorArgTypes));
}
/** The synthetic bridge method for a private static method in a class. */
public final MethodKey bridgeOfClassStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor method but is a constructor %s", this);
- return MethodKey.create(owner(), nameWithSuffix("bridge"), descriptor());
+ return create(owner(), nameWithSuffix("bridge"), descriptor());
}
/** The synthetic bridge method for a private instance method in a class. */
public final MethodKey bridgeOfClassInstanceMethod() {
- return MethodKey.create(
- owner(), nameWithSuffix("bridge"), instanceMethodToStaticDescriptor(this));
+ return create(owner(), nameWithSuffix("bridge"), instanceMethodToStaticDescriptor(this));
}
/** The substitute method for a private static method in an interface. */
public final MethodKey substituteOfInterfaceStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor: %s", this);
- return MethodKey.create(owner(), name(), descriptor());
+ return create(owner(), name(), descriptor());
}
/** The substitute method for a private instance method in an interface. */
public final MethodKey substituteOfInterfaceInstanceMethod() {
- return MethodKey.create(owner(), name(), instanceMethodToStaticDescriptor(this));
+ return create(owner(), name(), instanceMethodToStaticDescriptor(this));
}
/** The descriptor of the static version of a given instance method. */
@@ -94,11 +86,16 @@
checkState(!methodKey.isConstructor(), "Expect a Non-constructor method: %s", methodKey);
Type[] argumentTypes = methodKey.getArgumentTypes();
Type[] bridgeMethodArgTypes = new Type[argumentTypes.length + 1];
- bridgeMethodArgTypes[0] = Type.getObjectType(methodKey.owner());
+ bridgeMethodArgTypes[0] = Type.getObjectType(methodKey.ownerName());
System.arraycopy(argumentTypes, 0, bridgeMethodArgTypes, 1, argumentTypes.length);
return Type.getMethodDescriptor(methodKey.getReturnType(), bridgeMethodArgTypes);
}
+ @Override
+ public MethodKey acceptTypeMapper(TypeMapper typeMapper) {
+ return MethodKey.create(typeMapper.map(owner()), name(), typeMapper.mapDesc(descriptor()));
+ }
+
/**
* Accepts a {@link MethodInstrVisitor} that visits all kinds of method invocation instructions.
*/
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMappable.java b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMappable.java
new file mode 100644
index 0000000..b036e8b
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMappable.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * 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 com.google.devtools.build.android.desugar.langmodel;
+
+/**
+ * Imposes that the implementing class is to support deep type remapping with a given {@link
+ * TypeMapper}.
+ */
+@FunctionalInterface
+public interface TypeMappable<T> {
+
+ /**
+ * Accepts a type mapper and returns a new instance of remapped struct without changing the
+ * original source instance. Please apply {@param typeMapper} to any index-able state of the
+ * implementation class.
+ */
+ T acceptTypeMapper(TypeMapper typeMapper);
+}
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
new file mode 100644
index 0000000..4e0d1e9
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/langmodel/TypeMapper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Bazel Authors. All rights reserved.
+ *
+ * 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 com.google.devtools.build.android.desugar.langmodel;
+
+import java.util.function.Function;
+import org.objectweb.asm.commons.Remapper;
+
+/** Maps a type to another based on binary names. */
+public final class TypeMapper extends Remapper {
+
+ private final Function<ClassName, ClassName> classNameMapper;
+
+ public TypeMapper(Function<ClassName, ClassName> classNameMapper) {
+ this.classNameMapper = classNameMapper;
+ }
+
+ @Override
+ public String map(String binaryName) {
+ return map(ClassName.create(binaryName)).binaryName();
+ }
+
+ public ClassName map(ClassName internalName) {
+ return classNameMapper.apply(internalName);
+ }
+}
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 521e7fb..24d7b65 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
@@ -17,8 +17,8 @@
import com.google.devtools.build.android.desugar.langmodel.ClassAttributeRecord;
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.ClassMemberKey;
import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
+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;
import com.google.devtools.build.android.desugar.langmodel.MethodKey;
@@ -46,7 +46,7 @@
private final ClassAttributesBuilder classAttributesBuilder = ClassAttributes.builder();
- private String className;
+ private ClassName className;
private int classAccessCode;
private boolean isInNest;
@@ -65,7 +65,7 @@
String signature,
String superName,
String[] interfaces) {
- className = name;
+ className = ClassName.create(name);
classAccessCode = access;
classAttributesBuilder.setClassBinaryName(className);
super.visit(
@@ -115,14 +115,14 @@
@Override
public void visitNestHost(String nestHost) {
isInNest = true;
- classAttributesBuilder.setNestHost(nestHost);
+ classAttributesBuilder.setNestHost(ClassName.create(nestHost));
super.visitNestHost(nestHost);
}
@Override
public void visitNestMember(String nestMember) {
isInNest = true;
- classAttributesBuilder.addNestMember(nestMember);
+ classAttributesBuilder.addNestMember(ClassName.create(nestMember));
super.visitNestMember(nestMember);
}
@@ -161,7 +161,7 @@
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
- ClassMemberKey memberKey = FieldKey.create(owner, name, descriptor);
+ FieldKey memberKey = FieldKey.create(ClassName.create(owner), name, descriptor);
if (LangModelHelper.isCrossMateRefInNest(memberKey, enclosingMethodKey)) {
memberRecord.logMemberUse(memberKey, opcode);
}
@@ -171,7 +171,7 @@
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface) {
- ClassMemberKey memberKey = MethodKey.create(owner, name, descriptor);
+ MethodKey memberKey = MethodKey.create(ClassName.create(owner), name, descriptor);
if (isInterface || LangModelHelper.isCrossMateRefInNest(memberKey, enclosingMethodKey)) {
memberRecord.logMemberUse(memberKey, opcode);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/FieldAccessBridgeEmitter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/FieldAccessBridgeEmitter.java
index d2a4643..cbc1978 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/FieldAccessBridgeEmitter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/FieldAccessBridgeEmitter.java
@@ -47,7 +47,7 @@
/* signature= */ null,
/* exceptions= */ null);
- mv.visitFieldInsn(GETSTATIC, fieldKey.owner(), fieldKey.name(), fieldKey.descriptor());
+ mv.visitFieldInsn(GETSTATIC, fieldKey.ownerName(), fieldKey.name(), fieldKey.descriptor());
Type fieldType = fieldKey.getFieldType();
mv.visitInsn(fieldType.getOpcode(Opcodes.IRETURN));
int fieldTypeSize = fieldType.getSize();
@@ -72,7 +72,8 @@
mv.visitVarInsn(fieldType.getOpcode(Opcodes.ILOAD), 0);
mv.visitInsn(
LangModelHelper.getTypeSizeAlignedDupOpcode(ImmutableList.of(fieldKey.getFieldType())));
- mv.visitFieldInsn(Opcodes.PUTSTATIC, fieldKey.owner(), fieldKey.name(), fieldKey.descriptor());
+ mv.visitFieldInsn(
+ Opcodes.PUTSTATIC, fieldKey.ownerName(), fieldKey.name(), fieldKey.descriptor());
mv.visitInsn(fieldType.getOpcode(Opcodes.IRETURN));
int fieldTypeSize = fieldType.getSize();
mv.visitMaxs(fieldTypeSize, fieldTypeSize);
@@ -93,7 +94,8 @@
/* exceptions= */ null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
- mv.visitFieldInsn(Opcodes.GETFIELD, fieldKey.owner(), fieldKey.name(), fieldKey.descriptor());
+ mv.visitFieldInsn(
+ Opcodes.GETFIELD, fieldKey.ownerName(), fieldKey.name(), fieldKey.descriptor());
Type fieldType = fieldKey.getFieldType();
mv.visitInsn(fieldType.getOpcode(Opcodes.IRETURN));
int fieldTypeSize = fieldType.getSize();
@@ -121,7 +123,8 @@
LangModelHelper.getTypeSizeAlignedDupOpcode(
ImmutableList.of(fieldKey.getFieldType()),
ImmutableList.of(Type.getType(Object.class))));
- mv.visitFieldInsn(Opcodes.PUTFIELD, fieldKey.owner(), fieldKey.name(), fieldKey.descriptor());
+ mv.visitFieldInsn(
+ Opcodes.PUTFIELD, fieldKey.ownerName(), fieldKey.name(), fieldKey.descriptor());
mv.visitInsn(fieldType.getOpcode(Opcodes.IRETURN));
int fieldTypeSize = fieldType.getSize();
mv.visitMaxs(fieldTypeSize, fieldTypeSize);
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/MethodAccessorEmitter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/MethodAccessorEmitter.java
index f842243..c9e29b8 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/MethodAccessorEmitter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/MethodAccessorEmitter.java
@@ -21,6 +21,7 @@
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
import com.google.devtools.build.android.desugar.langmodel.MethodDeclInfo;
import com.google.devtools.build.android.desugar.langmodel.MethodDeclVisitor;
import com.google.devtools.build.android.desugar.langmodel.MethodKey;
@@ -36,10 +37,10 @@
final class MethodAccessorEmitter
implements MethodDeclVisitor<MethodVisitor, MethodDeclInfo, ClassVisitor> {
- private final NestCompanions nestCompanions;
+ private final NestDigest nestDigest;
- MethodAccessorEmitter(NestCompanions nestCompanions) {
- this.nestCompanions = nestCompanions;
+ MethodAccessorEmitter(NestDigest nestDigest) {
+ this.nestDigest = nestDigest;
}
/**
@@ -59,7 +60,7 @@
*/
@Override
public MethodVisitor visitClassConstructor(MethodDeclInfo methodDeclInfo, ClassVisitor cv) {
- String nestCompanion = nestCompanions.nestCompanion(methodDeclInfo.methodKey.owner());
+ ClassName nestCompanion = nestDigest.nestCompanion(methodDeclInfo.methodKey.owner());
MethodKey constructorBridge = methodDeclInfo.methodKey.bridgeOfConstructor(nestCompanion);
MethodVisitor mv =
cv.visitMethod(
@@ -79,7 +80,7 @@
}
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
- methodDeclInfo.methodKey.owner(),
+ methodDeclInfo.methodKey.ownerName(),
methodDeclInfo.methodKey.name(),
methodDeclInfo.methodKey.descriptor(),
/* isInterface= */ false);
@@ -126,7 +127,7 @@
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- methodDeclInfo.methodKey.owner(),
+ methodDeclInfo.methodKey.ownerName(),
methodDeclInfo.methodKey.name(),
methodDeclInfo.methodKey.descriptor(),
/* isInterface= */ false);
@@ -169,7 +170,7 @@
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
- methodDeclInfo.methodKey.owner(),
+ methodDeclInfo.methodKey.ownerName(),
methodDeclInfo.methodKey.name(),
methodDeclInfo.methodKey.descriptor(),
/* isInterface= */ false);
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 c4658cf..14a0e77 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
@@ -27,12 +27,12 @@
/**
* An analyzer that performs nest-based analysis and save the states to {@link ClassMemberRecord}
- * and generated {@link NestCompanions}.
+ * and generated {@link NestDigest}.
*/
public class NestAnalyzer {
private final ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents;
- private final NestCompanions nestCompanions;
+ private final NestDigest nestDigest;
private final ClassMemberRecord classMemberRecord;
private final ClassAttributeRecord classAttributeRecord;
@@ -42,27 +42,26 @@
*
* @return A manager class for nest companions.
*/
- public static NestCompanions analyzeNests(
+ public static NestDigest analyzeNests(
ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents,
ClassMemberRecord classMemberRecord,
ClassAttributeRecord classAttributeRecord)
throws IOException {
- NestCompanions nestCompanions = NestCompanions.create(classMemberRecord, classAttributeRecord);
+ NestDigest nestDigest = NestDigest.create(classMemberRecord, classAttributeRecord);
NestAnalyzer nestAnalyzer =
- new NestAnalyzer(
- inputFileContents, nestCompanions, classMemberRecord, classAttributeRecord);
+ new NestAnalyzer(inputFileContents, nestDigest, classMemberRecord, classAttributeRecord);
nestAnalyzer.analyze();
- return nestCompanions;
+ return nestDigest;
}
@VisibleForTesting
NestAnalyzer(
ImmutableList<FileContentProvider<? extends InputStream>> inputFileContents,
- NestCompanions nestCompanions,
+ NestDigest nestDigest,
ClassMemberRecord classMemberRecord,
ClassAttributeRecord classAttributeRecord) {
this.inputFileContents = checkNotNull(inputFileContents);
- this.nestCompanions = checkNotNull(nestCompanions);
+ this.nestDigest = checkNotNull(nestDigest);
this.classMemberRecord = checkNotNull(classMemberRecord);
this.classAttributeRecord = classAttributeRecord;
}
@@ -81,6 +80,6 @@
}
}
classMemberRecord.filterUsedMemberWithTrackedDeclaration();
- nestCompanions.prepareCompanionClasses();
+ nestDigest.prepareCompanionClasses();
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestBridgeRefConverter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestBridgeRefConverter.java
index 460ddc2..9844ed6 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestBridgeRefConverter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestBridgeRefConverter.java
@@ -17,7 +17,7 @@
import static com.google.devtools.build.android.desugar.langmodel.LangModelHelper.isCrossMateRefInNest;
import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
import com.google.devtools.build.android.desugar.langmodel.FieldInstrVisitor;
import com.google.devtools.build.android.desugar.langmodel.FieldKey;
import com.google.devtools.build.android.desugar.langmodel.LangModelHelper;
@@ -32,29 +32,26 @@
public final class NestBridgeRefConverter extends MethodVisitor {
private final MethodKey enclosingMethodKey;
- private final ClassMemberRecord bridgeOrigins;
+ private final NestDigest nestDigest;
private final FieldAccessToBridgeRedirector directFieldAccessReplacer;
private final MethodToBridgeRedirector methodToBridgeRedirector;
NestBridgeRefConverter(
- @Nullable MethodVisitor methodVisitor,
- MethodKey methodKey,
- ClassMemberRecord bridgeOrigins,
- NestCompanions nestCompanions) {
+ @Nullable MethodVisitor methodVisitor, MethodKey methodKey, NestDigest nestDigest) {
super(Opcodes.ASM7, methodVisitor);
this.enclosingMethodKey = methodKey;
- this.bridgeOrigins = bridgeOrigins;
+ this.nestDigest = nestDigest;
directFieldAccessReplacer = new FieldAccessToBridgeRedirector();
- methodToBridgeRedirector = new MethodToBridgeRedirector(nestCompanions);
+ methodToBridgeRedirector = new MethodToBridgeRedirector(nestDigest);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
- FieldKey fieldKey = FieldKey.create(owner, name, descriptor);
+ FieldKey fieldKey = FieldKey.create(ClassName.create(owner), name, descriptor);
MemberUseKind useKind = MemberUseKind.fromValue(opcode);
if (isCrossMateRefInNest(fieldKey, enclosingMethodKey)
- && bridgeOrigins.findAllMemberUseKind(fieldKey).contains(useKind)) {
+ && nestDigest.hasAnyUse(fieldKey, useKind)) {
fieldKey.accept(useKind, directFieldAccessReplacer, mv);
return;
}
@@ -64,10 +61,10 @@
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface) {
- MethodKey methodKey = MethodKey.create(owner, name, descriptor);
+ MethodKey methodKey = MethodKey.create(ClassName.create(owner), name, descriptor);
MemberUseKind useKind = MemberUseKind.fromValue(opcode);
if ((isInterface || isCrossMateRefInNest(methodKey, enclosingMethodKey))
- && bridgeOrigins.findAllMemberUseKind(methodKey).contains(useKind)) {
+ && nestDigest.hasAnyUse(methodKey, useKind)) {
methodKey.accept(useKind, isInterface, methodToBridgeRedirector, mv);
return;
}
@@ -78,10 +75,10 @@
static class MethodToBridgeRedirector
implements MethodInstrVisitor<MethodKey, MethodKey, MethodVisitor> {
- private final NestCompanions nestCompanions;
+ private final NestDigest nestDigest;
- MethodToBridgeRedirector(NestCompanions nestCompanions) {
- this.nestCompanions = nestCompanions;
+ MethodToBridgeRedirector(NestDigest nestDigest) {
+ this.nestDigest = nestDigest;
}
@Override
@@ -89,7 +86,7 @@
MethodKey bridgeMethodKey = methodKey.bridgeOfClassInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
@@ -101,7 +98,7 @@
MethodKey bridgeMethodKey = methodKey.bridgeOfClassInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
@@ -110,12 +107,12 @@
@Override
public MethodKey visitConstructorInvokeSpecial(MethodKey methodKey, MethodVisitor mv) {
- String nestCompanion = nestCompanions.nestCompanion(methodKey.owner());
+ ClassName nestCompanion = nestDigest.nestCompanion(ClassName.create(methodKey.ownerName()));
MethodKey constructorBridge = methodKey.bridgeOfConstructor(nestCompanion);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
- constructorBridge.owner(),
+ constructorBridge.ownerName(),
constructorBridge.name(),
constructorBridge.descriptor(),
/* isInterface= */ false);
@@ -127,7 +124,7 @@
MethodKey methodBridge = methodKey.substituteOfInterfaceInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- methodBridge.owner(),
+ methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
@@ -140,7 +137,7 @@
bridgeMethodKey = methodKey.bridgeOfClassStaticMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
@@ -153,7 +150,7 @@
methodBridge = methodKey.substituteOfInterfaceStaticMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- methodBridge.owner(),
+ methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
@@ -166,7 +163,7 @@
methodBridge = methodKey.substituteOfInterfaceInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- methodBridge.owner(),
+ methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
@@ -188,7 +185,7 @@
MethodKey bridgeMethodKey = fieldKey.bridgeOfStaticRead();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
@@ -200,7 +197,7 @@
MethodKey bridgeMethodKey = fieldKey.bridgeOfStaticWrite();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
@@ -217,7 +214,7 @@
MethodKey bridgeMethodKey = fieldKey.bridgeOfInstanceRead();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
@@ -229,7 +226,7 @@
MethodKey bridgeMethodKey = fieldKey.bridgeOfInstanceWrite();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
- bridgeMethodKey.owner(),
+ bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestCompanions.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestCompanions.java
deleted file mode 100644
index a8d511c..0000000
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestCompanions.java
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2019 The Bazel Authors. All rights reserved.
-//
-// 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 com.google.devtools.build.android.desugar.nest;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.devtools.build.android.desugar.langmodel.LangModelConstants.NEST_COMPANION_CLASS_SIMPLE_NAME;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Streams;
-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 java.io.ByteArrayInputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.objectweb.asm.ClassWriter;
-
-/** Manages the creation and IO stream for nest-companion classes. */
-public class NestCompanions {
-
- private final ClassMemberRecord classMemberRecord;
- private final ClassAttributeRecord classAttributeRecord;
- private final Map<String, String> nestCompanionToHostMap;
-
- /**
- * A map from the class binary names of nest hosts to the associated class writer of the nest's
- * companion.
- */
- private ImmutableMap<String, ClassWriter> companionWriters;
-
- public static NestCompanions create(
- ClassMemberRecord classMemberRecord, ClassAttributeRecord classAttributeRecord) {
- return new NestCompanions(classMemberRecord, classAttributeRecord, new HashMap<>());
- }
-
- private NestCompanions(
- ClassMemberRecord classMemberRecord,
- ClassAttributeRecord classAttributeRecord,
- HashMap<String, String> nestCompanionToHostMap) {
- this.classMemberRecord = classMemberRecord;
- this.classAttributeRecord = classAttributeRecord;
- this.nestCompanionToHostMap = nestCompanionToHostMap;
- }
-
- /**
- * 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<String> nestHostsWithCompanion =
- classMemberRecord.findAllConstructorMemberKeys().stream()
- .map(
- constructor ->
- nestHost(constructor.owner(), classAttributeRecord, nestCompanionToHostMap))
- .flatMap(Streams::stream)
- .distinct()
- .collect(toImmutableList());
- ImmutableMap.Builder<String, ClassWriter> companionWriterBuilders = ImmutableMap.builder();
- for (String nestHost : nestHostsWithCompanion) {
- String nestCompanion = nestHost + '$' + NEST_COMPANION_CLASS_SIMPLE_NAME;
- nestCompanionToHostMap.put(nestCompanion, nestHost);
- companionWriterBuilders.put(nestHost, new ClassWriter(ClassWriter.COMPUTE_MAXS));
- }
- companionWriters = companionWriterBuilders.build();
- }
-
- /**
- * The public API that finds the nest host for a given class. It is expected {@link
- * #prepareCompanionClasses()} executed before this API is ready. The method returns {@link
- * Optional#empty()} if the class is not part of a nest. A generated nest companion class and its
- * nest host are considered to be a nest host/member relationship.
- */
- public Optional<String> nestHost(String classBinaryName) {
- // Ensures prepareCompanionClasses has been executed.
- checkNotNull(companionWriters);
- return nestHost(classBinaryName, classAttributeRecord, nestCompanionToHostMap);
- }
-
- /**
- * The internal method finds the nest host for a given class from a class attribute record. The
- * method returns {@link * Optional#empty()} if the class is not part of a nest. A generated nest
- * companion class and its * nest host are considered to be a nest host/member relationship.
- *
- * <p>In addition to exam the NestHost_attribute from the class file, this method returns the
- * class under investigation itself for a class with NestMembers_attribute but without
- * NestHost_attribute.
- */
- private static Optional<String> nestHost(
- String classBinaryName,
- ClassAttributeRecord classAttributeRecord,
- Map<String, String> companionToHostMap) {
- if (companionToHostMap.containsKey(classBinaryName)) {
- return Optional.of(companionToHostMap.get(classBinaryName));
- }
- Optional<String> nestHost = classAttributeRecord.getNestHost(classBinaryName);
- if (nestHost.isPresent()) {
- return nestHost;
- }
- Set<String> nestMembers = classAttributeRecord.getNestMembers(classBinaryName);
- if (!nestMembers.isEmpty()) {
- return Optional.of(classBinaryName);
- }
- return Optional.empty();
- }
-
- /**
- * Returns the internal name of the nest companion class for a given class.
- *
- * <p>e.g. The nest host of a/b/C$D is a/b/C$NestCC
- */
- public String nestCompanion(String classBinaryName) {
- return nestHost(classBinaryName)
- .map(nestHost -> nestHost + '$' + NEST_COMPANION_CLASS_SIMPLE_NAME)
- .orElseThrow(
- () ->
- new IllegalStateException(
- String.format(
- "Expected the presence of NestHost attribute of %s to get nest companion.",
- classBinaryName)));
- }
-
- /**
- * Gets the class visitor of the affiliated nest host of the given class. E.g For the given class
- * com/google/a/b/Delta$Echo, it returns the class visitor of com/google/a/b/Delta$NestCC
- */
- @Nullable
- public ClassWriter getCompanionClassWriter(String classInternalName) {
- return nestHost(classInternalName).map(nestHost -> companionWriters.get(nestHost)).orElse(null);
- }
-
- /** Gets all nest companion classes required to be generated. */
- public ImmutableList<String> getAllCompanionClasses() {
- return companionWriters.keySet().stream().map(this::nestCompanion).collect(toImmutableList());
- }
-
- /** Gets all nest companion files required to be generated. */
- public ImmutableList<FileContentProvider<ByteArrayInputStream>> getCompanionFileProviders() {
- ImmutableList.Builder<FileContentProvider<ByteArrayInputStream>> fileContents =
- ImmutableList.builder();
- for (String companion : getAllCompanionClasses()) {
- fileContents.add(
- new FileContentProvider<>(
- companion + ".class", () -> getByteArrayInputStreamOfCompanionClass(companion)));
- }
- return fileContents.build();
- }
-
- private ByteArrayInputStream getByteArrayInputStreamOfCompanionClass(String companion) {
- ClassWriter companionClassWriter = getCompanionClassWriter(companion);
- checkNotNull(
- companionClassWriter,
- "Expected companion class (%s) to be present in (%s)",
- companionWriters);
- return new ByteArrayInputStream(companionClassWriter.toByteArray());
- }
-}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDesugaring.java
index b03500e..7d0c7de 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDesugaring.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDesugaring.java
@@ -19,7 +19,7 @@
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
-import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
+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.MethodDeclInfo;
import com.google.devtools.build.android.desugar.langmodel.MethodKey;
@@ -42,31 +42,21 @@
private static final int NEST_COMPANION_CLASS_ACCESS_CODE =
Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STATIC;
- private final NestCompanions nestCompanions;
-
- /**
- * All originating members of bridges, i.e. all fields, constructors, methods with an associated
- * bridge method or companion method.
- */
- private final ClassMemberRecord classMemberRecord;
+ private final NestDigest nestDigest;
private FieldAccessBridgeEmitter fieldAccessBridgeEmitter;
private MethodAccessorEmitter methodAccessorEmitter;
- private String className;
+ private ClassName className;
private int classAccess;
@Nullable private ClassVisitor nestCompanionVisitor;
/** Whether the class being visited is a nest host with with a nest companion class. */
private boolean isNestHostWithNestCompanion;
- public NestDesugaring(
- ClassVisitor classVisitor,
- NestCompanions nestCompanions,
- ClassMemberRecord classMemberRecord) {
+ public NestDesugaring(ClassVisitor classVisitor, NestDigest nestDigest) {
super(Opcodes.ASM7, classVisitor);
- this.nestCompanions = nestCompanions;
- this.classMemberRecord = classMemberRecord;
+ this.nestDigest = nestDigest;
}
@Override
@@ -77,14 +67,13 @@
String signature,
String superName,
String[] interfaces) {
- className = name;
+ className = ClassName.create(name);
classAccess = access;
- nestCompanionVisitor = nestCompanions.getCompanionClassWriter(name);
+ nestCompanionVisitor = nestDigest.getCompanionClassWriter(className);
isNestHostWithNestCompanion =
- (nestCompanionVisitor != null)
- && className.equals(nestCompanions.nestHost(className).get());
+ (nestCompanionVisitor != null) && className.equals(nestDigest.nestHost(className).get());
fieldAccessBridgeEmitter = new FieldAccessBridgeEmitter();
- methodAccessorEmitter = new MethodAccessorEmitter(nestCompanions);
+ methodAccessorEmitter = new MethodAccessorEmitter(nestDigest);
super.visit(
Math.min(version, NestDesugarConstants.MIN_VERSION),
access,
@@ -96,7 +85,7 @@
nestCompanionVisitor.visit(
Math.min(version, NestDesugarConstants.MIN_VERSION),
ACC_SYNTHETIC | ACC_ABSTRACT,
- nestCompanions.nestCompanion(className),
+ nestDigest.nestCompanion(className).binaryName(),
/* signature= */ null,
/* superName= */ "java/lang/Object",
/* interfaces= */ new String[0]);
@@ -109,8 +98,8 @@
FieldKey fieldKey = FieldKey.create(className, name, descriptor);
// Generates necessary bridge methods for this field, including field getter methods and field
// setter methods. See FieldAccessBridgeEmitter.
- classMemberRecord
- .findAllMemberUseKind(fieldKey)
+ nestDigest
+ .findAllMemberUseKinds(fieldKey)
.forEach(useKind -> fieldKey.accept(useKind, fieldAccessBridgeEmitter, cv));
return super.visitField(access, name, descriptor, signature, value);
}
@@ -119,7 +108,7 @@
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
MethodKey methodKey = MethodKey.create(className, name, descriptor);
- if (classMemberRecord.hasTrackingReason(methodKey)) {
+ if (nestDigest.hasAnyTrackingReason(methodKey)) {
MethodDeclInfo methodDeclInfo =
new MethodDeclInfo(methodKey, classAccess, access, signature, exceptions);
// For interfaces, the following .accept converts the original method into a
@@ -137,14 +126,11 @@
(classAccess & ACC_INTERFACE) != 0
? targetMethodVisitor
: super.visitMethod(access, name, descriptor, signature, exceptions);
- return new NestBridgeRefConverter(
- primaryMethodVisitor, methodKey, classMemberRecord, nestCompanions);
+ return new NestBridgeRefConverter(primaryMethodVisitor, methodKey, nestDigest);
}
// In absence of method invocation record, fallback to the delegate method visitor.
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
- return mv == null
- ? null
- : new NestBridgeRefConverter(mv, methodKey, classMemberRecord, nestCompanions);
+ return mv == null ? null : new NestBridgeRefConverter(mv, methodKey, nestDigest);
}
@Override
@@ -168,17 +154,17 @@
@Override
public void visitEnd() {
if (isNestHostWithNestCompanion) {
- String nestCompanionClassName = nestCompanions.nestCompanion(className);
+ ClassName nestCompanion = nestDigest.nestCompanion(className);
// In the nest companion class, marks its outer class as the nest host.
nestCompanionVisitor.visitInnerClass(
- nestCompanionClassName,
- className,
+ nestCompanion.binaryName(),
+ className.binaryName(),
NEST_COMPANION_CLASS_SIMPLE_NAME,
NEST_COMPANION_CLASS_ACCESS_CODE);
// In the nest host class, marks the nest companion as one of its inner classes.
cv.visitInnerClass(
- nestCompanionClassName,
- className,
+ nestCompanion.binaryName(),
+ className.binaryName(),
NEST_COMPANION_CLASS_SIMPLE_NAME,
NEST_COMPANION_CLASS_ACCESS_CODE);
}
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
new file mode 100644
index 0000000..71e7141
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/nest/NestDigest.java
@@ -0,0 +1,220 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// 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 com.google.devtools.build.android.desugar.nest;
+
+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 static java.util.stream.Collectors.toMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Streams;
+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.ClassMemberKey;
+import com.google.devtools.build.android.desugar.langmodel.ClassMemberRecord;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
+import com.google.devtools.build.android.desugar.langmodel.MemberUseKind;
+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;
+import javax.annotation.Nullable;
+import org.objectweb.asm.ClassWriter;
+
+/** Manages the creation and IO stream for nest-companion classes. */
+public class NestDigest implements TypeMappable<NestDigest> {
+
+ private final ClassMemberRecord classMemberRecord;
+ private final ClassAttributeRecord classAttributeRecord;
+ private final Map<ClassName, ClassName> nestCompanionToHostMap;
+
+ /**
+ * 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);
+ }
+
+ 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 boolean hasAnyTrackingReason(ClassMemberKey<?> classMemberKey) {
+ return classMemberRecord.hasTrackingReason(classMemberKey);
+ }
+
+ public boolean hasAnyUse(ClassMemberKey<?> classMemberKey, MemberUseKind useKind) {
+ return findAllMemberUseKinds(classMemberKey).contains(useKind);
+ }
+
+ public ImmutableList<MemberUseKind> findAllMemberUseKinds(ClassMemberKey<?> classMemberKey) {
+ return classMemberRecord.findAllMemberUseKind(classMemberKey);
+ }
+
+ /**
+ * The public API that finds the nest host for a given class. It is expected {@link
+ * #prepareCompanionClasses()} executed before this API is ready. The method returns {@link
+ * Optional#empty()} if the class is not part of a nest. A generated nest companion class and its
+ * nest host are considered to be a nest host/member relationship.
+ */
+ public Optional<ClassName> nestHost(ClassName className) {
+ // Ensures prepareCompanionClasses has been executed.
+ checkNotNull(companionWriters);
+ return nestHost(className, classAttributeRecord, nestCompanionToHostMap);
+ }
+
+ /**
+ * The internal method finds the nest host for a given class from a class attribute record. The
+ * method returns {@link * Optional#empty()} if the class is not part of a nest. A generated nest
+ * companion class and its * nest host are considered to be a nest host/member relationship.
+ *
+ * <p>In addition to exam the NestHost_attribute from the class file, this method returns the
+ * class under investigation itself for a class with NestMembers_attribute but without
+ * NestHost_attribute.
+ */
+ private static Optional<ClassName> nestHost(
+ ClassName className,
+ ClassAttributeRecord classAttributeRecord,
+ Map<ClassName, ClassName> companionToHostMap) {
+ if (companionToHostMap.containsKey(className)) {
+ return Optional.of(companionToHostMap.get(className));
+ }
+ Optional<ClassName> nestHost = classAttributeRecord.getNestHost(className);
+ if (nestHost.isPresent()) {
+ return nestHost;
+ }
+ Set<ClassName> nestMembers = classAttributeRecord.getNestMembers(className);
+ if (!nestMembers.isEmpty()) {
+ return Optional.of(className);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the internal name of the nest companion class for a given class.
+ *
+ * <p>e.g. The nest host of a/b/C$D is a/b/C$NestCC
+ */
+ public ClassName nestCompanion(ClassName className) {
+ return nestHost(className)
+ .map(nestHost -> nestHost.innerClass(NEST_COMPANION_CLASS_SIMPLE_NAME))
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ String.format(
+ "Expected the presence of NestHost attribute of %s to get nest companion.",
+ className)));
+ }
+
+ /**
+ * Gets the class visitor of the affiliated nest host of the given class. E.g For the given class
+ * com/google/a/b/Delta$Echo, it returns the class visitor of com/google/a/b/Delta$NestCC
+ */
+ @Nullable
+ public ClassWriter getCompanionClassWriter(ClassName className) {
+ return nestHost(className).map(nestHost -> companionWriters.get(nestHost)).orElse(null);
+ }
+
+ /** Gets all nest companion classes required to be generated. */
+ public ImmutableList<String> getAllCompanionClassNames() {
+ return getAllCompanionClasses().stream().map(ClassName::binaryName).collect(toImmutableList());
+ }
+
+ public ImmutableList<ClassName> getAllCompanionClasses() {
+ return companionWriters.keySet().stream().map(this::nestCompanion).collect(toImmutableList());
+ }
+
+ /** Gets all nest companion files required to be generated. */
+ public ImmutableList<FileContentProvider<ByteArrayInputStream>> getCompanionFileProviders() {
+ ImmutableList.Builder<FileContentProvider<ByteArrayInputStream>> fileContents =
+ ImmutableList.builder();
+ for (ClassName companion : getAllCompanionClasses()) {
+ fileContents.add(
+ new FileContentProvider<>(
+ companion.classFilePathName(),
+ () -> getByteArrayInputStreamOfCompanionClass(companion)));
+ }
+ return fileContents.build();
+ }
+
+ private ByteArrayInputStream getByteArrayInputStreamOfCompanionClass(ClassName companion) {
+ ClassWriter companionClassWriter = getCompanionClassWriter(companion);
+ companionClassWriter.visitEnd();
+ checkNotNull(
+ companionClassWriter,
+ "Expected companion class (%s) to be present in (%s)",
+ companionWriters);
+ return new ByteArrayInputStream(companionClassWriter.toByteArray());
+ }
+
+ @Override
+ public NestDigest acceptTypeMapper(TypeMapper typeMapper) {
+ return new NestDigest(
+ classMemberRecord.acceptTypeMapper(typeMapper),
+ classAttributeRecord.acceptTypeMapper(typeMapper),
+ nestCompanionToHostMap.keySet().stream()
+ .collect(
+ toMap(
+ companion -> companion.acceptTypeMapper(typeMapper),
+ companion ->
+ nestCompanionToHostMap.get(companion).acceptTypeMapper(typeMapper))),
+ companionWriters.keySet().stream()
+ .collect(
+ toImmutableMap(
+ nestHost -> nestHost.acceptTypeMapper(typeMapper),
+ nestHost -> new ClassWriter(ClassWriter.COMPUTE_MAXS))));
+ }
+}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/strconcat/IndyStringConcatDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/strconcat/IndyStringConcatDesugaring.java
index 4dece59..f86e7a9 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/strconcat/IndyStringConcatDesugaring.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/strconcat/IndyStringConcatDesugaring.java
@@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.desugar.langmodel.ClassMemberUse;
import com.google.devtools.build.android.desugar.langmodel.ClassMemberUseCounter;
+import com.google.devtools.build.android.desugar.langmodel.ClassName;
import com.google.devtools.build.android.desugar.langmodel.LangModelHelper;
import com.google.devtools.build.android.desugar.langmodel.MemberUseKind;
import com.google.devtools.build.android.desugar.langmodel.MethodKey;
@@ -38,7 +39,7 @@
public static final ClassMemberUse INVOKE_JDK11_STRING_CONCAT =
ClassMemberUse.create(
MethodKey.create(
- "java/lang/invoke/StringConcatFactory",
+ ClassName.create("java/lang/invoke/StringConcatFactory"),
"makeConcatWithConstants",
"(Ljava/lang/invoke/MethodHandles$Lookup;"
+ "Ljava/lang/String;"
@@ -51,7 +52,7 @@
private static final ClassMemberUse INVOKE_STRING_CONCAT_REPLACEMENT_METHOD =
ClassMemberUse.create(
MethodKey.create(
- "com/google/devtools/build/android/desugar/runtime/StringConcats",
+ ClassName.create("com/google/devtools/build/android/desugar/runtime/StringConcats"),
"concat",
"([Ljava/lang/Object;"
+ "Ljava/lang/String;"
@@ -95,7 +96,7 @@
ClassMemberUse bootstrapMethodInvocation =
ClassMemberUse.create(
MethodKey.create(
- bootstrapMethodHandle.getOwner(),
+ ClassName.create(bootstrapMethodHandle.getOwner()),
bootstrapMethodHandle.getName(),
bootstrapMethodHandle.getDesc()),
MemberUseKind.INVOKEDYNAMIC);
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
index b61244b..6dce8d6 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/testing/junit/RuntimeEntityResolver.java
@@ -33,6 +33,7 @@
import com.google.common.collect.Table;
import com.google.devtools.build.android.desugar.Desugar;
import com.google.devtools.build.android.desugar.langmodel.ClassMemberKey;
+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.MethodKey;
import com.google.devtools.build.android.desugar.testing.junit.RuntimeMethodHandle.MemberUseContext;
@@ -97,8 +98,8 @@
/** A table for the lookup of missing user-supplied class member descriptors. */
private final Table<
Integer, // Desugar round
- ClassMemberKey, // A class member without descriptor (empty descriptor string).
- Set<ClassMemberKey>> // The set of same-name class members with their descriptors.
+ ClassMemberKey<?>, // A class member without descriptor (empty descriptor string).
+ Set<ClassMemberKey<?>>> // The set of same-name class members with their descriptors.
descriptorLookupRepo = HashBasedTable.create();
/**
@@ -107,7 +108,7 @@
*/
private final Table<
Integer, // Desugar round
- ClassMemberKey, // A class member with descriptor.
+ ClassMemberKey<?>, // A class member with descriptor.
java.lang.reflect.Member> // A reflection-based Member instance.
reflectionBasedMembers = HashBasedTable.create();
@@ -194,12 +195,12 @@
private static void fillMissingClassMemberDescriptorRepo(
int round,
Class<?> classLiteral,
- Table<Integer, ClassMemberKey, Set<ClassMemberKey>> missingDescriptorLookupRepo) {
- String ownerName = Type.getInternalName(classLiteral);
+ Table<Integer, ClassMemberKey<?>, Set<ClassMemberKey<?>>> missingDescriptorLookupRepo) {
+ ClassName owner = ClassName.create(classLiteral);
for (Constructor<?> constructor : classLiteral.getDeclaredConstructors()) {
- ClassMemberKey memberKeyWithoutDescriptor = MethodKey.create(ownerName, "<init>", "");
- ClassMemberKey memberKeyWithDescriptor =
- MethodKey.create(ownerName, "<init>", Type.getConstructorDescriptor(constructor));
+ ClassMemberKey<?> memberKeyWithoutDescriptor = MethodKey.create(owner, "<init>", "");
+ ClassMemberKey<?> memberKeyWithDescriptor =
+ MethodKey.create(owner, "<init>", Type.getConstructorDescriptor(constructor));
if (missingDescriptorLookupRepo.contains(round, memberKeyWithoutDescriptor)) {
missingDescriptorLookupRepo
.get(round, memberKeyWithoutDescriptor)
@@ -210,9 +211,9 @@
}
}
for (Method method : classLiteral.getDeclaredMethods()) {
- ClassMemberKey memberKeyWithoutDescriptor = MethodKey.create(ownerName, method.getName(), "");
- ClassMemberKey memberKeyWithDescriptor =
- MethodKey.create(ownerName, method.getName(), Type.getMethodDescriptor(method));
+ ClassMemberKey<?> memberKeyWithoutDescriptor = MethodKey.create(owner, method.getName(), "");
+ ClassMemberKey<?> memberKeyWithDescriptor =
+ MethodKey.create(owner, method.getName(), Type.getMethodDescriptor(method));
if (missingDescriptorLookupRepo.contains(round, memberKeyWithoutDescriptor)) {
missingDescriptorLookupRepo
.get(round, memberKeyWithoutDescriptor)
@@ -223,9 +224,9 @@
}
}
for (Field field : classLiteral.getDeclaredFields()) {
- ClassMemberKey memberKeyWithoutDescriptor = FieldKey.create(ownerName, field.getName(), "");
- ClassMemberKey memberKeyWithDescriptor =
- FieldKey.create(ownerName, field.getName(), Type.getDescriptor(field.getType()));
+ ClassMemberKey<?> memberKeyWithoutDescriptor = FieldKey.create(owner, field.getName(), "");
+ ClassMemberKey<?> memberKeyWithDescriptor =
+ FieldKey.create(owner, field.getName(), Type.getDescriptor(field.getType()));
if (missingDescriptorLookupRepo.contains(round, memberKeyWithoutDescriptor)) {
missingDescriptorLookupRepo
.get(round, memberKeyWithoutDescriptor)
@@ -237,27 +238,27 @@
}
}
- private static ImmutableTable<Integer, ClassMemberKey, Member> getReflectionBasedClassMembers(
+ private static ImmutableTable<Integer, ClassMemberKey<?>, Member> getReflectionBasedClassMembers(
int round, Class<?> classLiteral) {
- ImmutableTable.Builder<Integer, ClassMemberKey, Member> reflectionBasedMembers =
+ ImmutableTable.Builder<Integer, ClassMemberKey<?>, Member> reflectionBasedMembers =
ImmutableTable.builder();
- String ownerName = Type.getInternalName(classLiteral);
+ ClassName owner = ClassName.create(classLiteral);
for (Field field : classLiteral.getDeclaredFields()) {
reflectionBasedMembers.put(
round,
- FieldKey.create(ownerName, field.getName(), Type.getDescriptor(field.getType())),
+ FieldKey.create(owner, field.getName(), Type.getDescriptor(field.getType())),
field);
}
for (Constructor<?> constructor : classLiteral.getDeclaredConstructors()) {
reflectionBasedMembers.put(
round,
- MethodKey.create(ownerName, "<init>", Type.getConstructorDescriptor(constructor)),
+ MethodKey.create(owner, "<init>", Type.getConstructorDescriptor(constructor)),
constructor);
}
for (Method method : classLiteral.getDeclaredMethods()) {
reflectionBasedMembers.put(
round,
- MethodKey.create(ownerName, method.getName(), Type.getMethodDescriptor(method)),
+ MethodKey.create(owner, method.getName(), Type.getMethodDescriptor(method)),
method);
}
return reflectionBasedMembers.build();
@@ -267,8 +268,8 @@
DynamicClassLiteral dynamicClassLiteralRequest,
List<JarTransformationRecord> jarTransformationRecords,
ClassLoader initialInputClassLoader,
- Table<Integer, ClassMemberKey, Member> reflectionBasedMembers,
- Table<Integer, ClassMemberKey, Set<ClassMemberKey>> missingDescriptorLookupRepo,
+ Table<Integer, ClassMemberKey<?>, Member> reflectionBasedMembers,
+ Table<Integer, ClassMemberKey<?>, Set<ClassMemberKey<?>>> missingDescriptorLookupRepo,
String workingJavaPackage)
throws Throwable {
int round = dynamicClassLiteralRequest.round();
@@ -326,8 +327,8 @@
Lookup lookup,
List<JarTransformationRecord> jarTransformationRecords,
ClassLoader initialInputClassLoader,
- Table<Integer, ClassMemberKey, java.lang.reflect.Member> reflectionBasedMembers,
- Table<Integer, ClassMemberKey, Set<ClassMemberKey>> missingDescriptorLookupRepo,
+ Table<Integer, ClassMemberKey<?>, java.lang.reflect.Member> reflectionBasedMembers,
+ Table<Integer, ClassMemberKey<?>, Set<ClassMemberKey<?>>> missingDescriptorLookupRepo,
String workingJavaPackage)
throws Throwable {
int round = methodHandleRequest.round();
@@ -340,14 +341,14 @@
missingDescriptorLookupRepo,
workingJavaPackage);
- String ownerInternalName = Type.getInternalName(classLiteral);
+ ClassName owner = ClassName.create(classLiteral);
String memberName = methodHandleRequest.memberName();
String memberDescriptor = methodHandleRequest.memberDescriptor();
- ClassMemberKey classMemberKey =
+ ClassMemberKey<?> classMemberKey =
methodHandleRequest.usage() == MemberUseContext.METHOD_INVOCATION
- ? MethodKey.create(ownerInternalName, memberName, memberDescriptor)
- : FieldKey.create(ownerInternalName, memberName, memberDescriptor);
+ ? MethodKey.create(owner, memberName, memberDescriptor)
+ : FieldKey.create(owner, memberName, memberDescriptor);
if (classMemberKey.descriptor().isEmpty()) {
classMemberKey = restoreMissingDescriptor(classMemberKey, round, missingDescriptorLookupRepo);
@@ -371,25 +372,25 @@
methodHandleRequest.usage(), MemberUseContext.class));
}
- private static ClassMemberKey restoreMissingDescriptor(
- ClassMemberKey classMemberKey,
+ private static ClassMemberKey<?> restoreMissingDescriptor(
+ ClassMemberKey<?> classMemberKey,
int round,
- Table<Integer, ClassMemberKey, Set<ClassMemberKey>> missingDescriptorLookupRepo) {
- Set<ClassMemberKey> restoredClassMemberKeys =
+ Table<Integer, ClassMemberKey<?>, Set<ClassMemberKey<?>>> missingDescriptorLookupRepo) {
+ Set<ClassMemberKey<?>> restoredClassMemberKey =
missingDescriptorLookupRepo.get(round, classMemberKey);
- if (restoredClassMemberKeys == null || restoredClassMemberKeys.isEmpty()) {
+ if (restoredClassMemberKey == null || restoredClassMemberKey.isEmpty()) {
throw new IllegalStateException(
String.format(
"Unable to find class member (%s). Please check its presence.",
- restoredClassMemberKeys));
- } else if (restoredClassMemberKeys.size() > 1) {
+ restoredClassMemberKey));
+ } else if (restoredClassMemberKey.size() > 1) {
throw new IllegalStateException(
String.format(
"Class Member (%s) has same-name overloaded members: (%s) \n"
+ "Please specify a descriptor to disambiguate overloaded method request.",
- classMemberKey, restoredClassMemberKeys));
+ classMemberKey, restoredClassMemberKey));
}
- return Iterables.getOnlyElement(restoredClassMemberKeys);
+ return Iterables.getOnlyElement(restoredClassMemberKey);
}
private static FieldNode getFieldNode(