Assure synthesized class is registered in TypeInfo.
Bug: 143686595
Change-Id: I58d1df683b12238c944d56140f89b8a4cae6aaa4
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index d569852..139a285 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -641,11 +641,6 @@
return null;
}
- public void registerNewType(DexType newType, DexType superType) {
- // We do not track subtyping relationships in the basic AppInfo. So do nothing.
- assert checkIfObsolete();
- }
-
public boolean isInMainDexList(DexType type) {
assert checkIfObsolete();
return app.mainDexList.contains(type);
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index faa25c2..fa4cea3 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
+
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.SetUtils;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -48,7 +51,7 @@
// Caching what interfaces this type is implementing. This includes super-interface hierarchy.
Set<DexType> implementedInterfaces = null;
- public TypeInfo(DexType type) {
+ TypeInfo(DexType type) {
this.type = type;
}
@@ -78,7 +81,7 @@
}
}
- public synchronized void addDirectSubtype(TypeInfo subtypeInfo) {
+ synchronized void addDirectSubtype(TypeInfo subtypeInfo) {
assert hierarchyLevel != UNKNOWN_LEVEL;
ensureDirectSubTypeSet();
directSubtypes.add(subtypeInfo.type);
@@ -149,6 +152,28 @@
@Override
public void addSynthesizedClass(DexProgramClass synthesizedClass) {
super.addSynthesizedClass(synthesizedClass);
+ // Register synthesized type, which has two side effects:
+ // 1) Set the hierarchy level of synthesized type based on that of its super type,
+ // 2) Register the synthesized type as a subtype of the supertype.
+ //
+ // The first one makes method resolutions on that synthesized class free from assertion errors
+ // about unknown hierarchy level.
+ //
+ // For the second one, note that such addition is synchronized, but the retrieval of direct
+ // subtypes isn't. Thus, there is a chance of race conditions: utils that check/iterate direct
+ // subtypes, e.g., allImmediateSubtypes, hasSubTypes, etc., may not be able to see this new
+ // synthesized class. However, in practice, this would be okay because, in most cases,
+ // synthesized class's super type is Object, which in general has other subtypes in any way.
+ // Also, iterating all subtypes of Object usually happens before/after IR processing, i.e., as
+ // part of structural changes, such as bottom-up traversal to collect all method signatures,
+ // which are free from such race conditions. Another exceptional case is synthesized classes
+ // whose synthesis is isolated from IR processing. For example, lambda group class that merges
+ // lambdas with the same interface are synthesized/finalized even after post processing of IRs.
+ assert synthesizedClass.superType == dexItemFactory().objectType
+ || synthesizedClass.type.toString().contains(LAMBDA_GROUP_CLASS_NAME_PREFIX)
+ : "Make sure retrieval and iteration of sub types of `" + synthesizedClass.superType
+ + "` is guaranteed to be thread safe and able to see `" + synthesizedClass + "`";
+ registerNewType(synthesizedClass.type, synthesizedClass.superType);
// TODO(b/129458850): Remove when we no longer synthesize classes on-the-fly.
Set<DexType> visited = SetUtils.newIdentityHashSet(synthesizedClass.allImmediateSupertypes());
@@ -217,8 +242,11 @@
return typeInfo.computeIfAbsent(type, TypeInfo::new);
}
- private void populateAllSuperTypes(Map<DexType, Set<DexType>> map, DexType holder,
- DexClass baseClass, Function<DexType, DexClass> definitions) {
+ private void populateAllSuperTypes(
+ Map<DexType, Set<DexType>> map,
+ DexType holder,
+ DexClass baseClass,
+ Function<DexType, DexClass> definitions) {
DexClass holderClass = definitions.apply(holder);
// Skip if no corresponding class is found.
if (holderClass != null) {
@@ -435,13 +463,17 @@
|| bootstrapMethod.asMethod() == dexItemFactory().stringConcatMethod);
}
- @Override
- public void registerNewType(DexType newType, DexType superType) {
+ private void registerNewType(DexType newType, DexType superType) {
assert checkIfObsolete();
// Register the relationship between this type and its superType.
getTypeInfo(superType).addDirectSubtype(getTypeInfo(newType));
}
+ @VisibleForTesting
+ public void registerNewTypeForTesting(DexType newType, DexType superType) {
+ registerNewType(newType, superType);
+ }
+
@Override
public boolean hasSubtyping() {
assert checkIfObsolete();
@@ -495,7 +527,7 @@
// Depending on optimizations, conservative answer of subtype relation may vary.
// Pass different `orElse` in that case.
- public boolean isStrictSubtypeOf(DexType subtype, DexType supertype, boolean orElse) {
+ private boolean isStrictSubtypeOf(DexType subtype, DexType supertype, boolean orElse) {
if (subtype == supertype) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 8a93298..9935044 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -939,8 +939,6 @@
count++;
result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
} while (appView.definitionFor(result) != null);
- // Register the newly generated type in the subtyping hierarchy, if we have one.
- appView.appInfo().registerNewType(result, appView.dexItemFactory().objectType);
return result;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 40932cb..9998c94 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -105,9 +105,6 @@
lambdaClassType,
factory.createProto(lambdaClassType, descriptor.captures.values),
rewriter.createInstanceMethodName);
-
- // We have to register this new class as a subtype of object.
- rewriter.converter.appView.appInfo().registerNewType(type, factory.objectType);
}
// Generate unique lambda class type for lambda descriptor and instantiation point context.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index e39c21b..2ac3abd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1353,7 +1353,6 @@
// No need to sort the direct methods as they are generated in sorted order.
// Build the outliner class.
- DexType superType = appView.dexItemFactory().createType("Ljava/lang/Object;");
DexTypeList interfaces = DexTypeList.empty();
DexString sourceFile = appView.dexItemFactory().createString("outline");
ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
@@ -1365,7 +1364,7 @@
null,
new SynthesizedOrigin("outlining", getClass()),
accessFlags,
- superType,
+ appView.dexItemFactory().objectType,
interfaces,
sourceFile,
null,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 57edf39..a165998 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -384,9 +384,6 @@
group.compact();
DexProgramClass lambdaGroupClass = group.synthesizeClass(appView.options());
result.put(group, lambdaGroupClass);
-
- // We have to register this new class as a subtype of object.
- appView.appInfo().registerNewType(lambdaGroupClass.type, lambdaGroupClass.superType);
}
return result;
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 42908d2..3c47534 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -536,7 +536,7 @@
@Test
public void testSelfOrderWithoutSubtypingInfo() {
DexType type = factory.createType("Lmy/Type;");
- appView.withSubtyping().appInfo().registerNewType(type, factory.objectType);
+ appView.withSubtyping().appInfo().registerNewTypeForTesting(type, factory.objectType);
TypeLatticeElement nonNullType = fromDexType(type, Nullability.definitelyNotNull(), appView);
ReferenceTypeLatticeElement nullableType =
nonNullType.asReferenceTypeLatticeElement().getOrCreateVariant(Nullability.maybeNull());