Merge commit 'a6d9f379e79bd2f7182718afa6d4d076b30330cf' into dev-release
diff --git a/build.gradle b/build.gradle
index 1fd0955..d3b511c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -838,7 +838,7 @@
// Execute r8 commands against a stable r8 with relocated dependencies.
// TODO(b/139725780): See if we can remove or lower the heap size (-Xmx6g).
return [org.gradle.internal.jvm.Jvm.current().getJavaExecutable(),
- "-Xmx6g", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
+ "-Xmx8g", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
}
def r8CfCommandLine(input, output, pgConfs = [], args = ["--release"], libs = []) {
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 2915114..7dd1027 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -10,10 +11,28 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
-public class BaseCompilerCommandParser {
+public class BaseCompilerCommandParser<
+ C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>> {
- static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+ static final Iterable<String> ASSERTIONS_USAGE_MESSAGE =
+ Arrays.asList(
+ " --force-enable-assertions[:[<class name>|<package name>...]]",
+ " --force-ea[:[<class name>|<package name>...]]",
+ " # Forcefully enable javac generated assertion code.",
+ " --force-disable-assertions[:[<class name>|<package name>...]]",
+ " --force-da[:[<class name>|<package name>...]]",
+ " # Forcefully disable javac generated assertion code. This",
+ " # is the default handling of javac assertion code when",
+ " # generating DEX file format.",
+ " --force-passthrough-assertions[:[<class name>|<package name>...]]",
+ " --force-pa[:[<class name>|<package name>...]]",
+ " # Don't change javac generated assertion code. This",
+ " # is the default handling of javac assertion code when",
+ " # generating class file format.");
+
+ void parseMinApi(B builder, String minApiString, Origin origin) {
int minApi;
try {
minApi = Integer.parseInt(minApiString);
@@ -28,6 +47,81 @@
builder.setMinApiLevel(minApi);
}
+ private static String PACAKGE_ASSERTION_POSTFIX = "...";
+
+ private void addAssertionTransformation(
+ B builder, AssertionTransformation transformation, String scope) {
+ if (scope == null) {
+ builder.addAssertionsConfiguration(
+ b -> b.setTransformation(transformation).setScopeAll().build());
+ } else {
+ assert scope.length() > 0;
+ if (scope.endsWith(PACAKGE_ASSERTION_POSTFIX)) {
+ builder.addAssertionsConfiguration(
+ b ->
+ b.setTransformation(transformation)
+ .setScopePackage(
+ scope.substring(0, scope.length() - PACAKGE_ASSERTION_POSTFIX.length()))
+ .build());
+ } else {
+ builder.addAssertionsConfiguration(
+ b -> b.setTransformation(transformation).setScopeClass(scope).build());
+ }
+ }
+ }
+
+ boolean tryParseAssertionArgument(B builder, String arg, Origin origin) {
+ String FORCE_ENABLE_ASSERTIONS = "--force-enable-assertions";
+ String FORCE_EA = "--force-ea";
+ String FORCE_DISABLE_ASSERTIONS = "--force-disable-assertions";
+ String FORCE_DA = "--force-da";
+ String FORCE_PASSTHROUGH_ASSERTIONS = "--force-passthrough-assertions";
+ String FORCE_PA = "--force-pa";
+
+ AssertionTransformation transformation = null;
+ String remaining = null;
+ if (arg.startsWith(FORCE_ENABLE_ASSERTIONS)) {
+ transformation = AssertionTransformation.ENABLE;
+ remaining = arg.substring(FORCE_ENABLE_ASSERTIONS.length());
+ } else if (arg.startsWith(FORCE_EA)) {
+ transformation = AssertionTransformation.ENABLE;
+ remaining = arg.substring(FORCE_EA.length());
+ } else if (arg.startsWith(FORCE_DISABLE_ASSERTIONS)) {
+ transformation = AssertionTransformation.DISABLE;
+ remaining = arg.substring(FORCE_DISABLE_ASSERTIONS.length());
+ } else if (arg.startsWith(FORCE_DA)) {
+ transformation = AssertionTransformation.DISABLE;
+ remaining = arg.substring(FORCE_DA.length());
+ } else if (arg.startsWith(FORCE_PASSTHROUGH_ASSERTIONS)) {
+ transformation = AssertionTransformation.PASSTHROUGH;
+ remaining = arg.substring(FORCE_PASSTHROUGH_ASSERTIONS.length());
+ } else if (arg.startsWith(FORCE_PA)) {
+ transformation = AssertionTransformation.PASSTHROUGH;
+ remaining = arg.substring(FORCE_PA.length());
+ }
+ if (transformation != null) {
+ if (remaining.length() == 0) {
+ addAssertionTransformation(builder, transformation, null);
+ return true;
+ } else {
+ if (remaining.length() == 1 || remaining.charAt(0) != ':') {
+ return false;
+ }
+ String classOrPackageScope = remaining.substring(1);
+ if (classOrPackageScope.contains(";")
+ || classOrPackageScope.contains("[")
+ || classOrPackageScope.contains("/")) {
+ builder.error(
+ new StringDiagnostic("Illegal assertion scope: " + classOrPackageScope, origin));
+ }
+ addAssertionTransformation(builder, transformation, remaining.substring(1));
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
/**
* This method must match the lookup in
* {@link com.android.tools.r8.JdkClassFileProvider#fromJdkHome}.
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 1099cf9..fda08a6 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.file.Files;
@@ -21,7 +22,7 @@
import java.util.List;
import java.util.Set;
-public class D8CommandParser extends BaseCompilerCommandParser {
+public class D8CommandParser extends BaseCompilerCommandParser<D8Command, D8Command.Builder> {
private static final Set<String> OPTIONS_WITH_PARAMETER =
ImmutableSet.of(
@@ -108,31 +109,33 @@
static final String USAGE_MESSAGE =
String.join(
"\n",
- Arrays.asList(
- "Usage: d8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --debug # Compile with debugging information (default).",
- " --release # Compile without debugging information.",
- " --output <file> # Output result in <outfile>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
- " --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
- + AndroidApiLevel.getDefault().getLevel()
- + ".",
- " --intermediate # Compile an intermediate result intended for later",
- " # merging.",
- " --file-per-class # Produce a separate dex file per input class",
- " --no-desugaring # Force disable desugaring.",
- " --desugared-lib <file> # Specify desugared library configuration.",
- " # <file> is a desugared library configuration (json).",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --main-dex-list-output <file>",
- " # Output resulting main dex list in <file>.",
- " --version # Print the version of d8.",
- " --help # Print this message."));
-
+ Iterables.concat(
+ Arrays.asList(
+ "Usage: d8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
+ " --classpath <file> # Add <file> as a classpath resource.",
+ " --min-api <number> # Minimum Android API level compatibility, default: "
+ + AndroidApiLevel.getDefault().getLevel()
+ + ".",
+ " --intermediate # Compile an intermediate result intended for later",
+ " # merging.",
+ " --file-per-class # Produce a separate dex file per input class",
+ " --no-desugaring # Force disable desugaring.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --main-dex-list-output <file>",
+ " # Output resulting main dex list in <file>."),
+ ASSERTIONS_USAGE_MESSAGE,
+ Arrays.asList(
+ " --version # Print the version of d8.",
+ " --help # Print this message.")));
/**
* Parse the D8 command-line.
*
@@ -143,7 +146,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static D8Command.Builder parse(String[] args, Origin origin) {
- return parse(args, origin, D8Command.builder());
+ return new D8CommandParser().parse(args, origin, D8Command.builder());
}
/**
@@ -157,10 +160,10 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, D8Command.builder(handler));
+ return new D8CommandParser().parse(args, origin, D8Command.builder(handler));
}
- private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+ private D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
CompilationMode compilationMode = null;
Path outputPath = null;
OutputMode outputMode = null;
@@ -250,11 +253,12 @@
builder.setDisableDesugaring(true);
} else if (arg.equals("--desugared-lib")) {
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
- } else {
- if (arg.startsWith("--")) {
+ } else if (arg.startsWith("--")) {
+ if (!tryParseAssertionArgument(builder, arg, origin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
continue;
}
+ } else {
builder.addProgramFiles(Paths.get(arg));
}
}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1ddd338..d190d6c 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -82,6 +81,7 @@
int minApiLevel,
Reporter diagnosticsHandler,
DesugaredLibraryConfiguration libraryConfiguration,
+ List<AssertionsConfiguration> assertionsConfiguration,
DexItemFactory factory) {
super(
inputApp,
@@ -94,7 +94,7 @@
false,
false,
(name, checksum) -> true,
- ImmutableList.of());
+ assertionsConfiguration);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.libraryConfiguration = libraryConfiguration;
@@ -315,6 +315,7 @@
getMinApiLevel(),
getReporter(),
libraryConfiguration,
+ getAssertionsConfiguration(),
factory);
}
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 1dc83c1..bf35b67 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -10,12 +10,13 @@
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
-public class L8CommandParser extends BaseCompilerCommandParser {
+public class L8CommandParser extends BaseCompilerCommandParser<L8Command, L8Command.Builder> {
private static final Set<String> OPTIONS_WITH_PARAMETER =
ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
@@ -32,23 +33,27 @@
static final String USAGE_MESSAGE =
String.join(
"\n",
- Arrays.asList(
- "Usage: l8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --debug # Compile with debugging information (default).",
- " --release # Compile without debugging information.",
- " --output <file> # Output result in <outfile>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
- + AndroidApiLevel.getDefault().getLevel()
- + ".",
- " --pg-conf <file> # Proguard configuration <file>.",
- " --desugared-lib <file> # Specify desugared library configuration.",
- " # <file> is a desugared library configuration (json).",
- " --version # Print the version of l8.",
- " --help # Print this message."));
+ Iterables.concat(
+ Arrays.asList(
+ "Usage: l8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
+ " --min-api <number> # Minimum Android API level compatibility, default: "
+ + AndroidApiLevel.getDefault().getLevel()
+ + ".",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration"
+ + " (json)."),
+ ASSERTIONS_USAGE_MESSAGE,
+ Arrays.asList(
+ " --version # Print the version of l8.",
+ " --help # Print this message.")));
/**
* Parse the D8 command-line.
@@ -60,7 +65,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static L8Command.Builder parse(String[] args, Origin origin) {
- return parse(args, origin, L8Command.builder());
+ return new L8CommandParser().parse(args, origin, L8Command.builder());
}
/**
@@ -74,10 +79,10 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static L8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, L8Command.builder(handler));
+ return new L8CommandParser().parse(args, origin, L8Command.builder(handler));
}
- private static L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
+ private L8Command.Builder parse(String[] args, Origin origin, L8Command.Builder builder) {
CompilationMode compilationMode = null;
Path outputPath = null;
OutputMode outputMode = null;
@@ -139,11 +144,12 @@
builder.addProguardConfigurationFiles(Paths.get(nextArg));
} else if (arg.equals("--desugared-lib")) {
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
- } else {
- if (arg.startsWith("--")) {
+ } else if (arg.startsWith("--")) {
+ if (!tryParseAssertionArgument(builder, arg, origin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
continue;
}
+ } else {
builder.addProgramFiles(Paths.get(arg));
}
}
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index dd9ab8e..f1541bd 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -8,12 +8,13 @@
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
-public class R8CommandParser extends BaseCompilerCommandParser {
+public class R8CommandParser extends BaseCompilerCommandParser<R8Command, R8Command.Builder> {
private static final Set<String> OPTIONS_WITH_PARAMETER =
ImmutableSet.of(
@@ -49,36 +50,40 @@
static final String USAGE_MESSAGE =
String.join(
"\n",
- Arrays.asList(
- "Usage: r8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --release # Compile without debugging information (default).",
- " --debug # Compile with debugging information.",
- " --dex # Compile program to DEX file format (default).",
- " --classfile # Compile program to Java classfile format.",
- " --output <file> # Output result in <file>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
- " --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
- + AndroidApiLevel.getDefault().getLevel()
- + ".",
- " --pg-conf <file> # Proguard configuration <file>.",
- " --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
- " --desugared-lib <file> # Specify desugared library configuration.",
- " # <file> is a desugared library configuration (json).",
- " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
- " --no-minification # Force disable minification of names.",
- " --no-data-resources # Ignore all data resources.",
- " --no-desugaring # Force disable desugaring.",
- " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
- " # primary dex file.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --main-dex-list-output <file> ",
- " # Output the full main-dex list in <file>.",
- " --version # Print the version of r8.",
- " --help # Print this message."));
+ Iterables.concat(
+ Arrays.asList(
+ "Usage: r8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --release # Compile without debugging information (default).",
+ " --debug # Compile with debugging information.",
+ " --dex # Compile program to DEX file format (default).",
+ " --classfile # Compile program to Java classfile format.",
+ " --output <file> # Output result in <file>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
+ " --classpath <file> # Add <file> as a classpath resource.",
+ " --min-api <number> # Minimum Android API level compatibility, default: "
+ + AndroidApiLevel.getDefault().getLevel()
+ + ".",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --pg-map-output <file> # Output the resulting name and line mapping to"
+ + " <file>.",
+ " --desugared-lib <file> # Specify desugared library configuration.",
+ " # <file> is a desugared library configuration (json).",
+ " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
+ " --no-minification # Force disable minification of names.",
+ " --no-data-resources # Ignore all data resources.",
+ " --no-desugaring # Force disable desugaring.",
+ " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+ " # primary dex file.",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --main-dex-list-output <file> ",
+ " # Output the full main-dex list in <file>."),
+ ASSERTIONS_USAGE_MESSAGE,
+ Arrays.asList(
+ " --version # Print the version of r8.",
+ " --help # Print this message.")));
/**
* Parse the R8 command-line.
*
@@ -213,10 +218,12 @@
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else if (arg.equals("--no-data-resources")) {
state.includeDataResources = false;
- } else {
- if (arg.startsWith("--")) {
+ } else if (arg.startsWith("--")) {
+ if (!tryParseAssertionArgument(builder, arg, argsOrigin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+ continue;
}
+ } else {
builder.addProgramFiles(Paths.get(arg));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 77545740..815e721 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -250,7 +250,7 @@
if (function != null) {
// Found a compatible function that is likely asked to keep.
builder.add(method);
- } else if (!method.isKotlinProperty(properties)) {
+ } else if (!method.isKotlinProperty(properties, appView)) {
// This could be a newly merged method that is not part of properties.
builder.add(method);
}
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 a928529..d7dc501 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -137,7 +137,7 @@
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.BOTTOM;
- private int classFileVersion = -1;
+ private int classFileVersion;
private DexEncodedMethod defaultInterfaceMethodImplementation = null;
@@ -151,6 +151,18 @@
// Any newly added `public` method should check if `this` instance is obsolete.
private boolean obsolete = false;
+ // This flag indicates if the method has been synthesized by D8/R8. Such method do not require
+ // a proguard mapping file entry. This flag is different from the synthesized access flag. When a
+ // non synthesized method is inlined into a synthesized method, the method no longer has the
+ // synthesized access flag, but the d8R8Synthesized flag is still there. Methods can also have
+ // the synthesized access flag prior to D8/R8 compilation, in which case d8R8Synthesized is not
+ // set.
+ private final boolean d8R8Synthesized;
+
+ public boolean isD8R8Synthesized() {
+ return d8R8Synthesized;
+ }
+
private void checkIfObsolete() {
assert !obsolete;
}
@@ -200,26 +212,49 @@
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code) {
+ this(method, accessFlags, annotations, parameterAnnotationsList, code, -1);
+ }
+
+ public DexEncodedMethod(
+ DexMethod method,
+ MethodAccessFlags accessFlags,
+ DexAnnotationSet annotations,
+ ParameterAnnotationsList parameterAnnotationsList,
+ Code code,
+ int classFileVersion) {
+ this(method, accessFlags, annotations, parameterAnnotationsList, code, classFileVersion, false);
+ }
+
+ public DexEncodedMethod(
+ DexMethod method,
+ MethodAccessFlags accessFlags,
+ DexAnnotationSet annotations,
+ ParameterAnnotationsList parameterAnnotationsList,
+ Code code,
+ boolean d8R8Synthesized) {
+ this(method, accessFlags, annotations, parameterAnnotationsList, code, -1, d8R8Synthesized);
+ }
+
+ public DexEncodedMethod(
+ DexMethod method,
+ MethodAccessFlags accessFlags,
+ DexAnnotationSet annotations,
+ ParameterAnnotationsList parameterAnnotationsList,
+ Code code,
+ int classFileVersion,
+ boolean d8R8Synthesized) {
this.method = method;
this.accessFlags = accessFlags;
this.annotations = annotations;
this.parameterAnnotationsList = parameterAnnotationsList;
this.code = code;
+ this.classFileVersion = classFileVersion;
+ this.d8R8Synthesized = d8R8Synthesized;
+
assert code == null || !shouldNotHaveCode();
assert parameterAnnotationsList != null;
}
- public DexEncodedMethod(
- DexMethod method,
- MethodAccessFlags flags,
- DexAnnotationSet annotationSet,
- ParameterAnnotationsList annotationsList,
- Code code,
- int classFileVersion) {
- this(method, flags, annotationSet, annotationsList, code);
- this.classFileVersion = classFileVersion;
- }
-
public OptionalBool isLibraryMethodOverride() {
return isNonPrivateVirtualMethod() ? isLibraryMethodOverride : OptionalBool.FALSE;
}
@@ -398,35 +433,20 @@
return null;
}
- // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`.
- // TODO(b/70169921): Handle different name patterns via @JvmName.
- boolean isKotlinProperty(List<KmProperty> properties) {
- // TODO(b/70169921): Avoid decoding.
- String methodName = method.name.toString();
- if (!methodName.startsWith("get")
- && !methodName.startsWith("set")
- && !methodName.endsWith("$annotations")) {
- return false;
+ boolean isKotlinProperty(List<KmProperty> properties, AppView<?> appView) {
+ return findCompatibleKotlinProperty(properties, appView) != null;
+ }
+
+ KmProperty findCompatibleKotlinProperty(List<KmProperty> properties, AppView<?> appView) {
+ if (isStaticMember()) {
+ return null;
}
for (KmProperty property : properties) {
- String propertyName = property.getName();
- assert propertyName.length() > 0;
- String annotations = propertyName + "$annotations";
- if (methodName.equals(annotations)) {
- return true;
- }
- String capitalized =
- Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
- String getter = "get" + capitalized;
- if (methodName.equals(getter)) {
- return true;
- }
- String setter = "set" + capitalized;
- if (methodName.equals(setter)) {
- return true;
+ if (KotlinMetadataSynthesizer.isCompatibleProperty(property, this, appView)) {
+ return property;
}
}
- return false;
+ return null;
}
public boolean isOnlyInlinedIntoNestMembers() {
@@ -924,23 +944,11 @@
return builder.build();
}
- public DexEncodedMethod toRenamedMethod(DexString name, DexItemFactory factory) {
- checkIfObsolete();
- if (method.name == name) {
- return this;
- }
- DexMethod newMethod = factory.createMethod(method.holder, method.proto, name);
- Builder builder = builder(this);
- builder.setMethod(newMethod);
- setObsolete();
- return builder.build();
- }
-
public DexEncodedMethod toInitializerForwardingBridge(DexClass holder, DexMethod newMethod) {
assert accessFlags.isPrivate()
: "Expected to create bridge for private constructor as part of nest-based access"
+ " desugaring";
- Builder builder = builder(this);
+ Builder builder = syntheticBuilder(this);
builder.setMethod(newMethod);
ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
ForwardMethodSourceCode.builder(newMethod);
@@ -988,7 +996,12 @@
}
});
return new DexEncodedMethod(
- newMethod, accessFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+ newMethod,
+ accessFlags,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
}
public DexEncodedMethod toRenamedHolderMethod(DexType newHolderType, DexItemFactory factory) {
@@ -1012,13 +1025,18 @@
interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
.generateCfCode();
return new DexEncodedMethod(
- newMethod, accessFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+ newMethod,
+ accessFlags,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
}
public DexEncodedMethod toStaticForwardingBridge(DexClass holder, DexMethod newMethod) {
assert accessFlags.isPrivate()
: "Expected to create bridge for private method as part of nest-based access desugaring";
- Builder builder = builder(this);
+ Builder builder = syntheticBuilder(this);
builder.setMethod(newMethod);
ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
ForwardMethodSourceCode.builder(newMethod);
@@ -1056,7 +1074,7 @@
DexMethod newMethod =
definitions.dexItemFactory().createMethod(holder.type, method.proto, method.name);
Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
- Builder builder = builder(this);
+ Builder builder = syntheticBuilder(this);
builder.setMethod(newMethod);
if (accessFlags.isAbstract()) {
// If the forwarding target is abstract, we can just create an abstract method. While it
@@ -1113,7 +1131,8 @@
newFlags,
target.annotations,
target.parameterAnnotationsList,
- new SynthesizedCode(forwardSourceCodeBuilder::build));
+ new SynthesizedCode(forwardSourceCodeBuilder::build),
+ true);
}
public DexEncodedMethod toStaticMethodWithoutThis() {
@@ -1247,6 +1266,10 @@
}
}
+ private static Builder syntheticBuilder(DexEncodedMethod from) {
+ return new Builder(from, true);
+ }
+
private static Builder builder(DexEncodedMethod from) {
return new Builder(from);
}
@@ -1261,8 +1284,13 @@
private CompilationState compilationState;
private MethodOptimizationInfo optimizationInfo;
private final int classFileVersion;
+ private boolean d8R8Synthesized;
private Builder(DexEncodedMethod from) {
+ this(from, from.d8R8Synthesized);
+ }
+
+ private Builder(DexEncodedMethod from, boolean d8R8Synthesized) {
// Copy all the mutable state of a DexEncodedMethod here.
method = from.method;
accessFlags = from.accessFlags.copy();
@@ -1271,6 +1299,7 @@
compilationState = from.compilationState;
optimizationInfo = from.optimizationInfo.mutableCopy();
classFileVersion = from.classFileVersion;
+ this.d8R8Synthesized = d8R8Synthesized;
if (from.parameterAnnotationsList.isEmpty()
|| from.parameterAnnotationsList.size() == method.proto.parameters.size()) {
@@ -1356,7 +1385,13 @@
|| parameterAnnotations.size() == method.proto.parameters.size();
DexEncodedMethod result =
new DexEncodedMethod(
- method, accessFlags, annotations, parameterAnnotations, code, classFileVersion);
+ method,
+ accessFlags,
+ annotations,
+ parameterAnnotations,
+ code,
+ classFileVersion,
+ d8R8Synthesized);
result.compilationState = compilationState;
result.optimizationInfo = optimizationInfo;
return result;
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 1fc834d..09a9055 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -719,10 +719,8 @@
: "Unable to map field `" + field.field.toSourceString() + "` back to original program";
}
for (DexEncodedMethod method : clazz.methods()) {
- if (method.accessFlags.isSynthetic()) {
- // This could be a bridge that has been inserted, for example, as a result of member
- // rebinding. Consider only skipping the check below for methods that have been
- // synthesized by R8.
+ if (method.isD8R8Synthesized()) {
+ // Methods synthesized by D8/R8 may not be mapped.
continue;
}
DexMethod originalMethod = getOriginalMethodSignature(method.method);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index d71b495..5203cb9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -281,7 +281,7 @@
}
}
for (Phi phi : trivials) {
- phi.removeTrivialPhi();
+ phi.removeTrivialPhi(null, affectedValues);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b3a6d66..6ef43a6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -219,7 +219,7 @@
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
: new InterfaceMethodRewriter(appView, this);
- this.lambdaRewriter = new LambdaRewriter(appView, this);
+ this.lambdaRewriter = new LambdaRewriter(appView);
this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView);
this.twrCloseResourceRewriter = null;
this.lambdaMerger = null;
@@ -244,7 +244,7 @@
this.methodOptimizationInfoCollector = null;
return;
}
- this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView, this) : null;
+ this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView) : null;
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled()
? new InterfaceMethodRewriter(appView, this)
@@ -402,8 +402,9 @@
private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
if (lambdaRewriter != null) {
- lambdaRewriter.adjustAccessibility();
- lambdaRewriter.synthesizeLambdaClasses(builder, executorService);
+ lambdaRewriter.adjustAccessibility(this);
+ lambdaRewriter.synthesizeLambdaClasses(builder);
+ lambdaRewriter.optimizeSynthesizedClasses(this, executorService);
}
}
@@ -814,7 +815,7 @@
if (lambdaRewriter != null) {
lambdaRewriter.synthesizeLambdaClassesForWave(
- wave, executorService, delayedOptimizationFeedback, lensCodeRewriter);
+ wave, executorService, delayedOptimizationFeedback, lensCodeRewriter, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 8f33a5e..8c632a2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -128,12 +128,14 @@
// Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
// infinite loops. We do direct resolution. This is a very uncommon case.
- if (invoke.isInvokeSuper()) {
+ if (invoke.isInvokeSuper()
+ && rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
DexEncodedMethod dexEncodedMethod =
appView
.appInfo()
.lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
- if (!dexEncodedMethod.isFinal()) { // Final methods can be rewritten as a normal invoke.
+ // Final methods can be rewritten as a normal invoke.
+ if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
DexMethod retargetMethod =
appView
.options()
@@ -212,7 +214,7 @@
Code code = provider.generateTemplateMethod(appView.options(), method);
DexEncodedMethod dexEncodedMethod =
new DexEncodedMethod(
- method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+ method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
boolean addToMainDexList =
referencingClasses.stream()
.anyMatch(clazz -> appView.appInfo().isInMainDexList(clazz.type));
@@ -390,7 +392,7 @@
factory.createMethod(
interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
return new DexEncodedMethod(
- newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null);
+ newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
}
private DexEncodedMethod generateHolderDispatchMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 565af2f..0204455 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -365,7 +365,8 @@
new SynthesizedCode(
callerPosition ->
new ExceptionThrowingSourceCode(
- clazz.type, method, callerPosition, dexItemFactory.icceType)));
+ clazz.type, method, callerPosition, dexItemFactory.icceType)),
+ true);
addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index e13f26f..a0fb1c8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -164,7 +164,8 @@
newAccessFlags,
method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
- new SynthesizedCode(forwardSourceCodeBuilder::build));
+ new SynthesizedCode(forwardSourceCodeBuilder::build),
+ true);
// Optimize to generate DexCode instead of SynthesizedCode.
converter.optimizeSynthesizedMethod(newVirtualMethod);
return newVirtualMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index e3e7ab0..bf48a16 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -403,7 +403,8 @@
newFlags,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- code);
+ code,
+ true);
}
private List<DexEncodedMethod> allImplementedMethods(DexClass libraryClass) {
@@ -474,7 +475,8 @@
accessFlags,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- code);
+ code,
+ true);
}
// Wrapper finalization section.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 9599834..ccb0f3c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -94,7 +94,7 @@
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
DexEncodedMethod implMethod = new DexEncodedMethod(
- companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code);
+ companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code, true);
virtual.setDefaultInterfaceMethodImplementation(implMethod);
companionMethods.add(implMethod);
graphLensBuilder.move(virtual.method, implMethod.method);
@@ -128,7 +128,7 @@
+ "either be public or private in " + iface.origin;
DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
companionMethods.add(new DexEncodedMethod(companionMethod, newFlags,
- direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
+ direct.annotations, direct.parameterAnnotationsList, direct.getCode(),true));
graphLensBuilder.move(oldMethod, companionMethod);
} else {
if (originalFlags.isPrivate()) {
@@ -147,7 +147,7 @@
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
companionMethods.add(new DexEncodedMethod(companionMethod,
- newFlags, direct.annotations, direct.parameterAnnotationsList, code));
+ newFlags, direct.annotations, direct.parameterAnnotationsList, code,true));
graphLensBuilder.move(oldMethod, companionMethod);
} else {
// Since there are no interface constructors at this point,
@@ -247,7 +247,8 @@
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new SynthesizedCode(forwardSourceCodeBuilder::build));
+ new SynthesizedCode(forwardSourceCodeBuilder::build),
+ true);
newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
dispatchMethods.add(newEncodedMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index ce77849..18edf5f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.InternalOptions;
@@ -83,7 +84,7 @@
this.type = lambdaClassType;
this.descriptor = descriptor;
- DexItemFactory factory = rewriter.factory;
+ DexItemFactory factory = rewriter.getFactory();
DexProto constructorProto = factory.createProto(
factory.voidType, descriptor.captures.values);
this.constructor = factory.createMethod(
@@ -103,6 +104,11 @@
// Generate unique lambda class type for lambda descriptor and instantiation point context.
static DexType createLambdaClassType(
LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
+ return createLambdaClassType(rewriter.getFactory(), accessedFrom, match);
+ }
+
+ public static DexType createLambdaClassType(
+ DexItemFactory factory, DexType accessedFrom, LambdaDescriptor match) {
StringBuilder lambdaClassDescriptor = new StringBuilder("L");
// We always create lambda class in the same package where it is referenced.
@@ -123,7 +129,7 @@
// Add unique lambda descriptor id
lambdaClassDescriptor.append(match.uniqueId).append(';');
- return rewriter.factory.createType(lambdaClassDescriptor.toString());
+ return factory.createType(lambdaClassDescriptor.toString());
}
final DexProgramClass getOrCreateLambdaClass() {
@@ -132,7 +138,7 @@
private DexProgramClass synthesizeLambdaClass() {
DexMethod mainMethod =
- rewriter.factory.createMethod(type, descriptor.erasedProto, descriptor.name);
+ rewriter.getFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
DexProgramClass clazz =
new DexProgramClass(
@@ -143,9 +149,9 @@
// classloader (package private access is not allowed across classloaders, b/72538146).
ClassAccessFlags.fromDexAccessFlags(
Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
- rewriter.factory.objectType,
+ rewriter.getFactory().objectType,
buildInterfaces(),
- rewriter.factory.createString("lambda"),
+ rewriter.getFactory().createString("lambda"),
null,
Collections.emptyList(),
null,
@@ -155,9 +161,9 @@
synthesizeInstanceFields(),
synthesizeDirectMethods(),
synthesizeVirtualMethods(mainMethod),
- rewriter.factory.getSkipNameValidationForTesting(),
+ rewriter.getFactory().getSkipNameValidationForTesting(),
LambdaClass::computeChecksumForSynthesizedClass);
- rewriter.converter.appView.appInfo().addSynthesizedClass(clazz);
+ rewriter.getAppInfo().addSynthesizedClass(clazz);
// The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
// ModificationException we must use synchronization.
@@ -186,8 +192,12 @@
}
final DexField getCaptureField(int index) {
- return rewriter.factory.createField(this.type,
- descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
+ return rewriter
+ .getFactory()
+ .createField(
+ this.type,
+ descriptor.captures.values[index],
+ rewriter.getFactory().createString("f$" + index));
}
final boolean isStateless() {
@@ -218,11 +228,13 @@
Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaMainMethodSynthesizedCode(this, mainMethod));
+ new LambdaMainMethodSynthesizedCode(this, mainMethod),
+ true);
// Synthesize bridge methods.
for (DexProto bridgeProto : descriptor.bridges) {
- DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name);
+ DexMethod bridgeMethod =
+ rewriter.getFactory().createMethod(type, bridgeProto, descriptor.name);
methods[index++] =
new DexEncodedMethod(
bridgeMethod,
@@ -234,7 +246,8 @@
false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod));
+ new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod),
+ true);
}
return methods;
}
@@ -254,7 +267,8 @@
true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaConstructorSynthesizedCode(this));
+ new LambdaConstructorSynthesizedCode(this),
+ true);
// Class constructor for stateless lambda classes.
if (stateless) {
@@ -265,7 +279,8 @@
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaClassConstructorSynthesizedCode(this));
+ new LambdaClassConstructorSynthesizedCode(this),
+ true);
}
return methods;
}
@@ -349,10 +364,6 @@
assert implMethod.holder == accessedFrom;
assert descriptor.targetFoundInClass(accessedFrom);
assert descriptor.getAccessibility() != null;
- // When coming from javac these are also private, but we don't assert that, as the
- // accessibility could have been modified (e.g. due to -allowaccessmodification).
-
- assert descriptor.getAccessibility().isSynthetic();
if (implHandle.type.isInvokeStatic()) {
return new StaticLambdaImplTarget();
@@ -363,26 +374,30 @@
// If the lambda$ method is an instance-private method on an interface we convert it into a
// public static method as it will be placed on the companion class.
if (implHandle.type.isInvokeDirect()
- && rewriter.converter.appView.definitionFor(implMethod.holder).isInterface()) {
+ && rewriter.getAppView().definitionFor(implMethod.holder).isInterface()) {
DexProto implProto = implMethod.proto;
DexType[] implParams = implProto.parameters.values;
DexType[] newParams = new DexType[implParams.length + 1];
newParams[0] = implMethod.holder;
System.arraycopy(implParams, 0, newParams, 1, implParams.length);
- DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams);
+ DexProto newProto = rewriter.getFactory().createProto(implProto.returnType, newParams);
return new InterfaceLambdaImplTarget(
- rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name));
+ rewriter.getFactory().createMethod(implMethod.holder, newProto, implMethod.name));
} else {
// Otherwise we need to ensure the method can be reached publicly by virtual dispatch.
// To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual
// we add the method-holder name as suffix to the lambda-method name.
return new InstanceLambdaImplTarget(
- rewriter.factory.createMethod(
- implMethod.holder,
- implMethod.proto,
- rewriter.factory.createString(
- implMethod.name.toString() + "$" + implMethod.holder.getName())));
+ rewriter
+ .getFactory()
+ .createMethod(
+ implMethod.holder,
+ implMethod.proto,
+ rewriter
+ .getFactory()
+ .createString(
+ implMethod.name.toString() + "$" + implMethod.holder.getName())));
}
}
@@ -408,9 +423,12 @@
DexType[] accessorParams = new DexType[1 + implParams.length];
accessorParams[0] = descriptor.getImplReceiverType();
System.arraycopy(implParams, 0, accessorParams, 1, implParams.length);
- DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams);
- DexMethod accessorMethod = rewriter.factory.createMethod(
- accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+ DexProto accessorProto =
+ rewriter.getFactory().createProto(implProto.returnType, accessorParams);
+ DexMethod accessorMethod =
+ rewriter
+ .getFactory()
+ .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
return new ClassMethodWithAccessorTarget(accessorMethod);
}
@@ -427,8 +445,13 @@
// We need to generate an accessor method in `accessedFrom` class/interface
// for accessing the original static impl-method. The accessor method will be
// static, package private with exactly same signature and the original method.
- DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
- descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName());
+ DexMethod accessorMethod =
+ rewriter
+ .getFactory()
+ .createMethod(
+ accessedFrom,
+ descriptor.implHandle.asMethod().proto,
+ generateUniqueLambdaMethodName());
return new ClassMethodWithAccessorTarget(accessorMethod);
}
@@ -449,10 +472,12 @@
// and return the newly created instance.
DexMethod implMethod = implHandle.asMethod();
DexType returnType = implMethod.holder;
- DexProto accessorProto = rewriter.factory.createProto(
- returnType, implMethod.proto.parameters.values);
- DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
- accessorProto, generateUniqueLambdaMethodName());
+ DexProto accessorProto =
+ rewriter.getFactory().createProto(returnType, implMethod.proto.parameters.values);
+ DexMethod accessorMethod =
+ rewriter
+ .getFactory()
+ .createMethod(accessedFrom, accessorProto, generateUniqueLambdaMethodName());
return new ClassMethodWithAccessorTarget(accessorMethod);
}
@@ -464,8 +489,9 @@
}
private DexString generateUniqueLambdaMethodName() {
- return rewriter.factory.createString(
- LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
+ return rewriter
+ .getFactory()
+ .createString(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
}
// Represents information about the method lambda class need to delegate the call to. It may
@@ -484,18 +510,18 @@
}
// Ensure access of the referenced symbol(s).
- abstract void ensureAccessibility();
+ abstract void ensureAccessibility(IRConverter converter);
DexClass definitionFor(DexType type) {
- return rewriter.converter.appView.appInfo().app().definitionFor(type);
+ return rewriter.getAppInfo().app().definitionFor(type);
}
DexProgramClass programDefinitionFor(DexType type) {
- return rewriter.converter.appView.appInfo().app().programDefinitionFor(type);
+ return rewriter.getAppInfo().app().programDefinitionFor(type);
}
boolean holderIsInterface() {
- InternalOptions options = rewriter.converter.appView.options();
+ InternalOptions options = rewriter.getAppView().options();
if (!options.isGeneratingClassFiles()) {
// When generating dex the value of this flag on invokes does not matter (unused).
// We cannot know if definitionFor(implMethod.holder) is null or not in that case,
@@ -527,7 +553,7 @@
}
@Override
- void ensureAccessibility() {}
+ void ensureAccessibility(IRConverter converter) {}
}
// Used for static private lambda$ methods. Only needs access relaxation.
@@ -538,7 +564,7 @@
}
@Override
- void ensureAccessibility() {
+ void ensureAccessibility(IRConverter converter) {
// We already found the static method to be called, just relax its accessibility.
assert descriptor.getAccessibility() != null;
descriptor.getAccessibility().unsetPrivate();
@@ -558,7 +584,7 @@
}
@Override
- void ensureAccessibility() {
+ void ensureAccessibility(IRConverter converter) {
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -582,12 +608,13 @@
newAccessFlags,
encodedMethod.annotations,
encodedMethod.parameterAnnotationsList,
- encodedMethod.getCode());
+ encodedMethod.getCode(),
+ true);
newMethod.copyMetadata(encodedMethod);
rewriter.methodMapping.put(encodedMethod.method, callTarget);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- newMethod.getCode(), callTarget.getArity(), rewriter.converter.appView);
+ newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
implMethodHolder.setDirectMethod(i, newMethod);
return;
}
@@ -605,7 +632,7 @@
}
@Override
- void ensureAccessibility() {
+ void ensureAccessibility(IRConverter converter) {
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -626,7 +653,8 @@
newAccessFlags,
encodedMethod.annotations,
encodedMethod.parameterAnnotationsList,
- encodedMethod.getCode());
+ encodedMethod.getCode(),
+ true);
newMethod.copyMetadata(encodedMethod);
rewriter.methodMapping.put(encodedMethod.method, callTarget);
// Move the method from the direct methods to the virtual methods set.
@@ -647,7 +675,7 @@
}
@Override
- void ensureAccessibility() {
+ void ensureAccessibility(IRConverter converter) {
// Create a static accessor with proper accessibility.
DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
assert accessorClass != null;
@@ -666,14 +694,15 @@
ParameterAnnotationsList.empty(),
new SynthesizedCode(
callerPosition ->
- new AccessorMethodSourceCode(LambdaClass.this, callerPosition)));
+ new AccessorMethodSourceCode(LambdaClass.this, callerPosition)),
+ true);
// We may arrive here concurrently so we need must update the methods of the class atomically.
synchronized (accessorClass) {
accessorClass.appendDirectMethod(accessorEncodedMethod);
}
- rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod);
+ converter.optimizeSynthesizedMethod(accessorEncodedMethod);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index ed49900..92d04f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -66,8 +66,6 @@
private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
private final AppView<?> appView;
- final IRConverter converter;
- final DexItemFactory factory;
final DexMethod objectInitMethod;
@@ -93,24 +91,34 @@
return clazz.getName().startsWith(LAMBDA_CLASS_NAME_PREFIX);
}
- public LambdaRewriter(AppView<?> appView, IRConverter converter) {
- assert converter != null;
+ public LambdaRewriter(AppView<?> appView) {
this.appView = appView;
- this.converter = converter;
- this.factory = appView.dexItemFactory();
+ this.constructorName = getFactory().createString(Constants.INSTANCE_INITIALIZER_NAME);
+ DexProto initProto = getFactory().createProto(getFactory().voidType);
+ this.objectInitMethod =
+ getFactory().createMethod(getFactory().objectType, initProto, constructorName);
+ this.classConstructorName = getFactory().createString(Constants.CLASS_INITIALIZER_NAME);
+ this.instanceFieldName = getFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
+ }
- this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
- DexProto initProto = factory.createProto(factory.voidType);
- this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
- this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
- this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
+ public AppView<?> getAppView() {
+ return appView;
+ }
+
+ public AppInfo getAppInfo() {
+ return getAppView().appInfo();
+ }
+
+ public DexItemFactory getFactory() {
+ return getAppView().dexItemFactory();
}
public void synthesizeLambdaClassesForWave(
Collection<DexEncodedMethod> wave,
ExecutorService executorService,
OptimizationFeedbackDelayed feedback,
- LensCodeRewriter lensCodeRewriter)
+ LensCodeRewriter lensCodeRewriter,
+ IRConverter converter)
throws ExecutionException {
Set<DexProgramClass> synthesizedLambdaClasses = Sets.newIdentityHashSet();
for (DexEncodedMethod method : wave) {
@@ -133,7 +141,7 @@
}
}
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ AppView<AppInfoWithLiveness> appViewWithLiveness = getAppView().withLiveness();
appViewWithLiveness.setAppInfo(
appViewWithLiveness.appInfo().withStaticFieldWrites(writesWithContexts));
@@ -160,7 +168,7 @@
// application (and, in particular, the class hierarchy) during wave processing.
code.registerCodeReferences(
method,
- new DefaultUseRegistry(appView.dexItemFactory()) {
+ new DefaultUseRegistry(getFactory()) {
@Override
public void registerCallSite(DexCallSite callSite) {
@@ -199,7 +207,7 @@
// We have a descriptor, get the lambda class. In D8, we synthesize the lambda classes
// during IR processing, and therefore we may need to create it now.
LambdaClass lambdaClass =
- appView.enableWholeProgramOptimizations()
+ getAppView().enableWholeProgramOptimizations()
? getKnownLambdaClass(descriptor, currentType)
: getOrCreateLambdaClass(descriptor, currentType);
assert lambdaClass != null;
@@ -212,7 +220,7 @@
}
}
if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
+ new TypeAnalysis(getAppView()).narrowing(affectedValues);
}
assert code.isConsistentSSA();
}
@@ -227,7 +235,7 @@
for (int i = 0; i < methodCount; i++) {
DexEncodedMethod encoded = directMethods.get(i);
DexMethod method = encoded.method;
- if (method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
+ if (method.isLambdaDeserializeMethod(getFactory())) {
assert encoded.accessFlags.isStatic();
assert encoded.accessFlags.isSynthetic();
clazz.removeDirectMethod(i);
@@ -242,17 +250,18 @@
}
/** Adjust accessibility of referenced application symbols or creates necessary accessors. */
- public void adjustAccessibility() {
+ public void adjustAccessibility(IRConverter converter) {
// For each lambda class perform necessary adjustment of the
// referenced symbols to make them accessible. This can result in
// method access relaxation or creation of accessor method.
for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
// This call may cause methodMapping to be updated.
- lambdaClass.target.ensureAccessibility();
+ lambdaClass.target.ensureAccessibility(converter);
}
- if (appView.enableWholeProgramOptimizations() && !methodMapping.isEmpty()) {
- appView.setGraphLense(
- new LambdaRewriterGraphLense(methodMapping, appView.graphLense(), factory));
+ if (getAppView().enableWholeProgramOptimizations() && !methodMapping.isEmpty()) {
+ getAppView()
+ .setGraphLense(
+ new LambdaRewriterGraphLense(methodMapping, getAppView().graphLense(), getFactory()));
}
}
@@ -266,14 +275,16 @@
}
/** Generates lambda classes and adds them to the builder. */
- public void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
- AppInfo appInfo = appView.appInfo();
+ public void synthesizeLambdaClasses(Builder<?> builder) {
for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
DexProgramClass synthesizedClass = lambdaClass.getOrCreateLambdaClass();
- appInfo.addSynthesizedClass(synthesizedClass);
+ getAppInfo().addSynthesizedClass(synthesizedClass);
builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
}
+ }
+
+ public void optimizeSynthesizedClasses(IRConverter converter, ExecutorService executorService)
+ throws ExecutionException {
converter.optimizeSynthesizedClasses(
knownLambdaClasses.values().stream()
.map(LambdaClass::getOrCreateLambdaClass)
@@ -299,12 +310,11 @@
LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
return descriptor != null
? descriptor
- : putIfAbsent(
- knownCallSites, callSite, LambdaDescriptor.infer(callSite, appView.appInfo()));
+ : putIfAbsent(knownCallSites, callSite, LambdaDescriptor.infer(callSite, getAppInfo()));
}
private boolean isInMainDexList(DexType type) {
- return appView.appInfo().isInMainDexList(type);
+ return getAppInfo().isInMainDexList(type);
}
// Returns a lambda class corresponding to the lambda descriptor and context,
@@ -319,11 +329,11 @@
knownLambdaClasses,
lambdaClassType,
new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
- if (appView.options().isDesugaredLibraryCompilation()) {
- DexType rewrittenType = appView.rewritePrefix.rewrittenType(accessedFrom);
+ if (getAppView().options().isDesugaredLibraryCompilation()) {
+ DexType rewrittenType = getAppView().rewritePrefix.rewrittenType(accessedFrom);
if (rewrittenType == null) {
rewrittenType =
- appView
+ getAppView()
.options()
.desugaredLibraryConfiguration
.getEmulateLibraryInterface()
@@ -334,7 +344,7 @@
}
}
}
- lambdaClass.addSynthesizedFrom(appView.definitionFor(accessedFrom).asProgramClass());
+ lambdaClass.addSynthesizedFrom(getAppView().definitionFor(accessedFrom).asProgramClass());
if (isInMainDexList(accessedFrom)) {
lambdaClass.addToMainDexList.set(true);
}
@@ -353,11 +363,14 @@
String rewrittenString = rewritten.toString();
String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
assert javaName.startsWith(actualPrefix);
- appView.rewritePrefix.rewriteType(
- lambdaClassType,
- factory.createType(
- DescriptorUtils.javaTypeToDescriptor(
- actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
+ getAppView()
+ .rewritePrefix
+ .rewriteType(
+ lambdaClassType,
+ getFactory()
+ .createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
}
private static <K, V> V getKnown(Map<K, V> map, K key) {
@@ -397,7 +410,8 @@
// The out value might be empty in case it was optimized out.
lambdaInstanceValue =
code.createValue(
- TypeLatticeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
+ TypeLatticeElement.fromDexType(
+ lambdaClass.type, Nullability.maybeNull(), getAppView()));
} else {
affectedValues.add(lambdaInstanceValue);
}
@@ -450,6 +464,6 @@
BasicBlock currentBlock = newInstance.getBlock();
BasicBlock nextBlock = instructions.split(code, blocks);
assert !instructions.hasNext();
- nextBlock.copyCatchHandlers(code, blocks, currentBlock, appView.options());
+ nextBlock.copyCatchHandlers(code, blocks, currentBlock, getAppView().options());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
index 2565687..7e50b88 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
@@ -21,7 +21,7 @@
}
final DexItemFactory dexItemFactory() {
- return lambda.rewriter.factory;
+ return lambda.rewriter.getFactory();
}
final LambdaDescriptor descriptor() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index cf66050..4c2a36d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -40,7 +40,7 @@
public abstract class NestBasedAccessDesugaring {
// Short names to avoid creating long strings
- private static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
+ public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
NEST_ACCESS_NAME_PREFIX + "sm";
@@ -69,6 +69,7 @@
}
DexType getNestConstructorType() {
+ assert nestConstructor != null;
return nestConstructor.type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
index 786b23c..7677cee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -40,7 +40,7 @@
}
final DexItemFactory factory() {
- return lambda.rewriter.factory;
+ return lambda.rewriter.getFactory();
}
final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 817fdb2..17250cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -134,7 +134,7 @@
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod,
- flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+ flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
// Create utility class.
DexProgramClass utilityClass =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 6de35d0..ab3274d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -971,12 +971,13 @@
classInitializationAnalysis.notifyCodeHasChanged();
strategy.updateTypeInformationIfNeeded(inlinee.code, blockIterator, block);
- // TODO(b/146114533): Fix inlining in synthetic methods.
- // If we inlined the invoke from a bridge method, it is no longer a bridge method.
- if (context.accessFlags.isBridge()) {
- context.accessFlags.unsetSynthetic();
+ // The synthetic and bridge flags are maintained only if the inlinee has also these flags.
+ if (context.accessFlags.isBridge() && !inlinee.code.method.accessFlags.isBridge()) {
context.accessFlags.unsetBridge();
}
+ if (context.accessFlags.isSynthetic() && !inlinee.code.method.accessFlags.isSynthetic()) {
+ context.accessFlags.unsetSynthetic();
+ }
context.copyMetadata(singleTarget);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 2ac3abd..e17e419 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1343,7 +1343,8 @@
methodAccess,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new OutlineCode(outline));
+ new OutlineCode(outline),
+ true);
if (appView.options().isGeneratingClassFiles()) {
direct[count].upgradeClassFileVersion(sites.get(0).getClassFileVersion());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 11f0a08..fa41f210d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -187,6 +187,14 @@
if (baseClazz == null || !baseClazz.isResolvable(appView)) {
return null;
}
+
+ // Don't allow the instantiated class to be in a feature, if it is, we can get a
+ // NoClassDefFoundError from dalvik/art.
+ if (baseClazz.isProgramClass()
+ && appView.options().featureSplitConfiguration != null
+ && appView.options().featureSplitConfiguration.isInFeature(baseClazz.asProgramClass())) {
+ return null;
+ }
// Make sure the (base) type is visible.
ConstraintWithTarget constraints =
ConstraintWithTarget.classIsVisible(context, baseType, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 29ad438..856baa9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -225,7 +225,8 @@
methodAccess,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()));
+ ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
+ true);
synthesizedClass.addDirectMethod(encodedMethod);
return encodedMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 74a5236..6bc95f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -127,7 +127,8 @@
method,
group.getLambdaIdField(factory),
implMethods,
- callerPosition))));
+ callerPosition)),
+ true));
}
}
@@ -177,7 +178,8 @@
new SynthesizedCode(
callerPosition ->
createInstanceInitializerSourceCode(
- groupClassType, initializerMethod, callerPosition)));
+ groupClassType, initializerMethod, callerPosition)),
+ true);
// Static class initializer for stateless lambdas.
if (needsSingletonInstances) {
@@ -194,7 +196,8 @@
ParameterAnnotationsList.empty(),
new SynthesizedCode(
callerPosition ->
- new ClassInitializerSourceCode(method, factory, group, callerPosition)));
+ new ClassInitializerSourceCode(method, factory, group, callerPosition)),
+ true);
}
return result;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 06db71b..f4503ce 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -72,6 +72,9 @@
superTypes.add(toKmType(addKotlinPrefix("Any;")));
}
+ if (!appView.options().enableKotlinMetadataRewriting) {
+ return;
+ }
List<KmConstructor> constructors = kmClass.getConstructors();
List<KmConstructor> originalConstructors = new ArrayList<>(constructors);
constructors.clear();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index 68b607df..4306645 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -44,6 +44,9 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ if (!appView.options().enableKotlinMetadataRewriting) {
+ return;
+ }
List<KmFunction> functions = kmPackage.getFunctions();
List<KmFunction> originalExtensions =
functions.stream()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index e0a3935..71538fc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -44,6 +44,9 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ if (!appView.options().enableKotlinMetadataRewriting) {
+ return;
+ }
List<KmFunction> functions = kmPackage.getFunctions();
List<KmFunction> originalExtensions =
functions.stream()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
new file mode 100644
index 0000000..85e3540
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -0,0 +1,178 @@
+// 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.Arrays;
+import java.util.List;
+import kotlinx.metadata.KmConstructor;
+import kotlinx.metadata.KmConstructorExtensionVisitor;
+import kotlinx.metadata.KmConstructorVisitor;
+import kotlinx.metadata.KmExtensionType;
+import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmFunctionExtensionVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmPropertyExtensionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+
+class KotlinMetadataJvmExtensionUtils {
+
+ private static boolean isValidJvmMethodSignature(String desc) {
+ return desc != null
+ && !desc.isEmpty()
+ && desc.charAt(0) == '('
+ && desc.lastIndexOf('(') == 0
+ && desc.indexOf(')') != -1
+ && desc.indexOf(')') == desc.lastIndexOf(')')
+ && desc.lastIndexOf(')') < desc.length();
+ }
+
+ /**
+ * Extract return type from {@link JvmMethodSignature}.
+ *
+ * Example of JVM signature is: `JvmMethodSignature("getX", "()Ljava/lang/Object;").`
+ * In this case, the return type is "Ljava/lang/Object;".
+ */
+ static String returnTypeFromJvmMethodSignature(JvmMethodSignature signature) {
+ if (signature == null) {
+ return null;
+ }
+ String desc = signature.getDesc();
+ if (!isValidJvmMethodSignature(desc)) {
+ return null;
+ }
+ int index = desc.lastIndexOf(')');
+ assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString();
+ return desc.substring(index + 1);
+ }
+
+ /**
+ * Extract parameters from {@link JvmMethodSignature}.
+ *
+ * Example of JVM signature is: `JvmMethodSignature("setX", "(Ljava/lang/Object;)V").`
+ * In this case, the parameter is the list with "Ljava/lang/Object;" as the first element.
+ */
+ static List<String> parameterTypesFromJvmMethodSignature(JvmMethodSignature signature) {
+ if (signature == null) {
+ return null;
+ }
+ String desc = signature.getDesc();
+ if (!isValidJvmMethodSignature(desc)) {
+ return null;
+ }
+ int index = desc.lastIndexOf(')');
+ assert desc.charAt(0) == '(' && 0 < index && index < desc.length() : signature.asString();
+ String params = desc.substring(1, index);
+ if (params.isEmpty()) {
+ return ImmutableList.of();
+ } else {
+ return Arrays.asList(params.split(","));
+ }
+ }
+
+ static class KmConstructorProcessor {
+ private JvmMethodSignature signature = null;
+
+ KmConstructorProcessor(KmConstructor kmConstructor) {
+ kmConstructor.accept(new KmConstructorVisitor() {
+ @Override
+ public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
+ if (type != JvmConstructorExtensionVisitor.TYPE) {
+ return null;
+ }
+ return new JvmConstructorExtensionVisitor() {
+ @Override
+ public void visit(JvmMethodSignature desc) {
+ assert signature == null : signature.asString();
+ signature = desc;
+ }
+ };
+ }
+ });
+ }
+
+ JvmMethodSignature signature() {
+ return signature;
+ }
+ }
+
+ static class KmFunctionProcessor {
+ // Custom name via @JvmName("..."). Otherwise, null.
+ private 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;
+ }
+ };
+ }
+ });
+ }
+
+ JvmMethodSignature signature() {
+ return signature;
+ }
+ }
+
+ static class KmPropertyProcessor {
+ private JvmFieldSignature fieldSignature = null;
+ // Custom getter via @get:JvmName("..."). Otherwise, null.
+ private JvmMethodSignature getterSignature = null;
+ // Custom getter via @set:JvmName("..."). Otherwise, null.
+ private 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;
+ }
+ };
+ }
+ });
+ }
+
+ JvmFieldSignature fieldSignature() {
+ return fieldSignature;
+ }
+
+ JvmMethodSignature getterSignature() {
+ return getterSignature;
+ }
+
+ JvmMethodSignature setterSignature() {
+ return setterSignature;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index ff9ad15..5f7843f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
+import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.parameterTypesFromJvmMethodSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.returnTypeFromJvmMethodSignature;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
import static kotlinx.metadata.FlagsKt.flagsOf;
@@ -14,14 +16,18 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmConstructorProcessor;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor;
+import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Box;
import java.util.List;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.jvm.JvmMethodSignature;
public class KotlinMetadataSynthesizer {
@@ -69,6 +75,13 @@
return kmType;
}
+ private static boolean isCompatible(String desc, DexType type) {
+ if (desc == null || type == null) {
+ return false;
+ }
+ return desc.equals(type.toDescriptorString());
+ }
+
private static boolean isCompatible(KmType kmType, DexType type, AppView<?> appView) {
if (kmType == null || type == null) {
return false;
@@ -85,8 +98,32 @@
return descriptor.equals(getDescriptorFromKmType(kmType));
}
+ private static boolean isCompatibleJvmMethodSignature(
+ JvmMethodSignature signature, DexEncodedMethod method) {
+ String methodName = method.method.name.toString();
+ if (!signature.getName().equals(methodName)) {
+ return false;
+ }
+ if (!isCompatible(
+ returnTypeFromJvmMethodSignature(signature), method.method.proto.returnType)) {
+ return false;
+ }
+ List<String> parameterTypes = parameterTypesFromJvmMethodSignature(signature);
+ if (parameterTypes == null || parameterTypes.size() != method.method.proto.parameters.size()) {
+ return false;
+ }
+ for (int i = 0; i < method.method.proto.parameters.size(); i++) {
+ if (!isCompatible(parameterTypes.get(i), method.method.proto.parameters.values[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public static boolean isCompatibleConstructor(
KmConstructor constructor, DexEncodedMethod method, AppView<?> appView) {
+ // Note that targets for @JvmName don't include constructor. So, it's not necessary to process
+ // JvmMethodSignature inside JvmConstructorExtension.
List<KmValueParameter> parameters = constructor.getValueParameters();
if (method.method.proto.parameters.size() != parameters.size()) {
return false;
@@ -102,6 +139,13 @@
public static boolean isCompatibleFunction(
KmFunction function, DexEncodedMethod method, AppView<?> appView) {
+ // Check if a custom name is set to avoid name clash.
+ KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(function);
+ JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature();
+ if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) {
+ return true;
+ }
+
if (!function.getName().equals(method.method.name.toString())) {
return false;
}
@@ -121,9 +165,15 @@
return true;
}
- // TODO(b/70169921): Handling JVM extensions as well.
public static boolean isCompatibleExtension(
KmFunction extension, DexEncodedMethod method, AppView<?> appView) {
+ // Check if a custom name is set to avoid name clash.
+ KmFunctionProcessor kmFunctionProcessor = new KmFunctionProcessor(extension);
+ JvmMethodSignature jvmMethodSignature = kmFunctionProcessor.signature();
+ if (jvmMethodSignature != null && isCompatibleJvmMethodSignature(jvmMethodSignature, method)) {
+ return true;
+ }
+
if (!extension.getName().equals(method.method.name.toString())) {
return false;
}
@@ -149,6 +199,58 @@
return true;
}
+ public static boolean isCompatibleProperty(
+ KmProperty kmProperty, DexEncodedMethod method, AppView<?> appView) {
+ KmPropertyProcessor kmPropertyProcessor = new KmPropertyProcessor(kmProperty);
+ // Check if a custom getter is defined via @get:JvmName("myGetter").
+ JvmMethodSignature getterSignature = kmPropertyProcessor.getterSignature();
+ if (getterSignature != null && isCompatibleJvmMethodSignature(getterSignature, method)) {
+ return true;
+ }
+ // Check if a custom setter is defined via @set:JvmName("mySetter").
+ JvmMethodSignature setterSignature = kmPropertyProcessor.setterSignature();
+ if (setterSignature != null && isCompatibleJvmMethodSignature(setterSignature, method)) {
+ return true;
+ }
+
+ // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`.
+ // For boolean property, though, getter is mapped to `isProp()Z`.
+ // TODO(b/70169921): Avoid decoding.
+ String methodName = method.method.name.toString();
+ if (!methodName.startsWith("is")
+ && !methodName.startsWith("get")
+ && !methodName.startsWith("set")
+ && !methodName.endsWith("$annotations")) {
+ return false;
+ }
+
+ String propertyName = kmProperty.getName();
+ assert propertyName.length() > 0;
+ String annotations = propertyName + "$annotations";
+ if (methodName.equals(annotations)) {
+ return true;
+ }
+ String capitalized =
+ Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+ String returnTypeDescriptor = getDescriptorFromKmType(kmProperty.returnType);
+ String getterPrefix =
+ returnTypeDescriptor != null && returnTypeDescriptor.endsWith("Boolean;") ? "is" : "get";
+ String getter = getterPrefix + capitalized;
+ if (methodName.equals(getter)
+ && method.method.proto.parameters.size() == 0
+ && isCompatible(kmProperty.returnType, method.method.proto.returnType, appView)) {
+ return true;
+ }
+ String setter = "set" + capitalized;
+ if (methodName.equals(setter)
+ && method.method.proto.returnType.isVoidType()
+ && method.method.proto.parameters.size() == 1
+ && isCompatible(kmProperty.returnType, method.method.proto.parameters.values[0], appView)) {
+ return true;
+ }
+ return false;
+ }
+
static KmConstructor toRenamedKmConstructor(
DexEncodedMethod method,
KmConstructor original,
@@ -161,9 +263,10 @@
}
// TODO(b/70169921): {@link KmConstructor.extensions} is private, i.e., no way to alter!
// Thus, we rely on original metadata for now.
- Box<Boolean> hasJvmExtension = new Box<>(false);
+ KmConstructorProcessor kmConstructorProcessor = new KmConstructorProcessor(original);
+ JvmMethodSignature jvmMethodSignature = kmConstructorProcessor.signature();
KmConstructor kmConstructor =
- hasJvmExtension.get()
+ jvmMethodSignature != null
? original
// TODO(b/70169921): Consult kotlinx.metadata.Flag.Constructor to set IS_PRIMARY.
: new KmConstructor(method.accessFlags.getAsKotlinFlags());
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 7af0752..f12d958 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -135,14 +135,15 @@
MethodNameMinifier(AppView<AppInfoWithLiveness> appView, MemberNamingStrategy strategy) {
this.appView = appView;
this.strategy = strategy;
- rootReservationState = MethodReservationState.createRoot(getKeyTransform());
+ rootReservationState = MethodReservationState.createRoot(getReservationKeyTransform());
rootNamingState =
- MethodNamingState.createRoot(getKeyTransform(), strategy, rootReservationState);
+ MethodNamingState.createRoot(getNamingKeyTransform(), strategy, rootReservationState);
namingStates.put(null, rootNamingState);
}
- private Function<DexMethod, ?> getKeyTransform() {
- if (appView.options().getProguardConfiguration().isOverloadAggressively()) {
+ private Function<DexMethod, ?> getReservationKeyTransform() {
+ if (appView.options().getProguardConfiguration().isOverloadAggressively()
+ && appView.options().isGeneratingClassFiles()) {
// Use the full proto as key, hence reuse names based on full signature.
return method -> method.proto;
} else {
@@ -151,6 +152,12 @@
}
}
+ private Function<DexMethod, ?> getNamingKeyTransform() {
+ return appView.options().isGeneratingClassFiles()
+ ? getReservationKeyTransform()
+ : method -> null;
+ }
+
static class MethodRenaming {
final Map<DexMethod, DexString> renaming;
@@ -199,23 +206,9 @@
MethodNamingState<?> namingState =
namingStates.computeIfAbsent(
type, ignore -> parentNamingState.createChild(reservationState));
- // The names for direct methods should not contribute to the naming of methods in sub-types:
- // class A {
- // public int foo() { ... } --> a
- // private int bar() { ... } --> b
- // }
- //
- // class B extends A {
- // public int baz() { ... } --> b
- // }
- //
- // A simple way to ensure this is to process virtual methods first and then direct methods.
DexClass holder = appView.definitionFor(type);
if (holder != null && strategy.allowMemberRenaming(holder)) {
- for (DexEncodedMethod method : holder.virtualMethodsSorted()) {
- assignNameToMethod(holder, method, namingState);
- }
- for (DexEncodedMethod method : holder.directMethodsSorted()) {
+ for (DexEncodedMethod method : holder.allMethodsSorted()) {
assignNameToMethod(holder, method, namingState);
}
}
@@ -239,9 +232,7 @@
if (encodedMethod.method.name != newName) {
renaming.put(encodedMethod.method, newName);
}
- if (!encodedMethod.isPrivateMethod()) {
- state.addRenaming(newName, encodedMethod.method);
- }
+ state.addRenaming(newName, encodedMethod.method);
}
private void reserveNamesInClasses() {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index c6b7ee0..7926de0 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.MethodNamingState.InternalNewNameState;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -72,8 +74,8 @@
}
boolean isAvailable(DexString candidate, DexMethod method) {
- Set<DexString> usedBy = getUsedBy(candidate, method);
- if (usedBy != null && usedBy.contains(method.name)) {
+ Set<Wrapper<DexMethod>> usedBy = getUsedBy(candidate, method);
+ if (usedBy != null && usedBy.contains(MethodSignatureEquivalence.get().wrap(method))) {
return true;
}
boolean isReserved = reservationState.isReserved(candidate, method);
@@ -82,16 +84,13 @@
}
// We now have a reserved name. We therefore have to check if the reservation is
// equal to candidate, otherwise the candidate is not available.
- if (isReserved && usedBy == null) {
- Set<DexString> methodReservedNames = reservationState.getReservedNamesFor(method);
- return methodReservedNames != null && methodReservedNames.contains(candidate);
- }
- return false;
+ Set<DexString> methodReservedNames = reservationState.getReservedNamesFor(method);
+ return methodReservedNames != null && methodReservedNames.contains(candidate);
}
- private Set<DexString> getUsedBy(DexString name, DexMethod method) {
+ private Set<Wrapper<DexMethod>> getUsedBy(DexString name, DexMethod method) {
InternalNewNameState internalState = getInternalState(method);
- Set<DexString> nameUsedBy = null;
+ Set<Wrapper<DexMethod>> nameUsedBy = null;
if (internalState != null) {
nameUsedBy = internalState.getUsedBy(name);
}
@@ -105,7 +104,7 @@
DexString assignedName = null;
InternalNewNameState internalState = getInternalState(method);
if (internalState != null) {
- assignedName = internalState.getAssignedName(method.name);
+ assignedName = internalState.getAssignedName(method);
}
if (assignedName == null && parentNamingState != null) {
assignedName = parentNamingState.getAssignedName(method);
@@ -125,14 +124,13 @@
static class InternalNewNameState implements InternalNamingState {
private final InternalNewNameState parentInternalState;
- private Map<DexString, DexString> originalToRenamedNames = new HashMap<>();
- private Map<DexString, Set<DexString>> usedBy = new HashMap<>();
+ private Map<Wrapper<DexMethod>, DexString> originalToRenamedNames = new HashMap<>();
+ private Map<DexString, Set<Wrapper<DexMethod>>> usedBy = new HashMap<>();
private static final int INITIAL_NAME_COUNT = 1;
private static final int INITIAL_DICTIONARY_INDEX = 0;
- private int virtualNameCount;
- private int directNameCount = 0;
+ private int nameCount;
private int dictionaryIndex;
private InternalNewNameState(InternalNewNameState parentInternalState) {
@@ -141,8 +139,8 @@
parentInternalState == null
? INITIAL_DICTIONARY_INDEX
: parentInternalState.dictionaryIndex;
- this.virtualNameCount =
- parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.virtualNameCount;
+ this.nameCount =
+ parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.nameCount;
}
@Override
@@ -155,40 +153,35 @@
return dictionaryIndex++;
}
- Set<DexString> getUsedBy(DexString name) {
+ Set<Wrapper<DexMethod>> getUsedBy(DexString name) {
return usedBy.get(name);
}
- DexString getAssignedName(DexString originalName) {
- return originalToRenamedNames.get(originalName);
+ DexString getAssignedName(DexMethod method) {
+ return originalToRenamedNames.get(MethodSignatureEquivalence.get().wrap(method));
}
void addRenaming(DexString newName, DexMethod method) {
- originalToRenamedNames.put(method.name, newName);
- usedBy.computeIfAbsent(newName, ignore -> new HashSet<>()).add(method.name);
+ final Wrapper<DexMethod> wrappedMethod = MethodSignatureEquivalence.get().wrap(method);
+ originalToRenamedNames.put(wrappedMethod, newName);
+ usedBy.computeIfAbsent(newName, ignore -> new HashSet<>()).add(wrappedMethod);
}
private boolean checkParentPublicNameCountIsLessThanOrEqual() {
int maxParentCount = 0;
InternalNewNameState tmp = parentInternalState;
while (tmp != null) {
- maxParentCount = Math.max(tmp.virtualNameCount, maxParentCount);
+ maxParentCount = Math.max(tmp.nameCount, maxParentCount);
tmp = tmp.parentInternalState;
}
- assert maxParentCount <= virtualNameCount;
+ assert maxParentCount <= nameCount;
return true;
}
@Override
public int incrementNameIndex(boolean isDirectMethodCall) {
assert checkParentPublicNameCountIsLessThanOrEqual();
- if (isDirectMethodCall) {
- return virtualNameCount + directNameCount++;
- } else {
- // TODO(b/144877828): is it guaranteed?
- assert directNameCount == 0;
- return virtualNameCount++;
- }
+ return nameCount++;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
index ad5b8cc..2ce35f9 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.MethodReservationState.InternalReservationState;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -36,7 +38,7 @@
void reserveName(DexString reservedName, DexMethod method) {
try {
- getOrCreateInternalState(method).reserveName(method.name, reservedName);
+ getOrCreateInternalState(method).reserveName(method, reservedName);
} catch (AssertionError err) {
throw new RuntimeException(
String.format(
@@ -61,7 +63,7 @@
InternalReservationState internalState = getInternalState(method);
Set<DexString> reservedName = null;
if (internalState != null) {
- reservedName = internalState.getAssignedNamesFor(method.name);
+ reservedName = internalState.getAssignedNamesFor(method);
}
if (reservedName == null && parentNamingState != null) {
reservedName = parentNamingState.getReservedNamesFor(method);
@@ -75,28 +77,28 @@
}
static class InternalReservationState {
- private Map<DexString, Set<DexString>> originalToReservedNames = null;
+ private Map<Wrapper<DexMethod>, Set<DexString>> originalToReservedNames = null;
private Set<DexString> reservedNames = null;
boolean isReserved(DexString name) {
return reservedNames != null && reservedNames.contains(name);
}
- Set<DexString> getAssignedNamesFor(DexString original) {
- Set<DexString> result = null;
- if (originalToReservedNames != null) {
- result = originalToReservedNames.get(original);
+ Set<DexString> getAssignedNamesFor(DexMethod method) {
+ if (originalToReservedNames == null) {
+ return null;
}
- return result;
+ return originalToReservedNames.get(MethodSignatureEquivalence.get().wrap(method));
}
- void reserveName(DexString originalName, DexString name) {
+ void reserveName(DexMethod method, DexString name) {
if (reservedNames == null) {
assert originalToReservedNames == null;
originalToReservedNames = new HashMap<>();
reservedNames = new HashSet<>();
}
- originalToReservedNames.computeIfAbsent(originalName, ignore -> new HashSet<>()).add(name);
+ final Wrapper<DexMethod> wrapped = MethodSignatureEquivalence.get().wrap(method);
+ originalToReservedNames.computeIfAbsent(wrapped, ignore -> new HashSet<>()).add(name);
reservedNames.add(name);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
index 773db00..1c99cc6 100644
--- a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
+++ b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
import java.util.function.Consumer;
public abstract class DelayedRootSetActionItem {
@@ -18,24 +18,22 @@
}
public static class InterfaceMethodSyntheticBridgeAction extends DelayedRootSetActionItem {
- private final DexEncodedMethod methodToKeep;
- private final DexEncodedMethod singleTarget;
+ private final ProgramMethod methodToKeep;
+ private final ProgramMethod singleTarget;
private final Consumer<RootSetBuilder> action;
InterfaceMethodSyntheticBridgeAction(
- DexEncodedMethod methodToKeep,
- DexEncodedMethod singleTarget,
- Consumer<RootSetBuilder> action) {
+ ProgramMethod methodToKeep, ProgramMethod singleTarget, Consumer<RootSetBuilder> action) {
this.methodToKeep = methodToKeep;
this.singleTarget = singleTarget;
this.action = action;
}
- public DexEncodedMethod getMethodToKeep() {
+ public ProgramMethod getMethodToKeep() {
return methodToKeep;
}
- public DexEncodedMethod getSingleTarget() {
+ public ProgramMethod getSingleTarget() {
return singleTarget;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ac1dea1..1b76214 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2263,6 +2263,13 @@
(field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO);
assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
+ for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
+ appView.appInfo().invalidateTypeCacheFor(bridge.holder.type);
+ bridge.holder.appendVirtualMethod(bridge.method);
+ targetedMethods.add(bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+ liveMethods.add(bridge.holder, bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+ }
+
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
appInfo,
@@ -2460,25 +2467,31 @@
return builder.buildConsequentRootSet();
}
+ private Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = new IdentityHashMap<>();
+
private void handleInterfaceMethodSyntheticBridgeAction(
InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
- if (rootSet.noShrinking.containsKey(action.getSingleTarget().method)) {
+ ProgramMethod methodToKeep = action.getMethodToKeep();
+ ProgramMethod singleTarget = action.getSingleTarget();
+ if (rootSet.noShrinking.containsKey(singleTarget.method.method)) {
return;
}
- DexEncodedMethod methodToKeep = action.getMethodToKeep();
- DexEncodedMethod singleTarget = action.getSingleTarget();
- DexClass clazz = getProgramClassOrNull(methodToKeep.method.holder);
if (methodToKeep != singleTarget) {
- // Insert a bridge method.
- if (appView.definitionFor(methodToKeep.method) == null) {
- clazz.appendVirtualMethod(methodToKeep);
- if (singleTarget.isLibraryMethodOverride().isTrue()) {
- methodToKeep.setLibraryMethodOverride(OptionalBool.TRUE);
+ assert null == methodToKeep.holder.lookupMethod(methodToKeep.method.method);
+ ProgramMethod old =
+ syntheticInterfaceMethodBridges.put(methodToKeep.method.method, methodToKeep);
+ if (old == null) {
+ if (singleTarget.method.isLibraryMethodOverride().isTrue()) {
+ methodToKeep.method.setLibraryMethodOverride(OptionalBool.TRUE);
}
- appView.appInfo().invalidateTypeCacheFor(methodToKeep.method.holder);
- // The addition of a bridge method can lead to a change of resolution, thus the cached
- // resolution targets are invalid.
- virtualTargetsMarkedAsReachable.remove(methodToKeep.method);
+ assert singleTarget.holder.isInterface();
+ markVirtualMethodAsReachable(
+ singleTarget.method.method,
+ singleTarget.holder.isInterface(),
+ null,
+ graphReporter.fakeReportShouldNotBeUsed());
+ enqueueMarkMethodLiveAction(
+ singleTarget.holder, singleTarget.method, graphReporter.fakeReportShouldNotBeUsed());
}
}
action.getAction().accept(builder);
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index cf820bc..c172b52 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -67,6 +67,10 @@
}
}
+ public KeepReasonWitness fakeReportShouldNotBeUsed() {
+ return KeepReasonWitness.INSTANCE;
+ }
+
public boolean verifyRootedPath(DexProgramClass liveType) {
assert verificationGraphConsumer != null;
ClassGraphNode node = getClassGraphNode(liveType.type);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index b842680..8555b93 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -23,7 +23,8 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
@@ -457,7 +458,9 @@
}
// TODO(b/143643942): Generalize the below approach to also work for subtyping hierarchies in
// fullmode.
- if (clazz.isProgramClass()) {
+ if (clazz.isProgramClass()
+ && rule.isProguardKeepRule()
+ && !rule.asProguardKeepRule().getModifiers().allowsShrinking) {
new SynthesizeMissingInterfaceMethodsForMemberRules(
clazz.asProgramClass(), memberKeepRules, rule, preconditionSupplier, ifRule)
.run();
@@ -484,6 +487,8 @@
ProguardConfigurationRule context,
Map<Predicate<DexDefinition>, DexDefinition> preconditionSupplier,
ProguardIfRule ifRule) {
+ assert context.isProguardKeepRule();
+ assert !context.asProguardKeepRule().getModifiers().allowsShrinking;
this.originalClazz = originalClazz;
this.memberKeepRules = memberKeepRules;
this.context = context;
@@ -525,49 +530,46 @@
}
private void tryAndKeepMethodOnClass(DexEncodedMethod method, ProguardMemberRule rule) {
- boolean shouldKeepMethod =
- context.isProguardKeepRule()
- && !context.asProguardKeepRule().getModifiers().allowsShrinking;
- if (!shouldKeepMethod) {
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(originalClazz, method.method).asSingleResolution();
+ if (resolutionResult == null || !resolutionResult.isVirtualTarget()) {
return;
}
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMethod(originalClazz, method.method);
- if (!resolutionResult.isVirtualTarget() || !resolutionResult.isSingleResolution()) {
+ if (resolutionResult.getResolvedHolder() == originalClazz
+ || resolutionResult.getResolvedHolder().isNotProgramClass()) {
return;
}
- DexEncodedMethod methodToKeep = resolutionResult.getSingleTarget();
- if (methodToKeep.method.holder == originalClazz.type) {
- return;
- }
- DexClass holder = appView.definitionFor(methodToKeep.method.holder);
- if (holder.isNotProgramClass()) {
- return;
- }
- if (!holder.isInterface()) {
+ if (!resolutionResult.getResolvedHolder().isInterface()) {
// TODO(b/143643942): For fullmode, this check should probably be removed.
return;
}
- if (canInsertForwardingMethod(originalClazz, methodToKeep)) {
- methodToKeep = methodToKeep.toForwardingMethod(originalClazz, appView);
- }
- final DexEncodedMethod finalKeepMethod = methodToKeep;
+ ProgramMethod resolutionMethod =
+ new ProgramMethod(
+ resolutionResult.getResolvedHolder().asProgramClass(),
+ resolutionResult.getResolvedMethod());
+ ProgramMethod methodToKeep =
+ canInsertForwardingMethod(originalClazz, resolutionMethod.method)
+ ? new ProgramMethod(
+ originalClazz, resolutionMethod.method.toForwardingMethod(originalClazz, appView))
+ : resolutionMethod;
+
delayedRootSetActionItems.add(
new InterfaceMethodSyntheticBridgeAction(
methodToKeep,
- resolutionResult.getSingleTarget(),
+ resolutionMethod,
(rootSetBuilder) -> {
if (Log.ENABLED) {
Log.verbose(
getClass(),
"Marking method `%s` due to `%s { %s }`.",
- finalKeepMethod,
+ methodToKeep,
context,
rule);
}
DexDefinition precondition =
- testAndGetPrecondition(finalKeepMethod, preconditionSupplier);
- rootSetBuilder.addItemToSets(finalKeepMethod, context, rule, precondition, ifRule);
+ testAndGetPrecondition(methodToKeep.method, preconditionSupplier);
+ rootSetBuilder.addItemToSets(
+ methodToKeep.method, context, rule, precondition, ifRule);
}));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index c518937..ae8eb7e 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1214,7 +1214,8 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
- method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
+ method.hasClassFileVersion() ? method.getClassFileVersion() : -1,
+ true);
if (method.accessFlags.isPromotedToPublic()) {
// The bridge is now the public method serving the role of the original method, and should
// reflect that this method was publicized.
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 1425bc0..9ed931c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -213,6 +213,8 @@
public boolean enablePropagationOfDynamicTypesAtCallSites = true;
// TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites.
public boolean enablePropagationOfConstantsAtCallSites = false;
+ // TODO(b/70169921): enable after branching.
+ public boolean enableKotlinMetadataRewriting = false;
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
public boolean enableCfInterfaceMethodDesugaring = false;
@@ -242,7 +244,8 @@
// TODO(b/125282093): Enable member value propagation for instance fields.
public boolean enableValuePropagationForInstanceFields = false;
public boolean enableUninstantiatedTypeOptimization = true;
- public boolean enableUninstantiatedTypeOptimizationForInterfaces = true;
+ // Currently disabled, see b/146957343.
+ public boolean enableUninstantiatedTypeOptimizationForInterfaces = false;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
@@ -1075,6 +1078,13 @@
enablePropagationOfConstantsAtCallSites = true;
}
+ // TODO(b/70169921): Remove this once enabled.
+ @VisibleForTesting
+ public void enableKotlinMetadataRewriting() {
+ assert !enableKotlinMetadataRewriting;
+ enableKotlinMetadataRewriting = true;
+ }
+
private boolean hasMinApi(AndroidApiLevel level) {
assert isGeneratingDex();
return minApiLevel >= level.getLevel();
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 933f4fb..c7324da 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -303,6 +303,10 @@
// deterministic behaviour: the algorithm will assign new line numbers in this order.
// Methods with different names can share the same line numbers, that's why they don't
// need to be sorted.
+ // If we are compiling to DEX we will try to not generate overloaded names. This saves
+ // space by allowing more debug-information to be canonicalized. If we have overloaded
+ // methods, we either did not rename them, we renamed them according to a supplied map or
+ // they may be bridges for interface methods with covariant return types.
sortMethods(methods);
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index db0baa3..fbacef6 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -13,6 +13,8 @@
import static org.junit.Assert.assertTrue;
import com.android.sdklib.AndroidVersion;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
import com.android.tools.r8.D8CommandParser.OrderedClassFileResourceProvider;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dex.Marker;
@@ -519,6 +521,51 @@
assertEquals(0, new ZipFile(emptyZip.toFile(), StandardCharsets.UTF_8).size());
}
+ private void checkSingleForceAllAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(1, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+ }
+
+ private void checkSingleForceClassAndPackageAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(2, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+ assertEquals("ClassName", entries.get(0).getValue());
+ assertEquals(transformation, entries.get(1).getTransformation());
+ assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+ assertEquals("PackageName", entries.get(1).getValue());
+ }
+
+ @Test
+ public void forceAssertionOption() throws Exception {
+ checkSingleForceAllAssertion(
+ parse("--force-enable-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceAllAssertion(
+ parse("--force-disable-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceAllAssertion(
+ parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ checkSingleForceClassAndPackageAssertion(
+ parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse(
+ "--force-passthrough-assertions:ClassName",
+ "--force-passthrough-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ }
+
@Test(expected = CompilationFailedException.class)
public void missingParameterForLastOption() throws CompilationFailedException {
DiagnosticsChecker.checkErrorsContains(
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 558ba0d..060bdbb 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -8,6 +8,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
@@ -182,11 +184,79 @@
@Test
public void desugaredLibrary() throws CompilationFailedException {
- L8Command l8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+ L8Command l8Command =
+ parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString());
assertFalse(
l8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
}
+ private void checkSingleForceAllAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(1, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+ }
+
+ private void checkSingleForceClassAndPackageAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(2, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+ assertEquals("ClassName", entries.get(0).getValue());
+ assertEquals(transformation, entries.get(1).getTransformation());
+ assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+ assertEquals("PackageName", entries.get(1).getValue());
+ }
+
+ @Test
+ public void forceAssertionOption() throws Exception {
+ checkSingleForceAllAssertion(
+ parse(
+ "--force-enable-assertions",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceAllAssertion(
+ parse(
+ "--force-disable-assertions",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceAllAssertion(
+ parse(
+ "--force-passthrough-assertions",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ checkSingleForceClassAndPackageAssertion(
+ parse(
+ "--force-enable-assertions:ClassName",
+ "--force-enable-assertions:PackageName...",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse(
+ "--force-disable-assertions:ClassName",
+ "--force-disable-assertions:PackageName...",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse(
+ "--force-passthrough-assertions:ClassName",
+ "--force-passthrough-assertions:PackageName...",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ }
+
private L8Command parse(String... args) throws CompilationFailedException {
return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 165b510..cc92bf3 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -10,6 +10,8 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dex.Marker;
@@ -632,6 +634,51 @@
runCustomResourceProcessing(false, false, 0);
}
+ private void checkSingleForceAllAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(1, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+ }
+
+ private void checkSingleForceClassAndPackageAssertion(
+ List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
+ assertEquals(2, entries.size());
+ assertEquals(transformation, entries.get(0).getTransformation());
+ assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+ assertEquals("ClassName", entries.get(0).getValue());
+ assertEquals(transformation, entries.get(1).getTransformation());
+ assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+ assertEquals("PackageName", entries.get(1).getValue());
+ }
+
+ @Test
+ public void forceAssertionOption() throws Exception {
+ checkSingleForceAllAssertion(
+ parse("--force-enable-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceAllAssertion(
+ parse("--force-disable-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceAllAssertion(
+ parse("--force-passthrough-assertions").getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ checkSingleForceClassAndPackageAssertion(
+ parse("--force-enable-assertions:ClassName", "--force-enable-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.ENABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse("--force-disable-assertions:ClassName", "--force-disable-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.DISABLE);
+ checkSingleForceClassAndPackageAssertion(
+ parse(
+ "--force-passthrough-assertions:ClassName",
+ "--force-passthrough-assertions:PackageName...")
+ .getAssertionsConfiguration(),
+ AssertionTransformation.PASSTHROUGH);
+ }
+
@Test(expected = CompilationFailedException.class)
public void missingParameterForLastOption() throws CompilationFailedException {
DiagnosticsChecker.checkErrorsContains(
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index e1ebfa2..bbc9d4e 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -129,9 +129,14 @@
return new CodeInspector(app);
}
- public RR inspect(Consumer<CodeInspector> consumer)
+ public RR inspect(ThrowingConsumer<CodeInspector, NoSuchMethodException> consumer)
throws IOException, ExecutionException {
- consumer.accept(inspector());
+ CodeInspector inspector = inspector();
+ try {
+ consumer.accept(inspector);
+ } catch (NoSuchMethodException exception) {
+ throw new RuntimeException(exception);
+ }
return self();
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 0ba8510..ddb0608 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -64,7 +64,7 @@
private static Pair<Path, Path> r8R8Release;
private final TestParameters parameters;
- private static boolean testExternal = true;
+ private static boolean testExternal = false;
@ClassRule public static TemporaryFolder testFolder = new TemporaryFolder();
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
index 8322b11..9d01c91 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -58,19 +60,30 @@
int methodNumBridges = parameters.isCfRuntime() ? 0 : 2;
ClassSubject methodMainClass = inspector.clazz(getMainClass("methods"));
assertEquals(
- methodNumBridges, methodMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+ methodNumBridges, methodMainClass.allMethods(this::isNestBridge).size());
// Two bridges for method and staticMethod.
int constructorNumBridges = parameters.isCfRuntime() ? 0 : 1;
ClassSubject constructorMainClass = inspector.clazz(getMainClass("constructors"));
assertEquals(
constructorNumBridges,
- constructorMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+ constructorMainClass.allMethods(this::isNestBridge).size());
// Four bridges for field and staticField, both get & set.
int fieldNumBridges = parameters.isCfRuntime() ? 0 : 4;
ClassSubject fieldMainClass = inspector.clazz(getMainClass("fields"));
assertEquals(
- fieldNumBridges, fieldMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+ fieldNumBridges, fieldMainClass.allMethods(this::isNestBridge).size());
+ }
+
+ private boolean isNestBridge(FoundMethodSubject methodSubject) {
+ DexEncodedMethod method = methodSubject.getMethod();
+ if (method.isInstanceInitializer()) {
+ return method.method.proto.parameters.size() > 0 && method.method.proto.parameters.values[
+ method.method.proto.parameters.size() - 1].toSourceString()
+ .contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME);
+ }
+ return method.method.name.toString()
+ .startsWith(NestBasedAccessDesugaring.NEST_ACCESS_NAME_PREFIX);
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
index d5297d1..36ac4fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInClasspathTypeTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import java.nio.file.Path;
@@ -72,6 +73,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.ImplKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String implClassName = pkg + ".classpath_lib_ext.Impl";
@@ -127,6 +129,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.ImplKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String implClassName = pkg + ".classpath_lib_ext.Impl";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
index e7692bc..2f583fa 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import java.nio.file.Path;
@@ -65,6 +66,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String superClassName = pkg + ".extension_lib.Super";
@@ -115,6 +117,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String superClassName = pkg + ".extension_lib.Super";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
index 9280756..4adeb3f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInMultifileClassTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -65,6 +66,7 @@
.addKeepRules("-keep class **.UtilKt")
.addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
@@ -125,6 +127,7 @@
.addKeepRules(
"-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String utilClassName = pkg + ".multifileclass_lib.UtilKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
index b470bb6..e493ba2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import java.nio.file.Path;
@@ -63,6 +64,7 @@
// Keep Itf, but allow minification.
.addKeepRules("-keep,allowobfuscation class **.Itf")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String itfClassName = pkg + ".parametertype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
index 2e92333..2c51bff 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import java.nio.file.Path;
@@ -63,6 +64,7 @@
// Keep non-private members of Impl
.addKeepRules("-keep public class **.Impl { !private *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String itfClassName = pkg + ".propertytype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
index bcf2181..1dc6c14 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import java.nio.file.Path;
@@ -62,6 +63,7 @@
// Keep Itf, but allow minification.
.addKeepRules("-keep,allowobfuscation class **.Itf")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addOptionsModification(InternalOptions::enableKotlinMetadataRewriting)
.compile();
String pkg = getClass().getPackage().getName();
final String itfClassName = pkg + ".returntype_lib.Itf";
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
index 39747f0..8c50aa7 100644
--- a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
@@ -6,119 +6,106 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
-import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
-interface SuperInterface {
- Super foo();
-}
-
-interface SubInterface extends SuperInterface {
- @Override
- Sub foo();
-}
-
-class Super {
- protected int bar() {
- return 0;
- }
-}
-
-class Sub extends Super {
- @Override
- protected int bar() {
- return 1;
- }
-}
-
-class SuperImplementer implements SuperInterface {
- @Override
- public Super foo() {
- return new Sub();
- }
-}
-
-class SubImplementer extends SuperImplementer implements SubInterface {
- @Override
- public Sub foo() {
- return (Sub) super.foo();
- }
-}
-
-class TestMain {
- public static void main(String[] args) {
- SubImplementer subImplementer = new SubImplementer();
- Super sup = subImplementer.foo();
- System.out.print(sup.bar());
- }
-}
-
-@RunWith(VmTestRunner.class)
+@RunWith(Parameterized.class)
public class CovariantReturnTypeInSubInterfaceTest extends TestBase {
+ interface SuperInterface {
+ Super foo();
+ }
+
+ interface SubInterface extends SuperInterface {
+ @Override
+ Sub foo();
+ }
+
+ static class Super {
+ protected int bar() {
+ return 0;
+ }
+ }
+
+ static class Sub extends Super {
+ @Override
+ protected int bar() {
+ return 1;
+ }
+ }
+
+ static class SuperImplementer implements SuperInterface {
+ @Override
+ public Super foo() {
+ return new Sub();
+ }
+ }
+
+ static class SubImplementer extends SuperImplementer implements SubInterface {
+ @Override
+ public Sub foo() {
+ return (Sub) super.foo();
+ }
+ }
+
+ static class TestMain {
+ public static void main(String[] args) {
+ SubImplementer subImplementer = new SubImplementer();
+ Super sup = subImplementer.foo();
+ System.out.print(sup.bar());
+ }
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public CovariantReturnTypeInSubInterfaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
private void test(boolean overloadAggressively) throws Exception {
- String mainName = TestMain.class.getCanonicalName();
- String aggressive =
- overloadAggressively ? "-overloadaggressively" : "# Not overload aggressively";
- List<String> config = ImmutableList.of(
- "-printmapping",
- aggressive,
- "-keep class " + mainName + " {",
- " public void main(...);",
- "}",
- "-keep,allowobfuscation class **.Super* {",
- " <methods>;",
- "}",
- "-keep,allowobfuscation class **.Sub* {",
- " <methods>;",
- "}"
- );
- AndroidApp app = readClasses(
- SuperInterface.class,
- SubInterface.class,
- Super.class,
- Sub.class,
- SuperImplementer.class,
- SubImplementer.class,
- TestMain.class
- );
- AndroidApp processedApp =
- compileWithR8(app, String.join(System.lineSeparator(), config), options -> {
- options.enableInlining = false;
- });
- CodeInspector inspector = new CodeInspector(processedApp);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(CovariantReturnTypeInSubInterfaceTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestMain.class)
+ .addKeepRules(
+ "-keep,allowobfuscation class **.*$Super* { <methods>; }",
+ "-keep,allowobfuscation class **.*$Sub* { <methods>; }",
+ overloadAggressively ? "-overloadaggressively" : "# Not overload aggressively")
+ .addOptionsModification(internalOptions -> internalOptions.enableInlining = false)
+ .run(parameters.getRuntime(), TestMain.class)
+ .inspect(inspector -> inspect(inspector, overloadAggressively));
+ }
+
+ private void inspect(CodeInspector inspector, boolean overloadAggressively)
+ throws NoSuchMethodException {
ClassSubject superInterface = inspector.clazz(SuperInterface.class);
assertThat(superInterface, isRenamed());
- MethodSubject foo1 = superInterface.method(
- Super.class.getCanonicalName(), "foo", ImmutableList.of());
+ MethodSubject foo1 = superInterface.uniqueMethodWithName("foo");
assertThat(foo1, isRenamed());
ClassSubject subInterface = inspector.clazz(SubInterface.class);
assertThat(subInterface, isRenamed());
- MethodSubject foo2 = subInterface.method(
- Sub.class.getCanonicalName(), "foo", ImmutableList.of());
+ MethodSubject foo2 = subInterface.method(SubInterface.class.getDeclaredMethod("foo"));
assertThat(foo2, isRenamed());
- assertEquals(foo1.getFinalName(), foo2.getFinalName());
-
- ProcessResult javaResult = ToolHelper.runJava(ToolHelper.getClassPathForTests(), mainName);
- assertEquals(0, javaResult.exitCode);
- Path outDex = temp.getRoot().toPath().resolve("dex.zip");
- processedApp.writeToZip(outDex, OutputMode.DexIndexed);
- ProcessResult artResult = ToolHelper.runArtNoVerificationErrorsRaw(outDex.toString(), mainName);
- assertEquals(0, artResult.exitCode);
- assertEquals(javaResult.stdout, artResult.stdout);
+ if (overloadAggressively && parameters.isDexRuntime()) {
+ assertNotEquals(foo1.getFinalName(), foo2.getFinalName());
+ } else {
+ assertEquals(foo1.getFinalName(), foo2.getFinalName());
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
index 1303232..6cb9837 100644
--- a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
@@ -87,6 +87,6 @@
minifiedMethodNames.add(methodSubject.getFinalName());
}
}
- assertEquals(3, minifiedMethodNames.size());
+ assertEquals(9, minifiedMethodNames.size());
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java b/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java
deleted file mode 100644
index 301fe07..0000000
--- a/src/test/java/com/android/tools/r8/naming/MethodNameMinificationPrivateNamedLastTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2019, 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.naming;
-
-import static junit.framework.TestCase.assertEquals;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.AnyOf.anyOf;
-import static org.hamcrest.core.Is.is;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NeverMerge;
-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 com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
-import org.hamcrest.core.AnyOf;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-/**
- * MethodNameMinificationPrivateNamedLastTest tests that private methods are named after public
- * methods. Private methods can be named freely and should not influence how public methods are
- * named.
- */
-@RunWith(Parameterized.class)
-public class MethodNameMinificationPrivateNamedLastTest extends TestBase {
-
- public static final String EXPECTED_OUTPUT =
- StringUtils.lines("A.m1", "A.m2", "B.m1", "B.m2", "B.m3", "C.m1", "C.m2", "C.m3", "C.m4");
-
- @NeverMerge
- public static class A {
-
- @NeverInline
- public void m1() {
- System.out.println("A.m1");
- m2();
- }
-
- @NeverInline
- private void m2() {
- System.out.println("A.m2");
- }
- }
-
- @NeverMerge
- public static class B extends A {
-
- @NeverInline
- public void m1() {
- System.out.println("B.m1");
- m2();
- }
-
- @NeverInline
- private void m2() {
- System.out.println("B.m2");
- }
-
- @NeverInline
- public void m3() {
- System.out.println("B.m3");
- }
- }
-
- @NeverMerge
- public static class C extends B {
-
- @NeverInline
- public void m1() {
- System.out.println("C.m1");
- m2();
- }
-
- @NeverInline
- private void m2() {
- System.out.println("C.m2");
- }
-
- @NeverInline
- public void m3() {
- System.out.println("C.m3");
- m4();
- }
-
- @NeverInline
- private void m4() {
- System.out.println("C.m4");
- }
- }
-
- public static class Runner {
-
- public static void main(String[] args) {
- A a = new A();
- a.m1();
- B b = new B();
- b.m1();
- b.m3();
- C c = new C();
- c.m1();
- c.m3();
- }
- }
-
- private TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
- }
-
- public MethodNameMinificationPrivateNamedLastTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testInheritedNamingState()
- throws IOException, CompilationFailedException, ExecutionException {
- testForR8(parameters.getBackend())
- .addInnerClasses(MethodNameMinificationPrivateNamedLastTest.class)
- .enableMergeAnnotations()
- .enableInliningAnnotations()
- .addKeepMainRule(Runner.class)
- .setMinApi(parameters.getRuntime())
- .compile()
- .run(parameters.getRuntime(), Runner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspect(
- inspector -> {
- assertEquals("a", inspector.clazz(A.class).uniqueMethodWithName("m1").getFinalName());
- assertEquals("b", inspector.clazz(A.class).uniqueMethodWithName("m2").getFinalName());
- assertEquals("a", inspector.clazz(B.class).uniqueMethodWithName("m1").getFinalName());
- assertEquals("c", inspector.clazz(B.class).uniqueMethodWithName("m2").getFinalName());
- assertEquals("b", inspector.clazz(B.class).uniqueMethodWithName("m3").getFinalName());
- ClassSubject cSubject = inspector.clazz(C.class);
- assertEquals("a", cSubject.uniqueMethodWithName("m1").getFinalName());
- assertEquals("b", cSubject.uniqueMethodWithName("m3").getFinalName());
- AnyOf<String> cPrivateNamesP = anyOf(is("c"), is("d"));
- assertThat(cSubject.uniqueMethodWithName("m2").getFinalName(), cPrivateNamesP);
- assertThat(cSubject.uniqueMethodWithName("m4").getFinalName(), cPrivateNamesP);
- });
- }
-}
diff --git a/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
new file mode 100644
index 0000000..6d9f975
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
@@ -0,0 +1,205 @@
+// Copyright (c) 2019, 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class OverloadUniqueNameTest extends TestBase {
+
+ public static class A {
+ public void foo(int i) {}
+
+ public void foo(Object o) {}
+ }
+
+ public static class B extends A {
+
+ // This is here to ensure that foo is not visited first when iterating sorted methods.
+ public void baz(int i) {}
+
+ // This is an override, so it needs to have the same name as A.foo(int).
+ @Override
+ public void foo(int i) {}
+
+ public void foo(boolean b) {}
+
+ public void foo(int i, int j) {}
+
+ private void foo(char c) {}
+
+ private static void foo(String s) {}
+ }
+
+ private interface I1 {
+
+ void foo(long l);
+ }
+
+ private interface I2 {
+
+ void bar(long l);
+
+ void foo(long l);
+ }
+
+ public static class C extends B implements I1, I2 {
+
+ @Override
+ public void bar(long l) {}
+
+ @Override
+ // This method should be named according to I1.foo() and I2.foo().
+ public void foo(long l) {}
+
+ @Override
+ public void foo(int i) {}
+ }
+
+ private interface I3 {
+ void foo(long l);
+ }
+
+ private interface I4 extends I3 {
+ @Override
+ void foo(long l);
+ }
+
+ public static class LambdaTest {
+
+ public static void lambdaInterface() {
+ I1 lambda = ((I1 & I3) l -> {});
+ }
+ }
+
+ public static class ReturnType {
+
+ int foo() { // <-- changed to int foo() to test overloading on return type
+ System.out.println("int foo();");
+ return 0;
+ }
+
+ void rewrite(int dummy) {
+ System.out.println("void foo();");
+ }
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public OverloadUniqueNameTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws CompilationFailedException, IOException, ExecutionException {
+ testForR8(Backend.DEX)
+ .addProgramClasses(
+ A.class, B.class, I1.class, I2.class, C.class, I3.class, I4.class, LambdaTest.class)
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .compile()
+ .inspect(
+ codeInspector -> {
+ inspectUniqueMethodsInClass(codeInspector, I1.class);
+ inspectUniqueMethodsInClass(codeInspector, I2.class);
+ inspectUniqueMethodsInClass(codeInspector, I3.class);
+ inspectUniqueMethodsInClass(codeInspector, I4.class);
+ inspectUniqueMethodsInClass(codeInspector, A.class);
+ inspectUniqueMethodsInClass(codeInspector, B.class);
+ inspectUniqueMethodsInClass(codeInspector, C.class);
+
+ // Ensure that virtual overrides in the class hierarchy has the same name.
+ final MethodSubject aFoo = codeInspector.clazz(A.class).method("void", "foo", "int");
+ final MethodSubject bFoo = codeInspector.clazz(B.class).method("void", "foo", "int");
+ assertEquals(aFoo.getFinalName(), bFoo.getFinalName());
+ final MethodSubject cFoo = codeInspector.clazz(C.class).method("void", "foo", "int");
+ assertEquals(aFoo.getFinalName(), cFoo.getFinalName());
+
+ // Ensure that all SAM interfaces has same method name.
+ final MethodSubject i1Foo = codeInspector.clazz(I1.class).uniqueMethodWithName("foo");
+ final MethodSubject i2Foo = codeInspector.clazz(I2.class).uniqueMethodWithName("foo");
+ assertEquals(i1Foo.getFinalName(), i2Foo.getFinalName());
+ final MethodSubject i3Foo = codeInspector.clazz(I3.class).uniqueMethodWithName("foo");
+ assertEquals(i1Foo.getFinalName(), i3Foo.getFinalName());
+
+ // Ensure C has the correct name for the interface method.
+ final MethodSubject cIFoo =
+ codeInspector.clazz(C.class).method("void", "foo", "long");
+ assertEquals(cIFoo.getFinalName(), i1Foo.getFinalName());
+
+ // Ensure that I4.foo(int) has the same name as I3.foo(int).
+ final MethodSubject i4Foo = codeInspector.clazz(I4.class).uniqueMethodWithName("foo");
+ assertEquals(i3Foo.getFinalName(), i4Foo.getFinalName());
+ });
+ }
+
+ @Test
+ public void testReturnType() throws IOException, CompilationFailedException, ExecutionException {
+ testForR8(Backend.DEX)
+ .addProgramClassFileData(
+ transformer(ReturnType.class)
+ .addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access,
+ String name,
+ String descriptor,
+ String signature,
+ String[] exceptions) {
+ if (name.equals("rewrite")) {
+ return super.visitMethod(access, "foo", "()V", signature, exceptions);
+ }
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ })
+ .transform())
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .compile()
+ .inspect(
+ codeInspector -> {
+ Set<String> seenMethodNames = new HashSet<>();
+ for (FoundMethodSubject method : codeInspector.clazz(ReturnType.class).allMethods()) {
+ assertTrue(seenMethodNames.add(method.getFinalName()));
+ }
+ });
+ }
+
+ private void inspectUniqueMethodsInClass(CodeInspector inspector, Class<?> clazz) {
+ Set<String> newNames = new HashSet<>();
+ for (Method method : clazz.getDeclaredMethods()) {
+ final MethodSubject methodSubject = inspector.method(method);
+ assertThat(methodSubject, isPresent());
+ assertTrue(methodSubject.isRenamed());
+ assertTrue(newNames.add(methodSubject.getFinalName()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index e034df6..ff61668 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
@@ -214,6 +215,7 @@
@Test
public void testMethodResolution_aggressively() throws Exception {
+ assumeTrue(backend == Backend.CF);
methodResolution(true);
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index eade0a5..3d9645d 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -93,7 +93,8 @@
" invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;"),
ImmutableList.of(
" aconst_null",
- " invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
+ " invokevirtual"
+ + " java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
return builder;
}
@@ -266,7 +267,7 @@
MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
- assertEquals(m1.getFinalName(), m2.getFinalName());
+ assertNotEquals(m1.getFinalName(), m2.getFinalName());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
@@ -296,7 +297,11 @@
MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
- assertEquals(m1.getFinalName(), m2.getFinalName());
+ if (backend == Backend.DEX) {
+ assertNotEquals(m1.getFinalName(), m2.getFinalName());
+ } else {
+ assertEquals(m1.getFinalName(), m2.getFinalName());
+ }
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
@@ -410,7 +415,7 @@
MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
- assertEquals(m1.getFinalName(), m2.getFinalName());
+ assertNotEquals(m1.getFinalName(), m2.getFinalName());
ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(sub.isPresent());
@@ -420,7 +425,7 @@
MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(subM2.isPresent());
assertTrue(subM2.isRenamed());
- assertEquals(subM1.getFinalName(), subM2.getFinalName());
+ assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
// No matter what, overloading methods should be renamed to the same name.
assertEquals(m1.getFinalName(), subM1.getFinalName());
@@ -455,7 +460,11 @@
MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
- assertEquals(m1.getFinalName(), m2.getFinalName());
+ if (backend == Backend.DEX) {
+ assertNotEquals(m1.getFinalName(), m2.getFinalName());
+ } else {
+ assertEquals(m1.getFinalName(), m2.getFinalName());
+ }
ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(sub.isPresent());
@@ -465,7 +474,11 @@
MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(subM2.isPresent());
assertTrue(subM2.isRenamed());
- assertEquals(subM1.getFinalName(), subM2.getFinalName());
+ if (backend == Backend.DEX) {
+ assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
+ } else {
+ assertEquals(subM1.getFinalName(), subM2.getFinalName());
+ }
// No matter what, overloading methods should be renamed to the same name.
assertEquals(m1.getFinalName(), subM1.getFinalName());
diff --git a/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
new file mode 100644
index 0000000..50305fd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
@@ -0,0 +1,97 @@
+// 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.reflection;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dexsplitter.SplitterTestBase;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestClassForNameWhenSplit extends TestBase {
+
+ private final TestParameters parameters;
+ private final String EXPECTED = "caught";
+
+ @Parameters(name="{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public TestClassForNameWhenSplit(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testClassNotFound() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testClassForNameIsKeept() throws Exception {
+ Path featurePath = temp.newFile("feature1.zip").toPath();
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addFeatureSplit(
+ builder ->
+ SplitterTestBase.simpleSplitProvider(
+ builder, featurePath, temp, Foobar.class))
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(Foobar.class)
+ .compile()
+ .disassemble()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ try {
+ foo();
+ } catch (ClassNotFoundException e) {
+ System.out.println("caught");
+ } catch (IllegalAccessException e) {
+ System.out.println("illegal access");
+ } catch (InstantiationException e) {
+ System.out.println("instantiation exception");
+ }
+ }
+
+ private static void foo()
+ throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+ try {
+ // Ensure cl init has been assumed triggered
+ new Foobar();
+ } catch (NoClassDefFoundError e) { }
+ // It is not valid to replace this with just classForName, even if we see that there is only
+ // a trivial clinit or the fact that we have already triggered it above.
+ Class<?> foobar =
+ Class.forName("com.android.tools.r8.reflection.TestClassForNameWhenSplit$Foobar");
+ foobar.newInstance();
+ }
+ }
+
+ public static class Foobar {
+ public Foobar() {
+ if (System.currentTimeMillis() < 2) {
+ System.out.println("A long time ago, in a galaxy far far away");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index b951b4f..b42be41 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -40,7 +40,8 @@
D8,
JAVAC,
PROGUARD,
- R8
+ R8,
+ R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
}
private enum Mode {
@@ -207,6 +208,27 @@
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(r8Result, Compiler.R8);
+
+ R8TestRunResult r8ResultWithUninstantiatedTypeOptimizationForInterfaces =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(inputJar)
+ .addKeepMainRule(mainClass.name)
+ .addKeepRules(
+ "-keep class TestClass { public static I g; }",
+ "-neverinline class TestClass { public static void m(); }")
+ .enableProguardTestOptions()
+ .addOptionsModification(
+ options -> {
+ if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
+ options.testing.allowTypeErrors = true;
+ }
+ options.enableUninstantiatedTypeOptimizationForInterfaces = true;
+ })
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), mainClass.name);
+ checkTestRunResult(
+ r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
+ Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES);
}
private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
@@ -219,7 +241,9 @@
if (useInterface) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
- if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+ || compiler == Compiler.PROGUARD) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
result
@@ -233,7 +257,8 @@
if (useInterface) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
- if (compiler == Compiler.R8) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
result
.assertFailureWithOutput(getExpectedOutput(compiler))
.assertFailureWithErrorThatMatches(
@@ -261,7 +286,9 @@
if (useInterface) {
return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
} else {
- if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
+ if (compiler == Compiler.R8
+ || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+ || compiler == Compiler.PROGUARD) {
// The unverifiable method has been removed as a result of tree shaking, so the code does
// not fail with a verification error when trying to load class `UnverifiableClass`.
return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
@@ -274,14 +301,14 @@
}
assert mode == Mode.INVOKE_UNVERIFIABLE_METHOD;
if (useInterface) {
- if (compiler == Compiler.R8) {
+ if (compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
return StringUtils.joinLines(
"Hello!",
"Unexpected outcome of getstatic",
"Unexpected outcome of checkcast",
"Goodbye!",
"");
- } else if (compiler == Compiler.PROGUARD) {
+ } else if (compiler == Compiler.R8 || compiler == Compiler.PROGUARD) {
return StringUtils.joinLines("Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
} else if (compiler == Compiler.DX || compiler == Compiler.D8) {
if (ToolHelper.getDexVm().getVersion() == Version.V4_0_4
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 74f4321..5c6a305 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -207,9 +207,9 @@
assertThat(clazz, isPresent());
assertEquals(minify, clazz.isRenamed());
MethodSubject f1 = clazz.uniqueMethodWithName("f1");
- assertThat(f1, not(isPresent()));
+ assertThat(f1, isPresent());
MethodSubject f2 = clazz.uniqueMethodWithName("f2");
- assertThat(f2, not(isPresent()));
+ assertThat(f2, isPresent());
MethodSubject f3 = clazz.uniqueMethodWithName("f3");
assertThat(f3, not(isPresent()));
MethodSubject f4 = clazz.uniqueMethodWithName("f4");
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 705337c..d7f09cb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -74,6 +74,10 @@
return method(returnType, name, ImmutableList.of());
}
+ public MethodSubject method(String returnType, String name, String... parameters) {
+ return method(returnType, name, Arrays.asList(parameters));
+ }
+
public abstract MethodSubject method(String returnType, String name, List<String> parameters);
public abstract MethodSubject uniqueMethodWithName(String name);