Reland "Enable pass through of bytecode for kotlin inline functions"
This reverts commit 3f9d7c48fa37653fa6ba74d82e92313ff42a492c.
Change-Id: I0579b95f4ce8f4dcf8a9657d61fb85615bdbc29f
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 802160f..9a0e76a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -475,7 +475,11 @@
timing.end();
}
- if (options.getProguardConfiguration().isOptimizing()) {
+ boolean isKotlinLibraryCompilationWithInlinePassThrough =
+ options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
+
+ if (!isKotlinLibraryCompilationWithInlinePassThrough
+ && options.getProguardConfiguration().isOptimizing()) {
if (options.enableHorizontalClassMerging) {
timing.begin("HorizontalStaticClassMerger");
StaticClassMerger staticClassMerger =
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 656f3aa..65cf1bd 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.kotlin.KotlinMetadataUtils;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -933,6 +934,9 @@
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.synthesizedClassPrefix = synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+ internal.enableCfByteCodePassThrough =
+ internal.isGeneratingClassFiles()
+ && KotlinMetadataUtils.isKeepingKotlinMetadataInRules(internal);
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 0b6547e..c3d4904 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -26,8 +26,10 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -67,6 +69,7 @@
private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
private VerticallyMergedClasses verticallyMergedClasses;
private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty();
+ private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
@@ -372,6 +375,10 @@
this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
}
+ public void setCfByteCodePassThrough(Set<DexMethod> cfByteCodePassThrough) {
+ this.cfByteCodePassThrough = cfByteCodePassThrough;
+ }
+
public <U> U withInitializedClassesInInstanceMethods(
Function<InitializedClassesInInstanceMethods, U> fn, U defaultValue) {
if (initializedClassesInInstanceMethods != null) {
@@ -463,7 +470,17 @@
}
public boolean isCfByteCodePassThrough(DexEncodedMethod method) {
+ if (!options.isGeneratingClassFiles()) {
+ return false;
+ }
+ if (cfByteCodePassThrough.contains(method.method)) {
+ return true;
+ }
return options.testing.cfByteCodePassThrough != null
- && options.testing.cfByteCodePassThrough.test(method);
+ && options.testing.cfByteCodePassThrough.test(method.method);
+ }
+
+ public boolean hasCfByteCodePassThroughMethods() {
+ return !cfByteCodePassThrough.isEmpty();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index d17a4d2..d660501 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -988,7 +988,8 @@
private static DebugParsingOptions getParsingOptions(
JarApplicationReader application, boolean reachabilitySensitive) {
int parsingOptions =
- application.options.testing.readInputStackMaps
+ (application.options.enableCfByteCodePassThrough
+ || application.options.testing.readInputStackMaps)
? ClassReader.EXPAND_FRAMES
: ClassReader.SKIP_FRAMES;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index b5bf1c4..dd450d6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -66,13 +66,14 @@
return;
}
+ timing.begin("Enqueue methods for reprocessing");
+ enqueueMethodsForReprocessing(appInfo, executorService);
+ timing.end(); // Enqueue methods for reprocessing
+
timing.begin("Clear reads from fields of interest");
clearReadsFromFieldsOfInterest(appInfo);
timing.end(); // Clear reads from fields of interest
- timing.begin("Enqueue methods for reprocessing");
- enqueueMethodsForReprocessing(appInfo, executorService);
- timing.end(); // Enqueue methods for reprocessing
timing.end(); // Trivial field accesses analysis
fieldsOfInterest.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
@@ -164,6 +165,11 @@
private boolean registerFieldAccess(DexField field, boolean isStatic) {
DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
if (encodedField != null) {
+ // We cannot remove references from pass through functions.
+ if (appView.isCfByteCodePassThrough(method.getDefinition())) {
+ fieldsOfInterest.remove(encodedField);
+ return true;
+ }
if (encodedField.isStatic() == isStatic) {
if (fieldsOfInterest.contains(encodedField)) {
methodsToReprocess.add(method);
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 f5c08f1..6bfdb1f 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
@@ -1217,6 +1217,16 @@
assert code.verifyTypes(appView);
assert code.isConsistentSSA();
+ if (appView.isCfByteCodePassThrough(method)) {
+ // If the code is pass trough, do not finalize by overwriting the existing code.
+ assert appView.enableWholeProgramOptimizations();
+ timing.begin("Collect optimization info");
+ collectOptimizationInfo(
+ method, code, ClassInitializerDefaultsResult.empty(), feedback, methodProcessor, timing);
+ timing.end();
+ return timing;
+ }
+
assertionsRewriter.run(method, code, timing);
if (serviceLoaderRewriter != null) {
@@ -1607,11 +1617,6 @@
timing.end();
}
- if (appView.isCfByteCodePassThrough(method)) {
- // If the code is pass trough, do not finalize by overwriting the existing code.
- return timing;
- }
-
printMethod(code, "Optimized IR (SSA)", previous);
timing.begin("Finalize IR");
finalizeIR(code, feedback, timing);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 2cb8fe4..6448696 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import kotlinx.metadata.KmClass;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmType;
@@ -75,7 +76,8 @@
KmClass kmClass,
DexClass hostClass,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : hostClass.fields()) {
fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -101,7 +103,7 @@
}
KotlinDeclarationContainerInfo container =
KotlinDeclarationContainerInfo.create(
- kmClass, methodMap, fieldMap, definitionSupplier, reporter);
+ kmClass, methodMap, fieldMap, definitionSupplier, reporter, keepByteCode);
setCompanionObject(kmClass, hostClass, reporter);
return new KotlinClassInfo(
kmClass.getFlags(),
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 a6d1105..27d0425 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -18,6 +19,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.function.Consumer;
import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -31,7 +33,8 @@
DexClass clazz,
DexDefinitionSupplier definitionSupplier,
Reporter reporter,
- boolean onlyProcessLambda) {
+ boolean onlyProcessLambda,
+ Consumer<DexEncodedMethod> keepByteCode) {
DexAnnotation meta =
clazz
.annotations()
@@ -42,7 +45,8 @@
if (onlyProcessLambda && kMetadata.getHeader().getKind() != KOTLIN_METADATA_KIND_LAMBDA) {
return NO_KOTLIN_INFO;
}
- return createKotlinInfo(kotlin, clazz, kMetadata, definitionSupplier, reporter);
+ return createKotlinInfo(
+ kotlin, clazz, kMetadata, definitionSupplier, reporter, keepByteCode);
} catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
reporter.info(
new StringDiagnostic(
@@ -100,14 +104,23 @@
DexClass clazz,
KotlinClassMetadata kMetadata,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
if (kMetadata instanceof KotlinClassMetadata.Class) {
return KotlinClassInfo.create(
- ((KotlinClassMetadata.Class) kMetadata).toKmClass(), clazz, definitionSupplier, reporter);
+ ((KotlinClassMetadata.Class) kMetadata).toKmClass(),
+ clazz,
+ definitionSupplier,
+ reporter,
+ keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
// e.g., B.kt becomes class `BKt`
return KotlinFileFacadeInfo.create(
- (KotlinClassMetadata.FileFacade) kMetadata, clazz, definitionSupplier, reporter);
+ (KotlinClassMetadata.FileFacade) kMetadata,
+ clazz,
+ definitionSupplier,
+ reporter,
+ keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
// multi-file class with the same @JvmName.
return KotlinMultiFileClassFacadeInfo.create(
@@ -115,7 +128,11 @@
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
// A single file, which is part of multi-file class.
return KotlinMultiFileClassPartInfo.create(
- (KotlinClassMetadata.MultiFileClassPart) kMetadata, clazz, definitionSupplier, reporter);
+ (KotlinClassMetadata.MultiFileClassPart) kMetadata,
+ clazz,
+ definitionSupplier,
+ reporter,
+ keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
return KotlinSyntheticClassInfo.create(
(KotlinClassMetadata.SyntheticClass) kMetadata,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 6dc4d4a..5f7c77e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -19,10 +19,12 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmTypeAlias;
+import kotlinx.metadata.internal.metadata.deserialization.Flags;
import kotlinx.metadata.jvm.JvmExtensionsKt;
import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -50,7 +52,8 @@
Map<String, DexEncodedMethod> methodSignatureMap,
Map<String, DexEncodedField> fieldSignatureMap,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
for (KmFunction kmFunction : container.getFunctions()) {
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
@@ -75,6 +78,7 @@
}
continue;
}
+ keepIfInline(kmFunction.getFlags(), method, keepByteCode);
method.setKotlinMemberInfo(kotlinFunctionInfo);
}
@@ -97,6 +101,7 @@
methodSignatureMap.get(propertyProcessor.getterSignature().asString());
if (method != null) {
hasBacking = true;
+ keepIfInline(kmProperty.getFlags(), method, keepByteCode);
method.setKotlinMemberInfo(kotlinPropertyInfo);
}
}
@@ -105,6 +110,7 @@
methodSignatureMap.get(propertyProcessor.setterSignature().asString());
if (method != null) {
hasBacking = true;
+ keepIfInline(kmProperty.getFlags(), method, keepByteCode);
method.setKotlinMemberInfo(kotlinPropertyInfo);
}
}
@@ -118,6 +124,13 @@
notBackedProperties.build());
}
+ private static void keepIfInline(
+ int flags, DexEncodedMethod method, Consumer<DexEncodedMethod> keepByteCode) {
+ if (Flags.IS_INLINE.get(flags) || Flags.IS_CROSSINLINE.get(flags)) {
+ keepByteCode.accept(method);
+ }
+ }
+
private static List<KotlinTypeAliasInfo> getTypeAliases(
List<KmTypeAlias> aliases, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
ImmutableList.Builder<KotlinTypeAliasInfo> builder = ImmutableList.builder();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index ccd957a..50ec7b7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -7,9 +7,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Reporter;
+import java.util.function.Consumer;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -28,9 +30,11 @@
FileFacade kmFileFacade,
DexClass clazz,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
return new KotlinFileFacadeInfo(
- KotlinPackageInfo.create(kmFileFacade.toKmPackage(), clazz, definitionSupplier, reporter));
+ KotlinPackageInfo.create(
+ kmFileFacade.toKmPackage(), clazz, definitionSupplier, reporter, keepByteCode));
}
@Override
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 6d01587..c5ad4ce 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -6,13 +6,17 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.shaking.Enqueuer;
+import com.google.common.collect.Sets;
+import java.util.Set;
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
private final AppView<?> appView;
private final DexDefinitionSupplier definitionSupplier;
+ private final Set<DexMethod> keepByteCodeFunctions = Sets.newIdentityHashSet();
public KotlinMetadataEnqueuerExtension(
AppView<?> appView, DexDefinitionSupplier definitionSupplier) {
@@ -35,7 +39,9 @@
clazz,
definitionSupplier,
appView.options().reporter,
- !keepMetadata || !enqueuer.isPinned(clazz.type)));
+ !keepMetadata || !enqueuer.isPinned(clazz.type),
+ method -> keepByteCodeFunctions.add(method.method)));
});
+ appView.setCfByteCodePassThrough(keepByteCodeFunctions);
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 79745af..f4cee6b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -188,10 +188,18 @@
ProguardConfigurationRule rule, DexItemFactory factory) {
if (rule.isProguardKeepRule()) {
ProguardKeepRule proguardKeepRule = rule.asProguardKeepRule();
- return proguardKeepRule.getType() == ProguardKeepRuleType.KEEP
- && !proguardKeepRule.getModifiers().allowsShrinking
- && !proguardKeepRule.getModifiers().allowsObfuscation
- && proguardKeepRule.getClassNames().matches(factory.kotlinMetadataType);
+ // Check if rule is a keep
+ if (proguardKeepRule.getType() != ProguardKeepRuleType.KEEP
+ || proguardKeepRule.getModifiers().allowsShrinking
+ || proguardKeepRule.getModifiers().allowsObfuscation) {
+ return false;
+ }
+ // Only match if -keep class kotlin.Metadata is specified explicitly, since we translate
+ // -dont*** to a global rule.
+ if (!proguardKeepRule.getClassNames().hasWildcards()
+ && proguardKeepRule.getClassNames().matches(factory.kotlinMetadataType)) {
+ return true;
+ }
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 6a06b7d..9a751ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -7,9 +7,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Reporter;
+import java.util.function.Consumer;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -30,10 +32,12 @@
MultiFileClassPart classPart,
DexClass clazz,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
return new KotlinMultiFileClassPartInfo(
classPart.getFacadeClassName(),
- KotlinPackageInfo.create(classPart.toKmPackage(), clazz, definitionSupplier, reporter));
+ KotlinPackageInfo.create(
+ classPart.toKmPackage(), clazz, definitionSupplier, reporter, keepByteCode));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 7a667e6..d98e109 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.Reporter;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Consumer;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.jvm.JvmExtensionsKt;
@@ -35,7 +36,8 @@
KmPackage kmPackage,
DexClass clazz,
DexDefinitionSupplier definitionSupplier,
- Reporter reporter) {
+ Reporter reporter,
+ Consumer<DexEncodedMethod> keepByteCode) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : clazz.fields()) {
fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
@@ -47,7 +49,7 @@
return new KotlinPackageInfo(
JvmExtensionsKt.getModuleName(kmPackage),
KotlinDeclarationContainerInfo.create(
- kmPackage, methodMap, fieldMap, definitionSupplier, reporter));
+ kmPackage, methodMap, fieldMap, definitionSupplier, reporter, keepByteCode));
}
public void rewrite(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 0b02b70..d00bc9a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -87,6 +87,10 @@
return Collections::emptyIterator;
}
+ public boolean hasWildcards() {
+ return getWildcards().iterator().hasNext();
+ }
+
static Iterable<ProguardWildcard> getWildcardsOrEmpty(ProguardClassNameList nameList) {
return nameList == null ? Collections::emptyIterator : nameList.getWildcards();
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e846e56..b48c81c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -303,6 +303,9 @@
// fused together by play store when shipped for pre-L devices.
public boolean ignoreMainDexMissingClasses = false;
+ // Boolean value indicating that byte code pass through may be enabled.
+ public boolean enableCfByteCodePassThrough = false;
+
// Hidden marker for classes.dex
private boolean hasMarker = false;
private Marker marker;
@@ -1186,7 +1189,7 @@
public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = null;
- public Predicate<DexEncodedMethod> cfByteCodePassThrough = null;
+ public Predicate<DexMethod> cfByteCodePassThrough = null;
}
@VisibleForTesting
diff --git a/src/test/java/com/android/tools/r8/code/PassThroughTest.java b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
index b1a9f2f..f8e4752 100644
--- a/src/test/java/com/android/tools/r8/code/PassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/code/PassThroughTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CfFrontendExamplesTest;
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.DirectoryClassFileProvider;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
@@ -21,6 +22,7 @@
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,7 +32,7 @@
@RunWith(Parameterized.class)
public class PassThroughTest extends TestBase {
- private final String EXPECTED = StringUtils.lines("0", "foo", "0");
+ private final String EXPECTED = StringUtils.lines("0", "foo", "0", "foo", "foo");
private final TestParameters parameters;
private final boolean keepDebug;
@@ -56,7 +58,8 @@
// Check that reading the same input is actual matches.
ClassFileResourceProvider original =
DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
- verifyInstructionsForMainMatchingExpectation(original, true, true);
+ verifyInstructionsForMethodMatchingExpectation(original, "main", true, true);
+ verifyInstructionsForMethodMatchingExpectation(original, "exceptionTest", true, true);
}
@Test
@@ -64,14 +67,16 @@
Path outputJar = temp.newFile("output.jar").toPath();
testForR8(parameters.getBackend())
.addProgramClasses(Main.class)
- .addKeepMainRule(Main.class)
+ .addKeepAllClassesRule()
+ .enableInliningAnnotations()
.ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
.compile()
.writeToZip(outputJar)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED);
- verifyInstructionsForMainMatchingExpectation(
- new ArchiveClassFileProvider(outputJar), keepDebug, false);
+ ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
+ verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, false);
+ verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, false);
}
@Test
@@ -79,22 +84,25 @@
Path outputJar = temp.newFile("output.jar").toPath();
testForR8(parameters.getBackend())
.addProgramClasses(Main.class)
- .addKeepMainRule(Main.class)
+ .addKeepAllClassesRule()
+ .enableInliningAnnotations()
.ifTrue(keepDebug, TestShrinkerBuilder::addKeepAllAttributes)
.addOptionsModification(
internalOptions ->
internalOptions.testing.cfByteCodePassThrough =
- method -> method.method.name.toString().equals("main"))
+ method -> !method.name.toString().equals("<init>"))
.compile()
.writeToZip(outputJar)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED);
- verifyInstructionsForMainMatchingExpectation(
- new ArchiveClassFileProvider(outputJar), keepDebug, true);
+ ArchiveClassFileProvider actual = new ArchiveClassFileProvider(outputJar);
+ verifyInstructionsForMethodMatchingExpectation(actual, "main", keepDebug, true);
+ verifyInstructionsForMethodMatchingExpectation(actual, "exceptionTest", keepDebug, true);
}
- private void verifyInstructionsForMainMatchingExpectation(
- ClassFileResourceProvider actual, boolean checkDebug, boolean expectation) throws Exception {
+ private void verifyInstructionsForMethodMatchingExpectation(
+ ClassFileResourceProvider actual, String methodName, boolean checkDebug, boolean expectation)
+ throws Exception {
ClassFileResourceProvider original =
DirectoryClassFileProvider.fromDirectory(ToolHelper.getClassPathForTests());
String descriptor = DescriptorUtils.javaTypeToDescriptor(Main.class.getTypeName());
@@ -103,43 +111,60 @@
if (!Arrays.equals(expectedBytes, actualBytes)) {
String expectedString = CfFrontendExamplesTest.asmToString(expectedBytes);
String actualString = CfFrontendExamplesTest.asmToString(actualBytes);
- verifyInstructionsForMainMatchingExpectation(
- getMethodInstructions(expectedString),
- getMethodInstructions(actualString),
+ verifyInstructionsForMethodMatchingExpectation(
+ getMethodInstructions(expectedString, methodName),
+ getMethodInstructions(actualString, methodName),
checkDebug,
expectation);
}
}
- private String getMethodInstructions(String asm) {
+ private String getMethodInstructions(String asm, String methodName) {
int methodIndexStart =
asm.indexOf(
- "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \"main\","
- + " \"([Ljava/lang/String;)V\", null, null);");
- int methodIndexEnd = asm.indexOf("}", methodIndexStart);
+ "methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, \""
+ + methodName
+ + "\",");
+ methodIndexStart = asm.indexOf("methodVisitor.visitCode();", methodIndexStart);
+ int methodIndexEnd = asm.indexOf("methodVisitor.visitEnd();", methodIndexStart);
return asm.substring(methodIndexStart, methodIndexEnd);
}
- private void verifyInstructionsForMainMatchingExpectation(
+ private void verifyInstructionsForMethodMatchingExpectation(
String originalInstructions,
String actualInstructions,
boolean checkDebug,
boolean expectation) {
- if (!checkDebug) {
- originalInstructions =
- StringUtils.splitLines(originalInstructions).stream()
- .filter(this::isNotDebugInstruction)
- .map(instr -> instr + "\n")
- .collect(Collectors.joining());
+ if (checkDebug) {
+ // We may rewrite jump instructions, so filter those out.
+ originalInstructions = filter(originalInstructions, this::isNotLabelOrJumpInstruction);
+ actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
+ } else {
+ originalInstructions = filter(originalInstructions, this::isNotDebugInstructionOrJump);
+ actualInstructions = filter(actualInstructions, this::isNotLabelOrJumpInstruction);
}
assertSame(expectation, actualInstructions.equals(originalInstructions));
}
- private boolean isNotDebugInstruction(String instruction) {
+ private String filter(String instructions, Predicate<String> predicate) {
+ return StringUtils.splitLines(instructions).stream()
+ .filter(predicate)
+ .map(instr -> instr + "\n")
+ .collect(Collectors.joining());
+ }
+
+ private boolean isNotDebugInstructionOrJump(String instruction) {
return !(instruction.startsWith("methodVisitor.visitLocalVariable")
|| instruction.startsWith("methodVisitor.visitLabel")
|| instruction.startsWith("Label")
- || instruction.startsWith("methodVisitor.visitLineNumber"));
+ || instruction.startsWith("methodVisitor.visitLineNumber")
+ || instruction.startsWith("methodVisitor.visitJumpInsn"));
+ }
+
+ private boolean isNotLabelOrJumpInstruction(String instruction) {
+ return !(instruction.startsWith("Label")
+ || instruction.startsWith("methodVisitor.visitJumpInsn")
+ || instruction.startsWith("methodVisitor.visitLabel"));
}
public static class Main {
@@ -155,6 +180,28 @@
}
System.out.println(foo);
System.out.println(j);
+ System.out.println(phiTest(args.length > 0 ? args[0] : null));
+ System.out.println(exceptionTest(args.length > 0 ? args[0] : null));
+ }
+
+ @NeverInline
+ public static String phiTest(String arg) {
+ String result;
+ if (arg == null) {
+ result = "foo";
+ } else {
+ result = "bar";
+ }
+ return result;
+ }
+
+ @NeverInline
+ public static String exceptionTest(String arg) {
+ try {
+ return arg.toLowerCase();
+ } catch (NullPointerException ignored) {
+ return "foo";
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 90190c4..56d1f50 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -9,7 +9,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
@@ -92,70 +91,6 @@
}
@Test
- public void testMetadataInClasspathType_merged() throws Exception {
- Path baseLibJar = baseLibJarMap.get(targetVersion);
- Path libJar =
- testForR8(parameters.getBackend())
- .addClasspathFiles(baseLibJar, ToolHelper.getKotlinStdlibJar())
- .addProgramFiles(extLibJarMap.get(targetVersion))
- .addKeepKotlinMetadata()
- // Keep the Extra class and its interface (which has the method).
- .addKeepRules("-keep class **.Extra")
- // Keep the ImplKt extension method which requires metadata
- // to be called with Kotlin syntax from other kotlin code.
- .addKeepRules("-keep class **.ImplKt { <methods>; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .compile()
- .inspect(this::inspectMerged)
- .writeToZip();
-
- Path output =
- kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
- .addClasspathFiles(baseLibJar, libJar)
- .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compile();
-
- testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar)
- .addClasspath(output)
- .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
- .assertSuccessWithOutput(EXPECTED);
- }
-
- private void inspectMerged(CodeInspector inspector) {
- String implClassName = PKG + ".classpath_lib_ext.Impl";
- String implKtClassName = PKG + ".classpath_lib_ext.ImplKt";
- String extraClassName = PKG + ".classpath_lib_ext.Extra";
-
- assertThat(inspector.clazz(implClassName), not(isPresent()));
-
- ClassSubject implKt = inspector.clazz(implKtClassName);
- assertThat(implKt, isPresent());
- assertThat(implKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = implKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("fooExt");
- assertThat(kmFunction, isPresent());
-
- ClassSubject extra = inspector.clazz(extraClassName);
- assertThat(extra, isPresent());
- assertThat(extra, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmClassSubject kmClass = extra.getKmClass();
- assertThat(kmClass, isPresent());
- List<ClassSubject> superTypes = kmClass.getSuperTypes();
- assertTrue(superTypes.stream().noneMatch(
- supertype -> supertype.getFinalDescriptor().contains("Impl")));
- // The super types are changed and we should not keep any information about it in the metadata.
- List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
- assertEquals(1, superTypeDescriptors.size());
- assertEquals(KT_ANY, superTypeDescriptors.get(0));
- }
-
- @Test
public void testMetadataInClasspathType_renamed() throws Exception {
Path baseLibJar = baseLibJarMap.get(targetVersion);
Path libJar =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 0a02782..8ba2119 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -244,12 +244,12 @@
// # setterFlags: 6,
// # setterParameter: null,
// # jvmFlags: 0,
- // # fieldSignature: name:Ljava/lang/String;,
+ // # fieldSignature: c:Ljava/lang/String;,
// # getterSignature: getName()Ljava/lang/String;,
// # setterSignature: setName(Ljava/lang/String;)V,
- assertEquals(name.fieldSignature().asString(), "name:Ljava/lang/String;");
- assertEquals(name.getterSignature().asString(), "getName()Ljava/lang/String;");
- assertEquals(name.setterSignature().asString(), "setName(Ljava/lang/String;)V");
+ assertEquals("c:Ljava/lang/String;", name.fieldSignature().asString());
+ assertEquals("getName()Ljava/lang/String;", name.getterSignature().asString());
+ assertEquals("setName(Ljava/lang/String;)V", name.setterSignature().asString());
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index c326ec4..431b2d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -14,7 +14,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -184,14 +183,8 @@
private void inspectInvalid(CodeInspector inspector) {
String exprClassName = PKG + ".sealed_lib.Expr";
- String numClassName = PKG + ".sealed_lib.Num";
String libClassName = PKG + ".sealed_lib.LibKt";
- // Without any specific keep rule and no instantiation point, it's not necessary to keep
- // sub classes of Expr.
- ClassSubject num = inspector.clazz(numClassName);
- assertThat(num, not(isPresent()));
-
ClassSubject expr = inspector.clazz(exprClassName);
assertThat(expr, isPresent());
assertThat(expr, not(isRenamed()));
@@ -199,8 +192,6 @@
KmClassSubject kmClass = expr.getKmClass();
assertThat(kmClass, isPresent());
- assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
-
ClassSubject libKt = inspector.clazz(libClassName);
assertThat(expr, isPresent());
assertThat(expr, not(isRenamed()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 5a32bba..313adc9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -16,7 +15,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -150,20 +148,16 @@
.compile()
.inspect(this::inspect)
.writeToZip();
-
- // TODO(b/152306391): Reified type-parameters are not flagged correctly.
- ProcessResult mainResult =
+ Path mainJar =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
- .setOutputPath(temp.newFolder().toPath())
- .compileRaw();
- assertEquals(1, mainResult.exitCode);
- assertThat(
- mainResult.stderr,
- containsString(
- "org.jetbrains.kotlin.codegen.CompilationException: "
- + "Back-end (JVM) Internal error: wrong bytecode generated"));
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(mainJar)
+ .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index da97837..004fc31 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -50,7 +50,7 @@
.addProgramFiles(ToolHelper.getKotlinReflectJar())
.addKeepMainRule(mainClassName)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .addKeepRules("-keep class kotlin.Metadata")
+ .addKeepKotlinMetadata()
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index bbcc8e2..239697e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.rewrite.assertions;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -249,10 +250,9 @@
String main = ClassWithAssertions.class.getCanonicalName();
// When running on the JVM enable assertions. For Art this is not possible, and assertions
// can only be activated at compile time.
- assert parameters.getRuntime().isCf();
+ assertTrue(parameters.isCfRuntime());
result.enableRuntimeAssertions();
result
- .disassemble()
.run(parameters.getRuntime(), main, "0")
.assertFailureWithOutput(StringUtils.lines("1"));
// Assertion is not hit.
@@ -304,10 +304,10 @@
assertTrue(clazz.isPresent());
MethodSubject conditionMethod =
clazz.method(new MethodSignature("condition", "boolean", new String[]{}));
- assertTrue(!conditionMethod.isPresent());
+ assertFalse(conditionMethod.isPresent());
MethodSubject clinit =
clazz.method(new MethodSignature(Constants.CLASS_INITIALIZER_NAME, "void", new String[]{}));
- assertTrue(!clinit.isPresent());
+ assertFalse(clinit.isPresent());
}
@Test