Merge commit '7d174afaaaf084fe85772b1e60db1a945947f83e' into dev-release
Change-Id: I01f170b8c438c1b5d032f4eb0981a981b34b807b
diff --git a/.gitignore b/.gitignore
index 67d9a55..27d89f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -219,6 +219,8 @@
third_party/opensource-apps/applymapping.tar.gz
third_party/opensource-apps/chanu
third_party/opensource-apps/chanu.tar.gz
+third_party/opensource-apps/chrome
+third_party/opensource-apps/chrome.tar.gz
third_party/opensource-apps/compose-examples/changed-bitwise-value-propagation
third_party/opensource-apps/compose-examples/changed-bitwise-value-propagation.tar.gz
third_party/opensource-apps/empty-activity
@@ -283,6 +285,8 @@
third_party/proto/test/proto2.tar.gz
third_party/proto/test/proto3
third_party/proto/test/proto3.tar.gz
+third_party/protoc
+third_party/protoc.tar.gz
third_party/r8
third_party/r8.tar.gz
third_party/r8-releases/2.0.74
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 25d24e38..1f748ae 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -400,7 +400,7 @@
const val javassist = "3.29.2-GA"
const val junitVersion = "4.13-beta-2"
const val kotlinVersion = "1.9.20"
- const val kotlinMetadataVersion = "2.0.0-Beta5"
+ const val kotlinMetadataVersion = "2.0.0"
const val mockito = "2.10.0"
const val smaliVersion = "3.0.3"
}
@@ -453,6 +453,10 @@
Paths.get("third_party", "chrome", "chrome_200430").toFile(),
Paths.get("third_party", "chrome", "chrome_200430.tar.gz.sha1").toFile(),
DependencyType.X20)
+ val chromeBenchmark = ThirdPartyDependency(
+ "chrome-benchmark",
+ Paths.get("third_party", "opensource-apps", "chrome").toFile(),
+ Paths.get("third_party", "opensource-apps", "chrome.tar.gz.sha1").toFile())
val compilerApi = ThirdPartyDependency(
"compiler-api",
Paths.get(
@@ -469,6 +473,70 @@
Paths.get(
"third_party", "opensource-apps", "compose-examples",
"changed-bitwise-value-propagation.tar.gz.sha1").toFile())
+ val composeSamplesCrane = ThirdPartyDependency(
+ "compose-samples-crane",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "crane").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "crane.tar.gz.sha1").toFile())
+ val composeSamplesJetCaster = ThirdPartyDependency(
+ "compose-samples-jetcaster",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetcaster").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetcaster.tar.gz.sha1").toFile())
+ val composeSamplesJetChat = ThirdPartyDependency(
+ "compose-samples-jetchat",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetchat").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetchat.tar.gz.sha1").toFile())
+ val composeSamplesJetLagged = ThirdPartyDependency(
+ "compose-samples-jetlagged",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetlagged").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetlagged.tar.gz.sha1").toFile())
+ val composeSamplesJetNews = ThirdPartyDependency(
+ "compose-samples-jetnews",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetnews").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetnews.tar.gz.sha1").toFile())
+ val composeSamplesJetSnack = ThirdPartyDependency(
+ "compose-samples-jetsnack",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetsnack").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "jetsnack.tar.gz.sha1").toFile())
+ val composeSamplesOwl = ThirdPartyDependency(
+ "compose-samples-owl",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "owl").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "owl.tar.gz.sha1").toFile())
+ val composeSamplesReply = ThirdPartyDependency(
+ "compose-samples-reply",
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "reply").toFile(),
+ Paths.get(
+ "third_party", "opensource-apps", "android", "compose-samples",
+ "reply.tar.gz.sha1").toFile())
val coreLambdaStubs = ThirdPartyDependency(
"coreLambdaStubs",
Paths.get("third_party", "core-lambda-stubs").toFile(),
@@ -615,6 +683,10 @@
Paths.get("third_party", "proguardsettings").toFile(),
Paths.get("third_party", "proguardsettings.tar.gz.sha1").toFile(),
DependencyType.X20)
+ val protoc = ThirdPartyDependency(
+ "protoc",
+ Paths.get("third_party", "protoc").toFile(),
+ Paths.get("third_party", "protoc.tar.gz.sha1").toFile())
val protoRuntimeEdition2023 = ThirdPartyDependency(
"protoRuntimeEdition2023",
Paths.get("third_party", "proto", "runtime", "edition2023").toFile(),
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index e58f672..a747444 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -2,14 +2,43 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import com.google.protobuf.gradle.proto
+import com.google.protobuf.gradle.ProtobufExtension
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
+
plugins {
`kotlin-dsl`
id("dependencies-plugin")
}
+// It seems like the use of a local maven repo does not allow adding the plugin with the id+version
+// syntax. Also, for some reason the 'protobuf' extension object cannot be directly referenced.
+// This configures the plugin "old style" and pulls out the extension object manually.
+buildscript {
+ dependencies {
+ classpath("com.google.protobuf:protobuf-gradle-plugin:0.9.4")
+ }
+}
+apply(plugin = "com.google.protobuf")
+var os = DefaultNativePlatform.getCurrentOperatingSystem()
+var protobuf = project.extensions.getByName("protobuf") as ProtobufExtension
+protobuf.protoc {
+ if (os.isLinux) {
+ path = getRoot().resolveAll("third_party", "protoc", "linux-x86_64", "bin", "protoc").path
+ } else if (os.isMacOsX) {
+ path = getRoot().resolveAll("third_party", "protoc", "osx-x86_64", "bin", "protoc").path
+ } else {
+ assert(os.isWindows);
+ path = getRoot().resolveAll("third_party", "protoc", "win64", "bin", "protoc.exe").path
+ }
+}
+
java {
sourceSets.main.configure {
java.srcDir(getRoot().resolveAll("src", "keepanno", "java"))
+ proto {
+ srcDir(getRoot().resolveAll("src", "keepanno", "proto"))
+ }
}
sourceCompatibility = JvmCompatibility.sourceCompatibility
targetCompatibility = JvmCompatibility.targetCompatibility
@@ -22,6 +51,7 @@
dependencies {
compileOnly(Deps.asm)
compileOnly(Deps.guava)
+ implementation("com.google.protobuf:protobuf-java:3.19.3")
}
tasks {
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 9eaf601..fd3282d 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -223,6 +223,7 @@
}
exclude("META-INF/*.kotlin_module")
exclude("**/*.kotlin_metadata")
+ exclude("keepanno.proto")
destinationDirectory.set(getRoot().resolveAll("build", "libs"))
archiveFileName.set("r8-full-exclude-deps.jar")
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index e2ce313..607d981 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
+import com.android.tools.r8.keepanno.proto.KeepAnnoProtos;
+
/**
* An edge in the keep graph.
*
@@ -144,6 +146,11 @@
private Builder() {}
+ public Builder applyProto(KeepAnnoProtos.Edge edge) {
+ // TODO(b/343389186): implement this.
+ return this;
+ }
+
public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
this.metaInfo = metaInfo;
return this;
@@ -227,4 +234,8 @@
+ consequences
+ '}';
}
+
+ public void buildProto(KeepAnnoProtos.Edge.Builder builder) {
+ // TODO(b/343389186): implement this.
+ }
}
diff --git a/src/keepanno/proto/keepanno.proto b/src/keepanno/proto/keepanno.proto
new file mode 100644
index 0000000..a4f96d7
--- /dev/null
+++ b/src/keepanno/proto/keepanno.proto
@@ -0,0 +1,56 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+syntax = "proto3";
+
+package com.android.tools.r8.keepanno.proto;
+
+// All messages are placed under the outer class. This makes it a bit nicer to
+// implement the AST <-> Proto conversions without type conflicts.
+option java_multiple_files = false;
+
+// Descriptive name of the outer class (default would have been `Keepanno`).
+option java_outer_classname = "KeepAnnoProtos";
+
+// Java package consistent with R8 convention.
+option java_package = "com.android.tools.r8.keepanno.proto";
+
+message Version {
+ uint32 major = 1;
+ uint32 minor = 2;
+ uint32 patch = 3;
+}
+
+message Context {
+ oneof context_oneof {
+ string class_descriptor = 1;
+ string method_descriptor = 2;
+ string field_descriptor = 3;
+ }
+}
+
+message MetaInfo {
+ Version version = 1;
+ Context context = 2;
+}
+
+message Declaration {
+ MetaInfo meta_info = 1;
+ oneof decl_oneof {
+ Edge edge = 2;
+ CheckRemoved check_removed = 3;
+ CheckDiscarded check_discarded = 4;
+ }
+}
+
+message CheckRemoved {
+ // TODO(b/343389186): Add content.
+}
+
+message CheckDiscarded {
+ // TODO(b/343389186): Add content.
+}
+
+message Edge {
+ // TODO(b/343389186): Add content.
+}
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index c52f655..3e87ab7 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -273,6 +273,10 @@
throw new RuntimeException("Unknown key value pair: " + key + " = " + value);
}
+ public String getAndroidApiExtensionPackages() {
+ return systemProperties.get("com.android.tools.r8.androidApiExtensionPackages");
+ }
+
public Tool getTool() {
return tool;
}
@@ -281,10 +285,18 @@
return compilationMode;
}
+ public boolean getIsolatedSplits() {
+ return isolatedSplits.orElse(false);
+ }
+
public int getMinApi() {
return minApi;
}
+ public boolean getEnableSameFilePolicy() {
+ return systemProperties.containsKey("com.android.tools.r8.enableSameFilePolicy");
+ }
+
private void addOptionalDumpEntry(
Map<String, String> buildProperties, String key, Optional<?> optionalValue) {
optionalValue.ifPresent(bool -> addDumpEntry(buildProperties, key, bool));
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index bd27dfc..d6216c3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -35,7 +35,7 @@
import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Position;
@@ -1579,13 +1579,16 @@
}
public Builder rewriteParameterAnnotations(
- DexEncodedMethod method, ArgumentInfoCollection argumentInfoCollection) {
+ DexEncodedMethod method, RewrittenPrototypeDescription rewrittenPrototypeDescription) {
if (parameterAnnotations.isEmpty()) {
// Nothing to do.
return this;
}
+
+ var argumentInfoCollection = rewrittenPrototypeDescription.getArgumentInfoCollection();
if (!argumentInfoCollection.hasArgumentPermutation()
- && !argumentInfoCollection.hasRemovedArguments()) {
+ && !argumentInfoCollection.hasRemovedArguments()
+ && !rewrittenPrototypeDescription.hasExtraParameters()) {
// Nothing to do.
return this;
}
@@ -1638,6 +1641,14 @@
newNumberOfMissingParameterAnnotations = 0;
}
+ if (rewrittenPrototypeDescription.hasExtraParameters()) {
+ for (int extraParameter = 0;
+ extraParameter < rewrittenPrototypeDescription.getExtraParameters().size();
+ extraParameter++) {
+ newParameterAnnotations.add(DexAnnotationSet.empty());
+ }
+ }
+
return setParameterAnnotations(
ParameterAnnotationsList.create(
newParameterAnnotations.toArray(DexAnnotationSet.EMPTY_ARRAY),
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 34c1b21..60bcbdc 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -68,12 +68,9 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.position.TextRange;
-import com.android.tools.r8.shaking.ProguardConfiguration;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -155,12 +152,7 @@
JarApplicationReader application = this.application;
assert context != null;
assert application != null;
- DexProgramClass programOwner = context.owner.asProgramClass();
- ReachabilitySensitiveValue reachabilitySensitive =
- programOwner != null
- ? programOwner.getReachabilitySensitiveValue()
- : ReachabilitySensitiveValue.DISABLED;
- DebugParsingOptions parsingOptions = getParsingOptions(application, reachabilitySensitive);
+ DebugParsingOptions parsingOptions = getParsingOptions(application);
// The ClassCodeVisitor is in charge of setting this.context to null.
try {
parseCode(context, false, parsingOptions);
@@ -1177,34 +1169,13 @@
}
}
- private static DebugParsingOptions getParsingOptions(
- JarApplicationReader application, ReachabilitySensitiveValue reachabilitySensitive) {
+ private static DebugParsingOptions getParsingOptions(JarApplicationReader application) {
// TODO(b/166841731): We should compute our own from the compressed format.
int parsingOptions =
application.options.canUseInputStackMaps()
? ClassReader.EXPAND_FRAMES
: ClassReader.SKIP_FRAMES;
- ProguardConfiguration configuration = application.options.getProguardConfiguration();
- if (configuration == null) {
- return new DebugParsingOptions(true, true, parsingOptions);
- }
- ProguardKeepAttributes keep =
- application.options.getProguardConfiguration().getKeepAttributes();
-
- boolean localsInfo =
- configuration.isKeepParameterNames()
- || keep.localVariableTable
- || keep.localVariableTypeTable
- || reachabilitySensitive.isEnabled();
- boolean lineInfo =
- (keep.lineNumberTable || application.options.canUseNativeDexPcInsteadOfDebugInfo());
- boolean methodParaeters = keep.methodParameters;
-
- if (!localsInfo && !lineInfo && !methodParaeters) {
- parsingOptions |= ClassReader.SKIP_DEBUG;
- }
-
- return new DebugParsingOptions(lineInfo, localsInfo, parsingOptions);
+ return new DebugParsingOptions(true, true, parsingOptions);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
index 85ee2db..396f92d 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.graph.proto;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
@@ -24,7 +23,6 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
-import java.util.function.Consumer;
public class ArgumentInfoCollection {
@@ -370,13 +368,4 @@
RewrittenPrototypeDescription.create(Collections.emptyList(), null, this);
return prototypeChanges.createMethodOptimizationInfoFixer();
}
-
- /**
- * Returns a function for rewriting the parameter annotations on a method info after prototype
- * changes were made.
- */
- public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
- DexEncodedMethod method) {
- return builder -> builder.rewriteParameterAnnotations(method, this);
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
index 67659b6..46d40d5 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
@@ -84,7 +84,7 @@
public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
DexEncodedMethod method) {
- return getArgumentInfoCollection().createParameterAnnotationsRemover(method);
+ return builder -> builder.rewriteParameterAnnotations(method, this);
}
public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index a6f39aa..7bafe1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -200,6 +200,15 @@
return operands;
}
+ public boolean hasOperandThatMatches(Predicate<Value> predicate) {
+ for (Value operand : operands) {
+ if (predicate.test(operand)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void removeOperand(int index) {
removeOperand(index, null, alwaysFalse());
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/ConcreteKotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/ConcreteKotlinPropertyInfo.java
index 526068d..aa46ee2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/ConcreteKotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/ConcreteKotlinPropertyInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -19,22 +18,14 @@
import java.util.List;
import java.util.function.Consumer;
import kotlin.metadata.KmProperty;
+import kotlin.metadata.KmPropertyAccessorAttributes;
import kotlin.metadata.jvm.JvmExtensionsKt;
// Holds information about KmProperty
public class ConcreteKotlinPropertyInfo implements KotlinPropertyInfo {
- // Original flags.
- private final int flags;
-
- // Original getter flags. E.g., for property getter.
- private final int getterFlags;
-
- // Original setter flags. E.g., for property setter.
- private final int setterFlags;
-
- // Original property name for (extension) property. Otherwise, null.
- private final String name;
+ // Original property.
+ private final KmProperty kmProperty;
// Original return type information. This should never be NULL (even for setters without field).
private final KotlinTypeInfo returnType;
@@ -45,10 +36,6 @@
private final List<KotlinTypeParameterInfo> typeParameters;
- private final KotlinVersionRequirementInfo versionRequirements;
-
- private final int jvmFlags;
-
private final KotlinJvmFieldSignatureInfo fieldSignature;
private final KotlinJvmMethodSignatureInfo getterSignature;
@@ -62,33 +49,23 @@
private final List<KotlinTypeInfo> contextReceiverTypes;
private ConcreteKotlinPropertyInfo(
- int flags,
- int getterFlags,
- int setterFlags,
- String name,
+ KmProperty kmProperty,
KotlinTypeInfo returnType,
KotlinTypeInfo receiverParameterType,
KotlinValueParameterInfo setterParameter,
List<KotlinTypeParameterInfo> typeParameters,
- KotlinVersionRequirementInfo versionRequirements,
- int jvmFlags,
KotlinJvmFieldSignatureInfo fieldSignature,
KotlinJvmMethodSignatureInfo getterSignature,
KotlinJvmMethodSignatureInfo setterSignature,
KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations,
KotlinJvmMethodSignatureInfo syntheticMethodForDelegate,
List<KotlinTypeInfo> contextReceiverTypes) {
- this.flags = flags;
- this.getterFlags = getterFlags;
- this.setterFlags = setterFlags;
- this.name = name;
assert returnType != null;
+ this.kmProperty = kmProperty;
this.returnType = returnType;
this.receiverParameterType = receiverParameterType;
this.setterParameter = setterParameter;
this.typeParameters = typeParameters;
- this.versionRequirements = versionRequirements;
- this.jvmFlags = jvmFlags;
this.fieldSignature = fieldSignature;
this.getterSignature = getterSignature;
this.setterSignature = setterSignature;
@@ -100,16 +77,11 @@
public static ConcreteKotlinPropertyInfo create(
KmProperty kmProperty, DexItemFactory factory, Reporter reporter) {
return new ConcreteKotlinPropertyInfo(
- kmProperty.getFlags(),
- kmProperty.getGetterFlags(),
- kmProperty.getSetterFlags(),
- kmProperty.getName(),
+ kmProperty,
KotlinTypeInfo.create(kmProperty.getReturnType(), factory, reporter),
KotlinTypeInfo.create(kmProperty.getReceiverParameterType(), factory, reporter),
KotlinValueParameterInfo.create(kmProperty.getSetterParameter(), factory, reporter),
KotlinTypeParameterInfo.create(kmProperty.getTypeParameters(), factory, reporter),
- KotlinVersionRequirementInfo.create(kmProperty.getVersionRequirements()),
- JvmExtensionsKt.getJvmFlags(kmProperty),
KotlinJvmFieldSignatureInfo.create(JvmExtensionsKt.getFieldSignature(kmProperty), factory),
KotlinJvmMethodSignatureInfo.create(
JvmExtensionsKt.getGetterSignature(kmProperty), factory),
@@ -152,54 +124,60 @@
DexEncodedMethod setter,
DexEncodedMethod syntheticMethodForAnnotationsMethod,
AppView<?> appView) {
- KmProperty kmProperty =
- consume(new KmProperty(flags, name, getterFlags, setterFlags), consumer);
+ KmProperty rewrittenKmProperty = new KmProperty(kmProperty.getName());
+ consumer.accept(rewrittenKmProperty);
+ KotlinFlagUtils.copyAllFlags(kmProperty, rewrittenKmProperty);
+ KotlinFlagUtils.copyAllFlags(kmProperty.getGetter(), rewrittenKmProperty.getGetter());
+ if (kmProperty.getSetter() != null) {
+ rewrittenKmProperty.setSetter(new KmPropertyAccessorAttributes());
+ KotlinFlagUtils.copyAllFlags(kmProperty.getSetter(), rewrittenKmProperty.getSetter());
+ }
boolean rewritten =
- rewriteIfNotNull(appView, returnType, kmProperty::setReturnType, KotlinTypeInfo::rewrite);
- assert returnType != null;
+ rewriteIfNotNull(
+ appView, returnType, rewrittenKmProperty::setReturnType, KotlinTypeInfo::rewrite);
rewritten |=
rewriteIfNotNull(
appView,
receiverParameterType,
- kmProperty::setReceiverParameterType,
+ rewrittenKmProperty::setReceiverParameterType,
KotlinTypeInfo::rewrite);
rewritten |=
rewriteIfNotNull(
appView,
setterParameter,
- kmProperty::setSetterParameter,
+ rewrittenKmProperty::setSetterParameter,
KotlinValueParameterInfo::rewrite);
rewritten |=
rewriteList(
appView,
typeParameters,
- kmProperty.getTypeParameters(),
+ rewrittenKmProperty.getTypeParameters(),
KotlinTypeParameterInfo::rewrite);
rewritten |=
rewriteList(
appView,
contextReceiverTypes,
- kmProperty.getContextReceiverTypes(),
+ rewrittenKmProperty.getContextReceiverTypes(),
KotlinTypeInfo::rewrite);
- rewritten |= versionRequirements.rewrite(kmProperty.getVersionRequirements()::addAll);
+ rewrittenKmProperty.getVersionRequirements().addAll(kmProperty.getVersionRequirements());
if (fieldSignature != null) {
rewritten |=
fieldSignature.rewrite(
- newSignature -> JvmExtensionsKt.setFieldSignature(kmProperty, newSignature),
+ newSignature -> JvmExtensionsKt.setFieldSignature(rewrittenKmProperty, newSignature),
field,
appView);
}
if (getterSignature != null) {
rewritten |=
getterSignature.rewrite(
- newSignature -> JvmExtensionsKt.setGetterSignature(kmProperty, newSignature),
+ newSignature -> JvmExtensionsKt.setGetterSignature(rewrittenKmProperty, newSignature),
getter,
appView);
}
if (setterSignature != null) {
rewritten |=
setterSignature.rewrite(
- newSignature -> JvmExtensionsKt.setSetterSignature(kmProperty, newSignature),
+ newSignature -> JvmExtensionsKt.setSetterSignature(rewrittenKmProperty, newSignature),
setter,
appView);
}
@@ -207,23 +185,23 @@
rewritten |=
syntheticMethodForAnnotations.rewrite(
newSignature ->
- JvmExtensionsKt.setSyntheticMethodForAnnotations(kmProperty, newSignature),
+ JvmExtensionsKt.setSyntheticMethodForAnnotations(
+ rewrittenKmProperty, newSignature),
syntheticMethodForAnnotationsMethod,
appView);
}
- JvmExtensionsKt.setJvmFlags(kmProperty, jvmFlags);
rewritten |=
rewriteIfNotNull(
appView,
syntheticMethodForDelegate,
- newMethod -> JvmExtensionsKt.setSyntheticMethodForDelegate(kmProperty, newMethod),
+ newMethod ->
+ JvmExtensionsKt.setSyntheticMethodForDelegate(rewrittenKmProperty, newMethod),
KotlinJvmMethodSignatureInfo::rewriteNoBacking);
return rewritten;
}
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
- assert returnType != null;
returnType.trace(definitionSupplier);
if (receiverParameterType != null) {
receiverParameterType.trace(definitionSupplier);
@@ -252,6 +230,6 @@
@Override
public String toString() {
- return "KotlinPropertyInfo(" + name + ")";
+ return "KotlinPropertyInfo(" + kmProperty.getName() + ")";
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
index 329dbbe..ffbbe57 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
@@ -59,18 +59,15 @@
private static class KotlinAnnotationClassValueInfo extends KotlinAnnotationArgumentInfo {
private final KotlinTypeReference value;
- private final int arrayDimensionCount;
- private KotlinAnnotationClassValueInfo(KotlinTypeReference value, int arrayDimensionCount) {
+ private KotlinAnnotationClassValueInfo(KotlinTypeReference value) {
this.value = value;
- this.arrayDimensionCount = arrayDimensionCount;
}
private static KotlinAnnotationClassValueInfo create(KClassValue arg, DexItemFactory factory) {
return new KotlinAnnotationClassValueInfo(
KotlinTypeReference.fromBinaryNameOrKotlinClassifier(
- arg.getClassName(), factory, arg.getClassName()),
- arg.getArrayDimensionCount());
+ arg.getClassName(), factory, arg.getClassName()));
}
@Override
@@ -81,7 +78,7 @@
@Override
boolean rewrite(Consumer<KmAnnotationArgument> consumer, AppView<?> appView) {
return value.toRenamedBinaryNameOrDefault(
- rewrittenValue -> consumer.accept(new KClassValue(rewrittenValue, arrayDimensionCount)),
+ rewrittenValue -> consumer.accept(new KClassValue(rewrittenValue)),
appView,
ClassClassifiers.anyName);
}
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 0314b43..1b00d0c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -4,12 +4,11 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.updateJvmMetadataVersionIfRequired;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
-import static kotlin.metadata.jvm.KotlinClassMetadata.Companion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -41,8 +40,7 @@
public class KotlinClassInfo implements KotlinClassLevelInfo {
- private final int flags;
- private final String name;
+ private final KotlinClassMetadata.Class classMetadata;
private final boolean nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin;
private final String moduleName;
private final List<KotlinConstructorInfo> constructorsWithNoBacking;
@@ -54,14 +52,11 @@
private final List<KotlinTypeReference> nestedClasses;
private final List<String> enumEntries;
- private final KotlinVersionRequirementInfo versionRequirements;
private final KotlinTypeReference anonymousObjectOrigin;
private final String packageName;
private final KotlinLocalDelegatedPropertyInfo localDelegatedProperties;
- private final int[] metadataVersion;
private final String inlineClassUnderlyingPropertyName;
private final KotlinTypeInfo inlineClassUnderlyingType;
- private final int jvmFlags;
private final String companionObjectName;
// Collection of context receiver types
private final List<KotlinTypeInfo> contextReceiverTypes;
@@ -70,8 +65,7 @@
private final KotlinMetadataMembersTracker originalMembersWithKotlinInfo;
private KotlinClassInfo(
- int flags,
- String name,
+ KotlinClassMetadata.Class classMetadata,
boolean nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin,
String moduleName,
KotlinDeclarationContainerInfo declarationContainerInfo,
@@ -81,19 +75,15 @@
List<KotlinTypeReference> sealedSubClasses,
List<KotlinTypeReference> nestedClasses,
List<String> enumEntries,
- KotlinVersionRequirementInfo versionRequirements,
KotlinTypeReference anonymousObjectOrigin,
String packageName,
KotlinLocalDelegatedPropertyInfo localDelegatedProperties,
- int[] metadataVersion,
String inlineClassUnderlyingPropertyName,
KotlinTypeInfo inlineClassUnderlyingType,
KotlinMetadataMembersTracker originalMembersWithKotlinInfo,
- int jvmFlags,
String companionObjectName,
List<KotlinTypeInfo> contextReceiverTypes) {
- this.flags = flags;
- this.name = name;
+ this.classMetadata = classMetadata;
this.nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin =
nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin;
this.moduleName = moduleName;
@@ -104,15 +94,12 @@
this.sealedSubClasses = sealedSubClasses;
this.nestedClasses = nestedClasses;
this.enumEntries = enumEntries;
- this.versionRequirements = versionRequirements;
this.anonymousObjectOrigin = anonymousObjectOrigin;
this.packageName = packageName;
this.localDelegatedProperties = localDelegatedProperties;
- this.metadataVersion = metadataVersion;
this.inlineClassUnderlyingPropertyName = inlineClassUnderlyingPropertyName;
this.inlineClassUnderlyingType = inlineClassUnderlyingType;
this.originalMembersWithKotlinInfo = originalMembersWithKotlinInfo;
- this.jvmFlags = jvmFlags;
this.companionObjectName = companionObjectName;
this.contextReceiverTypes = contextReceiverTypes;
}
@@ -120,7 +107,6 @@
public static KotlinClassInfo create(
KotlinClassMetadata.Class metadata,
String packageName,
- int[] metadataVersion,
DexClass hostClass,
AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
@@ -170,8 +156,7 @@
|| (anonymousObjectOrigin != null
&& kmClass.name.equals(anonymousObjectOrigin.toKotlinClassifier(true)));
return new KotlinClassInfo(
- kmClass.getFlags(),
- kmClass.name,
+ metadata,
nameCanBeDeducedFromClassOrOrigin,
JvmExtensionsKt.getModuleName(kmClass),
container,
@@ -181,16 +166,13 @@
getSealedSubClasses(kmClass.getSealedSubclasses(), factory),
getNestedClasses(hostClass, kmClass.getNestedClasses(), factory),
setEnumEntries(kmClass, hostClass),
- KotlinVersionRequirementInfo.create(kmClass.getVersionRequirements()),
anonymousObjectOrigin,
packageName,
KotlinLocalDelegatedPropertyInfo.create(
JvmExtensionsKt.getLocalDelegatedProperties(kmClass), factory, reporter),
- metadataVersion,
kmClass.getInlineClassUnderlyingPropertyName(),
KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
originalMembersWithKotlinInfo,
- JvmExtensionsKt.getJvmFlags(kmClass),
setCompanionObject(kmClass, hostClass, reporter),
ListUtils.map(
kmClass.getContextReceiverTypes(),
@@ -291,15 +273,16 @@
@Override
@SuppressWarnings("ReferenceEquality")
public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
- KmClass kmClass = new KmClass();
- // TODO(b/154348683): Set flags.
- kmClass.setFlags(flags);
+ KmClass kmClass = classMetadata.getKmClass();
+ KmClass rewrittenKmClass = new KmClass();
+ KotlinFlagUtils.copyAllFlags(kmClass, rewrittenKmClass);
// Set potentially renamed class name.
DexString originalDescriptor = clazz.type.descriptor;
DexString rewrittenDescriptor = appView.getNamingLens().lookupDescriptor(clazz.type);
boolean rewritten = !originalDescriptor.equals(rewrittenDescriptor);
+ String name = kmClass.getName();
if (!nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin) {
- kmClass.setName(this.name);
+ rewrittenKmClass.setName(name);
} else {
String rewrittenName = null;
// When the class has an anonymousObjectOrigin and the name equals the identifier there, we
@@ -316,7 +299,7 @@
rewrittenName =
KotlinMetadataUtils.getKotlinClassName(clazz, rewrittenDescriptor.toString());
}
- kmClass.setName(rewrittenName);
+ rewrittenKmClass.setName(rewrittenName);
rewritten |= !name.equals(rewrittenName);
}
// Find a companion object.
@@ -328,12 +311,13 @@
rewritten |=
kotlinInfo
.asCompanion()
- .rewrite(kmClass, field.getReference(), appView.getNamingLens());
+ .rewrite(rewrittenKmClass, field.getReference(), appView.getNamingLens());
foundCompanion = true;
} else if (kotlinInfo.isEnumEntry()) {
KotlinEnumEntryInfo kotlinEnumEntryInfo = kotlinInfo.asEnumEntry();
rewritten |=
- kotlinEnumEntryInfo.rewrite(kmClass, field.getReference(), appView.getNamingLens());
+ kotlinEnumEntryInfo.rewrite(
+ rewrittenKmClass, field.getReference(), appView.getNamingLens());
if (numberOfEnumEntries >= enumEntries.size()
|| !enumEntries.get(numberOfEnumEntries).equals(kotlinEnumEntryInfo.getEnumEntry())) {
rewritten = true;
@@ -353,32 +337,35 @@
}
// Take all not backed constructors because we will never find them in definitions.
for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
- rewritten |= constructorInfo.rewrite(kmClass, null, appView);
+ rewritten |= constructorInfo.rewrite(rewrittenKmClass, null, appView);
}
// Find all constructors.
KotlinMetadataMembersTracker rewrittenReferences = new KotlinMetadataMembersTracker(appView);
for (DexEncodedMethod method : clazz.methods()) {
if (method.getKotlinInfo().isConstructor()) {
KotlinConstructorInfo constructorInfo = method.getKotlinInfo().asConstructor();
- rewritten |= constructorInfo.rewrite(kmClass, method, appView);
+ rewritten |= constructorInfo.rewrite(rewrittenKmClass, method, appView);
rewrittenReferences.add(method.getReference());
}
}
// Rewrite functions, type-aliases and type-parameters.
rewritten |=
declarationContainerInfo.rewrite(
- kmClass.getFunctions()::add,
- kmClass.getProperties()::add,
- kmClass.getTypeAliases()::add,
+ rewrittenKmClass.getFunctions()::add,
+ rewrittenKmClass.getProperties()::add,
+ rewrittenKmClass.getTypeAliases()::add,
clazz,
appView,
rewrittenReferences);
// Rewrite type parameters.
rewritten |=
rewriteList(
- appView, typeParameters, kmClass.getTypeParameters(), KotlinTypeParameterInfo::rewrite);
+ appView,
+ typeParameters,
+ rewrittenKmClass.getTypeParameters(),
+ KotlinTypeParameterInfo::rewrite);
// Rewrite super types.
- List<KmType> rewrittenSuperTypes = kmClass.getSupertypes();
+ List<KmType> rewrittenSuperTypes = rewrittenKmClass.getSupertypes();
for (KotlinTypeInfo superType : superTypes) {
// Ensure the rewritten super type is not this type.
DexType rewrittenSuperType =
@@ -390,7 +377,7 @@
}
}
// Rewrite nested classes.
- List<String> rewrittenNestedClasses = kmClass.getNestedClasses();
+ List<String> rewrittenNestedClasses = rewrittenKmClass.getNestedClasses();
for (KotlinTypeReference nestedClass : this.nestedClasses) {
Box<String> nestedDescriptorBox = new Box<>();
boolean nestedClassRewritten =
@@ -409,7 +396,7 @@
rewritten |= nestedClassRewritten;
}
// Rewrite sealed sub-classes.
- List<String> rewrittenSealedClasses = kmClass.getSealedSubclasses();
+ List<String> rewrittenSealedClasses = rewrittenKmClass.getSealedSubclasses();
for (KotlinTypeReference sealedSubClass : sealedSubClasses) {
rewritten |=
sealedSubClass.toRenamedBinaryNameOrDefault(
@@ -424,26 +411,26 @@
appView,
null);
}
- rewritten |= versionRequirements.rewrite(kmClass.getVersionRequirements()::addAll);
+ rewrittenKmClass.getVersionRequirements().addAll(kmClass.getVersionRequirements());
if (inlineClassUnderlyingPropertyName != null && inlineClassUnderlyingType != null) {
- kmClass.setInlineClassUnderlyingPropertyName(inlineClassUnderlyingPropertyName);
+ rewrittenKmClass.setInlineClassUnderlyingPropertyName(inlineClassUnderlyingPropertyName);
rewritten |=
- inlineClassUnderlyingType.rewrite(kmClass::setInlineClassUnderlyingType, appView);
+ inlineClassUnderlyingType.rewrite(
+ rewrittenKmClass::setInlineClassUnderlyingType, appView);
}
rewritten |=
rewriteList(
appView,
contextReceiverTypes,
- kmClass.getContextReceiverTypes(),
+ rewrittenKmClass.getContextReceiverTypes(),
KotlinTypeInfo::rewrite);
- JvmExtensionsKt.setJvmFlags(kmClass, jvmFlags);
- JvmExtensionsKt.setModuleName(kmClass, moduleName);
+ JvmExtensionsKt.setModuleName(rewrittenKmClass, moduleName);
if (anonymousObjectOrigin != null) {
rewritten |=
anonymousObjectOrigin.toRenamedBinaryNameOrDefault(
renamedAnon -> {
if (renamedAnon != null) {
- JvmExtensionsKt.setAnonymousObjectOriginName(kmClass, renamedAnon);
+ JvmExtensionsKt.setAnonymousObjectOriginName(rewrittenKmClass, renamedAnon);
}
},
appView,
@@ -451,9 +438,11 @@
}
rewritten |=
localDelegatedProperties.rewrite(
- JvmExtensionsKt.getLocalDelegatedProperties(kmClass)::add, appView);
+ JvmExtensionsKt.getLocalDelegatedProperties(rewrittenKmClass)::add, appView);
+ classMetadata.setKmClass(rewrittenKmClass);
+ updateJvmMetadataVersionIfRequired(classMetadata);
return Pair.create(
- Companion.writeClass(kmClass, getCompatibleKotlinInfo(), 0),
+ classMetadata.write(),
rewritten || !originalMembersWithKotlinInfo.isEqual(rewrittenReferences, appView));
}
@@ -464,7 +453,7 @@
@Override
public int[] getMetadataVersion() {
- return metadataVersion;
+ return KotlinJvmMetadataVersionUtils.toIntArray(classMetadata.getVersion());
}
@Override
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 4a66565..715d376 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -218,29 +218,26 @@
Consumer<DexEncodedMethod> keepByteCode) {
Metadata metadata = extractMetadataWithPossiblyUnsupportedMetadataVersion(kMetadata);
String packageName = metadata.pn();
- int[] metadataVersion = KotlinJvmMetadataVersionUtils.toIntArray(kMetadata.getVersion());
if (kMetadata instanceof KotlinClassMetadata.Class) {
return KotlinClassInfo.create(
(KotlinClassMetadata.Class) kMetadata,
packageName,
- metadataVersion,
clazz,
appView,
keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
// e.g., B.kt becomes class `BKt`
return KotlinFileFacadeInfo.create(
- (FileFacade) kMetadata, packageName, metadataVersion, clazz, appView, keepByteCode);
+ (FileFacade) kMetadata, packageName, clazz, appView, keepByteCode);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
// multi-file class with the same @JvmName.
return KotlinMultiFileClassFacadeInfo.create(
- (MultiFileClassFacade) kMetadata, packageName, metadataVersion, appView.dexItemFactory());
+ (MultiFileClassFacade) kMetadata, packageName, appView.dexItemFactory());
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
// A single file, which is part of multi-file class.
return KotlinMultiFileClassPartInfo.create(
(MultiFileClassPart) kMetadata,
packageName,
- metadataVersion,
clazz,
appView,
keepByteCode);
@@ -248,7 +245,6 @@
return KotlinSyntheticClassInfo.create(
(KotlinClassMetadata.SyntheticClass) kMetadata,
packageName,
- metadataVersion,
clazz,
kotlin,
appView);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
index ef21b62..83f3612 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -18,7 +18,7 @@
import kotlin.metadata.KmClassifier;
import kotlin.metadata.KmClassifier.TypeAlias;
import kotlin.metadata.KmClassifier.TypeParameter;
-import kotlin.metadata.KmTypeVisitor;
+import kotlin.metadata.KmType;
public abstract class KotlinClassifierInfo implements EnqueuerMetadataTraceable {
@@ -49,7 +49,7 @@
}
}
- abstract boolean rewrite(KmTypeVisitor visitor, AppView<?> appView);
+ abstract boolean rewrite(KmType kmType, AppView<?> appView);
public DexType rewriteType(GraphLens graphLens, GraphLens codeLens) {
return null;
@@ -66,11 +66,12 @@
}
@Override
- boolean rewrite(KmTypeVisitor visitor, AppView<?> appView) {
+ boolean rewrite(KmType kmType, AppView<?> appView) {
return type.toRenamedDescriptorOrDefault(
descriptor ->
- visitor.visitClass(
- getKotlinLocalOrAnonymousNameFromDescriptor(descriptor, isLocalOrAnonymous)),
+ kmType.setClassifier(
+ new KmClassifier.Class(
+ getKotlinLocalOrAnonymousNameFromDescriptor(descriptor, isLocalOrAnonymous))),
appView,
ClassClassifiers.anyDescriptor);
}
@@ -95,8 +96,8 @@
}
@Override
- boolean rewrite(KmTypeVisitor visitor, AppView<?> appView) {
- visitor.visitTypeParameter(typeId);
+ boolean rewrite(KmType kmType, AppView<?> appView) {
+ kmType.setClassifier(new KmClassifier.TypeParameter(typeId));
return false;
}
@@ -115,8 +116,8 @@
}
@Override
- boolean rewrite(KmTypeVisitor visitor, AppView<?> appView) {
- visitor.visitTypeAlias(typeAlias);
+ boolean rewrite(KmType kmType, AppView<?> appView) {
+ kmType.setClassifier(new KmClassifier.TypeAlias(typeAlias));
return false;
}
@@ -134,8 +135,8 @@
}
@Override
- boolean rewrite(KmTypeVisitor visitor, AppView<?> appView) {
- visitor.visitClass(classifier);
+ boolean rewrite(KmType kmType, AppView<?> appView) {
+ kmType.setClassifier(new KmClassifier.Class(classifier));
return false;
}
@@ -153,8 +154,8 @@
}
@Override
- boolean rewrite(KmTypeVisitor visitor, AppView<?> appView) {
- visitor.visitTypeAlias(classifier);
+ boolean rewrite(KmType kmType, AppView<?> appView) {
+ kmType.setClassifier(new KmClassifier.TypeAlias(classifier));
return false;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
index c6981c9..7cbe845 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.NamingLens;
-import kotlin.metadata.KmClassVisitor;
+import kotlin.metadata.KmClass;
// Structure around a kotlin companion object that can be assigned to a field.
public class KotlinCompanionInfo implements KotlinFieldLevelInfo {
@@ -29,10 +29,10 @@
return this;
}
- boolean rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
+ boolean rewrite(KmClass clazz, DexField field, NamingLens lens) {
DexString dexString = lens.lookupName(field);
String finalName = dexString.toString();
- visitor.visitCompanionObject(finalName);
+ clazz.setCompanionObject(finalName);
return !finalName.equals(companionObjectFieldName);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index 28de1a1..44fb122 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -20,45 +20,41 @@
// Holds information about a KmConstructor object.
public class KotlinConstructorInfo implements KotlinMethodLevelInfo {
- // Information from original KmValueParameter(s) if available.
- private final int flags;
+ // Information from original constructor.
+ private final KmConstructor kmConstructor;
// Information about the value parameters.
private final List<KotlinValueParameterInfo> valueParameters;
// Information about version requirements.
- private final KotlinVersionRequirementInfo versionRequirements;
- // Information about the signature.
private final KotlinJvmMethodSignatureInfo signature;
private KotlinConstructorInfo(
- int flags,
+ KmConstructor kmConstructor,
List<KotlinValueParameterInfo> valueParameters,
- KotlinVersionRequirementInfo versionRequirements,
KotlinJvmMethodSignatureInfo signature) {
- this.flags = flags;
+ this.kmConstructor = kmConstructor;
this.valueParameters = valueParameters;
- this.versionRequirements = versionRequirements;
this.signature = signature;
}
public static KotlinConstructorInfo create(
KmConstructor kmConstructor, DexItemFactory factory, Reporter reporter) {
return new KotlinConstructorInfo(
- kmConstructor.getFlags(),
+ kmConstructor,
KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), factory, reporter),
- KotlinVersionRequirementInfo.create(kmConstructor.getVersionRequirements()),
KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), factory));
}
boolean rewrite(KmClass kmClass, DexEncodedMethod method, AppView<?> appView) {
// Note that JvmExtensionsKt.setSignature does not have an overload for KmConstructorVisitor,
// thus we rely on creating the KmConstructor manually.
- // TODO(b/154348683): Check for special flags to pass in.
- KmConstructor kmConstructor = new KmConstructor(flags);
+ KmConstructor rewrittenKmConstructor = new KmConstructor();
+ KotlinFlagUtils.copyAllFlags(kmConstructor, rewrittenKmConstructor);
boolean rewritten = false;
if (signature != null) {
rewritten =
signature.rewrite(
- rewrittenSignature -> JvmExtensionsKt.setSignature(kmConstructor, rewrittenSignature),
+ rewrittenSignature ->
+ JvmExtensionsKt.setSignature(rewrittenKmConstructor, rewrittenSignature),
method,
appView);
}
@@ -66,10 +62,10 @@
rewriteList(
appView,
valueParameters,
- kmConstructor.getValueParameters(),
+ rewrittenKmConstructor.getValueParameters(),
KotlinValueParameterInfo::rewrite);
- rewritten |= versionRequirements.rewrite(kmConstructor.getVersionRequirements()::addAll);
- kmClass.getConstructors().add(kmConstructor);
+ rewrittenKmConstructor.getVersionRequirements().addAll(kmConstructor.getVersionRequirements());
+ kmClass.getConstructors().add(rewrittenKmConstructor);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
index 1306045..4fb6774 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -54,7 +53,8 @@
if (this == NO_EFFECT) {
return false;
}
- KmContract kmContract = consume(new KmContract(), consumer);
+ KmContract kmContract = new KmContract();
+ consumer.accept(kmContract);
return rewriteList(appView, effects, kmContract.getEffects(), KotlinEffectInfo::rewrite);
}
}
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 21f5469..d1d9441 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -26,11 +26,12 @@
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
+import kotlin.metadata.Attributes;
import kotlin.metadata.KmDeclarationContainer;
import kotlin.metadata.KmFunction;
import kotlin.metadata.KmProperty;
+import kotlin.metadata.KmPropertyAccessorAttributes;
import kotlin.metadata.KmTypeAlias;
-import kotlin.metadata.internal.metadata.deserialization.Flags;
import kotlin.metadata.jvm.JvmExtensionsKt;
import kotlin.metadata.jvm.JvmMethodSignature;
@@ -85,7 +86,7 @@
}
continue;
}
- keepIfInline(kmFunction.getFlags(), method, signature, methodSignatureMap, keepByteCode);
+ keepIfInline(kmFunction, method, signature, methodSignatureMap, keepByteCode);
method.setKotlinMemberInfo(kotlinFunctionInfo);
originalAssignmentTracker.add(method.getReference());
}
@@ -110,7 +111,7 @@
methodSignatureMap.get(propertyProcessor.getterSignature().toString());
if (method != null) {
hasBacking = true;
- keepIfAccessorInline(kmProperty.getGetterFlags(), method, keepByteCode);
+ keepIfAccessorInline(kmProperty.getGetter(), method, keepByteCode);
method.setKotlinMemberInfo(
new KotlinPropertyInfoDelegate(kotlinPropertyInfo, PropertyType.GETTER));
originalAssignmentTracker.add(method.getReference());
@@ -121,7 +122,7 @@
methodSignatureMap.get(propertyProcessor.setterSignature().toString());
if (method != null) {
hasBacking = true;
- keepIfAccessorInline(kmProperty.getGetterFlags(), method, keepByteCode);
+ keepIfAccessorInline(kmProperty.getGetter(), method, keepByteCode);
method.setKotlinMemberInfo(
new KotlinPropertyInfoDelegate(kotlinPropertyInfo, PropertyType.SETTER));
originalAssignmentTracker.add(method.getReference());
@@ -150,12 +151,12 @@
}
private static void keepIfInline(
- int flags,
+ KmFunction kmFunction,
DexEncodedMethod method,
JvmMethodSignature signature,
Map<String, DexEncodedMethod> methodSignatureMap,
Consumer<DexEncodedMethod> keepByteCode) {
- if (Flags.IS_INLINE.get(flags)) {
+ if (Attributes.isInline(kmFunction)) {
// Check if we can find a default method. If there are more than 32 arguments another int
// index will be added to the default method.
for (int i = 1;
@@ -173,8 +174,10 @@
}
private static void keepIfAccessorInline(
- int flags, DexEncodedMethod method, Consumer<DexEncodedMethod> keepByteCode) {
- if (Flags.IS_INLINE_ACCESSOR.get(flags)) {
+ KmPropertyAccessorAttributes kmPropertyAccessorAttributes,
+ DexEncodedMethod method,
+ Consumer<DexEncodedMethod> keepByteCode) {
+ if (Attributes.isInline(kmPropertyAccessorAttributes)) {
keepByteCode.accept(method);
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
index ae218d2..f87cd78 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -24,42 +23,35 @@
private static final List<KotlinEffectExpressionInfo> NO_EXPRESSIONS = ImmutableList.of();
private static final KotlinEffectExpressionInfo NO_EXPRESSION =
- new KotlinEffectExpressionInfo(0, 0, null, null, NO_EXPRESSIONS, NO_EXPRESSIONS);
+ new KotlinEffectExpressionInfo(
+ new KmEffectExpression(), null, NO_EXPRESSIONS, NO_EXPRESSIONS);
- private final int flags;
- private final Integer parameterIndex;
- private final KmConstantValue constantValue;
+ private final KmEffectExpression kmEffectExpression;
private final KotlinTypeInfo isInstanceType;
private final List<KotlinEffectExpressionInfo> andArguments;
private final List<KotlinEffectExpressionInfo> orArguments;
private KotlinEffectExpressionInfo(
- int flags,
- Integer parameterIndex,
- KmConstantValue constantValue,
+ KmEffectExpression kmEffectExpression,
KotlinTypeInfo isInstanceType,
List<KotlinEffectExpressionInfo> andArguments,
List<KotlinEffectExpressionInfo> orArguments) {
- this.flags = flags;
- this.parameterIndex = parameterIndex;
- this.constantValue = constantValue;
+ this.kmEffectExpression = kmEffectExpression;
this.isInstanceType = isInstanceType;
this.andArguments = andArguments;
this.orArguments = orArguments;
}
static KotlinEffectExpressionInfo create(
- KmEffectExpression effectExpression, DexItemFactory factory, Reporter reporter) {
- if (effectExpression == null) {
+ KmEffectExpression kmEffectExpression, DexItemFactory factory, Reporter reporter) {
+ if (kmEffectExpression == null) {
return NO_EXPRESSION;
}
return new KotlinEffectExpressionInfo(
- effectExpression.getFlags(),
- effectExpression.getParameterIndex(),
- effectExpression.getConstantValue(),
- KotlinTypeInfo.create(effectExpression.isInstanceType(), factory, reporter),
- create(effectExpression.getAndArguments(), factory, reporter),
- create(effectExpression.getOrArguments(), factory, reporter));
+ kmEffectExpression,
+ KotlinTypeInfo.create(kmEffectExpression.isInstanceType(), factory, reporter),
+ create(kmEffectExpression.getAndArguments(), factory, reporter),
+ create(kmEffectExpression.getOrArguments(), factory, reporter));
}
static List<KotlinEffectExpressionInfo> create(
@@ -90,26 +82,31 @@
if (this == NO_EXPRESSION) {
return false;
}
- KmEffectExpression effectExpression = consume(new KmEffectExpression(), consumer);
- effectExpression.setFlags(flags);
- effectExpression.setParameterIndex(parameterIndex);
+ KmEffectExpression rewrittenKmEffectExpression = new KmEffectExpression();
+ consumer.accept(rewrittenKmEffectExpression);
+ KotlinFlagUtils.copyAllFlags(kmEffectExpression, rewrittenKmEffectExpression);
+ rewrittenKmEffectExpression.setParameterIndex(kmEffectExpression.getParameterIndex());
+ KmConstantValue constantValue = kmEffectExpression.getConstantValue();
if (constantValue != null) {
- effectExpression.setConstantValue(constantValue);
+ rewrittenKmEffectExpression.setConstantValue(constantValue);
}
boolean rewritten =
rewriteIfNotNull(
- appView, isInstanceType, effectExpression::setInstanceType, KotlinTypeInfo::rewrite);
+ appView,
+ isInstanceType,
+ rewrittenKmEffectExpression::setInstanceType,
+ KotlinTypeInfo::rewrite);
rewritten |=
rewriteList(
appView,
andArguments,
- effectExpression.getAndArguments(),
+ rewrittenKmEffectExpression.getAndArguments(),
KotlinEffectExpressionInfo::rewrite);
rewritten |=
rewriteList(
appView,
orArguments,
- effectExpression.getOrArguments(),
+ rewrittenKmEffectExpression.getOrArguments(),
KotlinEffectExpressionInfo::rewrite);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
index 0a973a1..6fc3950 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -52,7 +51,8 @@
}
boolean rewrite(Consumer<KmEffect> consumer, AppView<?> appView) {
- KmEffect kmEffect = consume(new KmEffect(type, invocationKind), consumer);
+ KmEffect kmEffect = new KmEffect(type, invocationKind);
+ consumer.accept(kmEffect);
boolean rewritten = conclusion.rewrite(kmEffect::setConclusion, appView);
rewritten |=
rewriteList(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java
index ff1336d..4c3197d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.NamingLens;
-import kotlin.metadata.KmClassVisitor;
+import kotlin.metadata.KmClass;
// Structure around a kotlin enum value that can be assigned to a field.
public class KotlinEnumEntryInfo implements KotlinFieldLevelInfo {
@@ -29,10 +29,10 @@
return this;
}
- boolean rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
+ boolean rewrite(KmClass clazz, DexField field, NamingLens lens) {
DexString dexString = lens.lookupName(field);
String finalName = dexString.toString();
- visitor.visitEnumEntry(finalName);
+ clazz.getEnumEntries().add(finalName);
return !finalName.equals(enumEntry);
}
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 2c46b82..07d4faa 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlin.metadata.jvm.KotlinClassMetadata.Companion;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.updateJvmMetadataVersionIfRequired;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -20,29 +19,28 @@
// Holds information about Metadata.FileFacade
public class KotlinFileFacadeInfo implements KotlinClassLevelInfo {
+ private final FileFacade kmFileFacade;
private final KotlinPackageInfo packageInfo;
private final String packageName;
- private final int[] metadataVersion;
private KotlinFileFacadeInfo(
- KotlinPackageInfo packageInfo, String packageName, int[] metadataVersion) {
+ FileFacade kmFileFacade, KotlinPackageInfo packageInfo, String packageName) {
+ this.kmFileFacade = kmFileFacade;
this.packageInfo = packageInfo;
this.packageName = packageName;
- this.metadataVersion = metadataVersion;
}
public static KotlinFileFacadeInfo create(
FileFacade kmFileFacade,
String packageName,
- int[] metadataVersion,
DexClass clazz,
AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
KmPackage kmPackage = kmFileFacade.getKmPackage();
return new KotlinFileFacadeInfo(
+ kmFileFacade,
KotlinPackageInfo.create(kmPackage, clazz, appView, keepByteCode),
- packageName,
- metadataVersion);
+ packageName);
}
@Override
@@ -59,8 +57,9 @@
public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
KmPackage kmPackage = new KmPackage();
boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
- return Pair.create(
- Companion.writeFileFacade(kmPackage, getCompatibleKotlinInfo(), 0), rewritten);
+ updateJvmMetadataVersionIfRequired(kmFileFacade);
+ kmFileFacade.setKmPackage(kmPackage);
+ return Pair.create(kmFileFacade.write(), rewritten);
}
@Override
@@ -74,7 +73,7 @@
@Override
public int[] getMetadataVersion() {
- return metadataVersion;
+ return KotlinJvmMetadataVersionUtils.toIntArray(kmFileFacade.getVersion());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFlagUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFlagUtils.java
new file mode 100644
index 0000000..3bb712e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlagUtils.java
@@ -0,0 +1,261 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import java.util.HashMap;
+import java.util.Map;
+import kotlin.metadata.Attributes;
+import kotlin.metadata.KmClass;
+import kotlin.metadata.KmConstructor;
+import kotlin.metadata.KmEffectExpression;
+import kotlin.metadata.KmFunction;
+import kotlin.metadata.KmProperty;
+import kotlin.metadata.KmPropertyAccessorAttributes;
+import kotlin.metadata.KmType;
+import kotlin.metadata.KmTypeAlias;
+import kotlin.metadata.KmTypeParameter;
+import kotlin.metadata.KmValueParameter;
+import kotlin.metadata.jvm.JvmAttributes;
+
+public class KotlinFlagUtils {
+
+ private static final String ANNOTATIONS_KEY = "hasAnnotations";
+ private static final String VISIBILITY_KEY = "visibility";
+ private static final String MODALITY_KEY = "modality";
+ private static final String KIND_KEY = "kind";
+ private static final String INNER_KEY = "inner";
+ private static final String DATA_KEY = "data";
+ private static final String VALUE_KEY = "value";
+ private static final String FUN_INTERFACE_KEY = "funInterface";
+ private static final String ENUM_ENTRIES_KEY = "enumEntries";
+ private static final String VAR_KEY = "var";
+ private static final String CONST_KEY = "const";
+ private static final String LATE_INIT_KEY = "lateInit";
+ private static final String CONSTANT_KEY = "hasConstant";
+ private static final String EXTERNAL_KEY = "external";
+ private static final String DELEGATED_KEY = "delegated";
+ private static final String EXPECT_KEY = "expect";
+ private static final String NOT_DEFAULT_KEY = "notDefault";
+ private static final String INLINE_KEY = "inline";
+ private static final String SECONDARY_KEY = "secondary";
+ private static final String NON_STABLE_PARAMETER_NAMES_KEY = "nonStableParameterNames";
+ private static final String NEGATED_KEY = "negated";
+ private static final String NULL_CHECK_PREDICATE_KEY = "nullCheckPredicate";
+ private static final String OPERATOR_KEY = "operator";
+ private static final String INFIX_KEY = "infix";
+ private static final String TAIL_REC_KEY = "tailRec";
+ private static final String SUSPEND_KEY = "suspend";
+ private static final String NULLABLE_KEY = "nullable";
+ private static final String DEFINITELY_NON_NULL_KEY = "definitelyNonNull";
+ private static final String DECLARES_DEFAULT_VALUE_KEY = "declaresDefaultValue";
+ private static final String CROSS_INLINE_KEY = "crossInline";
+ private static final String NO_INLINE_KEY = "noInline";
+ public static final String REIFIED_KEY = "reified";
+
+ public static Map<String, Object> extractFlags(KmProperty src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ map.put(MODALITY_KEY, Attributes.getModality(src));
+ map.put(KIND_KEY, Attributes.getKind(src));
+ map.put(VAR_KEY, Attributes.isVar(src));
+ map.put(CONST_KEY, Attributes.isConst(src));
+ map.put(LATE_INIT_KEY, Attributes.isLateinit(src));
+ map.put(CONSTANT_KEY, Attributes.getHasConstant(src));
+ map.put(EXTERNAL_KEY, Attributes.isExternal(src));
+ map.put(DELEGATED_KEY, Attributes.isDelegated(src));
+ map.put(EXPECT_KEY, Attributes.isExpect(src));
+
+ map.put("movedFromInterfaceCompanion", JvmAttributes.isMovedFromInterfaceCompanion(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmProperty src, KmProperty dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ Attributes.setModality(dest, Attributes.getModality(src));
+ Attributes.setKind(dest, Attributes.getKind(src));
+ Attributes.setVar(dest, Attributes.isVar(src));
+ Attributes.setConst(dest, Attributes.isConst(src));
+ Attributes.setLateinit(dest, Attributes.isLateinit(src));
+ Attributes.setHasConstant(dest, Attributes.getHasConstant(src));
+ Attributes.setExternal(dest, Attributes.isExternal(src));
+ Attributes.setDelegated(dest, Attributes.isDelegated(src));
+ Attributes.setExpect(dest, Attributes.isExpect(src));
+
+ JvmAttributes.setMovedFromInterfaceCompanion(
+ dest, JvmAttributes.isMovedFromInterfaceCompanion(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmType src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(NULLABLE_KEY, Attributes.isNullable(src));
+ map.put(SUSPEND_KEY, Attributes.isSuspend(src));
+ map.put(DEFINITELY_NON_NULL_KEY, Attributes.isDefinitelyNonNull(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmType src, KmType dest) {
+ Attributes.setNullable(dest, Attributes.isNullable(src));
+ Attributes.setSuspend(dest, Attributes.isSuspend(src));
+ Attributes.setDefinitelyNonNull(dest, Attributes.isDefinitelyNonNull(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmPropertyAccessorAttributes src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ map.put(MODALITY_KEY, Attributes.getModality(src));
+ map.put(NOT_DEFAULT_KEY, Attributes.isNotDefault(src));
+ map.put(EXTERNAL_KEY, Attributes.isExternal(src));
+ map.put(INLINE_KEY, Attributes.isInline(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmPropertyAccessorAttributes src, KmPropertyAccessorAttributes dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ Attributes.setModality(dest, Attributes.getModality(src));
+ Attributes.setNotDefault(dest, Attributes.isNotDefault(src));
+ Attributes.setExternal(dest, Attributes.isExternal(src));
+ Attributes.setInline(dest, Attributes.isInline(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmClass src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ map.put(MODALITY_KEY, Attributes.getModality(src));
+ map.put(KIND_KEY, Attributes.getKind(src));
+ map.put(INNER_KEY, Attributes.isInner(src));
+ map.put(DATA_KEY, Attributes.isData(src));
+ map.put(EXTERNAL_KEY, Attributes.isExternal(src));
+ map.put(EXPECT_KEY, Attributes.isExpect(src));
+ map.put(VALUE_KEY, Attributes.isValue(src));
+ map.put(FUN_INTERFACE_KEY, Attributes.isFunInterface(src));
+ map.put(ENUM_ENTRIES_KEY, Attributes.getHasEnumEntries(src));
+
+ map.put("compiledInCompatibilityMode", JvmAttributes.isCompiledInCompatibilityMode(src));
+ map.put("hasMethodBodiesInInterface", JvmAttributes.getHasMethodBodiesInInterface(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmClass src, KmClass dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ Attributes.setModality(dest, Attributes.getModality(src));
+ Attributes.setKind(dest, Attributes.getKind(src));
+ Attributes.setInner(dest, Attributes.isInner(src));
+ Attributes.setData(dest, Attributes.isData(src));
+ Attributes.setExternal(dest, Attributes.isExternal(src));
+ Attributes.setExpect(dest, Attributes.isExpect(src));
+ Attributes.setValue(dest, Attributes.isValue(src));
+ Attributes.setFunInterface(dest, Attributes.isFunInterface(src));
+ Attributes.setHasEnumEntries(dest, Attributes.getHasEnumEntries(src));
+
+ JvmAttributes.setCompiledInCompatibilityMode(
+ dest, JvmAttributes.isCompiledInCompatibilityMode(src));
+ JvmAttributes.setHasMethodBodiesInInterface(
+ dest, JvmAttributes.getHasMethodBodiesInInterface(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmConstructor src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ map.put(SECONDARY_KEY, Attributes.isSecondary(src));
+ map.put(NON_STABLE_PARAMETER_NAMES_KEY, Attributes.getHasNonStableParameterNames(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmConstructor src, KmConstructor dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ Attributes.setSecondary(dest, Attributes.isSecondary(src));
+ Attributes.setHasNonStableParameterNames(dest, Attributes.getHasNonStableParameterNames(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmFunction src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(KIND_KEY, Attributes.getKind(src));
+ map.put(MODALITY_KEY, Attributes.getModality(src));
+ map.put(OPERATOR_KEY, Attributes.isOperator(src));
+ map.put(INFIX_KEY, Attributes.isInfix(src));
+ map.put(INLINE_KEY, Attributes.isInline(src));
+ map.put(TAIL_REC_KEY, Attributes.isTailrec(src));
+ map.put(EXTERNAL_KEY, Attributes.isExternal(src));
+ map.put(SUSPEND_KEY, Attributes.isSuspend(src));
+ map.put(EXPECT_KEY, Attributes.isExpect(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ map.put(NON_STABLE_PARAMETER_NAMES_KEY, Attributes.getHasNonStableParameterNames(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmFunction src, KmFunction dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setKind(dest, Attributes.getKind(src));
+ Attributes.setModality(dest, Attributes.getModality(src));
+ Attributes.setOperator(dest, Attributes.isOperator(src));
+ Attributes.setInfix(dest, Attributes.isInfix(src));
+ Attributes.setInline(dest, Attributes.isInline(src));
+ Attributes.setTailrec(dest, Attributes.isTailrec(src));
+ Attributes.setExternal(dest, Attributes.isExternal(src));
+ Attributes.setSuspend(dest, Attributes.isSuspend(src));
+ Attributes.setExpect(dest, Attributes.isExpect(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ Attributes.setHasNonStableParameterNames(dest, Attributes.getHasNonStableParameterNames(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmEffectExpression src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(NEGATED_KEY, Attributes.isNegated(src));
+ map.put(NULL_CHECK_PREDICATE_KEY, Attributes.isNullCheckPredicate(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmEffectExpression src, KmEffectExpression dest) {
+ Attributes.setNegated(dest, Attributes.isNegated(src));
+ Attributes.setNullCheckPredicate(dest, Attributes.isNullCheckPredicate(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmTypeAlias src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(VISIBILITY_KEY, Attributes.getVisibility(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmTypeAlias src, KmTypeAlias dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setVisibility(dest, Attributes.getVisibility(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmValueParameter src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(ANNOTATIONS_KEY, Attributes.getHasAnnotations(src));
+ map.put(DECLARES_DEFAULT_VALUE_KEY, Attributes.getDeclaresDefaultValue(src));
+ map.put(CROSS_INLINE_KEY, Attributes.isCrossinline(src));
+ map.put(NO_INLINE_KEY, Attributes.isNoinline(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmValueParameter src, KmValueParameter dest) {
+ Attributes.setHasAnnotations(dest, Attributes.getHasAnnotations(src));
+ Attributes.setDeclaresDefaultValue(dest, Attributes.getDeclaresDefaultValue(src));
+ Attributes.setCrossinline(dest, Attributes.isCrossinline(src));
+ Attributes.setNoinline(dest, Attributes.isNoinline(src));
+ }
+
+ public static Map<String, Object> extractFlags(KmTypeParameter src) {
+ Map<String, Object> map = new HashMap<>();
+ map.put(REIFIED_KEY, Attributes.isReified(src));
+ return map;
+ }
+
+ static void copyAllFlags(KmTypeParameter src, KmTypeParameter dest) {
+ Attributes.setReified(dest, Attributes.isReified(src));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index aa453f6..c880a0e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -22,10 +21,8 @@
// Holds information about KmFunction
public final class KotlinFunctionInfo implements KotlinMethodLevelInfo {
- // Original flags
- private final int flags;
- // Original name;
- private final String name;
+ // Original function.
+ private final KmFunction kmFunction;
// Information from original KmValueParameter(s) if available.
private final List<KotlinValueParameterInfo> valueParameters;
// Information from original KmFunction.returnType. Null if this is from a KmConstructor.
@@ -38,8 +35,6 @@
private final KotlinJvmMethodSignatureInfo signature;
// Information about the lambdaClassOrigin.
private final KotlinTypeReference lambdaClassOrigin;
- // Information about version requirements.
- private final KotlinVersionRequirementInfo versionRequirements;
// Kotlin contract information.
private final KotlinContractInfo contract;
// A value describing if any of the parameters are crossinline.
@@ -48,27 +43,23 @@
private final List<KotlinTypeInfo> contextReceiverTypes;
private KotlinFunctionInfo(
- int flags,
- String name,
+ KmFunction kmFunction,
KotlinTypeInfo returnType,
KotlinTypeInfo receiverParameterType,
List<KotlinValueParameterInfo> valueParameters,
List<KotlinTypeParameterInfo> typeParameters,
KotlinJvmMethodSignatureInfo signature,
KotlinTypeReference lambdaClassOrigin,
- KotlinVersionRequirementInfo versionRequirements,
KotlinContractInfo contract,
boolean crossInlineParameter,
List<KotlinTypeInfo> contextReceiverTypes) {
- this.flags = flags;
- this.name = name;
+ this.kmFunction = kmFunction;
this.returnType = returnType;
this.receiverParameterType = receiverParameterType;
this.valueParameters = valueParameters;
this.typeParameters = typeParameters;
this.signature = signature;
this.lambdaClassOrigin = lambdaClassOrigin;
- this.versionRequirements = versionRequirements;
this.contract = contract;
this.crossInlineParameter = crossInlineParameter;
this.contextReceiverTypes = contextReceiverTypes;
@@ -90,15 +81,13 @@
}
}
return new KotlinFunctionInfo(
- kmFunction.getFlags(),
- kmFunction.getName(),
+ kmFunction,
KotlinTypeInfo.create(kmFunction.getReturnType(), factory, reporter),
KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), factory, reporter),
valueParameters,
KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), factory, reporter),
KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory),
getlambdaClassOrigin(kmFunction, factory),
- KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
KotlinContractInfo.create(kmFunction.getContract(), factory, reporter),
isCrossInline,
ListUtils.map(
@@ -117,7 +106,7 @@
}
public String getName() {
- return name;
+ return kmFunction.getName();
}
boolean rewriteNoBacking(Consumer<KmFunction> consumer, AppView<?> appView) {
@@ -127,7 +116,7 @@
boolean rewrite(Consumer<KmFunction> consumer, DexEncodedMethod method, AppView<?> appView) {
// TODO(b/154348683): Check method for flags to pass in.
boolean rewritten = false;
- String finalName = name;
+ String finalName = getName();
// Only rewrite the kotlin method name if it was equal to the method name when reading the
// metadata.
if (method != null) {
@@ -138,51 +127,56 @@
finalName = rewrittenName;
}
}
- KmFunction kmFunction = consume(new KmFunction(flags, finalName), consumer);
+ KmFunction rewrittenKmFunction = new KmFunction(finalName);
+ consumer.accept(rewrittenKmFunction);
+ KotlinFlagUtils.copyAllFlags(kmFunction, rewrittenKmFunction);
// TODO(b/154348149): ReturnType could have been merged to a subtype.
- rewritten |= returnType.rewrite(kmFunction::setReturnType, appView);
+ rewritten |= returnType.rewrite(rewrittenKmFunction::setReturnType, appView);
rewritten |=
rewriteList(
appView,
valueParameters,
- kmFunction.getValueParameters(),
+ rewrittenKmFunction.getValueParameters(),
KotlinValueParameterInfo::rewrite);
rewritten |=
rewriteList(
appView,
typeParameters,
- kmFunction.getTypeParameters(),
+ rewrittenKmFunction.getTypeParameters(),
KotlinTypeParameterInfo::rewrite);
rewritten |=
rewriteList(
appView,
contextReceiverTypes,
- kmFunction.getContextReceiverTypes(),
+ rewrittenKmFunction.getContextReceiverTypes(),
KotlinTypeInfo::rewrite);
rewritten |=
rewriteIfNotNull(
appView,
receiverParameterType,
- kmFunction::setReceiverParameterType,
+ rewrittenKmFunction::setReceiverParameterType,
KotlinTypeInfo::rewrite);
- rewritten |= versionRequirements.rewrite(kmFunction.getVersionRequirements()::addAll);
+ rewrittenKmFunction.getVersionRequirements().addAll(kmFunction.getVersionRequirements());
if (signature != null) {
rewritten |=
signature.rewrite(
- signature -> JvmExtensionsKt.setSignature(kmFunction, signature), method, appView);
+ signature -> JvmExtensionsKt.setSignature(rewrittenKmFunction, signature),
+ method,
+ appView);
}
if (lambdaClassOrigin != null) {
rewritten |=
lambdaClassOrigin.toRenamedBinaryNameOrDefault(
lambdaClassOriginName -> {
if (lambdaClassOriginName != null) {
- JvmExtensionsKt.setLambdaClassOriginName(kmFunction, lambdaClassOriginName);
+ JvmExtensionsKt.setLambdaClassOriginName(
+ rewrittenKmFunction, lambdaClassOriginName);
}
},
appView,
null);
}
- rewritten |= contract.rewrite(kmFunction::setContract, appView);
+ rewritten |= contract.rewrite(rewrittenKmFunction::setContract, appView);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 77673c0..d9ecef0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
import com.android.tools.r8.graph.AppView;
@@ -51,7 +50,8 @@
}
boolean rewrite(Consumer<KmLambda> consumer, DexClass clazz, AppView<?> appView) {
- KmLambda kmLambda = consume(new KmLambda(), consumer);
+ KmLambda kmLambda = new KmLambda();
+ consumer.accept(kmLambda);
if (!hasBacking) {
return function.rewrite(kmLambda::setFunction, null, appView);
}
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 c2f31ef..9516e45 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -29,9 +29,11 @@
import kotlin.metadata.jvm.JvmFieldSignature;
import kotlin.metadata.jvm.JvmMetadataVersion;
import kotlin.metadata.jvm.JvmMethodSignature;
+import kotlin.metadata.jvm.KotlinClassMetadata;
public class KotlinMetadataUtils {
+ private static final JvmMetadataVersion VERSION_1_4_0 = new JvmMetadataVersion(1, 4, 0);
private static final NoKotlinInfo NO_KOTLIN_INFO = new NoKotlinInfo("NO_KOTLIN_INFO");
private static final NoKotlinInfo INVALID_KOTLIN_INFO = new NoKotlinInfo("INVALID_KOTLIN_INFO");
@@ -220,18 +222,10 @@
return DescriptorUtils.descriptorToKotlinClassifier(descriptor);
}
- static int[] getCompatibleKotlinInfo() {
- // The kotlin metadata changelog recommends:
- // "Main migration path here is to replace KotlinClassMetadata.COMPATIBLE_METADATA_VERSION
- // with new value with the same meaning: JvmMetadataVersion.LATEST_STABLE_SUPPORTED."
- // The inspection error "Usage of Kotlin internal declaration from different module" does not
- // prevent the code to work correctly.
- return JvmMetadataVersion.LATEST_STABLE_SUPPORTED.toIntArray();
- }
-
- static <TKm> TKm consume(TKm tKm, Consumer<TKm> consumer) {
- consumer.accept(tKm);
- return tKm;
+ public static void updateJvmMetadataVersionIfRequired(KotlinClassMetadata metadata) {
+ if (metadata.getVersion().compareTo(VERSION_1_4_0) < 0) {
+ metadata.setVersion(VERSION_1_4_0);
+ }
}
static <TInfo, TKm> boolean rewriteIfNotNull(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index 24c5af9..9509c98 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -286,9 +286,23 @@
(nextNextIndent, kmProperty) -> appendKmProperty(nextNextIndent, sb, kmProperty)));
}
+ public static void appendFlags(
+ String indent, String keyword, StringBuilder sb, Map<String, Object> flags) {
+ sb.append(indent).append(keyword).append(": [").append(LINE_SEPARATOR);
+ flags.forEach(
+ (name, value) ->
+ sb.append(indent)
+ .append(INDENT)
+ .append(name)
+ .append(": ")
+ .append(value)
+ .append(",")
+ .append(LINE_SEPARATOR));
+ sb.append(indent).append("]");
+ }
+
public static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
- appendKeyValue(indent, "flags", sb, kmClass.getFlags() + "");
- appendKeyValue(indent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmClass) + "");
+ appendFlags(indent, "flags", sb, KotlinFlagUtils.extractFlags(kmClass));
appendKeyValue(indent, "name", sb, kmClass.getName());
appendKeyValue(
indent,
@@ -387,7 +401,7 @@
"KmConstructor",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, constructor.getFlags() + "");
+ appendFlags(indent, "flags", sb, KotlinFlagUtils.extractFlags(constructor));
appendKeyValue(
newIndent,
"valueParameters",
@@ -406,7 +420,7 @@
"KmFunction",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, function.getFlags() + "");
+ appendFlags(indent, "flags", sb, KotlinFlagUtils.extractFlags(function));
appendKeyValue(newIndent, "name", sb, function.getName());
appendKeyValue(
newIndent,
@@ -466,7 +480,7 @@
"KmProperty",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, kmProperty.getFlags() + "");
+ appendFlags(indent, "flags", sb, KotlinFlagUtils.extractFlags(kmProperty));
appendKeyValue(newIndent, "name", sb, kmProperty.getName());
appendKeyValue(
newIndent,
@@ -483,8 +497,12 @@
"typeParameters",
sb,
nextIndent -> appendTypeParameters(nextIndent, sb, kmProperty.getTypeParameters()));
- appendKeyValue(newIndent, "getterFlags", sb, kmProperty.getGetterFlags() + "");
- appendKeyValue(newIndent, "setterFlags", sb, kmProperty.getSetterFlags() + "");
+ appendFlags(
+ indent, "getterFlags", sb, KotlinFlagUtils.extractFlags(kmProperty.getGetter()));
+ if (kmProperty.getSetter() != null) {
+ appendFlags(
+ indent, "setterFlags", sb, KotlinFlagUtils.extractFlags(kmProperty.getSetter()));
+ }
appendKeyValue(
newIndent,
"setterParameter",
@@ -502,7 +520,6 @@
sb,
kmProperty.getContextReceiverTypes(),
(nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
- appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + "");
JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
appendKeyValue(newIndent, "fieldSignature", sb, Objects.toString(fieldSignature));
JvmMethodSignature getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty);
@@ -536,7 +553,7 @@
"KmType",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, kmType.getFlags() + "");
+ appendFlags(newIndent, "flags", sb, KotlinFlagUtils.extractFlags(kmType));
appendKeyValue(newIndent, "classifier", sb, kmType.classifier.toString());
appendKeyValue(
newIndent,
@@ -641,7 +658,7 @@
"KmValueParameter",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, valueParameter.getFlags() + "");
+ appendFlags(newIndent, "flags", sb, KotlinFlagUtils.extractFlags(valueParameter));
appendKeyValue(newIndent, "name", sb, valueParameter.getName());
appendKeyValue(
newIndent,
@@ -674,7 +691,7 @@
sb,
newIndent -> {
appendKeyValue(newIndent, "id", sb, typeParameter.getId() + "");
- appendKeyValue(newIndent, "flags", sb, typeParameter.getFlags() + "");
+ appendFlags(newIndent, "flags", sb, KotlinFlagUtils.extractFlags(typeParameter));
appendKeyValue(newIndent, "name", sb, typeParameter.getName());
appendKeyValue(newIndent, "variance", sb, typeParameter.getVariance().name());
appendKeyValue(
@@ -695,7 +712,7 @@
nextIndent ->
appendKmList(
nextIndent,
- "KmAnnotion",
+ "KmAnnotation",
sb,
JvmExtensionsKt.getAnnotations(typeParameter),
(nextNextIndent, kmAnnotation) ->
@@ -726,7 +743,7 @@
"expandedType",
sb,
nextIndent -> appendKmType(nextIndent, sb, kmTypeAlias.expandedType));
- appendKeyValue(newIndent, "flags", sb, kmTypeAlias.getFlags() + "");
+ appendFlags(newIndent, "flags", sb, KotlinFlagUtils.extractFlags(kmTypeAlias));
appendKeyValue(newIndent, "name", sb, kmTypeAlias.getName());
appendKeyValue(
newIndent,
@@ -895,7 +912,7 @@
"KmEffectExpression",
sb,
newIndent -> {
- appendKeyValue(newIndent, "flags", sb, expression.getFlags() + "");
+ appendFlags(indent, "flags", sb, KotlinFlagUtils.extractFlags(expression));
appendKeyValue(
newIndent,
"foo",
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
index db4ab66..b5a7771 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -4,9 +4,8 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.updateJvmMetadataVersionIfRequired;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
-import static kotlin.metadata.jvm.KotlinClassMetadata.Companion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -22,21 +21,22 @@
// Holds information about Metadata.MultiFileClassFace
public class KotlinMultiFileClassFacadeInfo implements KotlinClassLevelInfo {
+ private final MultiFileClassFacade kmMultiFileClassFacade;
private final List<KotlinTypeReference> partClassNames;
private final String packageName;
- private final int[] metadataVersion;
private KotlinMultiFileClassFacadeInfo(
- List<KotlinTypeReference> partClassNames, String packageName, int[] metadataVersion) {
+ MultiFileClassFacade kmMultiFileClassFacade,
+ List<KotlinTypeReference> partClassNames,
+ String packageName) {
+ this.kmMultiFileClassFacade = kmMultiFileClassFacade;
this.partClassNames = partClassNames;
this.packageName = packageName;
- this.metadataVersion = metadataVersion;
}
static KotlinMultiFileClassFacadeInfo create(
MultiFileClassFacade kmMultiFileClassFacade,
String packageName,
- int[] metadataVersion,
DexItemFactory factory) {
ImmutableList.Builder<KotlinTypeReference> builder = ImmutableList.builder();
for (String partClassName : kmMultiFileClassFacade.getPartClassNames()) {
@@ -44,7 +44,7 @@
KotlinTypeReference.fromBinaryNameOrKotlinClassifier(
partClassName, factory, partClassName));
}
- return new KotlinMultiFileClassFacadeInfo(builder.build(), packageName, metadataVersion);
+ return new KotlinMultiFileClassFacadeInfo(kmMultiFileClassFacade, builder.build(), packageName);
}
@Override
@@ -72,9 +72,9 @@
appView,
null);
}
- return Pair.create(
- Companion.writeMultiFileClassFacade(partClassNameStrings, getCompatibleKotlinInfo(), 0),
- rewritten);
+ kmMultiFileClassFacade.setPartClassNames(partClassNameStrings);
+ updateJvmMetadataVersionIfRequired(kmMultiFileClassFacade);
+ return Pair.create(kmMultiFileClassFacade.write(), rewritten);
}
@Override
@@ -84,7 +84,7 @@
@Override
public int[] getMetadataVersion() {
- return metadataVersion;
+ return KotlinJvmMetadataVersionUtils.toIntArray(kmMultiFileClassFacade.getVersion());
}
@Override
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 6c00f88..e18afb0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlin.metadata.jvm.KotlinClassMetadata.Companion;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.updateJvmMetadataVersionIfRequired;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -20,35 +19,26 @@
// Holds information about Metadata.MultiFileClassPartInfo
public class KotlinMultiFileClassPartInfo implements KotlinClassLevelInfo {
- private final String facadeClassName;
+ private final MultiFileClassPart classPart;
private final KotlinPackageInfo packageInfo;
private final String packageName;
- private final int[] metadataVersion;
private KotlinMultiFileClassPartInfo(
- String facadeClassName,
- KotlinPackageInfo packageInfo,
- String packageName,
- int[] metadataVersion) {
- this.facadeClassName = facadeClassName;
+ MultiFileClassPart classPart, KotlinPackageInfo packageInfo, String packageName) {
+ this.classPart = classPart;
this.packageInfo = packageInfo;
this.packageName = packageName;
- this.metadataVersion = metadataVersion;
}
static KotlinMultiFileClassPartInfo create(
MultiFileClassPart classPart,
String packageName,
- int[] metadataVersion,
DexClass clazz,
AppView<?> appView,
Consumer<DexEncodedMethod> keepByteCode) {
KmPackage kmPackage = classPart.getKmPackage();
return new KotlinMultiFileClassPartInfo(
- classPart.getFacadeClassName(),
- KotlinPackageInfo.create(kmPackage, clazz, appView, keepByteCode),
- packageName,
- metadataVersion);
+ classPart, KotlinPackageInfo.create(kmPackage, clazz, appView, keepByteCode), packageName);
}
@Override
@@ -65,9 +55,9 @@
public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
KmPackage kmPackage = new KmPackage();
boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
- return Pair.create(
- Companion.writeMultiFileClassPart(kmPackage, facadeClassName, getCompatibleKotlinInfo(), 0),
- rewritten);
+ updateJvmMetadataVersionIfRequired(classPart);
+ classPart.setKmPackage(kmPackage);
+ return Pair.create(classPart.write(), rewritten);
}
@Override
@@ -81,7 +71,7 @@
@Override
public int[] getMetadataVersion() {
- return metadataVersion;
+ return KotlinJvmMetadataVersionUtils.toIntArray(classPart.getVersion());
}
@Override
@@ -90,6 +80,6 @@
}
public String getFacadeClassName() {
- return facadeClassName;
+ return classPart.getFacadeClassName();
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 5e31efb..6b3c485 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlin.metadata.jvm.KotlinClassMetadata.Companion;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.updateJvmMetadataVersionIfRequired;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -19,9 +18,9 @@
// Holds information about a Metadata.SyntheticClass object.
public class KotlinSyntheticClassInfo implements KotlinClassLevelInfo {
+ private final SyntheticClass syntheticClass;
private final KotlinLambdaInfo lambda;
private final String packageName;
- private final int[] metadataVersion;
public enum Flavour {
KotlinStyleLambda,
@@ -32,29 +31,28 @@
private final Flavour flavour;
private KotlinSyntheticClassInfo(
- KotlinLambdaInfo lambda, Flavour flavour, String packageName, int[] metadataVersion) {
+ SyntheticClass syntheticClass, KotlinLambdaInfo lambda, Flavour flavour, String packageName) {
+ this.syntheticClass = syntheticClass;
this.lambda = lambda;
this.flavour = flavour;
this.packageName = packageName;
- this.metadataVersion = metadataVersion;
}
static KotlinSyntheticClassInfo create(
SyntheticClass syntheticClass,
String packageName,
- int[] metadataVersion,
DexClass clazz,
Kotlin kotlin,
AppView<?> appView) {
KmLambda lambda = syntheticClass.getKmLambda();
assert lambda == null || syntheticClass.isLambda();
return new KotlinSyntheticClassInfo(
+ syntheticClass,
lambda != null
? KotlinLambdaInfo.create(clazz, lambda, appView.dexItemFactory(), appView.reporter())
: null,
getFlavour(clazz, kotlin),
- packageName,
- metadataVersion);
+ packageName);
}
public boolean isLambda() {
@@ -73,14 +71,16 @@
@Override
public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+ updateJvmMetadataVersionIfRequired(syntheticClass);
if (lambda == null) {
- return Pair.create(Companion.writeSyntheticClass(getCompatibleKotlinInfo(), 0), false);
+ return Pair.create(syntheticClass.write(), false);
}
Box<KmLambda> newLambda = new Box<>();
boolean rewritten = lambda.rewrite(newLambda::set, clazz, appView);
assert newLambda.isSet();
- return Pair.create(
- Companion.writeLambda(newLambda.get(), getCompatibleKotlinInfo(), 0), rewritten);
+ syntheticClass.setKmLambda(newLambda.get());
+ syntheticClass.setFlags(0);
+ return Pair.create(syntheticClass.write(), rewritten);
}
@Override
@@ -97,7 +97,7 @@
@Override
public int[] getMetadataVersion() {
- return metadataVersion;
+ return KotlinJvmMetadataVersionUtils.toIntArray(syntheticClass.getVersion());
}
@SuppressWarnings("ReferenceEquality")
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index 038f838..9371fa1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -20,59 +19,56 @@
// Holds information about KmTypeAlias
public class KotlinTypeAliasInfo implements EnqueuerMetadataTraceable {
- private final int flags;
- private final String name;
+ private final KmTypeAlias kmTypeAlias;
private final KotlinTypeInfo underlyingType;
private final KotlinTypeInfo expandedType;
private final List<KotlinTypeParameterInfo> typeParameters;
private final List<KotlinAnnotationInfo> annotations;
- private final KotlinVersionRequirementInfo versionRequirements;
private KotlinTypeAliasInfo(
- int flags,
- String name,
+ KmTypeAlias kmTypeAlias,
KotlinTypeInfo underlyingType,
KotlinTypeInfo expandedType,
List<KotlinTypeParameterInfo> typeParameters,
- List<KotlinAnnotationInfo> annotations,
- KotlinVersionRequirementInfo versionRequirements) {
- this.flags = flags;
- this.name = name;
+ List<KotlinAnnotationInfo> annotations) {
+ this.kmTypeAlias = kmTypeAlias;
assert underlyingType != null;
assert expandedType != null;
this.underlyingType = underlyingType;
this.expandedType = expandedType;
this.typeParameters = typeParameters;
this.annotations = annotations;
- this.versionRequirements = versionRequirements;
}
public static KotlinTypeAliasInfo create(
KmTypeAlias alias, DexItemFactory factory, Reporter reporter) {
return new KotlinTypeAliasInfo(
- alias.getFlags(),
- alias.getName(),
+ alias,
KotlinTypeInfo.create(alias.underlyingType, factory, reporter),
KotlinTypeInfo.create(alias.expandedType, factory, reporter),
KotlinTypeParameterInfo.create(alias.getTypeParameters(), factory, reporter),
- KotlinAnnotationInfo.create(alias.getAnnotations(), factory),
- KotlinVersionRequirementInfo.create(alias.getVersionRequirements()));
+ KotlinAnnotationInfo.create(alias.getAnnotations(), factory));
}
boolean rewrite(Consumer<KmTypeAlias> consumer, AppView<?> appView) {
- KmTypeAlias kmTypeAlias = consume(new KmTypeAlias(flags, name), consumer);
- boolean rewritten = underlyingType.rewrite(kmTypeAlias::setUnderlyingType, appView);
- rewritten |= expandedType.rewrite(kmTypeAlias::setExpandedType, appView);
+ KmTypeAlias rewrittenKmTypeAlias = new KmTypeAlias(kmTypeAlias.getName());
+ consumer.accept(rewrittenKmTypeAlias);
+ KotlinFlagUtils.copyAllFlags(kmTypeAlias, rewrittenKmTypeAlias);
+ boolean rewritten = underlyingType.rewrite(rewrittenKmTypeAlias::setUnderlyingType, appView);
+ rewritten |= expandedType.rewrite(rewrittenKmTypeAlias::setExpandedType, appView);
rewritten |=
rewriteList(
appView,
typeParameters,
- kmTypeAlias.getTypeParameters(),
+ rewrittenKmTypeAlias.getTypeParameters(),
KotlinTypeParameterInfo::rewrite);
rewritten |=
rewriteList(
- appView, annotations, kmTypeAlias.getAnnotations(), KotlinAnnotationInfo::rewrite);
- rewritten |= versionRequirements.rewrite(kmTypeAlias.getVersionRequirements()::addAll);
+ appView,
+ annotations,
+ rewrittenKmTypeAlias.getAnnotations(),
+ KotlinAnnotationInfo::rewrite);
+ rewrittenKmTypeAlias.getVersionRequirements().addAll(kmTypeAlias.getVersionRequirements());
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index a0090b0..25dc018 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -28,7 +27,7 @@
private static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of();
- private final int flags;
+ private final KmType kmType;
private final KotlinClassifierInfo classifier;
private final KotlinTypeInfo abbreviatedType;
private final KotlinTypeInfo outerType;
@@ -38,7 +37,7 @@
private final boolean isRaw;
KotlinTypeInfo(
- int flags,
+ KmType kmType,
KotlinClassifierInfo classifier,
KotlinTypeInfo abbreviatedType,
KotlinTypeInfo outerType,
@@ -46,7 +45,7 @@
List<KotlinAnnotationInfo> annotations,
KotlinFlexibleTypeUpperBoundInfo flexibleTypeUpperBound,
boolean isRaw) {
- this.flags = flags;
+ this.kmType = kmType;
this.classifier = classifier;
this.abbreviatedType = abbreviatedType;
this.outerType = outerType;
@@ -61,7 +60,7 @@
return null;
}
return new KotlinTypeInfo(
- kmType.getFlags(),
+ kmType,
KotlinClassifierInfo.create(kmType.classifier, factory, reporter),
KotlinTypeInfo.create(kmType.getAbbreviatedType(), factory, reporter),
KotlinTypeInfo.create(kmType.getOuterType(), factory, reporter),
@@ -85,17 +84,21 @@
}
boolean rewrite(Consumer<KmType> consumer, AppView<?> appView) {
- // TODO(b/154348683): Check for correct flags
- KmType kmType = consume(new KmType(flags), consumer);
- boolean rewritten = classifier.rewrite(kmType, appView);
+ KmType rewrittenKmType = new KmType();
+ consumer.accept(rewrittenKmType);
+ KotlinFlagUtils.copyAllFlags(kmType, rewrittenKmType);
+ boolean rewritten = classifier.rewrite(rewrittenKmType, appView);
rewritten |=
rewriteIfNotNull(
- appView, abbreviatedType, kmType::setAbbreviatedType, KotlinTypeInfo::rewrite);
+ appView, abbreviatedType, rewrittenKmType::setAbbreviatedType, KotlinTypeInfo::rewrite);
rewritten |=
- rewriteIfNotNull(appView, outerType, kmType::setOuterType, KotlinTypeInfo::rewrite);
+ rewriteIfNotNull(
+ appView, outerType, rewrittenKmType::setOuterType, KotlinTypeInfo::rewrite);
rewritten |=
- rewriteList(appView, arguments, kmType.getArguments(), KotlinTypeProjectionInfo::rewrite);
- rewritten |= flexibleTypeUpperBound.rewrite(kmType::setFlexibleTypeUpperBound, appView);
+ rewriteList(
+ appView, arguments, rewrittenKmType.getArguments(), KotlinTypeProjectionInfo::rewrite);
+ rewritten |=
+ flexibleTypeUpperBound.rewrite(rewrittenKmType::setFlexibleTypeUpperBound, appView);
if (annotations.isEmpty() && !isRaw) {
return rewritten;
}
@@ -103,9 +106,9 @@
rewriteList(
appView,
annotations,
- JvmExtensionsKt.getAnnotations(kmType),
+ JvmExtensionsKt.getAnnotations(rewrittenKmType),
KotlinAnnotationInfo::rewrite);
- JvmExtensionsKt.setRaw(kmType, isRaw);
+ JvmExtensionsKt.setRaw(rewrittenKmType, isRaw);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index ba2aaa9..4ba90b6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
@@ -18,7 +17,6 @@
import java.util.function.Consumer;
import kotlin.metadata.KmType;
import kotlin.metadata.KmTypeParameter;
-import kotlin.metadata.KmVariance;
import kotlin.metadata.jvm.JvmExtensionsKt;
// Provides access to Kotlin information about a type-parameter.
@@ -27,24 +25,15 @@
private static final List<KotlinTypeParameterInfo> EMPTY_TYPE_PARAMETERS = ImmutableList.of();
private static final List<KotlinTypeInfo> EMPTY_UPPER_BOUNDS = ImmutableList.of();
- private final int flags;
- private final int id;
- private final String name;
- private final KmVariance variance;
+ private final KmTypeParameter kmTypeParameter;
private final List<KotlinTypeInfo> originalUpperBounds;
private final List<KotlinAnnotationInfo> annotations;
private KotlinTypeParameterInfo(
- int flags,
- int id,
- String name,
- KmVariance variance,
+ KmTypeParameter kmTypeParameter,
List<KotlinTypeInfo> originalUpperBounds,
List<KotlinAnnotationInfo> annotations) {
- this.flags = flags;
- this.id = id;
- this.name = name;
- this.variance = variance;
+ this.kmTypeParameter = kmTypeParameter;
this.originalUpperBounds = originalUpperBounds;
this.annotations = annotations;
}
@@ -52,10 +41,7 @@
private static KotlinTypeParameterInfo create(
KmTypeParameter kmTypeParameter, DexItemFactory factory, Reporter reporter) {
return new KotlinTypeParameterInfo(
- kmTypeParameter.getFlags(),
- kmTypeParameter.getId(),
- kmTypeParameter.getName(),
- kmTypeParameter.getVariance(),
+ kmTypeParameter,
getUpperBounds(kmTypeParameter.getUpperBounds(), factory, reporter),
KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmTypeParameter), factory));
}
@@ -85,19 +71,22 @@
}
boolean rewrite(Consumer<KmTypeParameter> consumer, AppView<?> appView) {
- KmTypeParameter kmTypeParameter =
- consume(new KmTypeParameter(flags, name, id, variance), consumer);
+ KmTypeParameter rewrittenTypeParameter =
+ new KmTypeParameter(
+ kmTypeParameter.getName(), kmTypeParameter.getId(), kmTypeParameter.getVariance());
+ consumer.accept(rewrittenTypeParameter);
+ KotlinFlagUtils.copyAllFlags(kmTypeParameter, rewrittenTypeParameter);
boolean rewritten =
rewriteList(
appView,
originalUpperBounds,
- kmTypeParameter.getUpperBounds(),
+ rewrittenTypeParameter.getUpperBounds(),
KotlinTypeInfo::rewrite);
rewritten |=
rewriteList(
appView,
annotations,
- JvmExtensionsKt.getAnnotations(kmTypeParameter),
+ JvmExtensionsKt.getAnnotations(rewrittenTypeParameter),
KotlinAnnotationInfo::rewrite);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index dca2632..5f54ce1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
import com.android.tools.r8.graph.AppView;
@@ -15,32 +14,29 @@
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.Consumer;
+import kotlin.metadata.Attributes;
import kotlin.metadata.KmType;
import kotlin.metadata.KmValueParameter;
-import kotlin.metadata.internal.metadata.deserialization.Flags;
// Provides access to Kotlin information about value parameter.
class KotlinValueParameterInfo implements EnqueuerMetadataTraceable {
private static final List<KotlinValueParameterInfo> EMPTY_VALUE_PARAMETERS = ImmutableList.of();
- // Original parameter name.
- final String name;
- // Original parameter flags, e.g., has default value.
- final int flags;
+ // Original parameter.
+ final KmValueParameter kmValueParameter;
// Original information about the type.
final KotlinTypeInfo type;
// Indicates whether the formal parameter is originally `vararg`.
final KotlinTypeInfo varargElementType;
private KotlinValueParameterInfo(
- int flags, String name, KotlinTypeInfo type, KotlinTypeInfo varargElementType) {
- this.name = name;
- this.flags = flags;
+ KmValueParameter kmValueParameter, KotlinTypeInfo type, KotlinTypeInfo varargElementType) {
+ this.kmValueParameter = kmValueParameter;
this.type = type;
this.varargElementType = varargElementType;
}
boolean isCrossInline() {
- return Flags.IS_CROSSINLINE.get(flags);
+ return Attributes.isCrossinline(kmValueParameter);
}
static KotlinValueParameterInfo create(
@@ -50,8 +46,7 @@
}
KmType kmType = kmValueParameter.getType();
return new KotlinValueParameterInfo(
- kmValueParameter.getFlags(),
- kmValueParameter.getName(),
+ kmValueParameter,
KotlinTypeInfo.create(kmType, factory, reporter),
KotlinTypeInfo.create(kmValueParameter.getVarargElementType(), factory, reporter));
}
@@ -69,13 +64,15 @@
}
boolean rewrite(Consumer<KmValueParameter> consumer, AppView<?> appView) {
- KmValueParameter kmValueParameter = consume(new KmValueParameter(flags, name), consumer);
- boolean rewritten = type.rewrite(kmValueParameter::setType, appView);
+ KmValueParameter rewrittenKmValueParameter = new KmValueParameter(kmValueParameter.getName());
+ consumer.accept(rewrittenKmValueParameter);
+ KotlinFlagUtils.copyAllFlags(kmValueParameter, rewrittenKmValueParameter);
+ boolean rewritten = type.rewrite(rewrittenKmValueParameter::setType, appView);
rewritten |=
rewriteIfNotNull(
appView,
varargElementType,
- kmValueParameter::setVarargElementType,
+ rewrittenKmValueParameter::setVarargElementType,
KotlinTypeInfo::rewrite);
return rewritten;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
deleted file mode 100644
index 7242cf5..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.kotlin;
-
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import java.util.function.Consumer;
-import kotlin.metadata.KmVersionRequirement;
-
-class KotlinVersionRequirementInfo {
-
- private static final KotlinVersionRequirementInfo NO_VERSION_REQUIREMENTS =
- new KotlinVersionRequirementInfo(ImmutableList.of());
-
- private final List<KmVersionRequirement> versionRequirements;
-
- private KotlinVersionRequirementInfo(List<KmVersionRequirement> versionRequirements) {
- this.versionRequirements = versionRequirements;
- }
-
- static KotlinVersionRequirementInfo create(List<KmVersionRequirement> kmVersionRequirements) {
- if (kmVersionRequirements.isEmpty()) {
- return NO_VERSION_REQUIREMENTS;
- }
- return new KotlinVersionRequirementInfo(ImmutableList.copyOf(kmVersionRequirements));
- }
-
- boolean rewrite(Consumer<List<KmVersionRequirement>> consumer) {
- if (this == NO_VERSION_REQUIREMENTS) {
- return false;
- }
- consumer.accept(versionRequirements);
- return false;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index 80cf637..45848ad 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
@@ -17,6 +19,8 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
@@ -26,7 +30,6 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -35,6 +38,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
@@ -44,6 +48,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class DefaultFieldValueJoiner {
@@ -69,6 +74,9 @@
// Find all the fields where we need to determine if each field read is guaranteed to be
// dominated by a write.
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = getFieldsOfInterest();
+ if (fieldsOfInterest.isEmpty()) {
+ return Collections.emptyMap();
+ }
// If constructor inlining is disabled, then we focus on whether each instance initializer
// definitely assigns the given field before it is read. We do the same for final and static
@@ -94,12 +102,12 @@
// constructor inlining, we find all new-instance instructions (including subtype allocations)
// and check if the field is written on each allocation before it is possibly read.
analyzeNewInstanceInstructions(
- fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add);
+ fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add, executorService);
return updateFlowGraphs(fieldsWithLiveDefaultValue, executorService);
}
- private Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
+ protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = new IdentityHashMap<>();
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramField(
@@ -240,13 +248,101 @@
private void analyzeNewInstanceInstructions(
Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
- Consumer<ProgramField> liveDefaultValueConsumer) {
- // Conservatively treat all fields as maybe read before written.
- // TODO(b/296030319): Implement analysis by building IR for all methods that instantiate the
- // relevant classes and analyzing the puts to the newly created instances.
- for (ProgramField field : IterableUtils.flatten(nonFinalInstanceFields.values())) {
- liveDefaultValueConsumer.accept(field);
+ Consumer<ProgramField> liveDefaultValueConsumer,
+ ExecutorService executorService)
+ throws ExecutionException {
+ // To simplify the analysis, we currently bail out for non-final classes.
+ // TODO(b/296030319): Handle non-final classes.
+ MapUtils.removeIf(
+ nonFinalInstanceFields,
+ (holderType, fields) -> {
+ assert !fields.isEmpty();
+ DexProgramClass holder = fields.iterator().next().getHolder();
+ // If the class is kept it could be instantiated directly, in which case all default field
+ // values could be live.
+ if (appView.getKeepInfo(holder).isPinned(appView.options())) {
+ fields.forEach(liveDefaultValueConsumer);
+ return true;
+ }
+ if (holder.isFinal() || !appView.appInfo().isInstantiatedIndirectly(holder)) {
+ // When the class is not explicitly marked final, the class could in principle have
+ // injected subclasses if it is pinned. However, none of the fields are pinned, so we
+ // should be allowed to reason about the field assignments in the program.
+ assert fields.stream()
+ .allMatch(
+ field -> appView.getKeepInfo(field).isValuePropagationAllowed(appView, field));
+ return false;
+ }
+ fields.forEach(liveDefaultValueConsumer);
+ return true;
+ });
+
+ // We analyze all allocations of the classes that declare one of the given fields.
+ ThreadUtils.processMethods(
+ appView,
+ method ->
+ analyzeNewInstanceInstructionsInMethod(
+ nonFinalInstanceFields, liveDefaultValueConsumer, method),
+ appView.options().getThreadingModule(),
+ executorService);
+ }
+
+ private void analyzeNewInstanceInstructionsInMethod(
+ Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
+ Consumer<ProgramField> liveDefaultValueConsumer,
+ ProgramMethod method) {
+ if (!maybeHasNewInstanceThatMatches(method, nonFinalInstanceFields::containsKey)) {
+ return;
}
+ IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
+ for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) {
+ ProgramFieldSet fieldsOfInterest = nonFinalInstanceFields.get(newInstance.getType());
+ if (fieldsOfInterest == null) {
+ continue;
+ }
+ FieldReadBeforeWriteDfsAnalysis analysis =
+ new FieldReadBeforeWriteDfsAnalysis(appView, code, fieldsOfInterest, newInstance) {
+
+ @Override
+ public AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field) {
+ // Remove this field from the `fieldsOfInterest`, so that we do not spend more time
+ // analyzing it.
+ if (fieldsOfInterest.remove(field)) {
+ liveDefaultValueConsumer.accept(field);
+ }
+ return AnalysisContinuation.abortIf(fieldsOfInterest.isEmpty());
+ }
+ };
+ analysis.run();
+ if (fieldsOfInterest.isEmpty()) {
+ nonFinalInstanceFields.remove(newInstance.getType());
+ }
+ }
+ }
+
+ private boolean maybeHasNewInstanceThatMatches(
+ ProgramMethod method, Predicate<DexType> predicate) {
+ Code code = method.getDefinition().getCode();
+ if (code == null || code.isSharedCodeObject()) {
+ return false;
+ }
+ if (code.isLirCode()) {
+ return code.asLirCode()
+ .hasConstantItemThatMatches(
+ constant -> constant instanceof DexType && predicate.test((DexType) constant));
+ }
+ assert appView.isCfByteCodePassThrough(method);
+ assert code.isCfCode();
+ return method.registerCodeReferencesWithResult(
+ new DefaultUseRegistryWithResult<>(appView, method, false) {
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ if (predicate.test(type)) {
+ setResult(true);
+ }
+ }
+ });
}
private Map<FlowGraph, Deque<FlowGraphNode>> updateFlowGraphs(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
new file mode 100644
index 0000000..1fdfb27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
@@ -0,0 +1,387 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.THROW;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockInstructionIterator;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.ProgramFieldSet;
+
+/**
+ * Analysis that is given an allocation site (a {@link NewInstance} instruction) and a set of fields
+ * that belong to that newly allocated instance.
+ *
+ * <p>The analysis computes the subset of the given fields which are maybe read before they are
+ * written. The default value of these fields is potentially read, whereas the default value of the
+ * complement field set are guaranteed to never be read.
+ *
+ * <p>The analysis works by exploring all possible paths starting from the given allocation site to
+ * the normal and the exceptional exits of the method, keeping track of which fields are definitely
+ * written before they are read and which fields have maybe been read.
+ */
+public abstract class FieldReadBeforeWriteDfsAnalysis extends FieldReadBeforeWriteDfsAnalysisState {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final IRCode code;
+ private final DexItemFactory dexItemFactory;
+ // The set of fields to consider. Note that this is a concurrent set and that the caller may
+ // concurrently remove fields from the set. This may happen if we concurrently find a
+ // read-before-write of one of the fields.
+ private final ProgramFieldSet fields;
+ private final WorkList<WorkItem> worklist = WorkList.newIdentityWorkList();
+
+ private final FieldReadBeforeWriteDfsAnalysis self = this;
+
+ public FieldReadBeforeWriteDfsAnalysis(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ ProgramFieldSet fields,
+ NewInstance newInstance) {
+ super(newInstance);
+ this.appView = appView;
+ this.code = code;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.fields = fields;
+ }
+
+ // Returns ABORT if all fields of interest are now maybe-read-before-written.
+ // Otherwise returns CONTINUE.
+ public abstract AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field);
+
+ public void run() {
+ worklist.addIfNotSeen(new InitialWorkItem());
+ worklist.run(WorkItem::process);
+ }
+
+ public enum AnalysisContinuation {
+ // Signals to abort the analysis completely (i.e., to break out of the DFS). This is used when
+ // we've reported all fields as being maybe read before written.
+ ABORT,
+ // Signals to continue the current DFS.
+ CONTINUE,
+ // Signals that all fields have been written before they are read on the current program path,
+ // meaning that the algorithm does not need to explore any further. The algorithm should instead
+ // backtrack and explore any other program paths.
+ BREAK;
+
+ static AnalysisContinuation abortIf(boolean condition) {
+ if (condition) {
+ return ABORT;
+ }
+ return CONTINUE;
+ }
+
+ boolean isAbort() {
+ return this == ABORT;
+ }
+
+ boolean isAbortOrContinue() {
+ return isAbort() || isContinue();
+ }
+
+ boolean isBreak() {
+ return this == BREAK;
+ }
+
+ boolean isContinue() {
+ return this == CONTINUE;
+ }
+
+ TraversalContinuation<?, ?> toTraversalContinuation() {
+ assert isAbortOrContinue();
+ return TraversalContinuation.breakIf(isAbort());
+ }
+ }
+
+ abstract class WorkItem {
+
+ abstract TraversalContinuation<?, ?> process();
+
+ void applyPhis(BasicBlock block) {
+ // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
+ // backtracking of this (i.e., we should remove the out value from the object aliases again).
+ for (Phi phi : block.getPhis()) {
+ if (phi.hasOperandThatMatches(self::isMaybeInstance)) {
+ addInstanceAlias(phi);
+ }
+ }
+ }
+
+ AnalysisContinuation applyInstructions(BasicBlockInstructionIterator instructionIterator) {
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ assert !instruction.hasOutValue() || !isMaybeInstance(instruction.outValue());
+ AnalysisContinuation continuation;
+ // TODO(b/339210038): Extend this to many other instructions, such as ConstClass,
+ // InstanceOf, *Binop, etc.
+ switch (instruction.opcode()) {
+ case ASSUME:
+ continuation = applyAssumeInstruction(instruction.asAssume());
+ break;
+ case CHECK_CAST:
+ continuation = applyCheckCastInstruction(instruction.asCheckCast());
+ break;
+ case CONST_NUMBER:
+ continuation = applyConstNumber(instruction.asConstNumber());
+ break;
+ case CONST_STRING:
+ continuation = applyConstString(instruction.asConstString());
+ break;
+ case GOTO:
+ continuation = applyGotoInstruction(instruction.asGoto());
+ break;
+ case INSTANCE_PUT:
+ continuation = applyInstancePut(instruction.asInstancePut());
+ break;
+ case INVOKE_DIRECT:
+ case INVOKE_INTERFACE:
+ case INVOKE_STATIC:
+ case INVOKE_SUPER:
+ case INVOKE_VIRTUAL:
+ continuation = applyInvokeMethodInstruction(instruction.asInvokeMethod());
+ break;
+ case RETURN:
+ continuation = applyReturnInstruction(instruction.asReturn());
+ break;
+ case THROW:
+ continuation = applyThrowInstruction(instruction.asThrow());
+ break;
+ default:
+ continuation = applyUnhandledInstruction();
+ break;
+ }
+ if (continuation.isAbort()) {
+ return continuation;
+ }
+ if (continuation.isBreak()) {
+ break;
+ }
+ }
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
+ // backtracking of this (i.e., we should remove the out value from the object aliases again).
+ private AnalysisContinuation applyAssumeInstruction(Assume assume) {
+ if (isMaybeInstance(assume.src())) {
+ addInstanceAlias(assume.outValue());
+ }
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
+ // backtracking of this (i.e., we should remove the out value from the object aliases again).
+ private AnalysisContinuation applyCheckCastInstruction(CheckCast checkCast) {
+ if (isMaybeInstance(checkCast.object())) {
+ addInstanceAlias(checkCast.outValue());
+ }
+ // If the instance has escaped to the heap and this check-cast instruction throws, then it is
+ // possible that the instance is retrieved from the heap and all fields are read.
+ return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
+ }
+
+ private AnalysisContinuation applyConstNumber(ConstNumber unusedConstNumber) {
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ private AnalysisContinuation applyConstString(ConstString unusedConstString) {
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ private AnalysisContinuation applyGotoInstruction(Goto gotoInstruction) {
+ BasicBlock targetBlock = gotoInstruction.getTarget();
+ if (isBlockOnStack(targetBlock)) {
+ // Bail out in case of cycles.
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ } else {
+ // Continue exploration into the successor block.
+ worklist.addIgnoringSeenSet(new ProcessBlockWorkItem(targetBlock));
+ return AnalysisContinuation.CONTINUE;
+ }
+ }
+
+ private AnalysisContinuation applyInstancePut(InstancePut instancePut) {
+ // If the instance has escaped and this instance-put instruction can throw, then the program
+ // can get the instance from the heap and read any field. Give up in this case.
+ if (isEscaped() && instancePut.instructionInstanceCanThrow(appView, code.context())) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ }
+
+ // Record if this is a definite write to one of the fields of interest.
+ if (isDefinitelyInstance(instancePut.object())) {
+ ProgramField resolvedField =
+ instancePut.resolveField(appView, code.context()).getProgramField();
+ if (resolvedField != null && fields.contains(resolvedField)) {
+ addWrittenBeforeRead(resolvedField);
+ }
+
+ // If all fields of interest are written before read, then stop the exploration of the
+ // current program path (but continue to explore any program paths from previous unexplored
+ // branches).
+ if (fields.allMatch(self::isWrittenBeforeRead)) {
+ return AnalysisContinuation.BREAK;
+ }
+ }
+
+ // Record if the instance has escaped as a result of this instance-put.
+ if (!isEscaped() && isMaybeInstance(instancePut.value())) {
+ setEscaped(instancePut);
+ }
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ private AnalysisContinuation applyInvokeMethodInstruction(InvokeMethod invoke) {
+ // Allow calls to java.lang.Object.<init>().
+ // TODO(b/339210038): Generalize this to other constructors.
+ if (invoke.isInvokeConstructor(dexItemFactory)
+ && isDefinitelyInstance(invoke.getFirstArgument())) {
+ DexClassAndMethod resolvedMethod =
+ invoke.resolveMethod(appView, code.context()).getResolutionPair();
+ if (resolvedMethod != null
+ && resolvedMethod
+ .getReference()
+ .isIdenticalTo(dexItemFactory.objectMembers.constructor)) {
+ return AnalysisContinuation.CONTINUE;
+ }
+ }
+
+ // Conservatively treat calls as reading any field if the receiver has escaped or is escaping.
+ if (!isEscaped()
+ && invoke.hasInValueThatMatches(self::isMaybeInstance)
+ && invoke.instructionMayHaveSideEffects(appView, code.context())) {
+ setEscaped(invoke);
+ }
+
+ if (isEscaped()) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ }
+
+ // Otherwise, this is a call to a method where none of the arguments is an alias of the
+ // instance, and the instance has not escaped. Therefore, this call cannot read any of fields
+ // from the instance.
+ return AnalysisContinuation.CONTINUE;
+ }
+
+ private AnalysisContinuation applyReturnInstruction(Return unusedReturnInstruction) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ }
+
+ private AnalysisContinuation applyThrowInstruction(Throw unusedThrowInstruction) {
+ return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
+ }
+
+ private AnalysisContinuation applyUnhandledInstruction() {
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ }
+
+ AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWritten() {
+ for (ProgramField field : fields) {
+ if (!isWrittenBeforeRead(field)) {
+ AnalysisContinuation continuation = acceptFieldMaybeReadBeforeWrite(field);
+ assert continuation.isAbortOrContinue();
+ if (continuation.isAbort()) {
+ return continuation;
+ }
+ }
+ }
+ // At this point we could also CONTINUE, but we check if the fields of interest have become
+ // empty as a result of concurrent modification.
+ return AnalysisContinuation.abortIf(fields.isEmpty());
+ }
+
+ AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped() {
+ if (isEscaped()) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten();
+ }
+ return AnalysisContinuation.CONTINUE;
+ }
+ }
+
+ class InitialWorkItem extends WorkItem {
+
+ @Override
+ TraversalContinuation<?, ?> process() {
+ // We start the analysis from the unique constructor invoke instead of from the NewInstance
+ // instruction, since no instructions before the constructor call can read any fields from the
+ // uninitialized this.
+ // TODO(b/339210038): In principle it may be possible for the NewInstance value to flow into a
+ // phi before the unique constructor invoke. If this happens we would not record the phi as
+ // an alias when starting the analysis from the invoke-direct.
+ InvokeDirect uniqueConstructorInvoke =
+ getNewInstance().getUniqueConstructorInvoke(dexItemFactory);
+ if (uniqueConstructorInvoke == null) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
+ }
+ BasicBlock block = uniqueConstructorInvoke.getBlock();
+ // TODO(b/339210038): Maybe allow exceptional control flow.
+ if (block.hasCatchHandlers()) {
+ return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
+ }
+ addBlockToStack(block);
+ addInstanceAlias(getNewInstance().outValue());
+ BasicBlockInstructionIterator instructionIterator = block.iterator(uniqueConstructorInvoke);
+ // Start the analysis from the invoke-direct instruction. This is important if we can tell
+ // that the constructor definitely writes some fields.
+ instructionIterator.previous();
+ return applyInstructions(instructionIterator).toTraversalContinuation();
+ }
+ }
+
+ class ProcessBlockWorkItem extends WorkItem {
+
+ private final BasicBlock block;
+
+ ProcessBlockWorkItem(BasicBlock block) {
+ this.block = block;
+ }
+
+ @Override
+ TraversalContinuation<?, ?> process() {
+ // TODO(b/339210038): Maybe allow exceptional control flow.
+ if (block.hasCatchHandlers()) {
+ return TraversalContinuation.breakIf(
+ markRemainingFieldsAsMaybeReadBeforeWritten().isAbort());
+ }
+ addBlockToStack(block);
+ applyPhis(block);
+ AnalysisContinuation continuation = applyInstructions(block.iterator());
+ assert continuation.isAbortOrContinue();
+ return TraversalContinuation.breakIf(continuation.isAbort());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
new file mode 100644
index 0000000..7e24c03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.collections.ProgramFieldSet;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * The state we track during the field-maybe-read-before-write/field-never-read-before-written
+ * analysis.
+ */
+public class FieldReadBeforeWriteDfsAnalysisState {
+
+ // The current allocation we are analyzing.
+ private final NewInstance newInstance;
+
+ // The first instruction on the current program path starting from the `newInstance` instruction
+ // from which the `newInstance` value escapes.
+ private Instruction escape = null;
+
+ // The set of values that *may* be aliases of the `newInstance` value.
+ private final Set<Value> instanceAliases = Sets.newIdentityHashSet();
+
+ // The set of blocks on the current program path.
+ private final Set<BasicBlock> stack = Sets.newIdentityHashSet();
+
+ // The set of fields that are guaranteed to be written before they are read on the current program
+ // path.
+ private final ProgramFieldSet writtenBeforeRead = ProgramFieldSet.create();
+
+ FieldReadBeforeWriteDfsAnalysisState(NewInstance newInstance) {
+ this.newInstance = newInstance;
+ }
+
+ void addInstanceAlias(Value instanceAlias) {
+ boolean changed = instanceAliases.add(instanceAlias);
+ assert changed;
+ }
+
+ void addBlockToStack(BasicBlock block) {
+ boolean changed = stack.add(block);
+ assert changed;
+ }
+
+ void addWrittenBeforeRead(ProgramField field) {
+ writtenBeforeRead.add(field);
+ }
+
+ NewInstance getNewInstance() {
+ return newInstance;
+ }
+
+ boolean isBlockOnStack(BasicBlock block) {
+ return stack.contains(block);
+ }
+
+ boolean isEscaped() {
+ return escape != null;
+ }
+
+ boolean isDefinitelyInstance(Value value) {
+ return value.getAliasedValue() == newInstance.outValue();
+ }
+
+ boolean isMaybeInstance(Value value) {
+ return instanceAliases.contains(value);
+ }
+
+ boolean isWrittenBeforeRead(ProgramField field) {
+ return writtenBeforeRead.contains(field);
+ }
+
+ void setEscaped(Instruction escape) {
+ assert !isEscaped();
+ this.escape = escape;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index d62328d..81e3424 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -46,7 +46,7 @@
final AppView<AppInfoWithLiveness> appView;
final Set<DexProgramClass> classesWithSingleCallerInlinedInstanceInitializers;
final IRConverter converter;
- final FieldStateCollection fieldStates;
+ protected final FieldStateCollection fieldStates;
final MethodStateCollectionByReference methodStates;
public InFlowPropagator(
@@ -113,12 +113,15 @@
private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
List<FlowGraph> flowGraphs, ExecutorService executorService) throws ExecutionException {
- DefaultFieldValueJoiner joiner =
- new DefaultFieldValueJoiner(
- appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
+ DefaultFieldValueJoiner joiner = createDefaultFieldValueJoiner(flowGraphs);
return joiner.joinDefaultFieldValuesForFieldsWithReadBeforeWrite(executorService);
}
+ protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(List<FlowGraph> flowGraphs) {
+ return new DefaultFieldValueJoiner(
+ appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
+ }
+
private void processFlowGraphs(List<FlowGraph> flowGraphs, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
index 8d7498c..22ed377 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -33,6 +34,8 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.DefaultFieldValueJoiner;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.FlowGraph;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
@@ -42,6 +45,8 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -92,7 +97,22 @@
InFlowPropagator inFlowPropagator =
new InFlowPropagator(
- appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates());
+ appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates()) {
+
+ @Override
+ protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(
+ List<FlowGraph> flowGraphs) {
+ return new DefaultFieldValueJoiner(appView, null, fieldStates, flowGraphs) {
+
+ @Override
+ protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
+ // We do not rely on the optimization of any fields in the Composable optimization
+ // pass.
+ return Collections.emptyMap();
+ }
+ };
+ }
+ };
inFlowPropagator.run(executorService);
ArgumentPropagatorOptimizationInfoPopulator optimizationInfoPopulator =
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index f58aab9..bcfa888 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -114,10 +114,9 @@
// TODO(b/343909250): Is this supposed to be kept on all live items?
return config.sourceDebugExtension;
}
- if (config.methodParameters
- && DexAnnotation.isParameterNameAnnotation(annotation, dexItemFactory)) {
- // TODO(b/343907109): This should be conditional on its own keep info bit.
- return true;
+ if (DexAnnotation.isParameterNameAnnotation(annotation, dexItemFactory)) {
+ KeepMethodInfo methodInfo = keepInfo.asMethodInfo();
+ return methodInfo != null && !methodInfo.isParameterNamesRemovalAllowed(options);
}
if (isAnnotationOnAnnotationClass
&& DexAnnotation.isAnnotationDefaultAnnotation(annotation, dexItemFactory)
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index c924664..7dfbfeb 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -24,6 +24,8 @@
boolean isForceKeepExceptionsAttributeEnabled();
+ boolean isForceKeepMethodParametersAttributeEnabled();
+
boolean isKeepEnclosingMethodAttributeEnabled();
boolean isKeepInnerClassesAttributeEnabled();
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 663d045..2483029 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -90,13 +90,15 @@
@Override
public Builder makeTop() {
return super.makeTop()
- .disallowFieldTypeStrengthening()
- .disallowRedundantFieldLoadElimination();
+ .setAllowFieldTypeStrengthening(false)
+ .setAllowRedundantFieldLoadElimination(false);
}
@Override
public Builder makeBottom() {
- return super.makeBottom().allowFieldTypeStrengthening().allowRedundantFieldLoadElimination();
+ return super.makeBottom()
+ .setAllowFieldTypeStrengthening(true)
+ .setAllowRedundantFieldLoadElimination(true);
}
public boolean isFieldTypeStrengtheningAllowed() {
@@ -108,14 +110,6 @@
return self();
}
- public Builder allowFieldTypeStrengthening() {
- return setAllowFieldTypeStrengthening(true);
- }
-
- public Builder disallowFieldTypeStrengthening() {
- return setAllowFieldTypeStrengthening(false);
- }
-
public boolean isRedundantFieldLoadEliminationAllowed() {
return allowRedundantFieldLoadElimination;
}
@@ -126,14 +120,6 @@
return self();
}
- public Builder allowRedundantFieldLoadElimination() {
- return setAllowRedundantFieldLoadElimination(true);
- }
-
- public Builder disallowRedundantFieldLoadElimination() {
- return setAllowRedundantFieldLoadElimination(false);
- }
-
@Override
public KeepFieldInfo getTopInfo() {
return TOP;
@@ -175,12 +161,12 @@
}
public Joiner disallowFieldTypeStrengthening() {
- builder.disallowFieldTypeStrengthening();
+ builder.setAllowFieldTypeStrengthening(false);
return self();
}
public Joiner disallowRedundantFieldLoadElimination() {
- builder.disallowRedundantFieldLoadElimination();
+ builder.setAllowRedundantFieldLoadElimination(false);
return self();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 4610887..23be490 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -334,28 +334,28 @@
}
B makeTop() {
- disallowAccessModification();
- disallowAccessModificationForTesting();
- disallowAnnotationRemoval();
- disallowTypeAnnotationRemoval();
- disallowMinification();
- disallowOptimization();
- disallowShrinking();
- disallowSignatureRemoval();
- unsetCheckDiscarded();
+ setAllowAccessModification(false);
+ setAllowAccessModificationForTesting(false);
+ setAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createTop());
+ setTypeAnnotationsInfo(KeepAnnotationCollectionInfo.Builder.createTop());
+ setAllowMinification(false);
+ setAllowOptimization(false);
+ setAllowShrinking(false);
+ setAllowSignatureRemoval(false);
+ setCheckDiscarded(false);
return self();
}
B makeBottom() {
- allowAccessModification();
- allowAccessModificationForTesting();
- allowAnnotationRemoval();
- allowTypeAnnotationRemoval();
- allowMinification();
- allowOptimization();
- allowShrinking();
- allowSignatureRemoval();
- unsetCheckDiscarded();
+ setAllowAccessModification(true);
+ setAllowAccessModificationForTesting(true);
+ setAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createBottom());
+ setTypeAnnotationsInfo(KeepAnnotationCollectionInfo.Builder.createBottom());
+ setAllowMinification(true);
+ setAllowOptimization(true);
+ setAllowShrinking(true);
+ setAllowSignatureRemoval(true);
+ setCheckDiscarded(false);
return self();
}
@@ -387,84 +387,44 @@
&& typeAnnotationsInfo.isEqualTo(other.internalTypeAnnotationsInfo());
}
- public boolean isAccessModificationAllowed() {
- return allowAccessModification;
- }
-
- public boolean isAccessModificationAllowedForTesting() {
- return allowAccessModificationForTesting;
- }
-
public boolean isCheckDiscardedEnabled() {
return checkDiscarded;
}
- public boolean isMinificationAllowed() {
- return allowMinification;
- }
-
- public boolean isOptimizationAllowed() {
- return allowOptimization;
- }
-
- public boolean isShrinkingAllowed() {
- return allowShrinking;
- }
-
- public boolean isSignatureRemovalAllowed() {
- return allowSignatureRemoval;
- }
-
- public B setAllowMinification(boolean allowMinification) {
- this.allowMinification = allowMinification;
- return self();
- }
-
- public B allowMinification() {
- return setAllowMinification(true);
- }
-
- public B disallowMinification() {
- return setAllowMinification(false);
- }
-
- public B setAllowOptimization(boolean allowOptimization) {
- this.allowOptimization = allowOptimization;
- return self();
- }
-
- public B allowOptimization() {
- return setAllowOptimization(true);
- }
-
- public B disallowOptimization() {
- return setAllowOptimization(false);
- }
-
- public B setAllowShrinking(boolean allowShrinking) {
- this.allowShrinking = allowShrinking;
- return self();
- }
-
- public B allowShrinking() {
- return setAllowShrinking(true);
- }
-
- public B disallowShrinking() {
- return setAllowShrinking(false);
- }
-
public B setCheckDiscarded(boolean checkDiscarded) {
this.checkDiscarded = checkDiscarded;
return self();
}
- public B setCheckDiscarded() {
- return setCheckDiscarded(true);
+ public boolean isMinificationAllowed() {
+ return allowMinification;
}
- public B unsetCheckDiscarded() {
- return setCheckDiscarded(false);
+ public B setAllowMinification(boolean allowMinification) {
+ this.allowMinification = allowMinification;
+ return self();
+ }
+
+ public boolean isOptimizationAllowed() {
+ return allowOptimization;
+ }
+
+ public B setAllowOptimization(boolean allowOptimization) {
+ this.allowOptimization = allowOptimization;
+ return self();
+ }
+
+ public boolean isShrinkingAllowed() {
+ return allowShrinking;
+ }
+
+ public B setAllowShrinking(boolean allowShrinking) {
+ this.allowShrinking = allowShrinking;
+ return self();
+ }
+
+ public boolean isAccessModificationAllowed() {
+ return allowAccessModification;
}
public B setAllowAccessModification(boolean allowAccessModification) {
@@ -472,12 +432,8 @@
return self();
}
- public B allowAccessModification() {
- return setAllowAccessModification(true);
- }
-
- public B disallowAccessModification() {
- return setAllowAccessModification(false);
+ public boolean isAccessModificationAllowedForTesting() {
+ return allowAccessModificationForTesting;
}
public B setAllowAccessModificationForTesting(boolean allowAccessModificationForTesting) {
@@ -485,69 +441,32 @@
return self();
}
- public B allowAccessModificationForTesting() {
- return setAllowAccessModificationForTesting(true);
- }
-
- public B disallowAccessModificationForTesting() {
- return setAllowAccessModificationForTesting(false);
- }
-
- KeepAnnotationCollectionInfo.Builder getAnnotationsInfo() {
+ public KeepAnnotationCollectionInfo.Builder getAnnotationsInfo() {
return annotationsInfo;
}
- public B allowAnnotationRemoval() {
- annotationsInfo = KeepAnnotationCollectionInfo.Builder.createBottom();
+ public B setAnnotationInfo(KeepAnnotationCollectionInfo.Builder infoBuilder) {
+ annotationsInfo = infoBuilder;
return self();
}
- public B disallowAnnotationRemoval() {
- annotationsInfo = KeepAnnotationCollectionInfo.Builder.createTop();
- return self();
- }
-
- public B disallowAnnotationRemoval(RetentionInfo retention) {
- annotationsInfo.destructiveJoinAnyTypeInfo(retention);
- return self();
- }
-
- public B disallowAnnotationRemoval(RetentionInfo retention, DexType type) {
- annotationsInfo.destructiveJoinTypeInfo(type, retention);
- return self();
- }
-
- KeepAnnotationCollectionInfo.Builder getTypeAnnotationsInfo() {
+ public KeepAnnotationCollectionInfo.Builder getTypeAnnotationsInfo() {
return typeAnnotationsInfo;
}
- public B allowTypeAnnotationRemoval() {
- typeAnnotationsInfo = KeepAnnotationCollectionInfo.Builder.createBottom();
+ public B setTypeAnnotationsInfo(KeepAnnotationCollectionInfo.Builder infoBuilder) {
+ typeAnnotationsInfo = infoBuilder;
return self();
}
- public B disallowTypeAnnotationRemoval() {
- typeAnnotationsInfo = KeepAnnotationCollectionInfo.Builder.createTop();
- return self();
+ public boolean isSignatureRemovalAllowed() {
+ return allowSignatureRemoval;
}
- public B disallowTypeAnnotationRemoval(RetentionInfo retention) {
- typeAnnotationsInfo.destructiveJoinAnyTypeInfo(retention);
- return self();
- }
-
- private B setAllowSignatureRemoval(boolean allowSignatureRemoval) {
+ public B setAllowSignatureRemoval(boolean allowSignatureRemoval) {
this.allowSignatureRemoval = allowSignatureRemoval;
return self();
}
-
- public B allowSignatureRemoval() {
- return setAllowSignatureRemoval(true);
- }
-
- public B disallowSignatureRemoval() {
- return setAllowSignatureRemoval(false);
- }
}
/** Joiner to construct monotonically increasing keep info object. */
@@ -653,52 +572,52 @@
}
public J disallowAccessModification() {
- builder.disallowAccessModification();
+ builder.setAllowAccessModification(false);
return self();
}
public J disallowAccessModificationForTesting() {
- builder.disallowAccessModificationForTesting();
+ builder.setAllowAccessModificationForTesting(false);
return self();
}
public J disallowAnnotationRemoval(RetentionInfo retention) {
- builder.disallowAnnotationRemoval(retention);
+ builder.getAnnotationsInfo().destructiveJoinAnyTypeInfo(retention);
return self();
}
public J disallowAnnotationRemoval(RetentionInfo retention, DexType type) {
- builder.disallowAnnotationRemoval(retention, type);
+ builder.getAnnotationsInfo().destructiveJoinTypeInfo(type, retention);
return self();
}
public J disallowTypeAnnotationRemoval(RetentionInfo retention) {
- builder.disallowTypeAnnotationRemoval(retention);
+ builder.getTypeAnnotationsInfo().destructiveJoinAnyTypeInfo(retention);
return self();
}
public J disallowMinification() {
- builder.disallowMinification();
+ builder.setAllowMinification(false);
return self();
}
public J disallowOptimization() {
- builder.disallowOptimization();
+ builder.setAllowOptimization(false);
return self();
}
public J disallowShrinking() {
- builder.disallowShrinking();
+ builder.setAllowShrinking(false);
return self();
}
public J disallowSignatureRemoval() {
- builder.disallowSignatureRemoval();
+ builder.setAllowSignatureRemoval(false);
return self();
}
public J setCheckDiscarded() {
- builder.setCheckDiscarded();
+ builder.setCheckDiscarded(true);
return self();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
index 201c2f9..7394058 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -74,14 +74,6 @@
return self();
}
- public B allowValuePropagation() {
- return setAllowValuePropagation(true);
- }
-
- public B disallowValuePropagation() {
- return setAllowValuePropagation(false);
- }
-
@Override
boolean internalIsEqualTo(K other) {
return super.internalIsEqualTo(other)
@@ -90,12 +82,12 @@
@Override
public B makeTop() {
- return super.makeTop().disallowValuePropagation();
+ return super.makeTop().setAllowValuePropagation(false);
}
@Override
public B makeBottom() {
- return super.makeBottom().allowValuePropagation();
+ return super.makeBottom().setAllowValuePropagation(true);
}
}
@@ -108,7 +100,7 @@
}
public J disallowValuePropagation() {
- builder.disallowValuePropagation();
+ builder.setAllowValuePropagation(false);
return self();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index caaba95..571d3e8 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -42,6 +42,7 @@
private final boolean allowSingleCallerInlining;
private final boolean allowUnusedArgumentOptimization;
private final boolean allowUnusedReturnValueOptimization;
+ private final boolean allowParameterNamesRemoval;
private final KeepAnnotationCollectionInfo parameterAnnotationsInfo;
protected KeepMethodInfo(Builder builder) {
@@ -60,6 +61,7 @@
this.allowSingleCallerInlining = builder.isSingleCallerInliningAllowed();
this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed();
this.allowUnusedReturnValueOptimization = builder.isUnusedReturnValueOptimizationAllowed();
+ this.allowParameterNamesRemoval = builder.isParameterNamesRemovalAllowed();
this.parameterAnnotationsInfo = builder.getParameterAnnotationsInfo().build();
}
@@ -242,6 +244,15 @@
return allowUnusedReturnValueOptimization;
}
+ public boolean isParameterNamesRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
+ return !configuration.isForceKeepMethodParametersAttributeEnabled()
+ && internalIsParameterNamesRemovalAllowed();
+ }
+
+ boolean internalIsParameterNamesRemovalAllowed() {
+ return allowParameterNamesRemoval;
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
@@ -273,6 +284,7 @@
private boolean allowSingleCallerInlining;
private boolean allowUnusedArgumentOptimization;
private boolean allowUnusedReturnValueOptimization;
+ private boolean allowParameterNamesRemoval;
private KeepAnnotationCollectionInfo.Builder parameterAnnotationsInfo;
public Builder() {
@@ -296,6 +308,7 @@
allowUnusedArgumentOptimization = original.internalIsUnusedArgumentOptimizationAllowed();
allowUnusedReturnValueOptimization =
original.internalIsUnusedReturnValueOptimizationAllowed();
+ allowParameterNamesRemoval = original.internalIsParameterNamesRemovalAllowed();
parameterAnnotationsInfo = original.internalParameterAnnotationsInfo().toBuilder();
}
@@ -303,21 +316,11 @@
return allowThrowsRemoval;
}
- private Builder setAllowThrowsRemoval(boolean allowThrowsRemoval) {
+ public Builder setAllowThrowsRemoval(boolean allowThrowsRemoval) {
this.allowThrowsRemoval = allowThrowsRemoval;
return self();
}
- public Builder allowThrowsRemoval() {
- return setAllowThrowsRemoval(true);
- }
-
- public Builder disallowThrowsRemoval() {
- return setAllowThrowsRemoval(false);
- }
-
- // Class inlining.
-
public boolean isClassInliningAllowed() {
return allowClassInlining;
}
@@ -327,16 +330,6 @@
return self();
}
- public Builder allowClassInlining() {
- return setAllowClassInlining(true);
- }
-
- public Builder disallowClassInlining() {
- return setAllowClassInlining(false);
- }
-
- // Closed world reasoning.
-
public boolean isClosedWorldReasoningAllowed() {
return allowClosedWorldReasoning;
}
@@ -346,16 +339,6 @@
return self();
}
- public Builder allowClosedWorldReasoning() {
- return setAllowClosedWorldReasoning(true);
- }
-
- public Builder disallowClosedWorldReasoning() {
- return setAllowClosedWorldReasoning(false);
- }
-
- // Constant argument optimization.
-
public boolean isConstantArgumentOptimizationAllowed() {
return allowConstantArgumentOptimization;
}
@@ -365,16 +348,6 @@
return self();
}
- public Builder allowConstantArgumentOptimization() {
- return setAllowConstantArgumentOptimization(true);
- }
-
- public Builder disallowConstantArgumentOptimization() {
- return setAllowConstantArgumentOptimization(false);
- }
-
- // Inlining.
-
public boolean isInliningAllowed() {
return allowInlining;
}
@@ -384,16 +357,6 @@
return self();
}
- public Builder allowInlining() {
- return setAllowInlining(true);
- }
-
- public Builder disallowInlining() {
- return setAllowInlining(false);
- }
-
- // Method staticizing.
-
public boolean isMethodStaticizingAllowed() {
return allowMethodStaticizing;
}
@@ -403,16 +366,6 @@
return self();
}
- public Builder allowMethodStaticizing() {
- return setAllowMethodStaticizing(true);
- }
-
- public Builder disallowMethodStaticizing() {
- return setAllowMethodStaticizing(false);
- }
-
- // Parameter removal.
-
public boolean isParameterRemovalAllowed() {
return allowParameterRemoval;
}
@@ -422,16 +375,6 @@
return self();
}
- public Builder allowParameterRemoval() {
- return setAllowParameterRemoval(true);
- }
-
- public Builder disallowParameterRemoval() {
- return setAllowParameterRemoval(false);
- }
-
- // Parameter reordering.
-
public boolean isParameterReorderingAllowed() {
return allowParameterReordering;
}
@@ -441,16 +384,6 @@
return self();
}
- public Builder allowParameterReordering() {
- return setAllowParameterReordering(true);
- }
-
- public Builder disallowParameterReordering() {
- return setAllowParameterReordering(false);
- }
-
- // Parameter type strengthening.
-
public boolean isParameterTypeStrengtheningAllowed() {
return allowParameterTypeStrengthening;
}
@@ -460,16 +393,6 @@
return self();
}
- public Builder allowParameterTypeStrengthening() {
- return setAllowParameterTypeStrengthening(true);
- }
-
- public Builder disallowParameterTypeStrengthening() {
- return setAllowParameterTypeStrengthening(false);
- }
-
- // Reprocessing.
-
public boolean isReprocessingAllowed() {
return allowReprocessing;
}
@@ -479,16 +402,6 @@
return self();
}
- public Builder allowReprocessing() {
- return setAllowReprocessing(true);
- }
-
- public Builder disallowReprocessing() {
- return setAllowReprocessing(false);
- }
-
- // Return type strengthening.
-
public boolean isReturnTypeStrengtheningAllowed() {
return allowReturnTypeStrengthening;
}
@@ -498,16 +411,6 @@
return self();
}
- public Builder allowReturnTypeStrengthening() {
- return setAllowReturnTypeStrengthening(true);
- }
-
- public Builder disallowReturnTypeStrengthening() {
- return setAllowReturnTypeStrengthening(false);
- }
-
- // Single caller inlining.
-
public boolean isSingleCallerInliningAllowed() {
return allowSingleCallerInlining;
}
@@ -517,16 +420,6 @@
return self();
}
- public Builder allowSingleCallerInlining() {
- return setAllowSingleCallerInlining(true);
- }
-
- public Builder disallowSingleCallerInlining() {
- return setAllowSingleCallerInlining(false);
- }
-
- // Unused argument optimization.
-
public boolean isUnusedArgumentOptimizationAllowed() {
return allowUnusedArgumentOptimization;
}
@@ -536,16 +429,6 @@
return self();
}
- public Builder allowUnusedArgumentOptimization() {
- return setAllowUnusedArgumentOptimization(true);
- }
-
- public Builder disallowUnusedArgumentOptimization() {
- return setAllowUnusedArgumentOptimization(false);
- }
-
- // Unused return value optimization.
-
public boolean isUnusedReturnValueOptimizationAllowed() {
return allowUnusedReturnValueOptimization;
}
@@ -556,32 +439,21 @@
return self();
}
- public Builder allowUnusedReturnValueOptimization() {
- return setAllowUnusedReturnValueOptimization(true);
+ public boolean isParameterNamesRemovalAllowed() {
+ return allowParameterNamesRemoval;
}
- public Builder disallowUnusedReturnValueOptimization() {
- return setAllowUnusedReturnValueOptimization(false);
+ public Builder setAllowParameterNamesRemoval(boolean allowParameterNamesRemoval) {
+ this.allowParameterNamesRemoval = allowParameterNamesRemoval;
+ return self();
}
- // Parameter annotations
-
- KeepAnnotationCollectionInfo.Builder getParameterAnnotationsInfo() {
+ public KeepAnnotationCollectionInfo.Builder getParameterAnnotationsInfo() {
return parameterAnnotationsInfo;
}
- public Builder allowParameterAnnotationsRemoval() {
- parameterAnnotationsInfo = KeepAnnotationCollectionInfo.Builder.createBottom();
- return self();
- }
-
- public Builder disallowParameterAnnotationsRemoval() {
- parameterAnnotationsInfo = KeepAnnotationCollectionInfo.Builder.createTop();
- return self();
- }
-
- public Builder disallowParameterAnnotationsRemoval(RetentionInfo retention) {
- parameterAnnotationsInfo.destructiveJoinAnyTypeInfo(retention);
+ public Builder setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder infoBuilder) {
+ parameterAnnotationsInfo = infoBuilder;
return self();
}
@@ -626,6 +498,7 @@
== other.internalIsUnusedArgumentOptimizationAllowed()
&& isUnusedReturnValueOptimizationAllowed()
== other.internalIsUnusedReturnValueOptimizationAllowed()
+ && isParameterNamesRemovalAllowed() == other.internalIsParameterNamesRemovalAllowed()
&& parameterAnnotationsInfo.isEqualTo(other.parameterAnnotationsInfo);
}
@@ -637,41 +510,43 @@
@Override
public Builder makeTop() {
return super.makeTop()
- .disallowThrowsRemoval()
- .disallowClassInlining()
- .disallowClosedWorldReasoning()
- .disallowConstantArgumentOptimization()
- .disallowInlining()
- .disallowMethodStaticizing()
- .disallowParameterRemoval()
- .disallowParameterReordering()
- .disallowParameterTypeStrengthening()
- .disallowReprocessing()
- .disallowReturnTypeStrengthening()
- .disallowSingleCallerInlining()
- .disallowUnusedArgumentOptimization()
- .disallowUnusedReturnValueOptimization()
- .disallowParameterAnnotationsRemoval();
+ .setAllowThrowsRemoval(false)
+ .setAllowClassInlining(false)
+ .setAllowClosedWorldReasoning(false)
+ .setAllowConstantArgumentOptimization(false)
+ .setAllowInlining(false)
+ .setAllowMethodStaticizing(false)
+ .setAllowParameterRemoval(false)
+ .setAllowParameterReordering(false)
+ .setAllowParameterTypeStrengthening(false)
+ .setAllowReprocessing(false)
+ .setAllowReturnTypeStrengthening(false)
+ .setAllowSingleCallerInlining(false)
+ .setAllowUnusedArgumentOptimization(false)
+ .setAllowUnusedReturnValueOptimization(false)
+ .setAllowParameterNamesRemoval(false)
+ .setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createTop());
}
@Override
public Builder makeBottom() {
return super.makeBottom()
- .allowThrowsRemoval()
- .allowClassInlining()
- .allowClosedWorldReasoning()
- .allowConstantArgumentOptimization()
- .allowInlining()
- .allowMethodStaticizing()
- .allowParameterRemoval()
- .allowParameterReordering()
- .allowParameterTypeStrengthening()
- .allowReprocessing()
- .allowReturnTypeStrengthening()
- .allowSingleCallerInlining()
- .allowUnusedArgumentOptimization()
- .allowUnusedReturnValueOptimization()
- .allowParameterAnnotationsRemoval();
+ .setAllowThrowsRemoval(true)
+ .setAllowClassInlining(true)
+ .setAllowClosedWorldReasoning(true)
+ .setAllowConstantArgumentOptimization(true)
+ .setAllowInlining(true)
+ .setAllowMethodStaticizing(true)
+ .setAllowParameterRemoval(true)
+ .setAllowParameterReordering(true)
+ .setAllowParameterTypeStrengthening(true)
+ .setAllowReprocessing(true)
+ .setAllowReturnTypeStrengthening(true)
+ .setAllowSingleCallerInlining(true)
+ .setAllowUnusedArgumentOptimization(true)
+ .setAllowUnusedReturnValueOptimization(true)
+ .setAllowParameterNamesRemoval(true)
+ .setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createBottom());
}
}
@@ -686,82 +561,88 @@
}
public Joiner disallowThrowsRemoval() {
- builder.disallowThrowsRemoval();
+ builder.setAllowThrowsRemoval(false);
return self();
}
public Joiner disallowClassInlining() {
- builder.disallowClassInlining();
+ builder.setAllowClassInlining(false);
return self();
}
public Joiner disallowClosedWorldReasoning() {
- builder.disallowClosedWorldReasoning();
+ builder.setAllowClosedWorldReasoning(false);
return self();
}
public Joiner disallowConstantArgumentOptimization() {
- builder.disallowConstantArgumentOptimization();
+ builder.setAllowConstantArgumentOptimization(false);
return self();
}
public Joiner disallowInlining() {
- builder.disallowInlining();
+ builder.setAllowInlining(false);
return self();
}
public Joiner disallowMethodStaticizing() {
- builder.disallowMethodStaticizing();
+ builder.setAllowMethodStaticizing(false);
return self();
}
public Joiner disallowParameterRemoval() {
- builder.disallowParameterRemoval();
+ builder.setAllowParameterRemoval(false);
return self();
}
public Joiner disallowParameterReordering() {
- builder.disallowParameterReordering();
+ builder.setAllowParameterReordering(false);
return self();
}
public Joiner disallowParameterTypeStrengthening() {
- builder.disallowParameterTypeStrengthening();
+ builder.setAllowParameterTypeStrengthening(false);
return self();
}
public Joiner disallowReprocessing() {
- builder.disallowReprocessing();
+ builder.setAllowReprocessing(false);
return self();
}
public Joiner disallowReturnTypeStrengthening() {
- builder.disallowReturnTypeStrengthening();
+ builder.setAllowReturnTypeStrengthening(false);
return self();
}
public Joiner disallowSingleCallerInlining() {
- builder.disallowSingleCallerInlining();
+ builder.setAllowSingleCallerInlining(false);
return self();
}
public Joiner disallowUnusedArgumentOptimization() {
- builder.disallowUnusedArgumentOptimization();
+ builder.setAllowUnusedArgumentOptimization(false);
return self();
}
public Joiner disallowUnusedReturnValueOptimization() {
- builder.disallowUnusedReturnValueOptimization();
+ builder.setAllowUnusedReturnValueOptimization(false);
+ return self();
+ }
+
+ public Joiner disallowParameterNamesRemoval() {
+ builder.setAllowParameterNamesRemoval(false);
return self();
}
public Joiner disallowParameterAnnotationsRemoval() {
- builder.disallowParameterAnnotationsRemoval();
+ builder.setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createTop());
return self();
}
public Joiner disallowParameterAnnotationsRemoval(RetentionInfo retention) {
- builder.disallowParameterAnnotationsRemoval(retention);
+ builder.getParameterAnnotationsInfo().destructiveJoinAnyTypeInfo(retention);
+ builder.self();
return self();
}
@@ -803,7 +684,10 @@
Joiner::disallowUnusedArgumentOptimization)
.applyIf(
!joiner.builder.isUnusedReturnValueOptimizationAllowed(),
- Joiner::disallowUnusedReturnValueOptimization);
+ Joiner::disallowUnusedReturnValueOptimization)
+ .applyIf(
+ !joiner.builder.isParameterNamesRemovalAllowed(),
+ Joiner::disallowParameterNamesRemoval);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index 19a6946..445e0b0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -42,7 +42,6 @@
public boolean enclosingMethod = false;
public boolean signature = false;
public boolean exceptions = false;
- public boolean lineNumberTable = false;
public boolean localVariableTable = false;
public boolean localVariableTypeTable = false;
public boolean methodParameters = false;
@@ -119,8 +118,6 @@
sourceDir = update(sourceDir, SOURCE_DIR, patterns);
innerClasses = update(innerClasses, INNER_CLASSES, patterns);
enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
- lineNumberTable = update(lineNumberTable, LINE_NUMBER_TABLE, patterns);
- localVariableTable = update(localVariableTable, LOCAL_VARIABLE_TABLE, patterns);
localVariableTypeTable = update(localVariableTypeTable, LOCAL_VARIABLE_TYPE_TABLE, patterns);
exceptions = update(exceptions, EXCEPTIONS, patterns);
methodParameters = update(methodParameters, METHOD_PARAMETERS, patterns);
@@ -156,16 +153,6 @@
throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. "
+ "Check -keepattributes directive.");
}
- if (forceProguardCompatibility && localVariableTable && !lineNumberTable) {
- // If locals are kept, assume line numbers should be kept too.
- lineNumberTable = true;
- }
- if (localVariableTable && !lineNumberTable) {
- throw new CompilationError(
- "Attribute " + LOCAL_VARIABLE_TABLE
- + " requires " + LINE_NUMBER_TABLE
- + ". Check -keepattributes directive.");
- }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index f6ff126..3d5324b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1678,6 +1678,11 @@
context.markAsUsed();
}
+ if (attributesConfig.methodParameters && item.isMethod()) {
+ itemJoiner.computeIfAbsent().asMethodJoiner().disallowParameterNamesRemoval();
+ context.markAsUsed();
+ }
+
if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
itemJoiner.computeIfAbsent().disallowMinification();
context.markAsUsed();
diff --git a/src/main/java/com/android/tools/r8/shaking/SyntheticKeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/SyntheticKeepMethodInfo.java
index 1b2c6a5..4686173 100644
--- a/src/main/java/com/android/tools/r8/shaking/SyntheticKeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/SyntheticKeepMethodInfo.java
@@ -32,21 +32,21 @@
}
@Override
- public Builder disallowMinification() {
- // Ignore as synthetic items can always be minified.
- return self();
+ public boolean isMinificationAllowed() {
+ // Synthetic items can always be minified.
+ return true;
}
@Override
- public Builder disallowOptimization() {
- // Ignore as synthetic items can always be optimized.
- return self();
+ public boolean isOptimizationAllowed() {
+ // Synthetic items can always be optimized.
+ return true;
}
@Override
- public Builder disallowShrinking() {
- // Ignore as synthetic items can always be removed.
- return self();
+ public boolean isShrinkingAllowed() {
+ // Synthetic items can always be removed.
+ return true;
}
@Override
@@ -77,7 +77,7 @@
}
@Override
- public KeepMethodInfo.Joiner disallowMinification() {
+ public Joiner disallowMinification() {
// Ignore as synthetic items can always be minified.
return self();
}
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 d68f123..91fbada 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -317,7 +317,6 @@
assert !proguardConfiguration.isObfuscating();
getProguardConfiguration().getKeepAttributes().sourceFile = true;
getProguardConfiguration().getKeepAttributes().sourceDebugExtension = true;
- getProguardConfiguration().getKeepAttributes().lineNumberTable = true;
getProguardConfiguration().getKeepAttributes().localVariableTable = true;
getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
}
@@ -829,6 +828,13 @@
}
@Override
+ public boolean isForceKeepMethodParametersAttributeEnabled() {
+ return proguardConfiguration == null
+ || (isForceProguardCompatibilityEnabled()
+ && proguardConfiguration.getKeepAttributes().methodParameters);
+ }
+
+ @Override
public boolean isKeepEnclosingMethodAttributeEnabled() {
return proguardConfiguration.getKeepAttributes().enclosingMethod;
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
index c778df3..891593a 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
@@ -8,14 +8,15 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Predicate;
import java.util.stream.Stream;
public class ProgramFieldSet implements Iterable<ProgramField> {
@@ -54,6 +55,10 @@
backing.putAll(fields.backing);
}
+ public boolean allMatch(Predicate<? super ProgramField> predicate) {
+ return Iterables.all(this, predicate);
+ }
+
public boolean createAndAdd(DexProgramClass clazz, DexEncodedField definition) {
return add(new ProgramField(clazz, definition));
}
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
index c3e2ad4..f289319 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
@@ -219,6 +219,11 @@
try {
XmlNode xmlNode = XmlNode.parseFrom(inputStream);
visitNode(xmlNode, xmlFile);
+ // Ensure that we trace the transitive reachable ids, without us having to iterate all
+ // resources for the reachable marker.
+ ProtoAndroidManifestUsageRecorderKt.recordUsagesFromNode(xmlNode, r8ResourceShrinkerModel)
+ .iterator()
+ .forEachRemaining(resource -> trace(resource.value));
} catch (IOException e) {
errorHandler.apply(e);
}
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt b/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
index 77bb8f6..c0f683e 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/usages/ProtoAndroidManifestUsageRecorder.kt
@@ -19,6 +19,7 @@
import com.android.aapt.Resources.XmlNode
import com.android.build.shrinker.ResourceShrinkerModel
import com.android.ide.common.resources.usage.ResourceUsageModel
+import com.android.ide.common.resources.usage.ResourceUsageModel.Resource
import java.nio.file.Files
import java.nio.file.Path
@@ -35,13 +36,13 @@
}
}
-fun recordUsagesFromNode(node: XmlNode, model: ResourceShrinkerModel) {
+fun recordUsagesFromNode(node: XmlNode, model: ResourceShrinkerModel) : Sequence<Resource>{
// Records only resources from element attributes that have reference items with resolved
// ids or names.
if (!node.hasElement()) {
- return
+ return emptySequence()
}
- node.element.attributeList.asSequence()
+ val reachableResources = node.element.attributeList.asSequence()
.filter { it.hasCompiledItem() }
.map { it.compiledItem }
.filter { it.hasRef() }
@@ -53,6 +54,6 @@
else -> model.resourceStore.getResourcesFromUrl("@${it.name}")
}.asSequence()
}
- .forEach { ResourceUsageModel.markReachable(it) }
- node.element.childList.forEach { recordUsagesFromNode(it, model) }
+ reachableResources.forEach {ResourceUsageModel.markReachable(it)}
+ return reachableResources + node.element.childList.flatMap{ recordUsagesFromNode(it, model) }
}
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidManifestWithTransitiveXmlReferenceTest.java b/src/test/java/com/android/tools/r8/androidresources/AndroidManifestWithTransitiveXmlReferenceTest.java
new file mode 100644
index 0000000..117e0b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidManifestWithTransitiveXmlReferenceTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.androidresources;
+
+import static com.android.tools.r8.androidresources.AndroidResourceTestingUtils.TINY_PNG;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AndroidManifestWithTransitiveXmlReferenceTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build();
+ }
+
+ public static String MANIFEST_WITH_XML_REFERENCE =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " xmlns:tools=\"http://schemas.android.com/tools\""
+ + " package=\"com.android.tools.r8\""
+ + ">\n"
+ + " <application\n"
+ + " android:label=\"@string/app_name\">\n"
+ + " <meta-data\n"
+ + " android:name=\"android.service.dream\"\n"
+ + " android:resource=\"@xml/xml_with_reference\" />\n"
+ + " </application>\n"
+ + "</manifest>";
+
+ public static String XML_WITH_XML_REFERENCE =
+ "<dream xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " android:previewImage=\"@drawable/image_with_ref_from_xml\"\n"
+ + " />\n";
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withManifest(MANIFEST_WITH_XML_REFERENCE)
+ .addXml("xml_with_reference.xml", XML_WITH_XML_REFERENCE)
+ .addDrawable("image_with_ref_from_xml.png", TINY_PNG)
+ .addStringValue("app_name", "The one and only.")
+ .build(temp);
+ }
+
+ @Test
+ public void testManifestReferences() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addAndroidResources(getTestResources(temp))
+ .enableOptimizedShrinking()
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "app_name");
+ resourceTableInspector.assertContainsResourceWithName("xml", "xml_with_reference");
+ resourceTableInspector.assertContainsResourceWithName(
+ "drawable", "image_with_ref_from_xml");
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/androidresources/XmlSelfReferenceTest.java b/src/test/java/com/android/tools/r8/androidresources/XmlSelfReferenceTest.java
new file mode 100644
index 0000000..f14c778
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/XmlSelfReferenceTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.androidresources;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class XmlSelfReferenceTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build();
+ }
+
+ public static String MANIFEST_WITH_XML_REFERENCE =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " xmlns:tools=\"http://schemas.android.com/tools\""
+ + " package=\"com.android.tools.r8\""
+ + ">\n"
+ + " <application\n"
+ + " android:label=\"@string/app_name\">\n"
+ + " <meta-data\n"
+ + " android:name=\"android.service.dream\"\n"
+ + " android:resource=\"@xml/xml_with_self_reference\" />\n"
+ + " </application>\n"
+ + "</manifest>";
+
+ public static String XML_WITH_SELF_REFERENCE =
+ "<dream xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " android:previewImage=\"@xml/xml_with_self_reference\"\n"
+ + " />\n";
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withManifest(MANIFEST_WITH_XML_REFERENCE)
+ .addXml("xml_with_self_reference.xml", XML_WITH_SELF_REFERENCE)
+ .addStringValue("app_name", "The one and only.")
+ .build(temp);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addAndroidResources(getTestResources(temp))
+ .enableOptimizedShrinking()
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "app_name");
+ resourceTableInspector.assertContainsResourceWithName(
+ "xml", "xml_with_self_reference");
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
index f7c60a4..4e2a4c2 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
@@ -5,6 +5,8 @@
import static java.util.Collections.emptyList;
+import com.android.tools.r8.benchmarks.appdumps.ChromeBenchmarks;
+import com.android.tools.r8.benchmarks.appdumps.ComposeSamplesBenchmarks;
import com.android.tools.r8.benchmarks.appdumps.NowInAndroidBenchmarks;
import com.android.tools.r8.benchmarks.appdumps.TiviBenchmarks;
import com.android.tools.r8.benchmarks.desugaredlib.L8Benchmark;
@@ -24,6 +26,13 @@
// Actual list of all configured benchmarks.
private final Map<String, List<BenchmarkConfig>> benchmarks = new HashMap<>();
+ @SafeVarargs
+ public BenchmarkCollection(List<BenchmarkConfig>... benchmarksCollection) {
+ for (List<BenchmarkConfig> benchmarks : benchmarksCollection) {
+ benchmarks.forEach(this::addBenchmark);
+ }
+ }
+
private void addBenchmark(BenchmarkConfig benchmark) {
List<BenchmarkConfig> variants =
benchmarks.computeIfAbsent(benchmark.getName(), k -> new ArrayList<>());
@@ -52,15 +61,16 @@
}
public static BenchmarkCollection computeCollection() {
- BenchmarkCollection collection = new BenchmarkCollection();
// Every benchmark that should be active on golem must be setup in this method.
- HelloWorldBenchmark.configs().forEach(collection::addBenchmark);
- LegacyDesugaredLibraryBenchmark.configs().forEach(collection::addBenchmark);
- L8Benchmark.configs().forEach(collection::addBenchmark);
- NowInAndroidBenchmarks.configs().forEach(collection::addBenchmark);
- TiviBenchmarks.configs().forEach(collection::addBenchmark);
- RetraceStackTraceBenchmark.configs().forEach(collection::addBenchmark);
- return collection;
+ return new BenchmarkCollection(
+ HelloWorldBenchmark.configs(),
+ LegacyDesugaredLibraryBenchmark.configs(),
+ L8Benchmark.configs(),
+ NowInAndroidBenchmarks.configs(),
+ TiviBenchmarks.configs(),
+ RetraceStackTraceBenchmark.configs(),
+ ComposeSamplesBenchmarks.configs(),
+ ChromeBenchmarks.configs());
}
/** Compute and print the golem configuration. */
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index ac4f229..9454d6e 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.benchmarks.appdumps;
import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.benchmarks.BenchmarkBase;
import com.android.tools.r8.benchmarks.BenchmarkConfig;
import com.android.tools.r8.benchmarks.BenchmarkConfigError;
@@ -83,12 +85,16 @@
}
public BenchmarkConfig buildR8() {
+ return buildR8(getDefaultR8Configuration());
+ }
+
+ public BenchmarkConfig buildR8(ThrowableConsumer<? super R8FullTestBuilder> configuration) {
verify();
return BenchmarkConfig.builder()
.setName(name)
.setTarget(BenchmarkTarget.R8_NON_COMPAT)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
- .setMethod(runR8(this))
+ .setMethod(runR8(this, configuration))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
.measureRunTime()
@@ -103,7 +109,7 @@
.setName(name)
.setTarget(BenchmarkTarget.R8_NON_COMPAT)
.setSuite(BenchmarkSuite.OPENSOURCE_BENCHMARKS)
- .setMethod(runR8WithResourceShrinking(this))
+ .setMethod(runR8WithResourceShrinking(this, getDefaultR8Configuration()))
.setFromRevision(fromRevision)
.addDependency(dumpDependency)
.addSubBenchmark(nameForCodePart(), BenchmarkMetric.CodeSize)
@@ -192,16 +198,32 @@
}
}
- private static BenchmarkMethod runR8(AppDumpBenchmarkBuilder builder) {
- return internalRunR8(builder, false);
+ private static ThrowableConsumer<? super R8FullTestBuilder> getDefaultR8Configuration() {
+ return testBuilder ->
+ testBuilder
+ .allowUnnecessaryDontWarnWildcards()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ // TODO(b/222228826): Disallow unrecognized diagnostics and open interfaces.
+ .allowDiagnosticMessages()
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
}
- private static BenchmarkMethod runR8WithResourceShrinking(AppDumpBenchmarkBuilder builder) {
- return internalRunR8(builder, true);
+ private static BenchmarkMethod runR8(
+ AppDumpBenchmarkBuilder builder, ThrowableConsumer<? super R8FullTestBuilder> configuration) {
+ return internalRunR8(builder, false, configuration);
+ }
+
+ private static BenchmarkMethod runR8WithResourceShrinking(
+ AppDumpBenchmarkBuilder builder, ThrowableConsumer<? super R8FullTestBuilder> configuration) {
+ return internalRunR8(builder, true, configuration);
}
private static BenchmarkMethod internalRunR8(
- AppDumpBenchmarkBuilder builder, boolean enableResourceShrinking) {
+ AppDumpBenchmarkBuilder builder,
+ boolean enableResourceShrinking,
+ ThrowableConsumer<? super R8FullTestBuilder> configuration) {
return environment ->
BenchmarkBase.runner(environment)
.setWarmupIterations(1)
@@ -214,16 +236,22 @@
.addProgramFiles(dump.getProgramArchive())
.addLibraryFiles(dump.getLibraryArchive())
.addKeepRuleFiles(dump.getProguardConfigFile())
- .setMinApi(dumpProperties.getMinApi())
- .apply(b -> addDesugaredLibrary(b, dump))
- .allowUnnecessaryDontWarnWildcards()
- .allowUnusedDontWarnPatterns()
- .allowUnusedProguardConfigurationRules()
- // TODO(b/222228826): Disallow unrecognized diagnostics and open interfaces.
- .allowDiagnosticMessages()
.addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ options -> {
+ options.apiModelingOptions().androidApiExtensionPackages =
+ dumpProperties.getAndroidApiExtensionPackages();
+ options
+ .horizontalClassMergerOptions()
+ .setEnableSameFilePolicy(dumpProperties.getEnableSameFilePolicy());
+ })
+ .enableIsolatedSplits(dumpProperties.getIsolatedSplits())
+ .setMinApi(dumpProperties.getMinApi())
+ .apply(
+ testBuilder -> {
+ dump.forEachFeatureArchive(testBuilder::addFeatureSplit);
+ addDesugaredLibrary(testBuilder, dump);
+ })
+ .apply(configuration)
.applyIf(
enableResourceShrinking,
b ->
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/ChromeBenchmarks.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/ChromeBenchmarks.java
new file mode 100644
index 0000000..9bcfcb7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/ChromeBenchmarks.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.benchmarks.appdumps;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ChromeBenchmarks extends BenchmarkBase {
+
+ private static final Path dir = Paths.get(ToolHelper.THIRD_PARTY_DIR, "opensource-apps/chrome");
+
+ public ChromeBenchmarks(BenchmarkConfig config, TestParameters parameters) {
+ super(config, parameters);
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return parametersFromConfigs(configs());
+ }
+
+ public static List<BenchmarkConfig> configs() {
+ return ImmutableList.of(
+ AppDumpBenchmarkBuilder.builder()
+ .setName("ChromeApp")
+ .setDumpDependencyPath(dir)
+ .setFromRevision(16457)
+ .buildR8(ChromeBenchmarks::configure));
+ }
+
+ private static void configure(R8FullTestBuilder testBuilder) {
+ testBuilder
+ .addDontWarn("android.adservices.common.AdServicesOutcomeReceiver")
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ .allowUnnecessaryDontWarnWildcards();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/ComposeSamplesBenchmarks.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/ComposeSamplesBenchmarks.java
new file mode 100644
index 0000000..b74a7df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/ComposeSamplesBenchmarks.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.benchmarks.appdumps;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ComposeSamplesBenchmarks extends BenchmarkBase {
+
+ private static final Path dir =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "opensource-apps/android/compose-samples");
+
+ public ComposeSamplesBenchmarks(BenchmarkConfig config, TestParameters parameters) {
+ super(config, parameters);
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return parametersFromConfigs(configs());
+ }
+
+ public static List<BenchmarkConfig> configs() {
+ return ImmutableList.of(
+ AppDumpBenchmarkBuilder.builder()
+ .setName("CraneApp")
+ .setDumpDependencyPath(dir.resolve("crane"))
+ .setFromRevision(16457)
+ .buildR8(),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("JetLaggedApp")
+ .setDumpDependencyPath(dir.resolve("jetlagged"))
+ .setFromRevision(16457)
+ .buildR8(),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("JetNewsApp")
+ .setDumpDependencyPath(dir.resolve("jetnews"))
+ .setFromRevision(16457)
+ .buildR8(),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("JetCasterApp")
+ .setDumpDependencyPath(dir.resolve("jetcaster"))
+ .setFromRevision(16457)
+ .buildR8(ComposeSamplesBenchmarks::configureJetCasterApp),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("JetChatApp")
+ .setDumpDependencyPath(dir.resolve("jetchat"))
+ .setFromRevision(16457)
+ .buildR8(ComposeSamplesBenchmarks::configureJetChatApp),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("JetSnackApp")
+ .setDumpDependencyPath(dir.resolve("jetsnack"))
+ .setFromRevision(16457)
+ .buildR8(),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("OwlApp")
+ .setDumpDependencyPath(dir.resolve("owl"))
+ .setFromRevision(16457)
+ .buildR8(),
+ AppDumpBenchmarkBuilder.builder()
+ .setName("ReplyApp")
+ .setDumpDependencyPath(dir.resolve("reply"))
+ .setFromRevision(16457)
+ .buildR8());
+ }
+
+ private static void configureJetCasterApp(R8FullTestBuilder testBuilder) {
+ testBuilder
+ .addDontWarn(
+ "org.bouncycastle.jsse.BCSSLParameters",
+ "org.bouncycastle.jsse.BCSSLSocket",
+ "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider",
+ "org.conscrypt.Conscrypt",
+ "org.conscrypt.Conscrypt$Version",
+ "org.openjsse.javax.net.ssl.SSLParameters",
+ "org.openjsse.javax.net.ssl.SSLSocket",
+ "org.openjsse.net.ssl.OpenJSSE",
+ "org.slf4j.impl.StaticLoggerBinder")
+ .allowDiagnosticInfoMessages()
+ .allowUnnecessaryDontWarnWildcards()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ .addOptionsModification(
+ options -> {
+ options.getCfCodeAnalysisOptions().setAllowUnreachableCfBlocks(true);
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
+ });
+ }
+
+ private static void configureJetChatApp(R8FullTestBuilder testBuilder) {
+ testBuilder
+ .allowDiagnosticInfoMessages()
+ .allowUnnecessaryDontWarnWildcards()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index 5927841..80a7beb 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -8,10 +8,13 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -24,6 +27,10 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.List;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
/**
* This tests that we produce valid code when having normal-flow with exceptional edges in blocks.
@@ -31,11 +38,19 @@
* instructions that lie on the boundary of the exception table that is generated for a basic block.
* If live-ranges are minimized this could produce VerifyErrors.
*/
+@RunWith(Parameterized.class)
public class TryRangeTestRunner extends TestBase {
+ @Parameters
+ public static TestParametersCollection data() {
+ return TestParameters.builder().withDefaultCfRuntime().build();
+ }
+
+ @Parameter public TestParameters parameters;
+
@Test
public void testRegisterAllocationLimitTrailingRange() throws Exception {
- testForR8(Backend.CF)
+ testForR8(parameters.getBackend())
.addProgramClasses(TryRangeTest.class)
.addKeepMainRule(TryRangeTest.class)
.setMode(CompilationMode.RELEASE)
@@ -43,14 +58,14 @@
.noTreeShaking()
.enableInliningAnnotations()
.addOptionsModification(o -> o.enableLoadStoreOptimization = false)
- .run(TryRangeTest.class)
+ .run(parameters.getRuntime(), TryRangeTest.class)
.assertSuccessWithOutput(StringUtils.lines("10", "7.0"));
}
@Test
public void testRegisterAllocationLimitLeadingRange() throws Exception {
CodeInspector inspector =
- testForR8(Backend.CF)
+ testForR8(parameters.getBackend())
.addProgramClasses(TryRangeTestLimitRange.class)
.addKeepMainRule(TryRangeTestLimitRange.class)
.setMode(CompilationMode.RELEASE)
@@ -62,7 +77,7 @@
o.enableLoadStoreOptimization = false;
o.testing.irModifier = this::processIR;
})
- .run(TryRangeTestLimitRange.class)
+ .run(parameters.getRuntime(), TryRangeTestLimitRange.class)
.assertSuccessWithOutput("")
.inspector();
// Assert that we do not have any register-modifying instructions in the throwing block:
@@ -80,9 +95,11 @@
index++;
}
assert instructions.get(index + 1) instanceof CfLoad;
- assert instructions.get(index + 2) instanceof CfInvoke;
- assert instructions.get(index + 3) == cfCode.getTryCatchRanges().get(0).end;
- assert instructions.get(index + 4) instanceof CfStackInstruction;
+ assert instructions.get(index + 2) instanceof CfLabel;
+ assert instructions.get(index + 3) instanceof CfPosition;
+ assert instructions.get(index + 4) instanceof CfInvoke;
+ assert instructions.get(index + 5) == cfCode.getTryCatchRanges().get(0).end;
+ assert instructions.get(index + 6) instanceof CfStackInstruction;
}
private void processIR(IRCode code, AppView<?> appView) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
index 0d912b3..fa46f4d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -35,7 +36,9 @@
.assertSuccessWithOutputLines("foo", "hello", "5", "foobar")
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(C.class), isPresent());
+ assertThat(
+ codeInspector.clazz(C.class),
+ isAbsentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
assertThat(codeInspector.clazz(D.class), isPresent());
assertThat(codeInspector.clazz(E.class), isPresent());
});
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java
index 35ae932..002d369 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java
@@ -47,18 +47,11 @@
List<StackTraceLine> stackTraceLines = stacktrace.getStackTraceLines();
assertEquals(1, stackTraceLines.size());
StackTraceLine stackTraceLine = stackTraceLines.get(0);
- // The frame will always have a line as the VM is reporting the PC.
+ // The frame will always have a line, either real or a PC.
assertTrue(stackTraceLine.hasLineNumber());
- if (parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
- // If the compile-time API is before native support then no line info is present.
- // The "line" will be the PC and thus small.
- assertTrue(stackTraceLine.lineNumber < 10);
- } else {
- // If the compile-time API is after native support then the compiler will retain and
- // emit the mapping from PC to original line. Here line 50 is to ensure it is not a
- // low PC value.
- assertTrue(stackTraceLine.lineNumber > 50);
- }
+ // The line should always map back to the original line.
+ // Here line 50 is to ensure it is not a low PC value.
+ assertTrue(stackTraceLine.lineNumber > 50);
});
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoKeepSourceFileAttributeTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoKeepSourceFileAttributeTest.java
index 9a68a09..dcf1db9 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoKeepSourceFileAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoKeepSourceFileAttributeTest.java
@@ -29,14 +29,6 @@
this.parameters = parameters;
}
- public boolean isRuntimeWithPcAsLineNumberSupport() {
- return parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -50,11 +42,9 @@
List<StackTraceLine> stackTraceLines = stacktrace.getStackTraceLines();
assertEquals(1, stackTraceLines.size());
StackTraceLine stackTraceLine = stackTraceLines.get(0);
- if (!isRuntimeWithPcAsLineNumberSupport()) {
+ if (parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
assertEquals("SourceFile", stackTraceLine.fileName);
} else {
- // VMs with native PC support and no debug info print "Unknown Source".
- // TODO(b/146565491): This will need a check for new VMs once fixed.
assertEquals("Unknown Source", stackTraceLine.fileName);
}
})
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
index 29d8d11..7c2fab0 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
@@ -44,7 +44,6 @@
"main(" + fileName + ")");
private final TestParameters parameters;
- private final boolean isAndroidOOrLater;
private final boolean isDalvik;
@Parameterized.Parameters(name = "{0}")
@@ -55,9 +54,6 @@
public LambdaInStacktraceTest(TestParameters parameters) {
this.parameters = parameters;
isDalvik = parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik();
- isAndroidOOrLater =
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0);
}
@Test
@@ -102,11 +98,6 @@
.getApiLevel()
.isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) {
return s.contains("(NULL)");
- } else if (isAndroidOOrLater) {
- // On VMs with native support, no line info results in no source file printing.
- // TODO(b/260384637): Create debug info for such methods to avoid this.
- return s.equals("main(NULL)")
- || (!s.startsWith("main") && s.contains("(SourceFile)"));
} else {
return s.contains("(SourceFile)");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
index f952fa9..142d565 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.membervaluepropagation;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
@@ -42,17 +42,11 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- // TODO(b/339210038): Should always be absent.
- assertThat(
- aClassSubject,
- isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(aClassSubject, isAbsent());
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
assertThat(mainMethodSubject, isPresent());
- // TODO(b/339210038): Should always contain 42.
- assertEquals(
- parameters.canInitNewInstanceUsingSuperclassConstructor(),
- mainMethodSubject.streamInstructions().noneMatch(i -> i.isConstNumber(42)));
+ assertTrue(mainMethodSubject.streamInstructions().anyMatch(i -> i.isConstNumber(42)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("42");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
new file mode 100644
index 0000000..358227d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LiveDefaultFieldValueOfReflectivelyInstantiatedClassTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndDefaultConstructor(BooleanBox.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) throws Exception {
+ Class<?> clazz = System.currentTimeMillis() > 0 ? BooleanBox.class : Object.class;
+ BooleanBox box = (BooleanBox) clazz.getDeclaredConstructor().newInstance();
+ System.out.println(box.value);
+ box.set();
+ }
+ }
+
+ static class BooleanBox {
+
+ boolean value;
+
+ BooleanBox() {}
+
+ @NeverInline
+ void set() {
+ value = true;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
index fca4473..862fbc9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -18,21 +18,20 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FieldInitializedByConstantArgumentMultipleConstructorsTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public FieldInitializedByConstantArgumentMultipleConstructorsTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -51,10 +50,7 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- // TODO(b/280275115): Constructor inlining regresses instance field value analysis.
- assertThat(
- testClassSubject.uniqueMethodWithOriginalName("dead"),
- isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
index a78dbbf..04b2b4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
@@ -222,12 +222,8 @@
FoundMethodSubject foundMethodSubject = method.asFoundMethodSubject();
assertEquals(
stringBuilderTest.stringBuilders, countStringBuilderInits(foundMethodSubject));
- if ((parameters.isCfRuntime()
- || parameters
- .getApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport()))
- && (stringBuilderTest.getMethodName().equals("diamondWithUseTest")
- || stringBuilderTest.getMethodName().equals("intoPhiTest"))) {
+ if (stringBuilderTest.getMethodName().equals("diamondWithUseTest")
+ || stringBuilderTest.getMethodName().equals("intoPhiTest")) {
// We are not doing block suffix optimization in CF and line/pc info prohibits
// sharing.
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithUnusedArgumentAndParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithUnusedArgumentAndParameterAnnotationsTest.java
new file mode 100644
index 0000000..17b34b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithUnusedArgumentAndParameterAnnotationsTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.unusedarguments;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CollisionWithUnusedArgumentAndParameterAnnotationsTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("A.<init>(B))", "a B");
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRuntimeVisibleParameterAnnotations()
+ .addKeepClassAndMembersRules(ParameterAnnotation.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRuntimeVisibleParameterAnnotations()
+ .addKeepClassAndMembersRules(ParameterAnnotation.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.PARAMETER)
+ public @interface ParameterAnnotation {}
+
+ static class B {
+ public String toString() {
+ return "a B";
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+ B b;
+
+ // Unused argument is removed, but then an extra argument is added to avoid the signature
+ // collision.
+ public A(@ParameterAnnotation B b, int unused) {
+ this.b = b;
+ System.out.println("A.<init>(B, int)");
+ }
+
+ public A(@ParameterAnnotation B b) {
+ this.b = b;
+ System.out.println("A.<init>(B))");
+ }
+
+ public String toString() {
+ return b.toString();
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(args.length == 0 ? new A(new B()) : new A(new B(), 0));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithInlineMembersTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithInlineMembersTest.java
new file mode 100644
index 0000000..1163008
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithInlineMembersTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.keepanno.annotations.KeepBinding;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class KeepConjunctiveBindingsWithInlineMembersTest extends KeepAnnoTestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ @Parameter public KeepAnnoParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ SingleTestRunResult<?> result =
+ testForKeepAnno(parameters)
+ .addProgramClasses(getInputClasses())
+ .addKeepMainRule(TestClass.class)
+ .setExcludedOuterClass(getClass())
+ .allowUnusedProguardConfigurationRules()
+ .run(TestClass.class);
+ if (parameters.isReference()) {
+ result.assertSuccessWithOutput(EXPECTED);
+ } else if (parameters.isPG()) {
+ // PG will make the field private and result in access error.
+ result.assertFailureWithErrorThatThrows(IllegalAccessException.class);
+ } else {
+ // R8 will remove the field.
+ result.assertSuccessWithOutput("").inspect(this::checkOutput);
+ }
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isAbsent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldB"), isAbsent());
+ }
+
+ static class A {
+
+ public String fieldA = "Hello, world";
+ public Integer fieldB = 42;
+
+ @KeepEdge(
+ bindings = @KeepBinding(bindingName = "A", classConstant = A.class),
+ consequences = {
+ // The binding on A defines the required structure of A on input, thus the binding will
+ // fail to find the required match when used. (This mirrors -keepclasseswithmembers).
+ // Contrast this with the test in KeepDisjunctiveConsequencesTest.
+ // This test should have equivalent behavior as KeepConjunctiveBindings and shows that
+ // the binding on A is sufficient to impose the two member structure on A.
+ @KeepTarget(classFromBinding = "A"),
+ @KeepTarget(classFromBinding = "A", fieldTypeConstant = String.class),
+ @KeepTarget(classFromBinding = "A", fieldType = "some.NonExistingClass")
+ })
+ public void foo() throws Exception {
+ for (Field field : getClass().getDeclaredFields()) {
+ if (field.getType().equals(String.class)) {
+ System.out.println(field.get(this));
+ }
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithoutHolderTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithoutHolderTest.java
new file mode 100644
index 0000000..cfd59fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepConjunctiveBindingsWithoutHolderTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.keepanno.annotations.KeepBinding;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class KeepConjunctiveBindingsWithoutHolderTest extends KeepAnnoTestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ @Parameter public KeepAnnoParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ SingleTestRunResult<?> result =
+ testForKeepAnno(parameters)
+ .addProgramClasses(getInputClasses())
+ .addKeepMainRule(TestClass.class)
+ .setExcludedOuterClass(getClass())
+ .run(TestClass.class);
+ if (parameters.isNativeR8()) {
+ // R8 native will remove the field.
+ result.assertSuccessWithOutput("").inspect(this::checkOutput);
+ } else {
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isAbsent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldB"), isAbsent());
+ }
+
+ static class A {
+
+ public String fieldA = "Hello, world";
+ public Integer fieldB = 42;
+
+ @KeepEdge(
+ bindings = {
+ @KeepBinding(bindingName = "A", classConstant = A.class),
+ @KeepBinding(
+ bindingName = "StringFields",
+ classFromBinding = "A",
+ fieldTypeConstant = String.class),
+ @KeepBinding(
+ bindingName = "NonMatchingFields",
+ classFromBinding = "A",
+ fieldType = "some.NonExistingClass"),
+ },
+ consequences = {
+ // The bindings on A defines the required structure of A on input, thus the binding will
+ // fail to find the required match when used. Since the consequences do not explicitly
+ // include the class, the extraction cannot use `keepclasseswithmembers` and will
+ // conservatively extract this to a disjunction.
+ @KeepTarget(memberFromBinding = "StringFields"),
+ @KeepTarget(memberFromBinding = "NonMatchingFields")
+ })
+ public void foo() throws Exception {
+ for (Field field : getClass().getDeclaredFields()) {
+ if (field.getType().equals(String.class)) {
+ System.out.println(field.get(this));
+ }
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index c6fa158..3df45e3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -203,7 +203,7 @@
KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName(foo.getFinalName());
assertThat(kmFunction, isPresent());
assertThat(kmFunction, not(isExtensionFunction()));
- assertEquals(foo.getJvmMethodSignatureAsString(), kmFunction.signature().asString());
+ assertEquals(foo.getJvmMethodSignatureAsString(), kmFunction.signature().toString());
ClassSubject impl = inspector.clazz(bClassName);
assertThat(impl, isPresentAndNotRenamed());
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 aa96b5b..70f3091 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
@@ -132,17 +132,17 @@
// Property name is not renamed, due to the kept getter.
assertEquals("name", name.name());
assertNotNull(name.fieldSignature());
- assertEquals(backingField.getJvmFieldSignatureAsString(), name.fieldSignature().asString());
+ assertEquals(backingField.getJvmFieldSignatureAsString(), name.fieldSignature().toString());
assertNotNull(name.getterSignature());
- assertEquals(getterForName.getJvmMethodSignatureAsString(), name.getterSignature().asString());
- assertEquals(name.setterSignature().asString(), "setName(Ljava/lang/String;)V");
+ assertEquals(getterForName.getJvmMethodSignatureAsString(), name.getterSignature().toString());
+ assertEquals(name.setterSignature().toString(), "setName(Ljava/lang/String;)V");
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, isPresent());
assertThat(familyName, not(isExtensionProperty()));
// No backing field for property `familyName`
assertNull(familyName.fieldSignature());
- assertEquals(familyName.getterSignature().asString(), "getFamilyName()Ljava/lang/String;");
+ assertEquals(familyName.getterSignature().toString(), "getFamilyName()Ljava/lang/String;");
assertNull(familyName.setterSignature());
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
@@ -150,9 +150,9 @@
assertThat(age, not(isExtensionProperty()));
assertEquals(
kotlinParameters.isNewerThanOrEqualTo(KOTLINC_1_5_0) ? "b:I" : "a:I",
- age.fieldSignature().asString());
- assertEquals("getAge()I", age.getterSignature().asString());
- assertEquals("setAge(I)V", age.setterSignature().asString());
+ age.fieldSignature().toString());
+ assertEquals("getAge()I", age.getterSignature().toString());
+ assertEquals("setAge(I)V", age.setterSignature().toString());
}
@Test
@@ -244,9 +244,9 @@
// # fieldSignature: name:Ljava/lang/String;,
// # getterSignature: getName()Ljava/lang/String;,
// # setterSignature: setName(Ljava/lang/String;)V,
- assertEquals("name:Ljava/lang/String;", name.fieldSignature().asString());
- assertEquals("getName()Ljava/lang/String;", name.getterSignature().asString());
- assertEquals("setName(Ljava/lang/String;)V", name.setterSignature().asString());
+ assertEquals("name:Ljava/lang/String;", name.fieldSignature().toString());
+ assertEquals("getName()Ljava/lang/String;", name.getterSignature().toString());
+ assertEquals("setName(Ljava/lang/String;)V", name.setterSignature().toString());
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, not(isPresent()));
@@ -257,8 +257,8 @@
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertEquals("age:I", age.fieldSignature().asString());
- assertEquals("getAge()I", age.getterSignature().asString());
- assertEquals("setAge(I)V", age.setterSignature().asString());
+ assertEquals("age:I", age.fieldSignature().toString());
+ assertEquals("getAge()I", age.getterSignature().toString());
+ assertEquals("setAge(I)V", age.setterSignature().toString());
}
}
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 12fe3cb..117ae24 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
@@ -15,6 +15,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinFlagUtils;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -28,9 +29,11 @@
import com.android.tools.r8.utils.codeinspector.KmTypeProjectionSubject;
import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
+import com.google.common.collect.ImmutableMap;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import kotlin.metadata.KmClassifier.TypeParameter;
import kotlin.metadata.KmVariance;
import org.junit.Test;
@@ -42,8 +45,10 @@
private static final String LIB_PKG = PKG + ".typeargument_lib.";
- private static final int FLAG_NONE = 0;
- private static final int FLAG_REIFIED = 1;
+ private static final Map<String, Object> FLAG_NONE =
+ ImmutableMap.of(KotlinFlagUtils.REIFIED_KEY, false);
+ private static final Map<String, Object> FLAG_REIFIED =
+ ImmutableMap.of(KotlinFlagUtils.REIFIED_KEY, true);
private static final String EXPECTED =
StringUtils.lines(
@@ -258,7 +263,11 @@
}
private void inspectTypeParameter(
- KmTypeParameterSubjectMixin subject, String name, int id, int flags, KmVariance variance) {
+ KmTypeParameterSubjectMixin subject,
+ String name,
+ int id,
+ Map<String, Object> flags,
+ KmVariance variance) {
KmTypeParameterSubject typeParameter = subject.kmTypeParameterWithUniqueName(name);
assertThat(typeParameter, isPresent());
assertEquals(id, typeParameter.getId());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
index b36ef5e..c7fef81 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -138,7 +138,7 @@
assertThat(kmClass, isPresent());
KmFunctionSubject staticFun = kmClass.kmFunctionWithUniqueName("staticFun");
assertThat(staticFun, isPresent());
- assertEquals("staticFun(Lkotlin/jvm/functions/Function0;)V", staticFun.signature().asString());
+ assertEquals("staticFun(Lkotlin/jvm/functions/Function0;)V", staticFun.signature().toString());
KmPropertySubject staticProp = kmClass.kmPropertyWithUniqueName("staticProp");
assertThat(staticProp, isPresent());
}
@@ -154,6 +154,6 @@
KmClassSubject kmClass = itfCompanion.getKmClass();
KmFunctionSubject greetKm = kmClass.kmFunctionWithUniqueName("greet");
assertThat(greetKm, isPresent());
- assertEquals("greet(Ljava/lang/String;)V", greetKm.signature().asString());
+ assertEquals("greet(Ljava/lang/String;)V", greetKm.signature().toString());
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentityMappingFileTest.java b/src/test/java/com/android/tools/r8/naming/IdentityMappingFileTest.java
index 6bffb78..b9a007e 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentityMappingFileTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentityMappingFileTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.FileUtils;
@@ -52,6 +53,7 @@
assertThat(mapping, containsString("# pg_map_id: "));
assertThat(mapping, containsString("# pg_map_hash: SHA-256 "));
// Check the mapping is the identity, e.g., only comments and identity entries are defined.
+ int numberOfIdentityMappings = 0;
for (String line : StringUtils.splitLines(mapping)) {
if (line.startsWith("#")) {
continue;
@@ -61,18 +63,21 @@
String left = parts[0];
String right = parts[1];
if (left.equals(right.substring(0, right.length() - 1))) {
+ numberOfIdentityMappings++;
continue;
}
}
fail("Expected comment or identity, got: " + line);
}
+ // It may be ok to not actually include any identity mapping, but currently we do.
+ assertTrue(numberOfIdentityMappings > 0);
}
@Test
public void testTheTestBuilder() throws Exception {
String mapping =
testForR8(Backend.DEX)
- .addProgramClasses(Main.class)
+ .addProgramClassFileData(getMainWithoutLineTable())
.setMinApi(AndroidApiLevel.B)
.addKeepMainRule(Main.class)
.compile()
@@ -85,7 +90,7 @@
Path mappingPath = temp.newFolder().toPath().resolve("mapping.map");
R8.run(
R8Command.builder()
- .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class))
+ .addClassProgramData(getMainWithoutLineTable(), Origin.unknown())
.addProguardConfiguration(
ImmutableList.of(keepMainProguardConfiguration(Main.class)), Origin.unknown())
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
@@ -102,7 +107,7 @@
StringBuilder mappingContent = new StringBuilder();
R8.run(
R8Command.builder()
- .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class))
+ .addClassProgramData(getMainWithoutLineTable(), Origin.unknown())
.addProguardConfiguration(
ImmutableList.of(keepMainProguardConfiguration(Main.class)), Origin.unknown())
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
@@ -124,6 +129,10 @@
checkIdentityMappingContent(mappingContent.toString());
}
+ private byte[] getMainWithoutLineTable() throws Exception {
+ return transformer(Main.class).removeLineNumberTable(MethodPredicate.all()).transform();
+ }
+
// Compiling this program with a keep main will result in an identity mapping for the residual
// program. The (identity) mapping should still be created and emitted to the client.
static class Main {
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
index 04efdac..e45c4ab 100644
--- a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveTest.java
@@ -138,6 +138,7 @@
// as this is a release build.
assertTrue(
(code.getDebugInfo() == null)
+ || code.getDebugInfo().isPcBasedInfo()
|| Arrays.stream(code.getDebugInfo().asEventBasedInfo().events)
.allMatch(event -> !(event instanceof StartLocal)));
}
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
index b0fdc84..940e463 100644
--- a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableSet;
import it.unimi.dsi.fastutil.ints.IntArraySet;
@@ -35,7 +36,9 @@
@Test
public void testNoLinesForNonInline() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(DebuginfoForInlineFrameRegressionTest.class)
+ .addProgramClassFileData(
+ transformer(InlineInto.class).removeLineNumberTable(MethodPredicate.all()).transform(),
+ transformer(InlineFrom.class).removeLineNumberTable(MethodPredicate.all()).transform())
.addKeepMainRule(InlineInto.class)
.addKeepRules("-keepparameternames")
.setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java
new file mode 100644
index 0000000..082d51b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java
@@ -0,0 +1,210 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.interfacetargets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestAppViewBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Regression for b/249859332
+@RunWith(Parameterized.class)
+public class MultipleMaximallySpecificTest extends TestBase {
+
+ private static final String EXPECTED_UPTO_JDK9 = StringUtils.lines("A.foo", "Got ICCE");
+ private static final String EXPECTED_AFTER_JDK9 = StringUtils.lines("A.foo", "Got AME");
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MultipleMaximallySpecificTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testResolution() throws Exception {
+ assumeTrue(parameters.isOrSimulateNoneRuntime());
+ AppView<AppInfoWithLiveness> appView =
+ TestAppViewBuilder.builder()
+ .addProgramClasses(getInputClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
+ .addTestingAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(AndroidApiLevel.LATEST)
+ .buildWithLiveness();
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexMethod fooI = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooJ = buildNullaryVoidMethod(J.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooK = buildNullaryVoidMethod(K.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ DexString fooName = fooA.getName();
+ DexProto fooProto = fooA.getProto();
+
+ // Resolving on A succeeds and is the override placed in A.
+ MethodResolutionResult resolveFooA =
+ appInfo.resolveMethodOnClass(fooA.holder, fooProto, fooName);
+ assertTrue(resolveFooA.isSingleResolution());
+
+ MethodResolutionResult resolveFooB =
+ appInfo.resolveMethodOnClass(fooB.holder, fooProto, fooName);
+ MethodResolutionResult resolveFooK = appInfo.resolveMethodOnInterface(fooK.holder, fooK);
+
+ // TODO(b/249859332): These should not be failed resolution results, but rather multi results.
+ // This is the likely cause of the issue as it may lead to replacing the call-site by ICCE.
+ for (MethodResolutionResult resolution : ImmutableList.of(resolveFooB, resolveFooK)) {
+ assertTrue(resolution.isFailedResolution());
+ Set<DexType> typesCausingFailures = new HashSet<>();
+ Set<DexMethod> methodCausingFailures = new HashSet<>();
+ resolution
+ .asFailedResolution()
+ .forEachFailureDependency(
+ typesCausingFailures::add, m -> methodCausingFailures.add(m.getReference()));
+ assertEquals(ImmutableSet.of(fooI, fooJ), methodCausingFailures);
+ assertEquals(
+ ImmutableSet.of(fooI.getHolderType(), fooJ.getHolderType()), typesCausingFailures);
+
+ // TODO(b/249859332): Why is it possible to do 'lookup' on a failed result, and since
+ // possible,
+ // why are the cause-of-failure methods not present in the lookup result?
+ DexProgramClass context =
+ appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
+ LookupResult lookupResult = resolution.lookupVirtualDispatchTargets(context, appView);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
+ Set<MethodReference> targets = new HashSet<>();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ target -> targets.add(target.getReference().asMethodReference()), lambda -> fail());
+ assertEquals(ImmutableSet.of(), targets);
+ }
+ }
+
+ private boolean isDesugaring() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport());
+ }
+
+ private boolean isNewCfRuntime() {
+ return parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThan(CfVm.JDK9);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(isNewCfRuntime() ? EXPECTED_AFTER_JDK9 : EXPECTED_UPTO_JDK9);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getInputClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/249859332): This should be same as for reference.
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ }
+
+ private Collection<Class<?>> getInputClasses() {
+ return ImmutableList.of(I.class, J.class, A.class, B.class, Main.class);
+ }
+
+ private Collection<byte[]> getTransformedClasses() throws Exception {
+ return ImmutableList.of(transformer(K.class).setImplements(I.class, J.class).transform());
+ }
+
+ @NoVerticalClassMerging
+ public interface I {
+
+ default void foo() {
+ System.out.println("I.foo");
+ }
+ }
+
+ @NoVerticalClassMerging
+ public interface J {
+
+ default void foo() {
+ System.out.println("J.foo");
+ }
+ }
+
+ @NoVerticalClassMerging
+ public interface K extends I /* and J via transformer */ {}
+
+ @NeverClassInline
+ public static class A implements K {
+
+ @Override
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B implements K {}
+
+ public static class Main {
+
+ public static K getK(int x) {
+ return x == 0 ? new A() : new B();
+ }
+
+ public static void main(String[] args) {
+ getK(args.length).foo();
+ try {
+ getK(args.length + 1).foo();
+ } catch (AbstractMethodError e) {
+ System.out.println("Got AME");
+ } catch (IncompatibleClassChangeError e) {
+ System.out.println("Got ICCE");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
index 8258c8b..778c78a 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import org.junit.Test;
@@ -61,16 +60,7 @@
assertEquals(
"SourceFile",
inspector.clazz(getTestClass()).getDexProgramClass().sourceFile.toString());
- // TODO(b/202919530): The stack-trace should have line positions since they are
- // essentially free when compiling for dex.
- if (parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0)) {
- assertThat(
- stacktrace, StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true)));
- } else {
- // Having stripped the line-number table, the raw stack will not have line info.
- assertThat(stacktrace, StackTrace.isSame(getExpectedStackTrace(false)));
- }
+ assertThat(stacktrace, StackTrace.isSame(getExpectedStackTrace(true)));
});
}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
index f37d00a..0b278cf 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
@@ -76,7 +76,6 @@
assertTrue(
clazz.getDexProgramClass().sourceFile == null
|| clazz.getDexProgramClass().sourceFile.toString().equals("SourceFile"));
- assertNull(main.getLineNumberTable());
assertTrue(main.getLocalVariableTable().isEmpty());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
index 5314db5..32c535c 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
@@ -59,7 +58,8 @@
"-keep class ** { *; }"
);
MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
- assertFalse(mainMethod.hasLineNumberTable());
+ // R8 now defaults to retain the line number table regardless of -keepattributes.
+ assertEquals(doesNotHavePcSupport(), mainMethod.hasLineNumberTable());
assertFalse(mainMethod.hasLocalVariableTable());
}
@@ -95,19 +95,12 @@
}
@Test
- public void keepLocalVariableTable() throws IOException, ExecutionException {
+ public void keepLocalVariableTable() throws Exception {
List<String> keepRules = ImmutableList.of(
"-keepattributes " + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
);
- // Compiling with a keep rule for locals but no line results in an error in R8.
- try {
- compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
- } catch (CompilationFailedException e) {
- assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LOCAL_VARIABLE_TABLE));
- assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LINE_NUMBER_TABLE));
- return;
- }
- fail("Expected error");
+ // Compiling with a keep rule for locals but no line table no longer results in an error in R8.
+ compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
}
private MethodSubject compileRunAndGetMain(List<String> keepRules, CompilationMode mode)
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
index 5f68743..9d35920 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-
+import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
@@ -36,6 +36,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MapIdTemplateProvider;
@@ -859,6 +860,18 @@
return self();
}
+ public T addFeatureSplit(Path featureJar) {
+ Path featureOutJar = getState().getNewTempFileUnchecked("feature.zip");
+ builder.addFeatureSplit(
+ builder ->
+ builder
+ .addProgramResourceProvider(ArchiveResourceProvider.fromArchive(featureJar, true))
+ .setProgramConsumer(new ArchiveConsumer(featureOutJar, true))
+ .build());
+ features.add(featureOutJar);
+ return self();
+ }
+
public T addFeatureSplitWithResources(
Collection<Pair<String, String>> nonJavaFiles, Class<?>... classes) throws IOException {
Path path = getState().getNewTempFolder().resolve("feature.zip");
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
index 9b51098..4edcc33 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -239,14 +238,17 @@
throws IOException {
assert getBackend() == runtime.getBackend();
ClassSubject mainClassSubject = inspector().clazz(SplitRunner.class);
- assertThat("Did you forget a keep rule for the main method?", mainClassSubject, Matchers.isPresent());
+ assertThat(
+ "Did you forget a keep rule for the main method?", mainClassSubject, Matchers.isPresent());
assertThat(
"Did you forget a keep rule for the main method?",
mainClassSubject.mainMethod(),
Matchers.isPresent());
ClassSubject mainFeatureClassSubject = featureInspector(feature).clazz(mainFeatureClass);
assertThat(
- "Did you forget a keep rule for the run method?", mainFeatureClassSubject, Matchers.isPresent());
+ "Did you forget a keep rule for the run method?",
+ mainFeatureClassSubject,
+ Matchers.isPresent());
assertThat(
"Did you forget a keep rule for the run method?",
mainFeatureClassSubject.uniqueMethodWithOriginalName("run"),
@@ -280,4 +282,13 @@
FileUtils.writeTextFile(path, getProguardMap());
return self();
}
+
+ @Override
+ public R8TestCompileResult benchmarkCodeSize(BenchmarkResults results)
+ throws IOException, ResourceException {
+ int applicationSizeWithFeatures =
+ AndroidApp.builder(app).addProgramFiles(features).build().applicationSize();
+ results.addCodeSizeResult(applicationSizeWithFeatures);
+ return self();
+ }
}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestState.java b/src/test/testbase/java/com/android/tools/r8/TestState.java
index 6b01a75..77d4a0b 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestState.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestState.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Set;
import java.util.function.BiFunction;
@@ -40,6 +41,14 @@
return getNewTempFolder().resolve(name);
}
+ public Path getNewTempFileUnchecked(String name) {
+ try {
+ return getNewTempFile(name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
public DiagnosticsHandler getDiagnosticsHandler() {
return messages;
}
diff --git a/src/test/testbase/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/testbase/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 8ce018e..9904b9b 100644
--- a/src/test/testbase/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -3,10 +3,8 @@
import static junit.framework.TestCase.fail;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
@@ -92,17 +90,7 @@
builder
.addProgramResourceProvider(ArchiveResourceProvider.fromArchive(featureJar, true))
- .setProgramConsumer(
- new ArchiveConsumer(outputPath, true) {
- @Override
- public void accept(
- int fileIndex,
- ByteDataView data,
- Set<String> descriptors,
- DiagnosticsHandler handler) {
- super.accept(fileIndex, data, descriptors, handler);
- }
- });
+ .setProgramConsumer(new ArchiveConsumer(outputPath, true));
}
public static FeatureSplit splitWithNonJavaFile(
diff --git a/src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java b/src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java
index c95193c..f572d3d 100644
--- a/src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java
+++ b/src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java
@@ -10,6 +10,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.function.Consumer;
public class CompilerDump {
@@ -29,6 +30,18 @@
this.directory = directory;
}
+ public void forEachFeatureArchive(Consumer<? super Path> consumer) {
+ int i = 1;
+ while (true) {
+ Path featureJar = directory.resolve("feature-" + i + ".jar");
+ if (!Files.exists(featureJar)) {
+ break;
+ }
+ consumer.accept(featureJar);
+ i++;
+ }
+ }
+
public Path getProgramArchive() {
return directory.resolve("program.jar");
}
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentKmTypeParameterSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentKmTypeParameterSubject.java
index 87e4427..1023d84 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentKmTypeParameterSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentKmTypeParameterSubject.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.errors.Unreachable;
+import com.google.common.collect.ImmutableMap;
import java.util.List;
+import java.util.Map;
import kotlin.metadata.KmVariance;
public class AbsentKmTypeParameterSubject extends KmTypeParameterSubject {
@@ -31,8 +33,8 @@
}
@Override
- public int getFlags() {
- return 0;
+ public Map<String, Object> getFlags() {
+ return ImmutableMap.of();
}
@Override
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
index 7a6ebc9..21329bf 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
@@ -10,19 +10,13 @@
import java.util.Objects;
import java.util.stream.Collectors;
import kotlin.metadata.KmDeclarationContainer;
-import kotlin.metadata.KmExtensionType;
import kotlin.metadata.KmFunction;
-import kotlin.metadata.KmFunctionExtensionVisitor;
-import kotlin.metadata.KmFunctionVisitor;
import kotlin.metadata.KmProperty;
-import kotlin.metadata.KmPropertyExtensionVisitor;
-import kotlin.metadata.KmPropertyVisitor;
import kotlin.metadata.KmType;
import kotlin.metadata.KmTypeAlias;
+import kotlin.metadata.jvm.JvmExtensionsKt;
import kotlin.metadata.jvm.JvmFieldSignature;
-import kotlin.metadata.jvm.JvmFunctionExtensionVisitor;
import kotlin.metadata.jvm.JvmMethodSignature;
-import kotlin.metadata.jvm.JvmPropertyExtensionVisitor;
public interface FoundKmDeclarationContainerSubject extends KmDeclarationContainerSubject {
@@ -61,21 +55,7 @@
JvmMethodSignature signature = null;
KmFunctionProcessor(KmFunction kmFunction) {
- kmFunction.accept(new KmFunctionVisitor() {
- @Override
- public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) {
- if (type != JvmFunctionExtensionVisitor.TYPE) {
- return null;
- }
- return new JvmFunctionExtensionVisitor() {
- @Override
- public void visit(JvmMethodSignature desc) {
- assert signature == null : signature.asString();
- signature = desc;
- }
- };
- }
- });
+ signature = JvmExtensionsKt.getSignature(kmFunction);
// We don't check Kotlin types in tests, but be aware of the relocation issue.
// See b/70169921#comment57 for more details.
}
@@ -161,29 +141,9 @@
JvmMethodSignature setterSignature = null;
KmPropertyProcessor(KmProperty kmProperty) {
- kmProperty.accept(new KmPropertyVisitor() {
- @Override
- public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
- if (type != JvmPropertyExtensionVisitor.TYPE) {
- return null;
- }
- return new JvmPropertyExtensionVisitor() {
- @Override
- public void visit(
- int flags,
- JvmFieldSignature fieldDesc,
- JvmMethodSignature getterDesc,
- JvmMethodSignature setterDesc) {
- assert fieldSignature == null : fieldSignature.asString();
- fieldSignature = fieldDesc;
- assert getterSignature == null : getterSignature.asString();
- getterSignature = getterDesc;
- assert setterSignature == null : setterSignature.asString();
- setterSignature = setterDesc;
- }
- };
- }
- });
+ fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
+ getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty);
+ setterSignature = JvmExtensionsKt.getSetterSignature(kmProperty);
// We don't check Kotlin types in tests, but be aware of the relocation issue.
// See b/70169921#comment57 for more details.
}
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
index 7640064..c8ce440 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
@@ -1,6 +1,8 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.kotlin.KotlinFlagUtils;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import kotlin.metadata.KmTypeParameter;
import kotlin.metadata.KmVariance;
@@ -36,8 +38,8 @@
}
@Override
- public int getFlags() {
- return kmTypeParameter.getFlags();
+ public Map<String, Object> getFlags() {
+ return KotlinFlagUtils.extractFlags(kmTypeParameter);
}
@Override
@@ -60,7 +62,8 @@
KmTypeParameter other = ((FoundKmTypeParameterSubject) obj).kmTypeParameter;
if (!kmTypeParameter.getName().equals(other.getName())
|| kmTypeParameter.getId() != other.getId()
- || kmTypeParameter.getFlags() != other.getFlags()
+ || !(KotlinFlagUtils.extractFlags(kmTypeParameter)
+ .equals(KotlinFlagUtils.extractFlags(other)))
|| kmTypeParameter.getVariance() != other.getVariance()) {
return false;
}
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeParameterSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeParameterSubject.java
index bc70b90..d484199 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeParameterSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeParameterSubject.java
@@ -5,13 +5,14 @@
package com.android.tools.r8.utils.codeinspector;
import java.util.List;
+import java.util.Map;
import kotlin.metadata.KmVariance;
public abstract class KmTypeParameterSubject extends Subject {
public abstract int getId();
- public abstract int getFlags();
+ public abstract Map<String, Object> getFlags();
public abstract KmVariance getVariance();
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index 0bdeb7e..81828a7 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -6,15 +6,16 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.kotlin.KotlinFlagUtils;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import kotlin.metadata.KmAnnotation;
+import kotlin.metadata.KmClassifier;
+import kotlin.metadata.KmClassifier.TypeAlias;
import kotlin.metadata.KmFlexibleTypeUpperBound;
import kotlin.metadata.KmType;
-import kotlin.metadata.KmTypeVisitor;
import kotlin.metadata.jvm.JvmExtensionsKt;
public class KmTypeSubject extends Subject {
@@ -32,23 +33,17 @@
if (kmType == null) {
return null;
}
- Box<String> descriptor = new Box<>(null);
- kmType.accept(new KmTypeVisitor() {
- @Override
- public void visitClass(String name) {
- // We don't check Kotlin types in tests, but be aware of the relocation issue.
- // See b/70169921#comment25 for more details.
- assert descriptor.get() == null;
- descriptor.set(getDescriptorFromKotlinClassifier(name));
- }
-
- @Override
- public void visitTypeAlias(String name) {
- assert descriptor.get() == null;
- descriptor.set(getDescriptorFromKotlinClassifier(name));
- }
- });
- return descriptor.get();
+ KmClassifier classifier = kmType.getClassifier();
+ if (classifier instanceof KmClassifier.Class) {
+ KmClassifier.Class classClassifier = (KmClassifier.Class) classifier;
+ return getDescriptorFromKotlinClassifier(classClassifier.getName());
+ }
+ if (classifier instanceof KmClassifier.TypeAlias) {
+ TypeAlias typeAliasClassifier = (TypeAlias) classifier;
+ return getDescriptorFromKotlinClassifier(typeAliasClassifier.getName());
+ }
+ // The case KmClassifier.TypeParameter is not implemented (?).
+ return null;
}
public String descriptor() {
@@ -107,7 +102,7 @@
if (one == null || other == null) {
return false;
}
- if (one.getFlags() != other.getFlags()) {
+ if (!KotlinFlagUtils.extractFlags(one).equals(KotlinFlagUtils.extractFlags(other))) {
return false;
}
if (!one.classifier.toString().equals(other.classifier.toString())) {
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
index 2586325..45ffeda 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
@@ -3,9 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
-import static kotlin.metadata.Flag.ValueParameter.DECLARES_DEFAULT_VALUE;
-
import com.android.tools.r8.errors.Unreachable;
+import kotlin.metadata.Attributes;
import kotlin.metadata.KmValueParameter;
public class KmValueParameterSubject extends Subject {
@@ -33,7 +32,7 @@
}
public boolean declaresDefaultValue() {
- return DECLARES_DEFAULT_VALUE.invoke(kmValueParameter.getFlags());
+ return Attributes.getDeclaresDefaultValue(kmValueParameter);
}
@Override
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1
index 362cc92..eacd0fe 100644
--- a/third_party/dependencies.tar.gz.sha1
+++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@
-fd84e5adc03d7770a211a2fcb7978f0339c6c02a
\ No newline at end of file
+69699e487d7f4e51da88351227dd09a2eb3147f1
\ No newline at end of file
diff --git a/third_party/dependencies_plugin.tar.gz.sha1 b/third_party/dependencies_plugin.tar.gz.sha1
index d6ec64f..7a7c496 100644
--- a/third_party/dependencies_plugin.tar.gz.sha1
+++ b/third_party/dependencies_plugin.tar.gz.sha1
@@ -1 +1 @@
-ce80383f6ea9554ad1bc99e9be4b5c040a7f7e09
\ No newline at end of file
+a98c3dc84926e632f3c7bd2599bb1c692c26be45
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1
index 43b6ec0..8151eb0 100644
--- a/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1
@@ -1 +1 @@
-54e1cfb2bd83e005ccd07179958261d5ed2c7102
\ No newline at end of file
+d8cd1490244b1dc97d889b9fabec76d08cef7c0e
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1
index e4fdd09..a850e28 100644
--- a/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1
@@ -1 +1 @@
-158d1e78d2055793960120b1c58654f83cd6d4d3
\ No newline at end of file
+0604c1cbdefc095b510e057720432024ea359dbc
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1
index 5e613de..94883e7 100644
--- a/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1
@@ -1 +1 @@
-0a6e35687efada2890624783e9936047ed10b434
\ No newline at end of file
+49259c45a5fedfd6eaf89c776a84a5398cf27384
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetlagged.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetlagged.tar.gz.sha1
new file mode 100644
index 0000000..1bf9ac9
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetlagged.tar.gz.sha1
@@ -0,0 +1 @@
+c649ac9267753c0e1451b3954d210b8657516660
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1
index 3fb24df..6574b20 100644
--- a/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1
@@ -1 +1 @@
-d1c89d1a22c716d3c9e2c8b7b725bc4716d12ea6
\ No newline at end of file
+0c8d2ca9a1cf572072830a8de6b5226b43ce74e7
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1
index 9562d67..c82ebde 100644
--- a/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1
@@ -1 +1 @@
-2e7d404796f7c4b20f47957fef00755665623526
\ No newline at end of file
+11905a3eb956d99d89259ca84b0e0c47f651dd39
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1
deleted file mode 100644
index 736edd4..0000000
--- a/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0dc41fbe14dbfb3bfc70ed64ff129b311bfcbf94
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1
index d6c218c..86fdccf 100644
--- a/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1
+++ b/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1
@@ -1 +1 @@
-9bb9c1cc3fea6d4ceb33df8e99057caa1bbe94f6
\ No newline at end of file
+be9dbb7f37c872d619ad3a89cbfa6fb4a93cc9fb
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1
deleted file mode 100644
index 3947935..0000000
--- a/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-86f3577a407a3cc3883f2d016d23879f3e8bd64e
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/reply.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/reply.tar.gz.sha1
new file mode 100644
index 0000000..120bf4c
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/reply.tar.gz.sha1
@@ -0,0 +1 @@
+343b9b35f15e8d5952514f91b9934f9e925aa880
\ No newline at end of file
diff --git a/third_party/opensource-apps/chrome.tar.gz.sha1 b/third_party/opensource-apps/chrome.tar.gz.sha1
new file mode 100644
index 0000000..b74ce8a
--- /dev/null
+++ b/third_party/opensource-apps/chrome.tar.gz.sha1
@@ -0,0 +1 @@
+2011d6b4b3394b50c514a9c281845b62b42db078
\ No newline at end of file
diff --git a/third_party/protoc.tar.gz.sha1 b/third_party/protoc.tar.gz.sha1
new file mode 100644
index 0000000..dbc9c9e
--- /dev/null
+++ b/third_party/protoc.tar.gz.sha1
@@ -0,0 +1 @@
+aa0e87a89da6cbbe443db5452725dcb643baa5db
\ No newline at end of file
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index c3f4819..0df88f7 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -24,7 +24,7 @@
ASM_VERSION = '9.7' # When updating update tools/asmifier.py and Toolhelper as well.
ESPRESSO_VERSION = '3.0.0'
FASTUTIL_VERSION = '7.2.1'
-KOTLIN_METADATA_VERSION = '2.0.0-Beta5'
+KOTLIN_METADATA_VERSION = '2.0.0'
KOTLIN_VERSION = '1.9.20'
GUAVA_VERSION = '32.1.2-jre'
GSON_VERSION = '2.10.1'
@@ -35,9 +35,11 @@
ERROR_PRONE_VERSION = '2.18.0'
TESTNG_VERSION = '6.10'
+# keepanno & resource shrinker dependencies
+PROTOBUF_VERSION = '3.19.3'
+
# Resource shrinker dependency versions
AAPT2_PROTO_VERSION = '8.2.0-alpha10-10154469'
-PROTOBUF_VERSION = '3.19.3'
STUDIO_SDK_VERSION = '31.5.0-alpha04'
BUILD_DEPENDENCIES = [
@@ -97,6 +99,7 @@
]
PLUGIN_DEPENDENCIES = [
+ 'com.google.protobuf:protobuf-gradle-plugin:0.9.4',
'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:pom:4.2.1',
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.10',
'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:pom:3.0.1',
diff --git a/tools/gradle.py b/tools/gradle.py
index ef77a5c..688b01f 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -18,6 +18,9 @@
GRADLE8_SHA1 = os.path.join(utils.THIRD_PARTY, 'gradle.tar.gz.sha1')
GRADLE8_TGZ = os.path.join(utils.THIRD_PARTY, 'gradle.tar.gz')
+PROTOC_ROOT = os.path.join(utils.THIRD_PARTY, 'protoc')
+PROTOC_SHA1 = os.path.join(utils.THIRD_PARTY, 'protoc.tar.gz.sha1')
+PROTOC_TGZ = os.path.join(utils.THIRD_PARTY, 'protoc.tar.gz')
def get_gradle():
gradle_dir = os.path.join(utils.THIRD_PARTY, 'gradle')
@@ -80,10 +83,18 @@
jdkSha1 = jdkTgz + '.sha1'
utils.EnsureDepFromGoogleCloudStorage(root, jdkTgz, jdkSha1, root)
+def EnsureProtoc():
+ utils.EnsureDepFromGoogleCloudStorage(
+ PROTOC_ROOT,
+ PROTOC_TGZ,
+ PROTOC_SHA1,
+ 'Proto Compiler')
+
def EnsureDeps():
EnsureGradle()
EnsureJdk()
+ EnsureProtoc()
def RunGradleIn(gradleCmd, args, cwd, throw_on_failure=True, env=None):
diff --git a/tools/perf.py b/tools/perf.py
index 0186435..5d3cb2b 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -11,9 +11,15 @@
import subprocess
import sys
-import upload_benchmark_data_to_google_storage
import utils
+if utils.is_bot():
+ import upload_benchmark_data_to_google_storage
+APPS = [
+ 'ChromeApp', 'CraneApp', 'JetLaggedApp', 'JetNewsApp', 'JetCasterApp',
+ 'JetChatApp', 'JetSnackApp', 'NowInAndroidApp', 'OwlApp', 'ReplyApp',
+ 'TiviApp'
+]
BUCKET = "r8-perf-results"
SAMPLE_BENCHMARK_RESULT_JSON = {
'benchmark_name': '<benchmark_name>',
@@ -23,13 +29,12 @@
}]
}
+
# Result structure on cloud storage
-# gs://bucket/benchmark_results/APP/TARGET/GIT_HASH/results
+# gs://bucket/benchmark_results/APP/TARGET/GIT_HASH/result.json
# meta
# where results simply contains the result lines and
# meta contains information about the execution (machine)
-
-
def ParseOptions():
result = argparse.ArgumentParser()
result.add_argument('--app',
@@ -62,12 +67,30 @@
help='Use R8 hash for the run (default local build)',
default=None)
options, args = result.parse_known_args()
- options.apps = options.app or ['NowInAndroidApp', 'TiviApp']
+ options.apps = options.app or APPS
options.quiet = not options.verbose
del options.app
return options, args
+def Build(options):
+ utils.Print('Building', quiet=options.quiet)
+ build_cmd = GetRunCmd('N/A', options, ['--iterations', '0'])
+ subprocess.check_call(build_cmd)
+
+
+def GetRunCmd(app, options, args):
+ base_cmd = [
+ 'tools/run_benchmark.py', '--benchmark', app, '--target', options.target
+ ]
+ if options.verbose:
+ base_cmd.append('--verbose')
+ if options.version:
+ base_cmd.extend(
+ ['--version', options.version, '--version-jar', r8jar, '--nolib'])
+ return base_cmd + args
+
+
def MergeBenchmarkResultJsonFiles(benchmark_result_json_files):
merged_benchmark_result_json = None
for benchmark_result_json_file in benchmark_result_json_files:
@@ -124,13 +147,14 @@
# --bottom 7486f01e0622cb5935b77a92b59ddf1ca8dbd2e2
def main():
options, args = ParseOptions()
+ Build(options)
+ any_failed = False
with utils.TempDir() as temp:
if options.version:
# Download r8.jar once instead of once per run_benchmark.py invocation.
download_options = argparse.Namespace(no_build=True, nolib=True)
r8jar = compiledump.download_distribution(options.version,
download_options, temp)
-
for app in options.apps:
if options.skip_if_output_exists:
if options.outdir:
@@ -142,36 +166,28 @@
print(f'Skipping run, {output} already exists.')
continue
- base_cmd = [
- 'tools/run_benchmark.py', '--benchmark', app, '--target',
- options.target
- ]
- if options.verbose:
- base_cmd.append('--verbose')
- if options.version:
- base_cmd.extend([
- '--version', options.version, '--version-jar', r8jar,
- '--nolib'
- ])
-
- # Build
- utils.Print(f'Preparing {app}', quiet=options.quiet)
- build_cmd = base_cmd + ['--iterations', '0']
- subprocess.check_output(build_cmd)
-
# Run benchmark.
benchmark_result_json_files = []
+ failed = False
for i in range(options.iterations):
utils.Print(f'Benchmarking {app} ({i+1}/{options.iterations})',
quiet=options.quiet)
benchhmark_result_file = os.path.join(temp, f'result_file_{i}')
- iteration_cmd = base_cmd + [
+ iteration_cmd = GetRunCmd(app, options, [
'--iterations',
str(options.iterations_inner), '--output',
benchhmark_result_file, '--no-build'
- ]
- subprocess.check_output(iteration_cmd)
- benchmark_result_json_files.append(benchhmark_result_file)
+ ])
+ try:
+ subprocess.check_call(iteration_cmd)
+ benchmark_result_json_files.append(benchhmark_result_file)
+ except subprocess.CalledProcessError as e:
+ failed = True
+ any_failed = True
+ break
+
+ if failed:
+ continue
# Merge results and write output.
result_file = os.path.join(temp, 'result_file')
@@ -198,6 +214,9 @@
if utils.is_bot():
upload_benchmark_data_to_google_storage.run()
+ if any_failed:
+ return 1
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/tools/perf/index.html b/tools/perf/index.html
index 8d3089c..ec09f14 100644
--- a/tools/perf/index.html
+++ b/tools/perf/index.html
@@ -5,7 +5,7 @@
<title>R8 perf</title>
</head>
<body>
- <select id="benchmark-selector"></select>
+ <div id="benchmark-selectors"></div>
<div>
<canvas id="myChart"></canvas>
</div>
@@ -54,7 +54,7 @@
// Import and reverse commits so that newest are last.
import commits from "./benchmark_data.json" with { type: "json" };
- commits.reverseInPlace()
+ commits.reverseInPlace();
// Amend the commits with their unique index.
for (var i = 0; i < commits.length; i++) {
@@ -62,125 +62,148 @@
}
// DOM references.
- const benchmarkSelector = document.getElementById('benchmark-selector')
+ const benchmarkSelectors = document.getElementById('benchmark-selectors');
const canvas = document.getElementById('myChart');
const showMoreLeft = document.getElementById('show-more-left');
const showLessLeft = document.getElementById('show-less-left');
const showLessRight = document.getElementById('show-less-right');
const showMoreRight = document.getElementById('show-more-right');
- // Initialize benchmark selector.
+ // Initialize benchmark selectors.
const benchmarks = new Set();
for (const commit of commits.values()) {
for (const benchmark in commit.benchmarks) {
- benchmarks.add(benchmark)
+ benchmarks.add(benchmark);
}
}
- var selectedBenchmark = window.location.hash.substring(1)
- if (!benchmarks.has(selectedBenchmark)) {
- selectedBenchmark = benchmarks.values().next().value;
- }
+ const selectedBenchmarks = new Set();
+ const urlOptions = unescape(window.location.hash.substring(1)).split(',');
for (const benchmark of benchmarks.values()) {
- const opt = document.createElement('option');
- opt.value = benchmark;
- opt.innerHTML = benchmark;
- benchmarkSelector.appendChild(opt);
- if (benchmark == selectedBenchmark) {
- benchmarkSelector.selectedIndex = benchmarkSelector.options.length - 1
- }
- }
- benchmarkSelector.onchange = function(event) {
- selectedBenchmark =
- benchmarkSelector.options[benchmarkSelector.selectedIndex].value;
- updateChart();
- window.location.hash = selectedBenchmark;
- };
-
- // Chart data provider.
- function getData(start = 0, end = commits.length) {
- const filteredCommits =
- commits
- .slice(start, end);
- //.filter(
- // commit =>
- // selectedBenchmark in commit.benchmarks
- // && commit.benchmarks[selectedBenchmark].results.length > 0);
- const labels = filteredCommits.map((c, i) => i);
- const codeSizeData =
- filteredCommits.map(
- (c, i) =>
- selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .first()
- .code_size
- : NaN);
- const codeSizeScatterData = [];
- for (const commit of filteredCommits.values()) {
- if (!(selectedBenchmark in commit.benchmarks)) {
- continue;
- }
- const codeSizes =
- commit.benchmarks[selectedBenchmark].results.map(result => result.code_size)
- const expectedCodeSize = codeSizes.first();
- if (codeSizes.any(codeSize => codeSize != expectedCodeSize)) {
- const seen = new Set();
- seen.add(expectedCodeSize);
- for (const codeSize of codeSizes.values()) {
- if (!seen.has(codeSize)) {
- codeSizeScatterData.push({ x: commit.index, y: codeSize });
- seen.add(codeSize);
- }
+ for (const filter of urlOptions.values()) {
+ if (filter) {
+ const match = benchmark.match(new RegExp(filter.replace("*", ".*")));
+ if (match) {
+ selectedBenchmarks.add(benchmark);
+ break;
}
}
}
- const runtimeData =
- filteredCommits.map(
- (c, i) =>
- selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .map(result => result.runtime)
- .min()
- .ns_to_s()
- : NaN);
- const runtimeScatterData = [];
- for (const commit of filteredCommits.values()) {
- if (!(selectedBenchmark in commit.benchmarks)) {
- continue;
+ }
+ if (selectedBenchmarks.size == 0) {
+ selectedBenchmarks.add(benchmarks.values().next().value);
+ }
+ for (const benchmark of benchmarks.values()) {
+ const input = document.createElement('input');
+ input.type = 'checkbox';
+ input.name = 'benchmark';
+ input.id = benchmark;
+ input.value = benchmark;
+ input.checked = selectedBenchmarks.has(benchmark);
+ input.onchange = function (e) {
+ if (e.target.checked) {
+ selectedBenchmarks.add(e.target.value);
+ } else {
+ selectedBenchmarks.delete(e.target.value);
}
- const runtimes =
- commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
- for (const runtime of runtimes.values()) {
- runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
- }
- }
+ updateChart(true, false);
+ };
- const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
- return {
- labels: labels,
- datasets: [
+ const label = document.createElement('label');
+ label.id = benchmark + 'Label';
+ label.htmlFor = benchmark;
+ label.innerHTML = benchmark;
+
+ benchmarkSelectors.appendChild(input);
+ benchmarkSelectors.appendChild(label);
+ }
+
+ // Chart data provider.
+ function getData() {
+ const filteredCommits = commits.slice(zoom.left, zoom.right);
+ const labels = filteredCommits.map((c, i) => c.index);
+ const datasets = [];
+ for (const selectedBenchmark of selectedBenchmarks.values()) {
+ const codeSizeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .first()
+ .code_size
+ : NaN);
+ const codeSizeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const codeSizes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.code_size)
+ const expectedCodeSize = codeSizes.first();
+ if (codeSizes.any(codeSize => codeSize != expectedCodeSize)) {
+ const seen = new Set();
+ seen.add(expectedCodeSize);
+ for (const codeSize of codeSizes.values()) {
+ if (!seen.has(codeSize)) {
+ codeSizeScatterData.push({ x: commit.index, y: codeSize });
+ seen.add(codeSize);
+ }
+ }
+ }
+ }
+ const runtimeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .map(result => result.runtime)
+ .min()
+ .ns_to_s()
+ : NaN);
+ const runtimeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const runtimes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
+ for (const runtime of runtimes.values()) {
+ runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
+ }
+ }
+
+ const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
+ datasets.push(...[
{
+ benchmark: selectedBenchmark,
type: 'line',
label: 'Code size',
data: codeSizeData,
tension: 0.1,
segment: {
borderColor: ctx =>
- skipped(ctx, 'rgba(54, 162, 235, 0.5)'),
+ skipped(
+ ctx,
+ myChart
+ ? myChart.data.datasets[ctx.datasetIndex].backgroundColor
+ : undefined),
borderDash: ctx => skipped(ctx, [6, 6]),
},
spanGaps: true
},
{
+ benchmark: selectedBenchmark,
type: 'scatter',
label: 'Nondeterminism',
data: codeSizeScatterData,
+ radius: 6,
pointBackgroundColor: 'red'
},
{
+ benchmark: selectedBenchmark,
type: 'line',
label: 'Runtime',
data: runtimeData,
@@ -188,34 +211,77 @@
yAxisID: 'y2',
segment: {
borderColor: ctx =>
- skipped(ctx, 'rgba(255, 160, 64, 0.5)'),
+ skipped(
+ ctx,
+ myChart
+ ? myChart.data.datasets[ctx.datasetIndex].backgroundColor
+ : undefined),
borderDash: ctx => skipped(ctx, [6, 6]),
},
spanGaps: true
},
{
+ benchmark: selectedBenchmark,
type: 'scatter',
label: 'Runtime variance',
data: runtimeScatterData,
yAxisID: 'y2'
}
- ],
+ ]);
+ }
+
+ return {
+ labels: labels,
+ datasets: datasets,
};
}
+ // Legend tracking.
+ const legends =
+ new Set(['Code size', 'Nondeterminism', 'Runtime', 'Runtime variance']);
+ const selectedLegends =
+ new Set(
+ unescape(window.location.hash.substring(1))
+ .split(',')
+ .filter(l => legends.has(l)));
+ if (selectedLegends.size == 0) {
+ legends.forEach(l => selectedLegends.add(l));
+ }
+
// Chart options.
const options = {
onHover: (event, chartElement) =>
event.native.target.style.cursor =
chartElement[0] ? 'pointer' : 'default',
plugins: {
+ legend: {
+ labels: {
+ filter: function(legendItem, data) {
+ // Only retain the legends for the first selected benchmark. If
+ // multiple benchmarks are selected, then use the legends of the
+ // first selected benchmark to control all selected benchmarks.
+ const numUniqueLegends =
+ data.datasets.length / selectedBenchmarks.size;
+ return legendItem.datasetIndex < numUniqueLegends;
+ },
+ },
+ onClick: function legendClickHandler(e, legendItem, legend) {
+ const clickedLegend = legendItem.text;
+ if (selectedLegends.has(clickedLegend)) {
+ selectedLegends.delete(clickedLegend);
+ } else {
+ selectedLegends.add(clickedLegend);
+ }
+ updateChart(false, true);
+ },
+ },
tooltip: {
callbacks: {
title: (context) => {
const elementInfo = context[0];
var commit;
if (elementInfo.dataset.type == 'line') {
- commit = commits[elementInfo.dataIndex];
+ commit = commits[zoom.left + elementInfo.dataIndex];
} else {
console.assert(elementInfo.dataset.type == 'scatter');
commit = commits[elementInfo.raw.x];
@@ -226,14 +292,17 @@
const elementInfo = context[0];
var commit;
if (elementInfo.dataset.type == 'line') {
- commit = commits[elementInfo.dataIndex];
+ commit = commits[zoom.left + elementInfo.dataIndex];
} else {
console.assert(elementInfo.dataset.type == 'scatter');
commit = commits[elementInfo.raw.x];
}
- return `Author: ${commit.author}\n`
+ const dataset = myChart.data.datasets[elementInfo.datasetIndex];
+ return `App: ${dataset.benchmark}\n`
+ + `Author: ${commit.author}\n`
+ `Submitted: ${new Date(commit.submitted * 1000).toLocaleString()}\n`
- + `Hash: ${commit.hash}`;
+ + `Hash: ${commit.hash}\n`
+ + `Index: ${commit.index}`;
}
}
}
@@ -258,12 +327,6 @@
}
};
- // Create chart.
- const myChart = new Chart(canvas, {
- data: getData(),
- options: options
- });
-
// Setup click handler.
canvas.onclick = function (event) {
const points =
@@ -277,60 +340,140 @@
};
// Setup chart navigation.
- var left = 0;
- var right = commits.length;
+ var zoom = { left: 0, right: commits.length };
+ for (const urlOption of urlOptions.values()) {
+ if (urlOption.startsWith('L')) {
+ var left = parseInt(urlOption.substring(1));
+ if (isNaN(left)) {
+ continue;
+ }
+ left = left >= 0 ? left : commits.length + left;
+ if (left < 0) {
+ zoom.left = 0;
+ } else if (left >= commits.length) {
+ zoom.left = commits.length - 1;
+ } else {
+ zoom.left = left;
+ }
+ }
+ }
showMoreLeft.onclick = function (event) {
- if (left == 0) {
+ if (zoom.left == 0) {
return;
}
- const currentSize = right - left;
- left = left - currentSize;
- if (left < 0) {
- left = 0;
+ const currentSize = zoom.right - zoom.left;
+ zoom.left = zoom.left - currentSize;
+ if (zoom.left < 0) {
+ zoom.left = 0;
}
- updateChart();
+ updateChart(true, false);
};
showLessLeft.onclick = function (event) {
- const currentSize = right - left;
- left = left + Math.floor(currentSize / 2);
- if (left >= right) {
- left = right - 1;
+ const currentSize = zoom.right - zoom.left;
+ zoom.left = zoom.left + Math.floor(currentSize / 2);
+ if (zoom.left >= zoom.right) {
+ zoom.left = zoom.right - 1;
}
- updateChart();
+ updateChart(true, false);
};
showLessRight.onclick = function (event) {
- if (right == 0) {
+ if (zoom.right == 0) {
return;
}
- const currentSize = right - left;
- right = right - Math.floor(currentSize / 2);
- if (right < left) {
- right = left;
+ const currentSize = zoom.right - zoom.left;
+ zoom.right = zoom.right - Math.floor(currentSize / 2);
+ if (zoom.right < zoom.left) {
+ zoom.right = zoom.left;
}
- updateChart();
+ updateChart(true, false);
};
showMoreRight.onclick = function (event) {
- const currentSize = right - left;
- right = right + currentSize;
- if (right > commits.length) {
- right = commits.length;
+ const currentSize = zoom.right - zoom.left;
+ zoom.right = zoom.right + currentSize;
+ if (zoom.right > commits.length) {
+ zoom.right = commits.length;
}
- updateChart();
+ updateChart(true, false);
};
- function updateChart() {
- console.assert(left <= right);
- const newData = getData(left, right);
- Object.assign(myChart.data, newData);
- myChart.update();
- showMoreLeft.disabled = left == 0;
- showLessLeft.disabled = left == right - 1;
- showLessRight.disabled = left == right - 1;
- showMoreRight.disabled = right == commits.length;
+ function updateChart(dataChanged, legendsChanged) {
+ console.assert(zoom.left <= zoom.right);
+
+ // Update datasets.
+ if (dataChanged) {
+ const newData = getData();
+ Object.assign(myChart.data, newData);
+ // Update chart.
+ myChart.update();
+ }
+
+ // Update legends.
+ if (legendsChanged || (dataChanged && selectedLegends.size < legends.size)) {
+ for (var datasetIndex = 0;
+ datasetIndex < myChart.data.datasets.length;
+ datasetIndex++) {
+ const datasetMeta = myChart.getDatasetMeta(datasetIndex);
+ datasetMeta.hidden = !selectedLegends.has(datasetMeta.label);
+ }
+ // Update chart.
+ myChart.update();
+ }
+
+
+ // Update checkbox colors.
+ const benchmarkColors = {};
+ for (var datasetIndex = 0;
+ datasetIndex < myChart.data.datasets.length;
+ datasetIndex++) {
+ if (myChart.getDatasetMeta(datasetIndex).hidden) {
+ continue;
+ }
+ const dataset = myChart.data.datasets[datasetIndex];
+ const benchmark = dataset.benchmark;
+ const benchmarkColor = dataset.borderColor;
+ if (!(benchmark in benchmarkColors)) {
+ benchmarkColors[benchmark] = benchmarkColor;
+ }
+ }
+ for (const benchmark of benchmarks.values()) {
+ const benchmarkLabel = document.getElementById(benchmark + 'Label');
+ const benchmarkColor = benchmarkColors[benchmark] || '#000000';
+ const benchmarkFontWeight = benchmark in benchmarkColors ? 'bold' : 'normal';
+ benchmarkLabel.style.color = benchmarkColor;
+ benchmarkLabel.style.fontWeight = benchmarkFontWeight;
+ }
+
+ // Update navigation.
+ showMoreLeft.disabled = zoom.left == 0;
+ showLessLeft.disabled = zoom.left == zoom.right - 1;
+ showLessRight.disabled = zoom.left == zoom.right - 1;
+ showMoreRight.disabled = zoom.right == commits.length;
+
+ // Update hash.
+ window.location.hash =
+ Array.from(selectedBenchmarks)
+ .concat(
+ selectedLegends.size == legends.size
+ ? []
+ : Array.from(selectedLegends))
+ .join(',');
+ }
+
+ // Create chart.
+ const myChart = new Chart(canvas, {
+ data: getData(),
+ options: options
+ });
+
+ // Hide disabled legends.
+ if (selectedLegends.size < legends.size) {
+ updateChart(false, true);
+ } else {
+ updateChart(false, false);
}
</script>
</body>
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index bf50da3..d6226be 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -386,101 +386,10 @@
'url': 'https://github.com/wikimedia/apps-android-wikipedia',
'revision': '0fa7cad843c66313be8e25790ef084cf1a1fa67e',
'folder': 'wikipedia',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'androidx.compose.samples.crane',
- 'name': 'compose-crane',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/crane',
- 'golem_duration': 240
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.jetcaster',
- 'name': 'compose-jetcaster',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/jetcaster',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.compose.jetchat',
- 'name': 'compose-jetchat',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/jetchat',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.jetnews',
- 'name': 'compose-jetnews',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/jetnews',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.jetsnack',
- 'name': 'compose-jetsnack',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/jetsnack',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.compose.jetsurvey',
- 'name': 'compose-jetsurvey',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/jetsurvey',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.owl',
- 'name': 'compose-owl',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/owl',
- }),
- # TODO(b/173167253): Check if monkey testing works.
- App({
- 'id': 'com.example.compose.rally',
- 'name': 'compose-rally',
- 'collections': ['compose-samples'],
- 'dump_app': 'dump_app.zip',
- 'apk_app': 'app-release-unsigned.apk',
- 'url': 'https://github.com/android/compose-samples',
- 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
- 'folder': 'android/compose-samples/rally',
- }),
+ })
]
-APP_COLLECTIONS = [AppCollection({
- 'name': 'compose-samples',
-})]
+APP_COLLECTIONS = []
def remove_print_lines(file):
diff --git a/tools/upload_benchmark_data_to_google_storage.py b/tools/upload_benchmark_data_to_google_storage.py
index 4fb9444..f293c2c 100755
--- a/tools/upload_benchmark_data_to_google_storage.py
+++ b/tools/upload_benchmark_data_to_google_storage.py
@@ -12,9 +12,9 @@
import sys
-APPS = ['NowInAndroidApp', 'TiviApp']
+APPS = perf.APPS
TARGETS = ['r8-full']
-NUM_COMMITS = 250
+NUM_COMMITS = 1000
INDEX_HTML = os.path.join(utils.TOOLS_DIR, 'perf/index.html')
@@ -92,9 +92,9 @@
# Write output files to public bucket.
perf.ArchiveOutputFile(benchmark_data_file,
- 'perf/benchmark_data.json',
+ 'benchmark_data.json',
header='Cache-Control:no-store')
- perf.ArchiveOutputFile(INDEX_HTML, 'perf/index.html')
+ perf.ArchiveOutputFile(INDEX_HTML, 'index.html')
def main():