Clean-up parsing kotlin metadata to not throw exception in thread
Bug: b/267334465
Change-Id: I42c5e986d3b0ea9448bc7a6512934283f88f92c9
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 7bae38e..a9d515a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.getFlavour;
@@ -18,9 +19,11 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.Flavour;
+import com.android.tools.r8.utils.StringDiagnostic;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Consumer;
+import java.util.function.Supplier;
import kotlin.Metadata;
import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -34,16 +37,58 @@
private static final int SYNTHETIC_CLASS_KIND = 3;
public static KotlinClassLevelInfo getKotlinInfo(
- DexClass clazz, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode)
- throws KotlinMetadataException {
+ AppView<?> appView,
+ DexClass clazz,
+ Consumer<DexEncodedMethod> keepByteCode,
+ Supplier<Boolean> reportUnknownMetadata) {
DexAnnotation meta =
clazz.annotations().getFirstMatching(appView.dexItemFactory().kotlinMetadataType);
- return meta != null ? getKotlinInfo(clazz, appView, keepByteCode, meta) : getNoKotlinInfo();
+ if (meta == null) {
+ return getNoKotlinInfo();
+ }
+ return getKotlinInfoFromAnnotation(appView, clazz, meta, keepByteCode, reportUnknownMetadata);
}
- public static KotlinClassLevelInfo getKotlinInfo(
- DexClass clazz,
+ public static KotlinClassLevelInfo getKotlinInfoFromAnnotation(
AppView<?> appView,
+ DexClass clazz,
+ DexAnnotation meta,
+ Consumer<DexEncodedMethod> keepByteCode,
+ Supplier<Boolean> reportUnknownMetadata) {
+ try {
+ return getKotlinInfo(appView, clazz, keepByteCode, meta);
+ } catch (KotlinMetadataException e) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + e.getMessage()));
+ return getInvalidKotlinInfo();
+ } catch (Throwable e) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Unexpected error while reading "
+ + clazz.type.toSourceString()
+ + "'s kotlin.Metadata: "
+ + e.getMessage()));
+ return getNoKotlinInfo();
+ }
+ }
+
+ private static KotlinClassLevelInfo getKotlinInfo(
+ AppView<?> appView,
+ DexClass clazz,
Consumer<DexEncodedMethod> keepByteCode,
DexAnnotation annotation)
throws KotlinMetadataException {
@@ -55,8 +100,8 @@
return createKotlinInfo(kotlin, clazz, kMetadata, appView, keepByteCode);
}
- public static boolean isLambda(AppView<?> appView, DexClass clazz)
- throws KotlinMetadataException {
+ public static boolean isLambda(
+ AppView<?> appView, DexClass clazz, Supplier<Boolean> reportUnknownMetadata) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
Kotlin kotlin = dexItemFactory.kotlin;
Flavour flavour = getFlavour(clazz, kotlin);
@@ -69,16 +114,31 @@
return false;
}
Map<DexString, DexAnnotationElement> elementMap = toElementMap(metadataAnnotation.annotation);
- if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) {
- KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap);
- if (kMetadata instanceof SyntheticClass) {
- return ((SyntheticClass) kMetadata).isLambda();
+ try {
+ if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) {
+ KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap);
+ if (kMetadata instanceof SyntheticClass) {
+ return ((SyntheticClass) kMetadata).isLambda();
+ }
}
+ assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass
+ == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND)
+ : "Synthetic class kinds should agree";
+ return false;
+ } catch (KotlinMetadataException exception) {
+ if (reportUnknownMetadata.get()) {
+ appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
+ }
+ appView
+ .reporter()
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + exception.getMessage()));
+ return false;
}
- assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass
- == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND)
- : "Synthetic class kinds should agree";
- return false;
}
public static boolean hasKotlinClassMetadataAnnotation(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 6b5d589..dd7f94a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getInvalidKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.errors.Unreachable;
@@ -27,9 +26,9 @@
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.android.tools.r8.shaking.KeepClassInfo;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
@@ -38,7 +37,7 @@
private final AppView<?> appView;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
- private boolean reportedUnknownMetadataVersion;
+ private final AtomicBoolean reportedUnknownMetadataVersion = new AtomicBoolean(false);
public KotlinMetadataEnqueuerExtension(
AppView<?> appView,
@@ -69,52 +68,30 @@
enqueuer.forAllLiveClasses(
clazz -> {
assert clazz.getKotlinInfo().isNoKotlinInformation();
- try {
- if (enqueuer
- .getKeepInfo(clazz)
- .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
- if (KotlinClassMetadataReader.isLambda(appView, clazz)
- && clazz.hasClassInitializer()) {
- feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
- }
- clazz.clearKotlinInfo();
- clazz.removeAnnotations(
- annotation ->
- annotation.getAnnotationType()
- == appView.dexItemFactory().kotlinMetadataType);
- } else {
- clazz.setKotlinInfo(
- KotlinClassMetadataReader.getKotlinInfo(
- clazz,
- appView,
- method -> keepByteCodeFunctions.add(method.getReference())));
- if (clazz.getEnclosingMethodAttribute() != null
- && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
- localOrAnonymousClasses.add(clazz);
- }
+ if (enqueuer
+ .getKeepInfo(clazz)
+ .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
+ if (KotlinClassMetadataReader.isLambda(
+ appView, clazz, () -> reportedUnknownMetadataVersion.getAndSet(true))
+ && clazz.hasClassInitializer()) {
+ feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
}
- } catch (KotlinMetadataException e) {
- appView
- .reporter()
- .info(
- new StringDiagnostic(
- "Class "
- + clazz.type.toSourceString()
- + " has malformed kotlin.Metadata: "
- + e.getMessage()));
- clazz.setKotlinInfo(getInvalidKotlinInfo());
- reportUnknownMetadataVersion();
- } catch (Throwable e) {
- appView
- .reporter()
- .info(
- new StringDiagnostic(
- "Unexpected error while reading "
- + clazz.type.toSourceString()
- + "'s kotlin.Metadata: "
- + e.getMessage()));
- clazz.setKotlinInfo(getNoKotlinInfo());
- reportUnknownMetadataVersion();
+ clazz.clearKotlinInfo();
+ clazz.removeAnnotations(
+ annotation ->
+ annotation.getAnnotationType()
+ == appView.dexItemFactory().kotlinMetadataType);
+ } else {
+ clazz.setKotlinInfo(
+ KotlinClassMetadataReader.getKotlinInfo(
+ appView,
+ clazz,
+ method -> keepByteCodeFunctions.add(method.getReference()),
+ () -> reportedUnknownMetadataVersion.getAndSet(true)));
+ if (clazz.getEnclosingMethodAttribute() != null
+ && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
+ localOrAnonymousClasses.add(clazz);
+ }
}
});
for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
@@ -167,13 +144,6 @@
});
}
- private void reportUnknownMetadataVersion() {
- if (!reportedUnknownMetadataVersion) {
- reportedUnknownMetadataVersion = true;
- appView.reporter().warning(KotlinMetadataDiagnostic.unknownMetadataVersion());
- }
- }
-
public class KotlinMetadataDefinitionSupplier implements DexDefinitionSupplier {
private final ProgramDefinition context;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index e57fc63..858a57c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -130,6 +131,7 @@
return;
}
final WriteMetadataFieldInfo writeMetadataFieldInfo = WriteMetadataFieldInfo.rewriteAll();
+ BooleanBox reportedUnknownMetadataVersion = new BooleanBox();
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
@@ -138,8 +140,12 @@
return;
}
KotlinClassLevelInfo kotlinInfo =
- KotlinClassMetadataReader.getKotlinInfo(
- clazz, appView, ConsumerUtils.emptyConsumer(), metadata);
+ KotlinClassMetadataReader.getKotlinInfoFromAnnotation(
+ appView,
+ clazz,
+ metadata,
+ ConsumerUtils.emptyConsumer(),
+ reportedUnknownMetadataVersion::getAndSet);
if (kotlinInfo == getNoKotlinInfo()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
index 5ad9494..554176e 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanBox.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -59,4 +59,10 @@
public boolean isAssigned() {
return assigned;
}
+
+ public Boolean getAndSet() {
+ boolean current = get();
+ set();
+ return current;
+ }
}