Merge commit '06a3766c088acb54072bd661e9e5421cc4926a15' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 7335dbb..998b838 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -186,7 +186,9 @@
if (!options.mainDexKeepRules.isEmpty()) {
MainDexInfo mainDexInfo =
- new GenerateMainDexList(options).traceMainDex(executor, appView.appInfo().app());
+ new GenerateMainDexList(options)
+ .traceMainDex(
+ executor, appView.appInfo().app(), appView.appInfo().getMainDexInfo());
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 2ebde33..07783b3 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -49,7 +49,7 @@
// consumer.
DexApplication application = new ApplicationReader(app, options, timing).read(executor);
List<String> result = new ArrayList<>();
- traceMainDex(executor, application)
+ traceMainDex(executor, application, MainDexInfo.none())
.forEach(type -> result.add(type.toBinaryName() + ".class"));
Collections.sort(result);
if (options.mainDexListConsumer != null) {
@@ -62,10 +62,11 @@
}
}
- public MainDexInfo traceMainDex(ExecutorService executor, DexApplication application)
+ public MainDexInfo traceMainDex(
+ ExecutorService executor, DexApplication application, MainDexInfo existingMainDexInfo)
throws ExecutionException {
AppView<? extends AppInfoWithClassHierarchy> appView =
- AppView.createForR8(application.toDirect());
+ AppView.createForR8(application.toDirect(), existingMainDexInfo);
appView.setAppServices(AppServices.builder(appView).build());
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 00d4c93..70fe54d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCompareHelper;
@@ -164,12 +165,20 @@
}
}
+ public boolean isInvokeConstructor(DexItemFactory dexItemFactory) {
+ return getMethod().isInstanceInitializer(dexItemFactory);
+ }
+
public boolean isInvokeSuper(DexType clazz) {
return opcode == Opcodes.INVOKESPECIAL &&
method.holder != clazz &&
!method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
}
+ public boolean isInvokeSpecial() {
+ return opcode == Opcodes.INVOKESPECIAL;
+ }
+
public boolean isInvokeStatic() {
return opcode == Opcodes.INVOKESTATIC;
}
@@ -225,11 +234,7 @@
if (method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
type = Type.DIRECT;
} else if (code.getOriginalHolder() == method.holder) {
- MethodAndInvokeType methodAndInvokeType =
- transformInvokeSpecialToNonInitMethodOnHolder(
- builder.appView, code, builder.getProgramMethod());
- type = methodAndInvokeType.getInvokeType();
- canonicalMethod = methodAndInvokeType.getMethod();
+ type = invokeTypeForInvokeSpecialToNonInitMethodOnHolder(builder.appView, code);
} else {
type = Type.SUPER;
}
@@ -364,26 +369,8 @@
return true;
}
- private static class MethodAndInvokeType {
- private final DexMethod method;
- private final Invoke.Type invokeType;
-
- private MethodAndInvokeType(DexMethod method, Type invokeType) {
- this.method = method;
- this.invokeType = invokeType;
- }
-
- public DexMethod getMethod() {
- return method;
- }
-
- public Type getInvokeType() {
- return invokeType;
- }
- }
-
- private MethodAndInvokeType transformInvokeSpecialToNonInitMethodOnHolder(
- AppView<?> appView, CfSourceCode code, ProgramMethod context) {
+ private Type invokeTypeForInvokeSpecialToNonInitMethodOnHolder(
+ AppView<?> appView, CfSourceCode code) {
boolean desugaringEnabled = appView.options().isInterfaceMethodDesugaringEnabled();
MethodLookupResult lookupResult = appView.graphLens().lookupMethod(method, method, Type.DIRECT);
if (lookupResult.getType() == Type.VIRTUAL) {
@@ -391,38 +378,30 @@
// publicized to be final. For example, if a private method A.m() is publicized, and A is
// subsequently merged with a class B, with declares a public non-final method B.m(), then the
// horizontal class merger will merge A.m() and B.m() into a new non-final public method.
- return new MethodAndInvokeType(method, Type.VIRTUAL);
+ return Type.VIRTUAL;
}
DexMethod rewrittenMethod = lookupResult.getReference();
- DexEncodedMethod encodedMethod = lookupMethodOnHolder(appView, rewrittenMethod);
- if (encodedMethod == null) {
+ DexEncodedMethod definition = lookupMethodOnHolder(appView, rewrittenMethod);
+ if (definition == null) {
// The method is not defined on the class, we can use super to target. When desugaring
// default interface methods, it is expected they are targeted with invoke-direct.
- return new MethodAndInvokeType(
- method, this.itf && desugaringEnabled ? Type.DIRECT : Type.SUPER);
+ return this.itf && desugaringEnabled ? Type.DIRECT : Type.SUPER;
}
- if (encodedMethod.isPrivateMethod() || !encodedMethod.isVirtualMethod()) {
- return new MethodAndInvokeType(method, Type.DIRECT);
+ if (definition.isPrivateMethod() || !definition.isVirtualMethod()) {
+ return Type.DIRECT;
}
- if (encodedMethod.accessFlags.isFinal()) {
+ if (definition.isFinal()) {
// This method is final which indicates no subtype will overwrite it, we can use
// invoke-virtual.
- return new MethodAndInvokeType(method, Type.VIRTUAL);
+ return Type.VIRTUAL;
}
- if (this.itf && encodedMethod.isDefaultMethod()) {
- return new MethodAndInvokeType(method, desugaringEnabled ? Type.DIRECT : Type.SUPER);
+ if (itf && definition.isDefaultMethod()) {
+ return desugaringEnabled ? Type.DIRECT : Type.SUPER;
}
- assert encodedMethod.isNonPrivateVirtualMethod();
- assert context.getHolderType() == method.holder;
- // This is an invoke-special to a virtual method on invoke-special method holder.
- // The invoke should be rewritten with a bridge.
- DexMethod directMethod =
- appView.getInvokeSpecialBridgeSynthesizer().registerBridgeForMethod(encodedMethod);
- // In R8 the target should have been inserted in the enqueuer,
- // while in D8, the target is inserted at the end of the compilation.
- assert appView.enableWholeProgramOptimizations()
- == (context.getHolder().lookupDirectMethod(directMethod) != null);
- return new MethodAndInvokeType(directMethod, Type.DIRECT);
+ // We cannot emulate the semantics of invoke-special in this case and should throw a compilation
+ // error.
+ throw new CompilationError(
+ "Failed to compile unsupported use of invokespecial", code.getOrigin());
}
private DexEncodedMethod lookupMethodOnHolder(AppView<?> appView, DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
new file mode 100644
index 0000000..aa3247c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
@@ -0,0 +1,191 @@
+// Copyright (c) 2021, 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.contexts;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class CompilationContext {
+
+ // Internal contract to compute a unique suffix for synthetics.
+ private abstract static class ContextDescriptorProvider {
+
+ // Method to construct a fully qualified description of the context.
+ // This is used to ensure that all contexts are unique during compilation.
+ abstract StringBuilder buildContextDescriptorForTesting(StringBuilder builder);
+
+ // Build a suffix to append to synthetic definitions.
+ abstract StringBuilder buildSyntheticSuffix(StringBuilder builder);
+ }
+
+ /**
+ * Create the initial compilation context.
+ *
+ * <p>This context should be a singleton for a given compilation allocated by AppView.
+ */
+ public static CompilationContext createInitialContext(InternalOptions options) {
+ return new CompilationContext(options);
+ }
+
+ private final Consumer<String> testingConsumer;
+ private final Set<String> seenSetForTesting = new HashSet<>();
+ private int nextProcessorId = 0;
+
+ private CompilationContext(InternalOptions options) {
+ testingConsumer = options.testing.processingContextsConsumer;
+ }
+
+ private boolean verifyContext(ContextDescriptorProvider context) {
+ String descriptor = context.buildContextDescriptorForTesting(new StringBuilder()).toString();
+ String suffix = context.buildSyntheticSuffix(new StringBuilder()).toString();
+ assert descriptor.endsWith(suffix);
+ if (testingConsumer != null) {
+ testingConsumer.accept(descriptor);
+ }
+ assert seenSetForTesting.add(descriptor)
+ : "Duplicated use of context descriptor: " + descriptor;
+ return true;
+ }
+
+ /**
+ * Creates the context for a "processor".
+ *
+ * <p>A "processor" is just a compilation task but which is deterministically ordered as part of
+ * the full compilation pipeline. Thus, this method should only be called on the main-thread
+ * ensuring that the assigned ids are deterministic. The id itself has not particular meaning.
+ */
+ public ProcessorContext createProcessorContext() {
+ ProcessorContext processorContext = new ProcessorContext(this, nextProcessorId++);
+ assert verifyContext(processorContext);
+ return processorContext;
+ }
+
+ public static class ProcessorContext extends ContextDescriptorProvider {
+ private final CompilationContext parent;
+ private final int processorId;
+
+ private ProcessorContext(CompilationContext parent, int processorId) {
+ this.parent = parent;
+ this.processorId = processorId;
+ }
+
+ private boolean verifyContext(ContextDescriptorProvider context) {
+ assert parent.verifyContext(context);
+ return true;
+ }
+
+ /**
+ * Create processing context for a single method.
+ *
+ * <p>There should only ever be a single allocation of the particular method-processing context.
+ * This is generally ensured by, eg, a MethodProcessor, having private access to the processing
+ * context and ensuring a safe single allocation of the individual method-processing contexts.
+ */
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ MethodProcessingContext methodProcessingContext = new MethodProcessingContext(this, method);
+ assert verifyContext(methodProcessingContext);
+ return methodProcessingContext;
+ }
+
+ private StringBuilder buildSuffix(StringBuilder builder) {
+ return builder.append('$').append(processorId);
+ }
+
+ @Override
+ StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
+ return buildSuffix(builder);
+ }
+
+ @Override
+ StringBuilder buildSyntheticSuffix(StringBuilder builder) {
+ return buildSuffix(builder);
+ }
+ }
+
+ /** Description of the method context from which to synthesize. */
+ public static class MethodProcessingContext extends ContextDescriptorProvider {
+ private final ProcessorContext parent;
+ private final ProgramMethod method;
+ private int nextId = 0;
+
+ private MethodProcessingContext(ProcessorContext parent, ProgramMethod method) {
+ this.parent = parent;
+ this.method = method;
+ }
+
+ /**
+ * Create a unique processing context.
+ *
+ * <p>The uniqueness of the context requires that the parent, eg, method-context, is unique and
+ * that the processing of that entity is such that the calls to this method happen in a
+ * deterministic order, eg, by the processing of method instructions being single threaded.
+ */
+ public UniqueContext createUniqueContext() {
+ UniqueContext uniqueContext = new UniqueContext(this, nextId++);
+ assert parent.verifyContext(uniqueContext);
+ return uniqueContext;
+ }
+
+ DexProgramClass getClassContext() {
+ return method.getHolder();
+ }
+
+ private StringBuilder buildSuffix(StringBuilder builder) {
+ // TODO(b/172194101): Sanitize the method descriptor instead of hashing.
+ Hasher hasher = Hashing.sha256().newHasher();
+ method.getReference().hash(hasher);
+ return builder.append('$').append(hasher.hash().toString());
+ }
+
+ @Override
+ StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
+ // Put the type first in the context descriptor.
+ builder.append(getClassContext().getType().toDescriptorString());
+ return buildSuffix(parent.buildContextDescriptorForTesting(builder));
+ }
+
+ @Override
+ StringBuilder buildSyntheticSuffix(StringBuilder builder) {
+ return buildSuffix(parent.buildSyntheticSuffix(builder));
+ }
+ }
+
+ public static class UniqueContext extends ContextDescriptorProvider {
+ private final MethodProcessingContext parent;
+ private final int positionId;
+
+ private UniqueContext(MethodProcessingContext parent, int positionId) {
+ this.parent = parent;
+ this.positionId = positionId;
+ }
+
+ private StringBuilder buildSuffix(StringBuilder builder) {
+ return builder.append('$').append(positionId);
+ }
+
+ @Override
+ StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
+ return buildSuffix(parent.buildContextDescriptorForTesting(builder));
+ }
+
+ @Override
+ StringBuilder buildSyntheticSuffix(StringBuilder builder) {
+ return buildSuffix(parent.buildSyntheticSuffix(builder));
+ }
+
+ public DexProgramClass getClassContext() {
+ return parent.getClassContext();
+ }
+
+ public String getSyntheticSuffix() {
+ return buildSyntheticSuffix(new StringBuilder()).toString();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index de3d976..f2064b3 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.InternalOptions;
-import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
@@ -137,7 +136,7 @@
}
}
- public Collection<DexProgramClass> classes() {
+ public List<DexProgramClass> classes() {
assert checkIfObsolete();
return app.classes();
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index b56d682..8b09674 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.contexts.CompilationContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.DexValue.DexValueString;
@@ -19,8 +21,6 @@
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
-import com.android.tools.r8.ir.desugar.InvokeSpecialBridgeSynthesizer;
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -72,13 +72,11 @@
private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
new InstanceFieldInitializationInfoFactory();
- private final MethodProcessingId.Factory methodProcessingIdFactory;
private final SimpleInliningConstraintFactory simpleInliningConstraintFactory =
new SimpleInliningConstraintFactory();
// Desugaring.
public final PrefixRewritingMapper rewritePrefix;
- private final InvokeSpecialBridgeSynthesizer invokeSpecialBridgeSynthesizer;
// Modeling.
private final LibraryMethodSideEffectModelCollection libraryMethodSideEffectModelCollection;
@@ -103,20 +101,22 @@
// desugared. This information is populated in the IR converter.
private Set<DexType> alreadyLibraryDesugared = null;
+ private final CompilationContext context;
+
+ private final Thread mainThread = Thread.currentThread();
+
private AppView(
T appInfo,
WholeProgramOptimizations wholeProgramOptimizations,
PrefixRewritingMapper mapper) {
assert appInfo != null;
+ this.context = CompilationContext.createInitialContext(appInfo.options());
this.appInfo = appInfo;
this.dontWarnConfiguration = DontWarnConfiguration.create(options().getProguardConfiguration());
this.wholeProgramOptimizations = wholeProgramOptimizations;
this.graphLens = GraphLens.getIdentityLens();
this.initClassLens = InitClassLens.getDefault();
- this.methodProcessingIdFactory =
- new MethodProcessingId.Factory(options().testing.methodProcessingIdConsumer);
this.rewritePrefix = mapper;
- this.invokeSpecialBridgeSynthesizer = new InvokeSpecialBridgeSynthesizer(this);
if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
this.callSiteOptimizationInfoPropagator =
@@ -135,6 +135,11 @@
}
}
+ public boolean verifyMainThread() {
+ assert mainThread == Thread.currentThread();
+ return true;
+ }
+
@Override
public boolean isModeled(DexType type) {
return libraryMemberOptimizer.isModeled(type);
@@ -188,10 +193,6 @@
return instanceFieldInitializationInfoFactory;
}
- public MethodProcessingId.Factory methodProcessingIdFactory() {
- return methodProcessingIdFactory;
- }
-
public SimpleInliningConstraintFactory simpleInliningConstraintFactory() {
return simpleInliningConstraintFactory;
}
@@ -306,6 +307,17 @@
return wholeProgramOptimizations == WholeProgramOptimizations.ON;
}
+ /**
+ * Create a new processor context.
+ *
+ * <p>The order of processor contexts for a compilation must be deterministic so this is required
+ * to be called on the main thread only.
+ */
+ public ProcessorContext createProcessorContext() {
+ assert verifyMainThread();
+ return context.createProcessorContext();
+ }
+
public SyntheticItems getSyntheticItems() {
return appInfo.getSyntheticItems();
}
@@ -314,10 +326,6 @@
return callSiteOptimizationInfoPropagator;
}
- public InvokeSpecialBridgeSynthesizer getInvokeSpecialBridgeSynthesizer() {
- return invokeSpecialBridgeSynthesizer;
- }
-
public LibraryMemberOptimizer libraryMethodOptimizer() {
return libraryMemberOptimizer;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 7b1fe7c..1c30bbf 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -6,8 +6,8 @@
import static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.kotlin.Kotlin;
@@ -24,8 +24,6 @@
public class AssemblyWriter extends DexByteCodeWriter {
- private final MethodProcessingId.Factory methodProcessingIdFactory =
- new MethodProcessingId.Factory();
private final boolean writeAllClassInfo;
private final boolean writeFields;
private final boolean writeAnnotations;
@@ -34,6 +32,7 @@
private final AppInfo appInfo;
private final Kotlin kotlin;
private final Timing timing = new Timing("AssemblyWriter");
+ private final CompilationContext compilationContext;
public AssemblyWriter(
DexApplication application,
@@ -42,6 +41,7 @@
boolean writeIR,
boolean writeCode) {
super(application, options);
+ this.compilationContext = CompilationContext.createInitialContext(options);
this.writeAllClassInfo = allInfo;
this.writeFields = allInfo;
this.writeAnnotations = allInfo;
@@ -177,14 +177,14 @@
CfgPrinter printer = new CfgPrinter();
IRConverter converter = new IRConverter(appInfo, timing, printer);
OneTimeMethodProcessor methodProcessor =
- OneTimeMethodProcessor.create(method, methodProcessingIdFactory);
+ OneTimeMethodProcessor.create(method, compilationContext.createProcessorContext());
methodProcessor.forEachWaveWithExtension(
- (ignore, methodProcesingId) ->
- converter.processMethod(
+ (ignore, methodProcessingContext) ->
+ converter.processDesugaredMethod(
method,
OptimizationFeedbackIgnore.getInstance(),
methodProcessor,
- methodProcesingId));
+ methodProcessingContext));
ps.println(printer.toString());
}
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 df80a80..08d2880 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -63,7 +63,12 @@
/** InnerClasses table. If this class is an inner class, it will have an entry here. */
private List<InnerClassAttribute> innerClasses;
+ /**
+ * Nest attributes. If this class was compiled in JDK 11 and higher, and is in a nest, one of the
+ * two attributes will be set.
+ */
private NestHostClassAttribute nestHost;
+
private final List<NestMemberClassAttribute> nestMembers;
/** Generic signature information if the attribute is present in the input */
@@ -587,6 +592,10 @@
return accessFlags.isEnum();
}
+ public boolean isRecord() {
+ return accessFlags.isRecord();
+ }
+
public abstract void addDependencies(MixedSectionCollection collector);
@Override
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 c7a6e70..afc04c7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -11,6 +11,7 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
@@ -1287,14 +1288,10 @@
.setCode(
ForwardMethodBuilder.builder(dexItemFactory)
.setStaticSource(newMethod)
- .apply(
- builder -> {
- if (isStatic()) {
- builder.setStaticTarget(getReference(), holder.isInterface());
- } else {
- builder.setDirectTarget(getReference(), holder.isInterface());
- }
- })
+ .applyIf(
+ isStatic(),
+ builder -> builder.setStaticTarget(getReference(), holder.isInterface()),
+ builder -> builder.setDirectTarget(getReference(), holder.isInterface()))
.build())
.setAccessFlags(
MethodAccessFlags.builder()
@@ -1306,61 +1303,66 @@
.build());
}
- public DexEncodedMethod toPrivateSyntheticMethod(DexMethod method) {
- assert !accessFlags.isStatic();
- assert !accessFlags.isPrivate();
- assert getHolderType() == method.holder;
+ public ProgramMethod toPrivateSyntheticMethod(DexProgramClass holder, DexMethod method) {
+ assert !isStatic();
+ assert !isPrivate();
+ assert getHolderType() == method.getHolderType();
checkIfObsolete();
- Builder builder = syntheticBuilder(this);
- builder.setMethod(method);
- builder.accessFlags.setSynthetic();
- builder.accessFlags.unsetProtected();
- builder.accessFlags.unsetPublic();
- builder.accessFlags.setPrivate();
- return builder.build();
+ return new ProgramMethod(
+ holder,
+ syntheticBuilder(this)
+ .setMethod(method)
+ .modifyAccessFlags(
+ accessFlags -> {
+ accessFlags.setSynthetic();
+ accessFlags.unsetProtected();
+ accessFlags.unsetPublic();
+ accessFlags.setPrivate();
+ })
+ .build());
}
- public DexEncodedMethod toForwardingMethod(DexClass holder, DexDefinitionSupplier definitions) {
- DexMethod newMethod = method.withHolder(holder.type, definitions.dexItemFactory());
+ public DexEncodedMethod toForwardingMethod(
+ DexClass newHolder, DexDefinitionSupplier definitions) {
+ DexMethod newMethod = method.withHolder(newHolder, definitions.dexItemFactory());
checkIfObsolete();
+
// Clear the final flag, as this method is now overwritten. Do this before creating the builder
// for the forwarding method, as the forwarding method will copy the access flags from this,
// and if different forwarding methods are created in different subclasses the first could be
// final.
accessFlags.demoteFromFinal();
- 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
- // will not actually forward, it will create the same exception when hit at runtime.
- builder.accessFlags.setAbstract();
- } else {
- // Create code that forwards the call to the target.
- DexClass target = definitions.definitionFor(method.holder);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
- forwardSourceCodeBuilder
- .setReceiver(accessFlags.isStatic() ? null : newMethod.getHolderType())
- .setTargetReceiver(accessFlags.isStatic() ? null : method.holder)
- .setTarget(method)
- .setInvokeType(accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER)
- .setIsInterface(target.isInterface());
- builder.setCode(
- new SynthesizedCode(
- forwardSourceCodeBuilder::build,
- registry -> {
- if (accessFlags.isStatic()) {
- registry.registerInvokeStatic(method);
- } else {
- registry.registerInvokeSuper(method);
- }
- }));
- builder.accessFlags.setBridge();
- }
- builder.accessFlags.setSynthetic();
- // Note that we are not marking this instance obsolete, since it is not: the newly synthesized
- // forwarding method has a separate code that literally forwards to the current method.
- return builder.build();
+
+ return syntheticBuilder(this)
+ .setMethod(newMethod)
+ .modifyAccessFlags(MethodAccessFlags::setSynthetic)
+ // If the forwarding target is abstract, we can just create an abstract method. While it
+ // will not actually forward, it will create the same exception when hit at runtime.
+ // Otherwise, we need to create code that forwards the call to the target.
+ .applyIf(
+ !isAbstract(),
+ builder ->
+ builder
+ .setCode(
+ ForwardMethodBuilder.builder(definitions.dexItemFactory())
+ .setStaticSource(newMethod)
+ .applyIf(
+ isStatic(),
+ codeBuilder ->
+ codeBuilder
+ .setStaticSource(newMethod)
+ .setStaticTarget(
+ getReference(),
+ method.getHolderType().isInterface(definitions)),
+ codeBuilder ->
+ codeBuilder
+ .setNonStaticSource(newMethod)
+ .setSuperTarget(
+ getReference(),
+ method.getHolderType().isInterface(definitions)))
+ .build())
+ .modifyAccessFlags(MethodAccessFlags::setBridge))
+ .build();
}
public static DexEncodedMethod createDesugaringForwardingMethod(
@@ -1571,6 +1573,20 @@
}
}
+ public Builder applyIf(boolean condition, Consumer<Builder> thenConsumer) {
+ return applyIf(condition, thenConsumer, emptyConsumer());
+ }
+
+ public Builder applyIf(
+ boolean condition, Consumer<Builder> thenConsumer, Consumer<Builder> elseConsumer) {
+ if (condition) {
+ thenConsumer.accept(this);
+ } else {
+ elseConsumer.accept(this);
+ }
+ return this;
+ }
+
public Builder setSimpleInliningConstraint(
DexProgramClass holder, SimpleInliningConstraint simpleInliningConstraint) {
return addBuildConsumer(
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index f806886..aaeb156 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -319,6 +319,10 @@
return false;
}
+ public boolean isInterface(DexDefinitionSupplier definitionSupplier) {
+ return definitionSupplier.definitionFor(this).isInterface();
+ }
+
public boolean isProgramType(DexDefinitionSupplier definitions) {
DexClass clazz = definitions.definitionFor(this);
return clazz != null && clazz.isProgramClass();
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 1f67c4d..35b4574 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
@@ -205,6 +206,7 @@
private DexString sourceFile;
private NestHostClassAttribute nestHost = null;
private final List<NestMemberClassAttribute> nestMembers = new ArrayList<>();
+ private final Set<DexField> recordComponents = Sets.newIdentityHashSet();
private EnclosingMethodAttribute enclosingMember = null;
private final List<InnerClassAttribute> innerClasses = new ArrayList<>();
private ClassSignature classSignature = ClassSignature.noSignature();
@@ -301,7 +303,16 @@
@Override
public RecordComponentVisitor visitRecordComponent(
String name, String descriptor, String signature) {
- // TODO(b/169645628): Support Records.
+ assert name != null;
+ assert descriptor != null;
+ // Javac generated record components are only the instance fields, so we just reuse the field
+ // to avoid duplicating the field and field signature rewriting logic.
+ DexField field =
+ application
+ .getFactory()
+ .createField(
+ type, application.getTypeFromDescriptor(descriptor), application.getString(name));
+ recordComponents.add(field);
return super.visitRecordComponent(name, descriptor, signature);
}
@@ -327,12 +338,6 @@
}
this.deprecated = AsmUtils.isDeprecated(access);
accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access));
- if (accessFlags.isRecord()) {
- // TODO(b/169645628): Support records in all compilation.
- if (!application.options.canUseRecords()) {
- throw new CompilationError("Records are not supported", origin);
- }
- }
type = application.getTypeFromName(name);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
@@ -426,6 +431,7 @@
type, defaultAnnotations, application.getFactory()));
}
checkReachabilitySensitivity();
+ checkRecord();
T clazz =
classKind.create(
type,
@@ -489,6 +495,28 @@
classConsumer.accept(clazz);
}
+ private void checkRecord() {
+ if (!accessFlags.isRecord()) {
+ return;
+ }
+ // TODO(b/169645628): Support records in all compilation.
+ if (!application.options.canUseRecords()) {
+ throw new CompilationError("Records are not supported", origin);
+ }
+ // TODO(b/169645628): Change this logic if we start stripping the record components.
+ // Another approach would be to mark a bit in fields that are record components instead.
+ String message = "Records are expected to have one record component per instance field.";
+ if (recordComponents.size() != instanceFields.size()) {
+ throw new CompilationError(message, origin);
+ }
+ for (DexEncodedField instanceField : instanceFields) {
+ if (!recordComponents.contains(instanceField.field)) {
+ throw new CompilationError(
+ message + " Unmatched field " + instanceField.field + ".", origin);
+ }
+ }
+ }
+
private ChecksumSupplier getChecksumSupplier(ClassKind<T> classKind) {
if (application.options.encodeChecksums && classKind == ClassKind.PROGRAM) {
CRC32 crc = new CRC32();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index e9b454a..f15ad69 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -157,12 +157,12 @@
SortedProgramMethodSet.create(this::forEachFindLiteExtensionByNumberMethod);
OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
methodProcessor.forEachWaveWithExtension(
- (method, methodProcessingId) ->
- converter.processMethod(
+ (method, methodProcessingContext) ->
+ converter.processDesugaredMethod(
method,
OptimizationFeedbackIgnore.getInstance(),
methodProcessor,
- methodProcessingId),
+ methodProcessingContext),
executorService);
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 45f5083..ab3673e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -81,12 +81,12 @@
SortedProgramMethodSet wave = SortedProgramMethodSet.create(this::forEachDynamicMethod);
OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
methodProcessor.forEachWaveWithExtension(
- (method, methodProcessingId) ->
- converter.processMethod(
+ (method, methodProcessingContext) ->
+ converter.processDesugaredMethod(
method,
OptimizationFeedbackIgnore.getInstance(),
methodProcessor,
- methodProcessingId),
+ methodProcessingContext),
executorService);
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java
index 00e824f..5c3692c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.analysis.proto;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -12,6 +11,7 @@
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -20,7 +20,8 @@
private final ProtoReferences references;
- private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap =
+ private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap = new IdentityHashMap<>();
+ private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMapDelayed =
new ConcurrentHashMap<>();
public ProtoEnumSwitchMapRemover(ProtoReferences references) {
@@ -34,10 +35,15 @@
assert clazz.isEnum();
EnumStaticFieldValues enumStaticFieldValues = staticFieldValues.asEnumStaticFieldValues();
if (isProtoEnum(clazz)) {
- staticFieldValuesMap.put(clazz.type, enumStaticFieldValues);
+ staticFieldValuesMapDelayed.put(clazz.type, enumStaticFieldValues);
}
}
+ public void updateVisibleStaticFieldValues() {
+ staticFieldValuesMap.putAll(staticFieldValuesMapDelayed);
+ staticFieldValuesMapDelayed.clear();
+ }
+
private boolean isProtoEnum(DexProgramClass clazz) {
assert clazz.isEnum();
if (clazz.type == references.methodToInvokeType) {
@@ -53,9 +59,8 @@
}
EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
if (enumStaticFieldValues == null) {
- if (enumClass.type == references.methodToInvokeType) {
- throw new CompilationError("Proto optimizations: missing information for MethodToInvoke.");
- }
+ // If the switch map is found in a wave previous to the wave containing the enum clinit,
+ // then bail out. This can happen but is extremely uncommon.
return null;
}
ObjectState state =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 7d32802..1a25cb1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -5,9 +5,12 @@
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -18,10 +21,12 @@
public abstract class ClassConverter {
+ protected final AppView<?> appView;
private final IRConverter converter;
private final D8MethodProcessor methodProcessor;
- ClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
+ ClassConverter(AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+ this.appView = appView;
this.converter = converter;
this.methodProcessor = methodProcessor;
}
@@ -30,18 +35,16 @@
AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
return appView.options().desugarSpecificOptions().allowAllDesugaredInput
? new LibraryDesugaredClassConverter(appView, converter, methodProcessor)
- : new DefaultClassConverter(converter, methodProcessor);
+ : new DefaultClassConverter(appView, converter, methodProcessor);
}
- public void convertClasses(DexApplication application, ExecutorService executorService)
- throws ExecutionException {
- internalConvertClasses(application, executorService);
+ public void convertClasses(ExecutorService executorService) throws ExecutionException {
+ internalConvertClasses(executorService);
notifyAllClassesConverted();
}
- private void internalConvertClasses(DexApplication application, ExecutorService executorService)
- throws ExecutionException {
- List<DexProgramClass> classes = application.classes();
+ private void internalConvertClasses(ExecutorService executorService) throws ExecutionException {
+ List<DexProgramClass> classes = appView.appInfo().classes();
while (!classes.isEmpty()) {
Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
List<DexProgramClass> deferred = new ArrayList<>(classes.size() / 2);
@@ -51,31 +54,70 @@
deferred.add(clazz);
} else {
wave.add(clazz);
+
+ // TODO(b/179755192): Avoid marking classes as scheduled by building up waves of methods.
+ methodProcessor.addScheduled(clazz);
}
}
- ThreadUtils.processItems(wave, this::convertClass, executorService);
+
+ // Process the wave and wait for all IR processing to complete.
+ D8CfInstructionDesugaringEventConsumer desugaringEventConsumer =
+ CfInstructionDesugaringEventConsumer.createForD8();
+ methodProcessor.newWave();
+ ThreadUtils.processItems(
+ wave, clazz -> convertClass(clazz, desugaringEventConsumer), executorService);
methodProcessor.awaitMethodProcessing();
+
+ // Finalize the desugaring of the processed classes. This may require reprocessing of some
+ // methods, because nest-based access desugaring changes the body of virtual methods.
+ List<ProgramMethod> needsReprocessing = desugaringEventConsumer.finalizeDesugaring(appView);
+ if (!needsReprocessing.isEmpty()) {
+ // Create a new processor context to ensure unique method processing contexts.
+ methodProcessor.newWave();
+
+ // Process the methods that require reprocessing. These are all simple bridge methods and
+ // should therefore not lead to additional desugaring.
+ ThreadUtils.processItems(
+ needsReprocessing,
+ method -> {
+ DexEncodedMethod definition = method.getDefinition();
+ assert definition.isProcessed();
+ definition.markNotProcessed();
+ methodProcessor.processMethod(method, desugaringEventConsumer);
+ },
+ executorService);
+
+ // Verify that there is no more desugaring to do, and that all IR processing has been
+ // completed.
+ assert desugaringEventConsumer.verifyNothingToFinalize();
+ assert methodProcessor.verifyNoPendingMethodProcessing();
+ }
+
classes = deferred;
}
}
- abstract void convertClass(DexProgramClass clazz);
+ abstract void convertClass(
+ DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer);
- void convertMethods(DexProgramClass clazz) {
- converter.convertMethods(clazz, methodProcessor);
+ void convertMethods(
+ DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
+ converter.convertMethods(clazz, desugaringEventConsumer, methodProcessor);
}
abstract void notifyAllClassesConverted();
static class DefaultClassConverter extends ClassConverter {
- DefaultClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
- super(converter, methodProcessor);
+ DefaultClassConverter(
+ AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+ super(appView, converter, methodProcessor);
}
@Override
- void convertClass(DexProgramClass clazz) {
- convertMethods(clazz);
+ void convertClass(
+ DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
+ convertMethods(clazz, desugaringEventConsumer);
}
@Override
@@ -86,24 +128,23 @@
static class LibraryDesugaredClassConverter extends ClassConverter {
- private final AppView<?> appView;
private final Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
LibraryDesugaredClassConverter(
AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
- super(converter, methodProcessor);
- this.appView = appView;
+ super(appView, converter, methodProcessor);
}
@Override
- void convertClass(DexProgramClass clazz) {
+ void convertClass(
+ DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
// Classes which has already been through library desugaring will not go through IR
// processing again.
LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
alreadyLibraryDesugared.add(clazz.getType());
} else {
- convertMethods(clazz);
+ convertMethods(clazz, desugaringEventConsumer);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
index a10c562..86b7f73 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CodeOptimization.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -25,10 +26,10 @@
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId);
+ MethodProcessingContext methodProcessingContext);
static CodeOptimization from(Consumer<IRCode> consumer) {
- return (code, feedback, methodProcessor, methodProcessingId) -> {
+ return (code, feedback, methodProcessor, methodProcessingContext) -> {
consumer.accept(code);
};
}
@@ -38,9 +39,9 @@
}
static CodeOptimization sequence(Collection<CodeOptimization> codeOptimizations) {
- return (code, feedback, methodProcessor, methodProcessingId) -> {
+ return (code, feedback, methodProcessor, methodProcessingContext) -> {
for (CodeOptimization codeOptimization : codeOptimizations) {
- codeOptimization.optimize(code, feedback, methodProcessor, methodProcessingId);
+ codeOptimization.optimize(code, feedback, methodProcessor, methodProcessingContext);
}
};
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index d8fb60c..2d3247e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -3,13 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -19,10 +25,23 @@
private final IRConverter converter;
private final ExecutorService executorService;
private final List<Future<?>> futures = Collections.synchronizedList(new ArrayList<>());
+ private final Set<DexType> scheduled = Sets.newIdentityHashSet();
+
+ private ProcessorContext processorContext;
public D8MethodProcessor(IRConverter converter, ExecutorService executorService) {
this.converter = converter;
this.executorService = executorService;
+ this.processorContext = converter.appView.createProcessorContext();
+ }
+
+ public void addScheduled(DexProgramClass clazz) {
+ boolean added = scheduled.add(clazz.getType());
+ assert added;
+ }
+
+ public void newWave() {
+ this.processorContext = converter.appView.createProcessorContext();
}
@Override
@@ -37,11 +56,22 @@
}
@Override
- public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+ public void scheduleDesugaredMethodForProcessing(ProgramMethod method) {
+ // TODO(b/179755192): By building up waves of methods in the class converter, we can avoid the
+ // following check and always process the method asynchronously.
+ if (!scheduled.contains(method.getHolderType())
+ && !converter.appView.getSyntheticItems().isNonLegacySynthetic(method.getHolder())) {
+ // The non-synthetic holder is not scheduled. It will be processed once holder is scheduled.
+ return;
+ }
futures.add(
ThreadUtils.processAsynchronously(
() ->
- converter.rewriteCode(method, OptimizationFeedbackIgnore.getInstance(), this, null),
+ converter.rewriteDesugaredCode(
+ method,
+ OptimizationFeedbackIgnore.getInstance(),
+ this,
+ processorContext.createMethodProcessingContext(method)),
executorService));
}
@@ -54,4 +84,18 @@
ThreadUtils.awaitFutures(futures);
futures.clear();
}
+
+ public void processMethod(
+ ProgramMethod method, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
+ converter.convertMethod(
+ method,
+ desugaringEventConsumer,
+ this,
+ processorContext.createMethodProcessingContext(method));
+ }
+
+ public boolean verifyNoPendingMethodProcessing() {
+ assert futures.isEmpty();
+ return true;
+ }
}
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 b1e68ace..7a45467 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -42,6 +43,8 @@
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
@@ -51,6 +54,7 @@
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBridgeConsumer;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -97,7 +101,9 @@
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.SupplierUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -131,6 +137,7 @@
private final StringBuilderOptimizer stringBuilderOptimizer;
private final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
private final LambdaRewriter lambdaRewriter;
+ private final InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring;
private final D8NestBasedAccessDesugaring d8NestBasedAccessDesugaring;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
@@ -233,6 +240,7 @@
TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(appView.options())
? new TwrCloseResourceRewriter(appView)
: null;
+ this.invokeSpecialToSelfDesugaring = new InvokeSpecialToSelfDesugaring(appView);
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.covariantReturnTypeAnnotationTransformer = null;
@@ -313,6 +321,7 @@
this.devirtualizer =
options.enableDevirtualization ? new Devirtualizer(appViewWithLiveness) : null;
this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
+ this.invokeSpecialToSelfDesugaring = null;
this.d8NestBasedAccessDesugaring = null;
this.serviceLoaderRewriter =
options.enableServiceLoaderRewriting
@@ -339,6 +348,7 @@
this.identifierNameStringMarker = null;
this.devirtualizer = null;
this.typeChecker = null;
+ this.invokeSpecialToSelfDesugaring = new InvokeSpecialToSelfDesugaring(appView);
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.desugaredLibraryAPIConverter =
@@ -437,12 +447,6 @@
}
}
- private void synthesizeInvokeSpecialBridges(ExecutorService executorService)
- throws ExecutionException {
- assert !appView.enableWholeProgramOptimizations();
- appView.getInvokeSpecialBridgeSynthesizer().insertBridgesForD8(this, executorService);
- }
-
private void synthesizeEnumUnboxingUtilityMethods(ExecutorService executorService)
throws ExecutionException {
if (enumUnboxer != null) {
@@ -464,7 +468,7 @@
DexApplication application = appView.appInfo().app();
timing.begin("IR conversion");
- convertClasses(application, executor);
+ convertClasses(executor);
reportNestDesugarDependencies();
@@ -485,7 +489,6 @@
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
processSynthesizedJava8UtilityClasses(executor);
synthesizeRetargetClass(builder, executor);
- synthesizeInvokeSpecialBridges(executor);
processCovariantReturnTypeAnnotations(builder);
generateDesugaredLibraryAPIWrappers(builder, executor);
@@ -498,33 +501,45 @@
appView.appInfo().getMainDexInfo()));
}
- private void convertClasses(DexApplication application, ExecutorService executorService)
- throws ExecutionException {
+ private void convertClasses(ExecutorService executorService) throws ExecutionException {
D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executorService);
ClassConverter classConverter = ClassConverter.create(appView, this, methodProcessor);
- classConverter.convertClasses(application, executorService);
+ classConverter.convertClasses(executorService);
synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
methodProcessor.awaitMethodProcessing();
}
- void convertMethods(DexProgramClass clazz, MethodProcessor methodProcessor) {
+ void convertMethods(
+ DexProgramClass clazz,
+ D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ D8MethodProcessor methodProcessor) {
boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
// When converting all methods on a class always convert <clinit> first.
- DexEncodedMethod classInitializer = clazz.getClassInitializer();
+ ProgramMethod classInitializer = clazz.getProgramClassInitializer();
+
+ // TODO(b/179755192): We currently need to copy the class' methods, to avoid a
+ // ConcurrentModificationException from the insertion of methods due to invoke-special
+ // desugaring. By building up waves of methods in the class converter, we would not need to
+ // iterate the methods of a class during while its methods are being processed, which avoids
+ // the need to copy the method list.
+ List<ProgramMethod> methods = ListUtils.newArrayList(clazz::forEachProgramMethod);
if (classInitializer != null) {
classInitializer
+ .getDefinition()
.getMutableOptimizationInfo()
.setReachabilitySensitive(isReachabilitySensitive);
- convertMethod(new ProgramMethod(clazz, classInitializer), methodProcessor);
+ methodProcessor.processMethod(classInitializer, desugaringEventConsumer);
}
- clazz.forEachProgramMethodMatching(
- definition -> !definition.isClassInitializer(),
- method -> {
- DexEncodedMethod definition = method.getDefinition();
- definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
- convertMethod(method, methodProcessor);
- });
+
+ for (ProgramMethod method : methods) {
+ if (!method.getDefinition().isClassInitializer()) {
+ DexEncodedMethod definition = method.getDefinition();
+ definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
+ methodProcessor.processMethod(method, desugaringEventConsumer);
+ }
+ }
+
// The class file version is downgraded after compilation. Some of the desugaring might need
// the initial class file version to determine how far a method can be downgraded.
if (clazz.hasClassFileVersion()) {
@@ -533,7 +548,11 @@
}
}
- private void convertMethod(ProgramMethod method, MethodProcessor methodProcessor) {
+ void convertMethod(
+ ProgramMethod method,
+ D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
DexEncodedMethod definition = method.getDefinition();
if (definition.hasClassFileVersion()) {
definition.downgradeClassFileVersion(
@@ -552,7 +571,12 @@
if (options.isGeneratingClassFiles()
|| !(options.passthroughDexCode && definition.getCode().isDexCode())) {
// We do not process in call graph order, so anything could be a leaf.
- rewriteCode(method, simpleOptimizationFeedback, methodProcessor, null);
+ rewriteCode(
+ method,
+ desugaringEventConsumer,
+ simpleOptimizationFeedback,
+ methodProcessor,
+ methodProcessingContext);
} else {
assert definition.getCode().isDexCode();
}
@@ -687,8 +711,9 @@
outliner.createOutlineMethodIdentifierGenerator();
}
primaryMethodProcessor.forEachMethod(
- (method, methodProcessingId) ->
- processMethod(method, feedback, primaryMethodProcessor, methodProcessingId),
+ (method, methodProcessingContext) ->
+ processDesugaredMethod(
+ method, feedback, primaryMethodProcessor, methodProcessingContext),
this::waveStart,
this::waveDone,
timing,
@@ -872,6 +897,9 @@
if (options.enableFieldAssignmentTracker) {
fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
}
+ if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
+ appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
+ }
assert delayedOptimizationFeedback.noUpdatesLeft();
onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
onWaveDoneActions = null;
@@ -1003,9 +1031,9 @@
OneTimeMethodProcessor methodProcessor =
OneTimeMethodProcessor.create(synthesizedMethod, appView);
methodProcessor.forEachWaveWithExtension(
- (method, methodProcessingId) ->
- processMethod(
- method, delayedOptimizationFeedback, methodProcessor, methodProcessingId));
+ (method, methodProcessingContext) ->
+ processDesugaredMethod(
+ method, delayedOptimizationFeedback, methodProcessor, methodProcessingContext));
}
}
@@ -1014,9 +1042,9 @@
if (!wave.isEmpty()) {
OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
methodProcessor.forEachWaveWithExtension(
- (method, methodProcessingId) ->
- processMethod(
- method, delayedOptimizationFeedback, methodProcessor, methodProcessingId),
+ (method, methodProcessingContext) ->
+ processDesugaredMethod(
+ method, delayedOptimizationFeedback, methodProcessor, methodProcessingContext),
executorService);
}
}
@@ -1036,16 +1064,16 @@
}
// TODO(b/140766440): Make this receive a list of CodeOptimizations to conduct.
- public Timing processMethod(
+ public Timing processDesugaredMethod(
ProgramMethod method,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
DexEncodedMethod definition = method.getDefinition();
Code code = definition.getCode();
boolean matchesMethodFilter = options.methodMatchesFilter(definition);
if (code != null && matchesMethodFilter) {
- return rewriteCode(method, feedback, methodProcessor, methodProcessingId);
+ return rewriteDesugaredCode(method, feedback, methodProcessor, methodProcessingContext);
} else {
// Mark abstract methods as processed as well.
definition.markProcessed(ConstraintWithTarget.NEVER);
@@ -1063,20 +1091,41 @@
Timing rewriteCode(
ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
return ExceptionUtils.withOriginAndPositionAttachmentHandler(
method.getOrigin(),
new MethodPosition(method.getReference().asMethodReference()),
- () -> rewriteCodeInternal(method, feedback, methodProcessor, methodProcessingId));
+ () ->
+ rewriteCodeInternal(
+ method,
+ desugaringEventConsumer,
+ feedback,
+ methodProcessor,
+ methodProcessingContext));
+ }
+
+ Timing rewriteDesugaredCode(
+ ProgramMethod method,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ return rewriteCode(
+ method,
+ CfInstructionDesugaringEventConsumer.createForDesugaredCode(),
+ feedback,
+ methodProcessor,
+ methodProcessingContext);
}
private Timing rewriteCodeInternal(
ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
if (options.verbose) {
options.reporter.info(
new StringDiagnostic("Processing: " + method.toSourceString()));
@@ -1088,7 +1137,8 @@
method.toSourceString(),
logCode(options, method.getDefinition()));
}
- boolean didDesugar = desugar(method, methodProcessor);
+ boolean didDesugar =
+ desugar(method, desugaringEventConsumer, methodProcessor, methodProcessingContext);
if (Log.ENABLED && didDesugar) {
Log.debug(
getClass(),
@@ -1108,29 +1158,37 @@
feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
return Timing.empty();
}
- return optimize(code, feedback, methodProcessor, methodProcessingId);
+ return optimize(code, feedback, methodProcessor, methodProcessingContext);
}
- private boolean desugar(ProgramMethod method, MethodProcessor methodProcessor) {
- if (options.desugarState != DesugarState.ON) {
+ private boolean desugar(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ if (options.desugarState.isOff() || !method.getDefinition().getCode().isCfCode()) {
return false;
}
- if (!method.getDefinition().getCode().isCfCode()) {
- return false;
- }
+
boolean didDesugar = false;
Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
- Suppliers.memoize(appView::appInfoForDesugaring);
+ SupplierUtils.nonThreadSafeMemoize(appView::appInfoForDesugaring);
if (lambdaRewriter != null) {
- didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
+ didDesugar |=
+ lambdaRewriter.desugarLambdas(method, lazyAppInfo.get(), methodProcessingContext) > 0;
}
if (backportedMethodRewriter != null) {
- didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
+ didDesugar |=
+ backportedMethodRewriter.desugar(method, lazyAppInfo.get(), methodProcessingContext);
}
if (d8NestBasedAccessDesugaring != null) {
NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
didDesugar |= d8NestBasedAccessDesugaring.desugar(method, bridgeConsumer);
}
+ if (invokeSpecialToSelfDesugaring != null) {
+ didDesugar |= invokeSpecialToSelfDesugaring.desugar(method, desugaringEventConsumer);
+ }
+
return didDesugar;
}
@@ -1139,7 +1197,7 @@
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
ProgramMethod context = code.context();
DexEncodedMethod method = context.getDefinition();
DexProgramClass holder = context.getHolder();
@@ -1222,7 +1280,7 @@
if (serviceLoaderRewriter != null) {
assert appView.appInfo().hasLiveness();
timing.begin("Rewrite service loaders");
- serviceLoaderRewriter.rewrite(code, methodProcessingId);
+ serviceLoaderRewriter.rewrite(code, methodProcessingContext);
timing.end();
}
@@ -1296,7 +1354,7 @@
timing.begin("Optimize library methods");
appView
.libraryMethodOptimizer()
- .optimize(code, feedback, methodProcessor, methodProcessingId);
+ .optimize(code, feedback, methodProcessor, methodProcessingContext);
timing.end();
assert code.isConsistentSSA();
}
@@ -1314,7 +1372,7 @@
timing.begin("Remove trivial type checks/casts");
codeRewriter.removeTrivialCheckCastAndInstanceOfInstructions(
- code, context, methodProcessor, methodProcessingId);
+ code, context, methodProcessor, methodProcessingContext);
timing.end();
if (enumValueOptimizer != null) {
@@ -1361,7 +1419,7 @@
if (codeRewriter.simplifyControlFlow(code)) {
timing.begin("Remove trivial type checks/casts");
codeRewriter.removeTrivialCheckCastAndInstanceOfInstructions(
- code, context, methodProcessor, methodProcessingId);
+ code, context, methodProcessor, methodProcessingContext);
timing.end();
}
timing.end();
@@ -1439,7 +1497,7 @@
code,
feedback,
methodProcessor,
- methodProcessingId,
+ methodProcessingContext,
inliner,
Suppliers.memoize(
() ->
@@ -1459,9 +1517,9 @@
if (interfaceMethodRewriter != null) {
timing.begin("Rewrite interface methods");
- interfaceMethodRewriter.rewriteMethodReferences(code, methodProcessor, methodProcessingId);
+ interfaceMethodRewriter.rewriteMethodReferences(
+ code, methodProcessor, methodProcessingContext);
timing.end();
- assert code.isConsistentSSA();
}
previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
@@ -1480,7 +1538,7 @@
if (twrCloseResourceRewriter != null) {
timing.begin("Rewrite TWR close");
- twrCloseResourceRewriter.rewriteIR(code);
+ twrCloseResourceRewriter.rewriteIR(code, methodProcessingContext);
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessingId.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessingId.java
deleted file mode 100644
index 143d6ff..0000000
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessingId.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.conversion;
-
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import java.util.function.BiConsumer;
-
-public class MethodProcessingId {
-
- private final int primaryId;
- private int secondaryId = 1;
-
- private MethodProcessingId(int primaryId) {
- this.primaryId = primaryId;
- }
-
- public String getAndIncrementId() {
- String id = getId();
- secondaryId++;
- return id;
- }
-
- public String getFullyQualifiedIdAndIncrement() {
- String id = getFullyQualifiedId();
- secondaryId++;
- return id;
- }
-
- public String getId() {
- if (secondaryId == 1) {
- return Integer.toString(primaryId);
- }
- return getFullyQualifiedId();
- }
-
- public String getFullyQualifiedId() {
- return primaryId + "$" + secondaryId;
- }
-
- public int getPrimaryId() {
- return primaryId;
- }
-
- public static class Factory {
-
- private final BiConsumer<ProgramMethod, MethodProcessingId> consumer;
- private int nextId = 1;
-
- public Factory() {
- this(null);
- }
-
- public Factory(BiConsumer<ProgramMethod, MethodProcessingId> consumer) {
- this.consumer = consumer;
- }
-
- public ReservedMethodProcessingIds reserveIds(SortedProgramMethodSet wave) {
- ReservedMethodProcessingIds result = new ReservedMethodProcessingIds(nextId, wave.size());
- nextId += wave.size();
- return result;
- }
-
- public class ReservedMethodProcessingIds {
-
- private final int firstReservedId;
- private final int numberOfReservedIds;
-
- private final ProgramMethodSet seen =
- InternalOptions.assertionsEnabled() ? ProgramMethodSet.createConcurrent() : null;
-
- public ReservedMethodProcessingIds(int firstReservedId, int numberOfReservedIds) {
- this.firstReservedId = firstReservedId;
- this.numberOfReservedIds = numberOfReservedIds;
- }
-
- public MethodProcessingId get(ProgramMethod method, int index) {
- assert index >= 0;
- assert index < numberOfReservedIds;
- assert seen.add(method);
- MethodProcessingId result = new MethodProcessingId(firstReservedId + index);
- if (consumer != null) {
- consumer.accept(method, result);
- }
- return result;
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index 3be22fa..140e0ce 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -15,7 +15,7 @@
public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
- public abstract void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method);
+ public abstract void scheduleDesugaredMethodForProcessing(ProgramMethod method);
public abstract CallSiteInformation getCallSiteInformation();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
index c50e8e3..d0f2413 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
@@ -22,7 +22,7 @@
}
@Override
- public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+ public void scheduleDesugaredMethodForProcessing(ProgramMethod method) {
waveExtension.add(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index be427aa..f637225 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -3,11 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.MethodProcessingId.Factory.ReservedMethodProcessingIds;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ThrowingBiConsumer;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -18,33 +18,30 @@
*/
public class OneTimeMethodProcessor extends MethodProcessorWithWave {
- private final MethodProcessingId.Factory methodProcessingIdFactory;
+ private final ProcessorContext processorContext;
- private OneTimeMethodProcessor(
- MethodProcessingId.Factory methodProcessingIdFactory, SortedProgramMethodSet wave) {
- this.methodProcessingIdFactory = methodProcessingIdFactory;
+ private OneTimeMethodProcessor(ProcessorContext processorContext, SortedProgramMethodSet wave) {
+ this.processorContext = processorContext;
this.wave = wave;
}
public static OneTimeMethodProcessor create(ProgramMethod methodToProcess, AppView<?> appView) {
- return create(methodToProcess, appView.methodProcessingIdFactory());
+ return create(SortedProgramMethodSet.create(methodToProcess), appView);
}
public static OneTimeMethodProcessor create(
- ProgramMethod methodToProcess, MethodProcessingId.Factory methodProcessingIdFactory) {
- return new OneTimeMethodProcessor(
- methodProcessingIdFactory, SortedProgramMethodSet.create(methodToProcess));
+ ProgramMethod methodToProcess, ProcessorContext processorContext) {
+ return create(SortedProgramMethodSet.create(methodToProcess), processorContext);
}
public static OneTimeMethodProcessor create(
SortedProgramMethodSet methodsToProcess, AppView<?> appView) {
- return create(methodsToProcess, appView.methodProcessingIdFactory());
+ return create(methodsToProcess, appView.createProcessorContext());
}
public static OneTimeMethodProcessor create(
- SortedProgramMethodSet methodsToProcess,
- MethodProcessingId.Factory methodProcessingIdFactory) {
- return new OneTimeMethodProcessor(methodProcessingIdFactory, methodsToProcess);
+ SortedProgramMethodSet methodsToProcess, ProcessorContext processorContext) {
+ return new OneTimeMethodProcessor(processorContext, methodsToProcess);
}
@Override
@@ -52,27 +49,26 @@
return true;
}
- public <E extends Exception> void forEachWaveWithExtension(
- ThrowingBiConsumer<ProgramMethod, MethodProcessingId, E> consumer) throws E {
+ @FunctionalInterface
+ public interface MethodAction<E extends Exception> {
+ void accept(ProgramMethod method, MethodProcessingContext methodProcessingContext) throws E;
+ }
+
+ public <E extends Exception> void forEachWaveWithExtension(MethodAction<E> consumer) throws E {
while (!wave.isEmpty()) {
- ReservedMethodProcessingIds methodProcessingIds = methodProcessingIdFactory.reserveIds(wave);
- int i = 0;
for (ProgramMethod method : wave) {
- consumer.accept(method, methodProcessingIds.get(method, i++));
+ consumer.accept(method, processorContext.createMethodProcessingContext(method));
}
prepareForWaveExtensionProcessing();
}
}
public <E extends Exception> void forEachWaveWithExtension(
- ThrowingBiConsumer<ProgramMethod, MethodProcessingId, E> consumer,
- ExecutorService executorService)
- throws ExecutionException {
+ MethodAction<E> consumer, ExecutorService executorService) throws ExecutionException {
while (!wave.isEmpty()) {
- ReservedMethodProcessingIds methodProcessingIds = methodProcessingIdFactory.reserveIds(wave);
ThreadUtils.processItems(
wave,
- (method, index) -> consumer.accept(method, methodProcessingIds.get(method, index)),
+ method -> consumer.accept(method, processorContext.createMethodProcessingContext(method)),
executorService);
prepareForWaveExtensionProcessing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index ba7fa7f..06ddf34 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -13,7 +14,6 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.conversion.MethodProcessingId.Factory.ReservedMethodProcessingIds;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.logging.Log;
@@ -35,6 +35,7 @@
public class PostMethodProcessor extends MethodProcessorWithWave {
+ private final ProcessorContext processorContext;
private final AppView<AppInfoWithLiveness> appView;
private final Collection<CodeOptimization> defaultCodeOptimizations;
private final Map<DexMethod, Collection<CodeOptimization>> methodsMap;
@@ -46,6 +47,7 @@
Collection<CodeOptimization> defaultCodeOptimizations,
Map<DexMethod, Collection<CodeOptimization>> methodsMap,
CallGraph callGraph) {
+ this.processorContext = appView.createProcessorContext();
this.appView = appView;
this.defaultCodeOptimizations = defaultCodeOptimizations;
this.methodsMap = methodsMap;
@@ -173,16 +175,13 @@
assert !wave.isEmpty();
assert waveExtension.isEmpty();
do {
- ReservedMethodProcessingIds methodProcessingIds =
- appView.methodProcessingIdFactory().reserveIds(wave);
ThreadUtils.processItems(
wave,
- (method, index) -> {
+ method -> {
Collection<CodeOptimization> codeOptimizations =
methodsMap.get(method.getReference());
assert codeOptimizations != null && !codeOptimizations.isEmpty();
- forEachMethod(
- method, codeOptimizations, feedback, methodProcessingIds.get(method, index));
+ forEachMethod(method, codeOptimizations, feedback);
},
executorService);
processed.addAll(wave);
@@ -194,8 +193,7 @@
private void forEachMethod(
ProgramMethod method,
Collection<CodeOptimization> codeOptimizations,
- OptimizationFeedback feedback,
- MethodProcessingId methodProcessingId) {
+ OptimizationFeedback feedback) {
// TODO(b/140766440): Make IRConverter#process receive a list of CodeOptimization to conduct.
// Then, we can share IRCode creation there.
if (appView.options().skipIR) {
@@ -209,7 +207,8 @@
}
// TODO(b/140768815): Reprocessing may trigger more methods to revisit. Update waves on-the-fly.
for (CodeOptimization codeOptimization : codeOptimizations) {
- codeOptimization.optimize(code, feedback, this, methodProcessingId);
+ codeOptimization.optimize(
+ code, feedback, this, processorContext.createMethodProcessingContext(method));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 065d00b..8969cba 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
-import com.android.tools.r8.ir.conversion.MethodProcessingId.Factory.ReservedMethodProcessingIds;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ThrowingBiFunction;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -36,8 +36,8 @@
void notifyWaveStart(ProgramMethodSet wave);
}
+ private final AppView<?> appView;
private final CallSiteInformation callSiteInformation;
- private final MethodProcessingId.Factory methodProcessingIdFactory;
private final PostMethodProcessor.Builder postMethodProcessorBuilder;
private final Deque<SortedProgramMethodSet> waves;
@@ -45,8 +45,8 @@
AppView<AppInfoWithLiveness> appView,
PostMethodProcessor.Builder postMethodProcessorBuilder,
CallGraph callGraph) {
+ this.appView = appView;
this.callSiteInformation = callGraph.createCallSiteInformation(appView);
- this.methodProcessingIdFactory = appView.methodProcessingIdFactory();
this.postMethodProcessorBuilder = postMethodProcessorBuilder;
this.waves = createWaves(appView, callGraph, callSiteInformation);
}
@@ -104,6 +104,11 @@
return waves;
}
+ @FunctionalInterface
+ public interface MethodAction<E extends Exception> {
+ Timing apply(ProgramMethod method, MethodProcessingContext methodProcessingContext) throws E;
+ }
+
/**
* Applies the given method to all leaf nodes of the graph.
*
@@ -111,7 +116,7 @@
* processed at the same time is passed. This can be used to avoid races in concurrent processing.
*/
<E extends Exception> void forEachMethod(
- ThrowingBiFunction<ProgramMethod, MethodProcessingId, Timing, E> consumer,
+ MethodAction<E> consumer,
WaveStartAction waveStartAction,
Consumer<ProgramMethodSet> waveDone,
Timing timing,
@@ -120,18 +125,19 @@
TimingMerger merger =
timing.beginMerger("primary-processor", ThreadUtils.getNumberOfThreads(executorService));
while (!waves.isEmpty()) {
+ ProcessorContext processorContext = appView.createProcessorContext();
wave = waves.removeFirst();
assert !wave.isEmpty();
assert waveExtension.isEmpty();
do {
waveStartAction.notifyWaveStart(wave);
- ReservedMethodProcessingIds methodProcessingIds =
- methodProcessingIdFactory.reserveIds(wave);
Collection<Timing> timings =
ThreadUtils.processItemsWithResults(
wave,
- (method, index) -> {
- Timing time = consumer.apply(method, methodProcessingIds.get(method, index));
+ method -> {
+ Timing time =
+ consumer.apply(
+ method, processorContext.createMethodProcessingContext(method));
time.end();
return time;
},
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 09fd687..704ed92 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -34,13 +35,10 @@
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -53,7 +51,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
-import java.util.function.Supplier;
import org.objectweb.asm.Opcodes;
public final class BackportedMethodRewriter {
@@ -109,12 +106,18 @@
BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
}
- public boolean desugar(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
- return desugar(method, appInfo, synthesizedMethods::add);
+ public boolean desugar(
+ ProgramMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ MethodProcessingContext methodProcessingContext) {
+ return desugar(method, appInfo, methodProcessingContext, synthesizedMethods::add);
}
public boolean desugar(
- ProgramMethod method, AppInfoWithClassHierarchy appInfo, Consumer<ProgramMethod> consumer) {
+ ProgramMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ MethodProcessingContext methodProcessingContext,
+ Consumer<ProgramMethod> consumer) {
if (!enabled) {
return false;
}
@@ -131,14 +134,6 @@
}
CfCode code = method.getDefinition().getCode().asCfCode();
ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
- // TODO(b/172194101): Make this part of a unique context construction.
- IntBox nextBackportId = new IntBox();
- Supplier<String> methodIdSupplier =
- () -> {
- Hasher hasher = Hashing.sha256().newHasher();
- method.getReference().hash(hasher);
- return "$" + hasher.hash().toString() + "$" + nextBackportId.getAndIncrement();
- };
boolean replaced = false;
while (iterator.hasNext()) {
CfInvoke invoke = iterator.next().asInvoke();
@@ -155,7 +150,7 @@
iterator = mutableInstructions.listIterator(iterator.previousIndex());
iterator.next();
}
- provider.rewriteInvoke(invoke, iterator, method, appInfo, consumer, methodIdSupplier);
+ provider.rewriteInvoke(invoke, iterator, appInfo, consumer, methodProcessingContext);
replaced = true;
}
}
@@ -1357,10 +1352,9 @@
public abstract void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod,
- Supplier<String> methodIdProvider);
+ MethodProcessingContext methodProcessingContext);
}
private static final class InvokeRewriter extends MethodProvider {
@@ -1376,10 +1370,9 @@
public void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod,
- Supplier<String> methodIdProvider) {
+ MethodProcessingContext methodProcessingContext) {
rewriter.rewrite(invoke, iterator, appInfo.dexItemFactory());
}
}
@@ -1403,33 +1396,29 @@
public void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod,
- Supplier<String> methodIdProvider) {
- ProgramMethod method = getSyntheticMethod(context, methodIdProvider, appInfo);
+ MethodProcessingContext methodProcessingContext) {
+ ProgramMethod method = getSyntheticMethod(appInfo, methodProcessingContext);
registerSynthesizedMethod.accept(method);
iterator.remove();
iterator.add(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
}
private ProgramMethod getSyntheticMethod(
- ProgramMethod context,
- Supplier<String> methodIdProvider,
- AppInfoWithClassHierarchy appInfo) {
+ AppInfoWithClassHierarchy appInfo, MethodProcessingContext methodProcessingContext) {
return appInfo
.getSyntheticItems()
.createMethod(
SyntheticNaming.SyntheticKind.BACKPORT,
- context,
+ methodProcessingContext.createUniqueContext(),
appInfo.dexItemFactory(),
builder ->
builder
.setProto(getProto(appInfo.dexItemFactory()))
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
- methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)),
- methodIdProvider);
+ methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)));
}
public DexProto getProto(DexItemFactory itemFactory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
new file mode 100644
index 0000000..08a7934
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.ProgramMethod;
+import java.util.List;
+
+/** Interface for desugaring a single class-file instruction. */
+public interface CfInstructionDesugaring {
+
+ /**
+ * Given an instruction, returns the list of instructions that the instruction should be desugared
+ * to. If no desugaring is needed, {@code null} should be returned (for efficiency).
+ */
+ List<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer consumer,
+ ProgramMethod context);
+
+ /**
+ * Returns true if the given instruction needs desugaring.
+ *
+ * <p>This should return true if-and-only-if {@link #desugarInstruction} returns non-null.
+ */
+ boolean needsDesugaring(CfInstruction instruction, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
new file mode 100644
index 0000000..bf63ace
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+/**
+ * Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
+ * new class-file instructions).
+ *
+ * <p>The combined set of low-level desugarings provide a way to desugar a method in full
+ */
+public abstract class CfInstructionDesugaringCollection {
+
+ public static CfInstructionDesugaringCollection empty() {
+ return new EmptyCfInstructionDesugaringCollection();
+ }
+
+ /** Desugars the instructions in the given method. */
+ public abstract void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer);
+
+ /** Returns true if the given method needs desugaring. */
+ public abstract boolean needsDesugaring(ProgramMethod method);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
new file mode 100644
index 0000000..0b01594
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Class that gets notified for structural changes made as a result of desugaring (e.g., the
+ * inserting of a new method).
+ */
+public abstract class CfInstructionDesugaringEventConsumer {
+
+ public static D8CfInstructionDesugaringEventConsumer createForD8() {
+ return new D8CfInstructionDesugaringEventConsumer();
+ }
+
+ public static R8CfInstructionDesugaringEventConsumer createForR8() {
+ return new R8CfInstructionDesugaringEventConsumer();
+ }
+
+ public static CfInstructionDesugaringEventConsumer createForDesugaredCode() {
+ return new CfInstructionDesugaringEventConsumer() {
+ @Override
+ public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
+ assert false;
+ }
+ };
+ }
+
+ public abstract void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info);
+
+ public static class D8CfInstructionDesugaringEventConsumer
+ extends CfInstructionDesugaringEventConsumer {
+
+ private final Map<DexReference, InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges =
+ new LinkedHashMap<>();
+
+ @Override
+ public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
+ synchronized (pendingInvokeSpecialBridges) {
+ assert !pendingInvokeSpecialBridges.containsKey(info.getNewDirectMethod().getReference());
+ pendingInvokeSpecialBridges.put(info.getNewDirectMethod().getReference(), info);
+ }
+ }
+
+ public List<ProgramMethod> finalizeDesugaring(AppView<?> appView) {
+ List<ProgramMethod> needsReprocessing = new ArrayList<>();
+ finalizeInvokeSpecialDesugaring(appView, needsReprocessing::add);
+ return needsReprocessing;
+ }
+
+ private void finalizeInvokeSpecialDesugaring(
+ AppView<?> appView, Consumer<ProgramMethod> needsReprocessing) {
+ // Fixup the code of the new private methods have that been synthesized.
+ pendingInvokeSpecialBridges
+ .values()
+ .forEach(
+ info -> {
+ ProgramMethod newDirectMethod = info.getNewDirectMethod();
+ newDirectMethod
+ .getDefinition()
+ .setCode(info.getVirtualMethod().getDefinition().getCode(), appView);
+ });
+
+ // Reprocess the methods that were subject to invoke-special desugaring (because their body
+ // has been moved to a private method).
+ pendingInvokeSpecialBridges
+ .values()
+ .forEach(
+ info -> {
+ info.getVirtualMethod()
+ .getDefinition()
+ .setCode(info.getVirtualMethodCode(), appView);
+ needsReprocessing.accept(info.getVirtualMethod());
+ });
+
+ pendingInvokeSpecialBridges.clear();
+ }
+
+ public boolean verifyNothingToFinalize() {
+ assert pendingInvokeSpecialBridges.isEmpty();
+ return true;
+ }
+ }
+
+ public static class R8CfInstructionDesugaringEventConsumer
+ extends CfInstructionDesugaringEventConsumer {
+
+ private final List<InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new ArrayList<>();
+
+ @Override
+ public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
+ synchronized (pendingInvokeSpecialBridges) {
+ pendingInvokeSpecialBridges.add(info);
+ }
+ }
+
+ public void finalizeDesugaring(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ Collections.sort(pendingInvokeSpecialBridges);
+ pendingInvokeSpecialBridges.forEach(
+ info ->
+ info.getVirtualMethod()
+ .getDefinition()
+ .setCode(info.getVirtualMethodCode(), appView));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
new file mode 100644
index 0000000..8c56b4d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
+
+ @Override
+ public void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public boolean needsDesugaring(ProgramMethod method) {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index db127f2..6ffc8e2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
@@ -63,7 +64,6 @@
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
@@ -269,7 +269,9 @@
// Rewrites the references to static and default interface methods.
// NOTE: can be called for different methods concurrently.
public void rewriteMethodReferences(
- IRCode code, MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
+ IRCode code,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
ProgramMethod context = code.context();
if (synthesizedMethods.contains(context)) {
return;
@@ -302,7 +304,7 @@
affectedValues,
blocksToRemove,
methodProcessor,
- methodProcessingId);
+ methodProcessingContext);
break;
case INVOKE_SUPER:
rewriteInvokeSuper(instruction.asInvokeSuper(), instructions, context);
@@ -407,7 +409,7 @@
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
DexMethod invokedMethod = invoke.getInvokedMethod();
if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
// We did not create this code yet, but it will not require rewriting.
@@ -456,7 +458,7 @@
.getSyntheticItems()
.createMethod(
SyntheticNaming.SyntheticKind.STATIC_INTERFACE_CALL,
- context.getHolder(),
+ methodProcessingContext.createUniqueContext(),
factory,
syntheticMethodBuilder ->
syntheticMethodBuilder
@@ -504,9 +506,9 @@
UtilityMethodForCodeOptimizations throwMethod =
resolutionResult == null
? UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
- appView, context, methodProcessingId)
+ appView, methodProcessingContext)
: UtilityMethodsForCodeOptimizations.synthesizeThrowIncompatibleClassChangeErrorMethod(
- appView, context, methodProcessingId);
+ appView, methodProcessingContext);
throwMethod.optimize(methodProcessor);
InvokeStatic throwInvoke =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InvokeSpecialBridgeSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/InvokeSpecialBridgeSynthesizer.java
deleted file mode 100644
index 51003cb..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/InvokeSpecialBridgeSynthesizer.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.collect.Sets;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-
-/**
- * It is possible in class files to have an invoke-special to a virtual method in the same class
- * than the method holding the invoke-special. Such invoke-special are executed correctly on the
- * JVM, but cannot be expressed in terms of invoke-direct or invoke-super in dex. This class
- * introduces bridges to support the case described: the virtual method code is moved to a private
- * synthetic method, and a bridging virtual method with the initial method name and flags is
- * inserted.
- */
-public class InvokeSpecialBridgeSynthesizer {
-
- private static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
-
- private final AppView<?> appView;
-
- private final Map<DexMethod, DexMethod> bridges = new ConcurrentHashMap<>();
- private final Set<DexMethod> seenBridges = Sets.newIdentityHashSet();
-
- public InvokeSpecialBridgeSynthesizer(AppView<?> appView) {
- this.appView = appView;
- }
-
- public DexMethod registerBridgeForMethod(DexEncodedMethod method) {
- assert method.isVirtualMethod();
- assert !method.getAccessFlags().isFinal();
- return bridges.computeIfAbsent(
- method.getReference(),
- vMethod ->
- vMethod.withName(
- appView
- .dexItemFactory()
- .createString(INVOKE_SPECIAL_BRIDGE_PREFIX + vMethod.name.toString()),
- appView.dexItemFactory()));
- }
-
- // In R8, insertBridgesForR8 is called multiple times until fixed point.
- // The bridges are inserted prior to IR conversion.
- public SortedProgramMethodSet insertBridgesForR8() {
- SortedProgramMethodSet insertedDirectMethods = SortedProgramMethodSet.create();
- bridges.forEach(
- (vMethod, dMethod) -> {
- if (seenBridges.add(vMethod)) {
- insertedDirectMethods.add(insertBridge(getVirtualMethod(vMethod), dMethod));
- }
- });
- return insertedDirectMethods;
- }
-
- // In D8, insertBridgesForD8 is called once.
- // The bridges are inserted after IR conversion hence the bridges need to be processed.
- public void insertBridgesForD8(IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
- SortedProgramMethodSet insertedBridges = SortedProgramMethodSet.create();
- bridges.forEach(
- (virtualMethod, directMethod) -> {
- ProgramMethod programVirtualMethod = getVirtualMethod(virtualMethod);
- insertBridge(programVirtualMethod, directMethod);
- insertedBridges.add(programVirtualMethod);
- });
- converter.processMethodsConcurrently(insertedBridges, executorService);
- }
-
- private ProgramMethod getVirtualMethod(DexMethod virtualMethod) {
- DexProgramClass holder = appView.definitionFor(virtualMethod.holder).asProgramClass();
- assert holder.lookupVirtualMethod(virtualMethod) != null;
- DexEncodedMethod encodedVirtualMethod = holder.lookupVirtualMethod(virtualMethod);
- return new ProgramMethod(holder, encodedVirtualMethod);
- }
-
- private ProgramMethod insertBridge(ProgramMethod virtualMethod, DexMethod directMethod) {
- assert virtualMethod.getHolderType() == directMethod.holder;
- DexProgramClass holder = virtualMethod.getHolder();
- assert holder.lookupDirectMethod(directMethod) == null;
- DexEncodedMethod initialVirtualMethod = virtualMethod.getDefinition();
- DexEncodedMethod newDirectMethod = initialVirtualMethod.toPrivateSyntheticMethod(directMethod);
- CfCode forwardingCode =
- ForwardMethodBuilder.builder(appView.dexItemFactory())
- .setDirectTarget(directMethod, holder.isInterface())
- .setNonStaticSource(virtualMethod.getReference())
- .build();
- initialVirtualMethod.setCode(forwardingCode, appView);
- initialVirtualMethod.markNotProcessed();
- holder.addDirectMethod(newDirectMethod);
- return new ProgramMethod(holder, newDirectMethod);
- }
-
- public EnqueuerInvokeAnalysis getEnqueuerInvokeAnalysis() {
- return new InvokeSpecialBridgeAnalysis();
- }
-
- private class InvokeSpecialBridgeAnalysis implements EnqueuerInvokeAnalysis {
-
- @Override
- public void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {}
-
- @Override
- public void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
- DexEncodedMethod lookup = context.getHolder().lookupMethod(invokedMethod);
- if (lookup != null
- && lookup.isNonPrivateVirtualMethod()
- && context.getHolderType() == invokedMethod.holder
- && !context.getHolder().isInterface()
- && !lookup.accessFlags.isFinal()) {
- registerBridgeForMethod(lookup);
- }
- }
-
- @Override
- public void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {}
-
- @Override
- public void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {}
-
- @Override
- public void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {}
- }
-}
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 c8c6a54..b12b2a7 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -37,8 +38,6 @@
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -114,7 +113,10 @@
*
* <p>NOTE: this method can be called concurrently for several different methods.
*/
- public int desugarLambdas(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
+ public int desugarLambdas(
+ ProgramMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ MethodProcessingContext methodProcessingContext) {
return desugarLambdas(
method,
callsite -> {
@@ -122,7 +124,7 @@
if (descriptor == null) {
return null;
}
- return createLambdaClass(descriptor, method);
+ return createLambdaClass(descriptor, method, methodProcessingContext);
});
}
@@ -211,24 +213,16 @@
}
// Creates a lambda class corresponding to the lambda descriptor and context.
- public LambdaClass createLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
- int nextId =
- methodIds.compute(
- accessedFrom.getReference(), (method, value) -> value == null ? 0 : value + 1);
+ public LambdaClass createLambdaClass(
+ LambdaDescriptor descriptor, ProgramMethod accessedFrom, MethodProcessingContext context) {
Box<LambdaClass> box = new Box<>();
DexProgramClass clazz =
appView
.getSyntheticItems()
.createClass(
SyntheticNaming.SyntheticKind.LAMBDA,
- accessedFrom.getHolder(),
+ context.createUniqueContext(),
appView.dexItemFactory(),
- // TODO(b/172194101): Make this part of a unique context construction.
- () -> {
- Hasher hasher = Hashing.sha256().newHasher();
- accessedFrom.getReference().hash(hasher);
- return "$" + hasher.hash().toString() + "$" + nextId;
- },
builder ->
box.set(new LambdaClass(builder, appView, this, accessedFrom, descriptor)));
// Immediately set the actual program class on the lambda.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
new file mode 100644
index 0000000..6a11e86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
+import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
+
+ private final AppView<?> appView;
+ private final List<CfInstructionDesugaring> desugarings = new ArrayList<>();
+ private final InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring;
+ private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
+
+ public NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
+ this.appView = appView;
+ this.invokeSpecialToSelfDesugaring = new InvokeSpecialToSelfDesugaring(appView);
+ this.nestBasedAccessDesugaring =
+ appView.options().shouldDesugarNests() ? new NestBasedAccessDesugaring(appView) : null;
+ registerIfNotNull(invokeSpecialToSelfDesugaring);
+ registerIfNotNull(nestBasedAccessDesugaring);
+ }
+
+ private void registerIfNotNull(CfInstructionDesugaring desugaring) {
+ if (desugaring != null) {
+ desugarings.add(desugaring);
+ }
+ }
+
+ @Override
+ public void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ Code code = method.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ new StringDiagnostic(
+ "Unsupported attempt to desugar non-CF code",
+ method.getOrigin(),
+ method.getPosition()));
+ return;
+ }
+
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> desugaredInstructions =
+ ListUtils.flatMap(
+ cfCode.getInstructions(),
+ instruction -> desugarInstruction(instruction, consumer, method),
+ null);
+ if (desugaredInstructions != null) {
+ cfCode.setInstructions(desugaredInstructions);
+ } else {
+ assert false : "Expected code to be desugared";
+ }
+ }
+
+ private List<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer consumer,
+ ProgramMethod context) {
+ // TODO(b/177810578): Migrate other cf-to-cf based desugaring here.
+ Iterator<CfInstructionDesugaring> iterator = desugarings.iterator();
+ while (iterator.hasNext()) {
+ CfInstructionDesugaring desugaring = iterator.next();
+ List<CfInstruction> replacement =
+ desugaring.desugarInstruction(instruction, consumer, context);
+ if (replacement != null) {
+ assert verifyNoOtherDesugaringNeeded(instruction, context, iterator);
+ return replacement;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean needsDesugaring(ProgramMethod method) {
+ if (!method.getDefinition().hasCode()) {
+ return false;
+ }
+
+ Code code = method.getDefinition().getCode();
+ if (code.isDexCode()) {
+ return false;
+ }
+
+ if (!code.isCfCode()) {
+ throw new Unreachable("Unexpected attempt to determine if non-CF code needs desugaring");
+ }
+
+ return Iterables.any(
+ code.asCfCode().getInstructions(), instruction -> needsDesugaring(instruction, method));
+ }
+
+ private boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return Iterables.any(
+ desugarings, desugaring -> desugaring.needsDesugaring(instruction, context));
+ }
+
+ private static boolean verifyNoOtherDesugaringNeeded(
+ CfInstruction instruction,
+ ProgramMethod context,
+ Iterator<CfInstructionDesugaring> iterator) {
+ assert IteratorUtils.nextUntil(
+ iterator,
+ desugaring ->
+ desugaring.desugarInstruction(
+ instruction,
+ CfInstructionDesugaringEventConsumer.createForDesugaredCode(),
+ context)
+ != null)
+ == null;
+ return true;
+ }
+}
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 98acb2d..69b4dc2 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexItemFactory;
@@ -57,7 +58,10 @@
dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
}
- public int rewriteCf(ProgramMethod method, Consumer<ProgramMethod> newMethodCallback) {
+ public int rewriteCf(
+ ProgramMethod method,
+ Consumer<ProgramMethod> newMethodCallback,
+ MethodProcessingContext methodProcessingContext) {
CfCode code = method.getDefinition().getCode().asCfCode();
List<CfInstruction> instructions = code.getInstructions();
Supplier<List<CfInstruction>> lazyNewInstructions =
@@ -72,7 +76,7 @@
continue;
}
// Synthesize a new method.
- ProgramMethod closeMethod = createSyntheticCloseResourceMethod(method);
+ ProgramMethod closeMethod = createSyntheticCloseResourceMethod(methodProcessingContext);
newMethodCallback.accept(closeMethod);
// Rewrite the invoke to the new synthetic.
int newInstructionIndex = i + newInstructionDelta;
@@ -90,7 +94,7 @@
}
// Rewrites calls to $closeResource() method. Can be invoked concurrently.
- public void rewriteIR(IRCode code) {
+ public void rewriteIR(IRCode code, MethodProcessingContext methodProcessingContext) {
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
InvokeStatic invoke = iterator.next().asInvokeStatic();
@@ -102,7 +106,8 @@
// Replace with a call to a synthetic utility.
assert invoke.outValue() == null;
assert invoke.inValues().size() == 2;
- ProgramMethod closeResourceMethod = createSyntheticCloseResourceMethod(code.context());
+ ProgramMethod closeResourceMethod =
+ createSyntheticCloseResourceMethod(methodProcessingContext);
InvokeStatic newInvoke =
new InvokeStatic(closeResourceMethod.getReference(), null, invoke.inValues());
iterator.replaceCurrentInstruction(newInvoke);
@@ -117,12 +122,13 @@
&& method.proto == factory.twrCloseResourceMethodProto;
}
- private ProgramMethod createSyntheticCloseResourceMethod(ProgramMethod method) {
+ private ProgramMethod createSyntheticCloseResourceMethod(
+ MethodProcessingContext methodProcessingContext) {
return appView
.getSyntheticItems()
.createMethod(
SyntheticKind.TWR_CLOSE_RESOURCE,
- method,
+ methodProcessingContext.createUniqueContext(),
appView.dexItemFactory(),
methodBuilder ->
methodBuilder
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialBridgeInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialBridgeInfo.java
new file mode 100644
index 0000000..5ef0bb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialBridgeInfo.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.invokespecial;
+
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class InvokeSpecialBridgeInfo implements Comparable<InvokeSpecialBridgeInfo> {
+
+ private final ProgramMethod newDirectMethod;
+ private final ProgramMethod virtualMethod;
+ private final CfCode virtualMethodCode;
+
+ InvokeSpecialBridgeInfo(
+ ProgramMethod newDirectMethod, ProgramMethod virtualMethod, CfCode virtualMethodCode) {
+ this.newDirectMethod = newDirectMethod;
+ this.virtualMethod = virtualMethod;
+ this.virtualMethodCode = virtualMethodCode;
+ }
+
+ public ProgramMethod getNewDirectMethod() {
+ return newDirectMethod;
+ }
+
+ public ProgramMethod getVirtualMethod() {
+ return virtualMethod;
+ }
+
+ public CfCode getVirtualMethodCode() {
+ return virtualMethodCode;
+ }
+
+ @Override
+ public int compareTo(InvokeSpecialBridgeInfo info) {
+ return getNewDirectMethod().getReference().compareTo(info.getNewDirectMethod().getReference());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
new file mode 100644
index 0000000..2d99341
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.invokespecial;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+/** This class defines the desugaring of a single invoke-special instruction. */
+public class InvokeSpecialToSelfDesugaring implements CfInstructionDesugaring {
+
+ private static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
+
+ private final AppView<?> appView;
+ private final DexItemFactory dexItemFactory;
+
+ public InvokeSpecialToSelfDesugaring(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (instruction.isInvoke()) {
+ return needsDesugaring(instruction.asInvoke(), context) != null;
+ }
+ return false;
+ }
+
+ /** @return the resolved method if desugaring is needed, otherwise null. */
+ private ProgramMethod needsDesugaring(CfInvoke invoke, ProgramMethod context) {
+ if (!invoke.isInvokeSpecial() || invoke.isInvokeConstructor(dexItemFactory)) {
+ return null;
+ }
+
+ DexMethod invokedMethod = invoke.getMethod();
+ if (invokedMethod.getHolderType() != context.getHolderType()) {
+ return null;
+ }
+
+ ProgramMethod method = context.getHolder().lookupProgramMethod(invokedMethod);
+ if (method == null
+ || method.getAccessFlags().isPrivate()
+ || method.getDefinition().isStatic()
+ || (invoke.isInterface() && method.isDefaultMethod())) {
+ return null;
+ }
+
+ return method;
+ }
+
+ public boolean desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ Code code = method.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ new StringDiagnostic(
+ "Unsupported attempt to desugar non-CF code",
+ method.getOrigin(),
+ method.getPosition()));
+ return false;
+ }
+
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> desugaredInstructions =
+ ListUtils.flatMap(
+ cfCode.getInstructions(),
+ instruction -> desugarInstruction(instruction, consumer, method),
+ null);
+ if (desugaredInstructions != null) {
+ cfCode.setInstructions(desugaredInstructions);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public List<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer consumer,
+ ProgramMethod context) {
+ if (instruction.isInvoke()) {
+ return desugarInvokeInstruction(instruction.asInvoke(), consumer, context);
+ }
+ return null;
+ }
+
+ private List<CfInstruction> desugarInvokeInstruction(
+ CfInvoke invoke, CfInstructionDesugaringEventConsumer consumer, ProgramMethod context) {
+ ProgramMethod method = needsDesugaring(invoke, context);
+ if (method == null) {
+ return null;
+ }
+
+ if (method.getAccessFlags().isFinal()) {
+ // This method is final thus we can use invoke-virtual.
+ return ImmutableList.of(
+ new CfInvoke(Opcodes.INVOKEVIRTUAL, invoke.getMethod(), invoke.isInterface()));
+ }
+
+ // This is an invoke-special to a virtual method on invoke-special method holder.
+ // The invoke should be rewritten with a bridge.
+ DexMethod bridgeMethod = ensureInvokeSpecialBridge(method, consumer);
+ return ImmutableList.of(
+ new CfInvoke(Opcodes.INVOKESPECIAL, bridgeMethod, invoke.isInterface()));
+ }
+
+ private DexMethod ensureInvokeSpecialBridge(
+ ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ DexMethod bridgeReference = getInvokeSpecialBridgeReference(method);
+ DexProgramClass clazz = method.getHolder();
+ synchronized (clazz.getMethodCollection()) {
+ if (clazz.lookupProgramMethod(bridgeReference) == null) {
+ // Create a new private method holding the code of the virtual method.
+ ProgramMethod newDirectMethod =
+ method.getDefinition().toPrivateSyntheticMethod(clazz, bridgeReference);
+
+ // Create the new cf code object for the virtual method.
+ CfCode virtualMethodCode =
+ ForwardMethodBuilder.builder(dexItemFactory)
+ .setDirectTarget(bridgeReference, clazz.isInterface())
+ .setNonStaticSource(method.getReference())
+ .build();
+
+ // Add the newly created direct method to its holder.
+ clazz.addDirectMethod(newDirectMethod.getDefinition());
+
+ consumer.acceptInvokeSpecialBridgeInfo(
+ new InvokeSpecialBridgeInfo(newDirectMethod, method, virtualMethodCode));
+ }
+ }
+ return bridgeReference;
+ }
+
+ private DexMethod getInvokeSpecialBridgeReference(DexClassAndMethod method) {
+ return method
+ .getReference()
+ .withName(
+ dexItemFactory.createString(INVOKE_SPECIAL_BRIDGE_PREFIX + method.getName().toString()),
+ dexItemFactory);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
index 7468cba..953801f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
@@ -18,16 +18,16 @@
@Override
public void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge) {
- methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
public void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge) {
- methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
public void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge) {
- methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index c516ce8..86aba1e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -29,6 +29,8 @@
import com.android.tools.r8.graph.LibraryMember;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ListUtils;
@@ -45,7 +47,7 @@
// NestBasedAccessDesugaring contains common code between the two subclasses
// which are specialized for d8 and r8
-public class NestBasedAccessDesugaring {
+public class NestBasedAccessDesugaring implements CfInstructionDesugaring {
// Short names to avoid creating long strings
public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
@@ -93,7 +95,6 @@
Code code = method.getDefinition().getCode();
if (code.isDexCode()) {
- assert appView.testing().allowDexInputForTesting;
return false;
}
@@ -105,7 +106,8 @@
code.asCfCode().getInstructions(), instruction -> needsDesugaring(instruction, method));
}
- private boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
if (instruction.isFieldInstruction()) {
return needsDesugaring(instruction.asFieldInstruction().getField(), context);
}
@@ -166,6 +168,14 @@
return false;
}
+ @Override
+ public List<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer consumer,
+ ProgramMethod context) {
+ return desugarInstruction(instruction, context, null);
+ }
+
public List<CfInstruction> desugarInstruction(
CfInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
if (instruction.isFieldInstruction()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 3fa8be0..0713d06 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.algorithms.scc.SCC;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
@@ -82,7 +83,6 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
@@ -1371,7 +1371,7 @@
IRCode code,
ProgramMethod context,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
if (!appView.enableWholeProgramOptimizations()) {
return;
}
@@ -1422,7 +1422,7 @@
context,
affectedValues,
methodProcessor,
- methodProcessingId);
+ methodProcessingContext);
if (removeResult != RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS) {
assert removeResult == RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
needToRemoveTrivialPhis |= hasPhiUsers;
@@ -1461,7 +1461,7 @@
ProgramMethod context,
Set<Value> affectedValues,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
Value inValue = checkCast.object();
Value outValue = checkCast.outValue();
DexType castType = checkCast.getType();
@@ -1534,7 +1534,7 @@
// Replace the check-cast instruction by throwClassCastExceptionIfNotNull().
UtilityMethodForCodeOptimizations throwClassCastExceptionIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeThrowClassCastExceptionIfNotNullMethod(
- appView, context, methodProcessingId);
+ appView, methodProcessingContext);
throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
InvokeStatic replacement =
InvokeStatic.builder()
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 dbad5bd..8dc0dd3 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
@@ -7,6 +7,8 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
@@ -68,6 +70,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -1340,6 +1343,8 @@
}
public List<ProgramMethod> buildOutlineMethods() {
+ ProcessorContext outlineProcessorContext = appView.createProcessorContext();
+ Map<DexMethod, MethodProcessingContext> methodProcessingContexts = new IdentityHashMap<>();
List<ProgramMethod> outlineMethods = new ArrayList<>();
// By now the candidates are the actual selected outlines. Iterate the outlines in a
// consistent order, to provide deterministic naming of the internal-synthetics.
@@ -1349,13 +1354,18 @@
for (Outline outline : outlines) {
List<ProgramMethod> sites = outlineSites.get(outline);
assert !sites.isEmpty();
+ // The representative might be shared among multiple outlines.
ProgramMethod representative = findDeterministicRepresentative(sites);
+ MethodProcessingContext methodProcessingContext =
+ methodProcessingContexts.computeIfAbsent(
+ representative.getReference(),
+ key -> outlineProcessorContext.createMethodProcessingContext(representative));
ProgramMethod outlineMethod =
appView
.getSyntheticItems()
.createMethod(
SyntheticKind.OUTLINE,
- representative,
+ methodProcessingContext.createUniqueContext(),
appView.dexItemFactory(),
builder -> {
builder
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 efc440b..6758593 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -20,7 +21,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -70,7 +70,7 @@
return serviceLoadMethods;
}
- public void rewrite(IRCode code, MethodProcessingId methodProcessingId) {
+ public void rewrite(IRCode code, MethodProcessingContext methodProcessingContext) {
DexItemFactory factory = appView.dexItemFactory();
InstructionListIterator instructionIterator = code.instructionListIterator();
// Create a map from service type to loader methods local to this context since two
@@ -170,7 +170,7 @@
constClass.getValue(),
service -> {
DexEncodedMethod addedMethod =
- createSynthesizedMethod(service, classes, methodProcessingId, code.context());
+ createSynthesizedMethod(service, classes, methodProcessingContext);
if (appView.options().isGeneratingClassFiles()) {
addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
}
@@ -185,15 +185,14 @@
private DexEncodedMethod createSynthesizedMethod(
DexType serviceType,
List<DexClass> classes,
- MethodProcessingId methodProcessingId,
- ProgramMethod context) {
+ MethodProcessingContext methodProcessingContext) {
DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
ProgramMethod method =
appView
.getSyntheticItems()
.createMethod(
SyntheticKind.SERVICE_LOADER,
- context,
+ methodProcessingContext.createUniqueContext(),
appView.dexItemFactory(),
builder ->
builder
@@ -202,8 +201,7 @@
.setCode(
m ->
ServiceLoaderSourceCode.generate(
- serviceType, classes, appView.dexItemFactory())),
- methodProcessingId);
+ serviceType, classes, appView.dexItemFactory())));
synchronized (serviceLoadMethods) {
serviceLoadMethods.add(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index 9e360b4..61b0f30 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexItemFactory;
@@ -12,7 +14,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.templates.CfUtilityMethodsForCodeOptimizations;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -22,7 +23,7 @@
public class UtilityMethodsForCodeOptimizations {
public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod(
- AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
@@ -30,15 +31,14 @@
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
SyntheticNaming.SyntheticKind.TO_STRING_IF_NOT_NULL,
- context,
+ methodProcessingContext.createUniqueContext(),
dexItemFactory,
builder ->
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
.setCode(method -> getToStringIfNotNullCodeTemplate(method, options))
- .setProto(proto),
- methodProcessingId);
+ .setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -49,15 +49,16 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowClassCastExceptionIfNotNullMethod(
- AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
+ UniqueContext positionContext = methodProcessingContext.createUniqueContext();
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
SyntheticNaming.SyntheticKind.THROW_CCE_IF_NOT_NULL,
- context,
+ positionContext,
dexItemFactory,
builder ->
builder
@@ -65,8 +66,7 @@
.setClassFileVersion(CfVersion.V1_8)
.setCode(
method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options))
- .setProto(proto),
- methodProcessingId);
+ .setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -78,7 +78,7 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowIncompatibleClassChangeErrorMethod(
- AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.icceType);
@@ -86,7 +86,7 @@
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
SyntheticNaming.SyntheticKind.THROW_ICCE,
- context,
+ methodProcessingContext.createUniqueContext(),
dexItemFactory,
builder ->
builder
@@ -94,8 +94,7 @@
.setClassFileVersion(CfVersion.V1_8)
.setCode(
method -> getThrowIncompatibleClassChangeErrorCodeTemplate(method, options))
- .setProto(proto),
- methodProcessingId);
+ .setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -107,7 +106,7 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowNoSuchMethodErrorMethod(
- AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.noSuchMethodErrorType);
@@ -115,15 +114,14 @@
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
SyntheticNaming.SyntheticKind.THROW_NSME,
- context,
+ methodProcessingContext.createUniqueContext(),
dexItemFactory,
builder ->
builder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setClassFileVersion(CfVersion.V1_8)
.setCode(method -> getThrowNoSuchMethodErrorCodeTemplate(method, options))
- .setProto(proto),
- methodProcessingId);
+ .setProto(proto));
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -148,7 +146,7 @@
}
public void optimize(MethodProcessor methodProcessor) {
- methodProcessor.scheduleMethodForProcessingAfterCurrentWave(method);
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
optimized = true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 13865a0..78dc973 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,7 +19,6 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -133,7 +133,7 @@
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId,
+ MethodProcessingContext methodProcessingContext,
Inliner inliner,
Supplier<InliningOracle> defaultOracle) {
@@ -253,7 +253,7 @@
// have more information about the types of the arguments at the call site. This is
// particularly important for bridge methods.
codeRewriter.removeTrivialCheckCastAndInstanceOfInstructions(
- code, method, methodProcessor, methodProcessingId);
+ code, method, methodProcessor, methodProcessingContext);
// If a method was inlined we may be able to prune additional branches.
codeRewriter.simplifyControlFlow(code);
// If a method was inlined we may see more trivial computation/conversion of String.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index 01c4cf7..b7bc845 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.library;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
@@ -17,7 +18,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.CodeOptimization;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.google.common.collect.Sets;
@@ -112,7 +112,7 @@
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ MethodProcessingContext methodProcessingContext) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
InstructionListIterator instructionIterator = code.instructionListIterator();
Map<LibraryMethodModelCollection<?>, LibraryMethodModelCollection.State> optimizationStates =
@@ -146,10 +146,15 @@
optimizationStates.computeIfAbsent(
optimizer,
libraryMethodModelCollection ->
- libraryMethodModelCollection.createInitialState(
- methodProcessor, methodProcessingId));
+ libraryMethodModelCollection.createInitialState(methodProcessor));
optimizer.optimize(
- code, instructionIterator, invoke, singleTarget, affectedValues, optimizationState);
+ code,
+ instructionIterator,
+ invoke,
+ singleTarget,
+ affectedValues,
+ optimizationState,
+ methodProcessingContext);
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
index 5cb30b3..da38111 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -4,13 +4,13 @@
package com.android.tools.r8.ir.optimize.library;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.library.LibraryMethodModelCollection.State;
import java.util.Set;
@@ -18,8 +18,7 @@
/** Used to model the behavior of library methods for optimization purposes. */
public interface LibraryMethodModelCollection<T extends State> {
- default T createInitialState(
- MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
+ default T createInitialState(MethodProcessor methodProcessor) {
return null;
}
@@ -39,7 +38,8 @@
InvokeMethod invoke,
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
- T state);
+ T state,
+ MethodProcessingContext methodProcessingContext);
@SuppressWarnings("unchecked")
default void optimize(
@@ -48,8 +48,16 @@
InvokeMethod invoke,
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
- Object state) {
- optimize(code, instructionIterator, invoke, singleTarget, affectedValues, (T) state);
+ Object state,
+ MethodProcessingContext methodProcessingContext) {
+ optimize(
+ code,
+ instructionIterator,
+ invoke,
+ singleTarget,
+ affectedValues,
+ (T) state,
+ methodProcessingContext);
}
/** Thread local optimization state to allow caching, etc. */
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
index 8b36204..df15197 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.ir.optimize.library;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection.State;
import java.util.Set;
@@ -18,8 +18,7 @@
implements LibraryMethodModelCollection<State> {
@Override
- public final State createInitialState(
- MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
+ public final State createInitialState(MethodProcessor methodProcessor) {
return null;
}
@@ -37,7 +36,8 @@
InvokeMethod invoke,
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
- State state) {
+ State state,
+ MethodProcessingContext methodProcessingContext) {
assert state == null;
optimize(code, instructionIterator, invoke, singleTarget, affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index d75e060..07ab344 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -11,6 +11,7 @@
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -26,7 +27,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
@@ -55,9 +55,8 @@
}
@Override
- public State createInitialState(
- MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
- return new State(methodProcessor, methodProcessingId);
+ public State createInitialState(MethodProcessor methodProcessor) {
+ return new State(methodProcessor);
}
@Override
@@ -72,11 +71,18 @@
InvokeMethod invoke,
DexClassAndMethod singleTarget,
Set<Value> affectedValues,
- State state) {
+ State state,
+ MethodProcessingContext methodProcessingContext) {
if (invoke.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invokeWithReceiver = invoke.asInvokeMethodWithReceiver();
if (stringBuilderMethods.isAppendMethod(singleTarget.getReference())) {
- optimizeAppend(code, instructionIterator, invokeWithReceiver, singleTarget, state);
+ optimizeAppend(
+ code,
+ instructionIterator,
+ invokeWithReceiver,
+ singleTarget,
+ state,
+ methodProcessingContext);
} else if (singleTarget.getReference() == dexItemFactory.stringBuilderMethods.toString) {
optimizeToString(instructionIterator, invokeWithReceiver);
}
@@ -88,7 +94,8 @@
InstructionListIterator instructionIterator,
InvokeMethodWithReceiver invoke,
DexClassAndMethod singleTarget,
- State state) {
+ State state,
+ MethodProcessingContext methodProcessingContext) {
if (!state.isUnusedBuilder(invoke.getReceiver())) {
return;
}
@@ -121,7 +128,7 @@
// Replace the instruction by toStringIfNotNull().
UtilityMethodForCodeOptimizations toStringIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeToStringIfNotNullMethod(
- appView, code.context(), state.methodProcessingId);
+ appView, methodProcessingContext);
toStringIfNotNullMethod.optimize(state.methodProcessor);
InvokeStatic replacement =
InvokeStatic.builder()
@@ -146,13 +153,11 @@
class State implements LibraryMethodModelCollection.State {
final MethodProcessor methodProcessor;
- final MethodProcessingId methodProcessingId;
final Reference2BooleanMap<Value> unusedBuilders = new Reference2BooleanOpenHashMap<>();
- State(MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
+ State(MethodProcessor methodProcessor) {
this.methodProcessor = methodProcessor;
- this.methodProcessingId = methodProcessingId;
}
boolean isUnusedBuilder(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index bf9f986..5f54ab6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -33,7 +33,6 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.AssumeInserter;
@@ -365,13 +364,12 @@
OneTimeMethodProcessor methodProcessor =
OneTimeMethodProcessor.create(methodsToReprocess, appView);
methodProcessor.forEachWaveWithExtension(
- (method, methodProcessingId) ->
+ (method, methodProcessingContext) ->
forEachMethod(
method,
processingQueue.get(method.getDefinition()).build(),
feedback,
- methodProcessor,
- methodProcessingId),
+ methodProcessor),
executorService);
// TODO(b/140767158): No need to clear if we can do every thing in one go.
methodsToReprocess.clear();
@@ -383,8 +381,7 @@
ProgramMethod method,
Collection<BiConsumer<IRCode, MethodProcessor>> codeOptimizations,
OptimizationFeedback feedback,
- OneTimeMethodProcessor methodProcessor,
- MethodProcessingId methodProcessingId) {
+ OneTimeMethodProcessor methodProcessor) {
IRCode code = method.buildIR(appView);
codeOptimizations.forEach(codeOptimization -> codeOptimization.accept(code, methodProcessor));
CodeRewriter.removeAssumeInstructions(appView, code);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 794fd3e..0b340af 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.synthetic;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
@@ -61,6 +63,23 @@
return this;
}
+ public ForwardMethodBuilder applyIf(
+ boolean condition, Consumer<ForwardMethodBuilder> thenConsumer) {
+ return applyIf(condition, thenConsumer, emptyConsumer());
+ }
+
+ public ForwardMethodBuilder applyIf(
+ boolean condition,
+ Consumer<ForwardMethodBuilder> thenConsumer,
+ Consumer<ForwardMethodBuilder> elseConsumer) {
+ if (condition) {
+ thenConsumer.accept(this);
+ } else {
+ elseConsumer.accept(this);
+ }
+ return this;
+ }
+
public ForwardMethodBuilder setNonStaticSource(DexMethod method) {
sourceMethod = method;
staticSource = false;
@@ -87,6 +106,13 @@
return this;
}
+ public ForwardMethodBuilder setSuperTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.SPECIAL;
+ this.isInterface = isInterface;
+ return this;
+ }
+
public ForwardMethodBuilder setVirtualTarget(DexMethod method, boolean isInterface) {
targetMethod = method;
invokeType = InvokeType.VIRTUAL;
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index cf6a117..1c6adbb 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -213,6 +213,18 @@
: "A nest host cannot also be a nest member.";
}
+ if (clazz.isRecord()) {
+ // TODO(b/169645628): Strip record components if not kept.
+ for (DexEncodedField instanceField : clazz.instanceFields()) {
+ String componentName = namingLens.lookupName(instanceField.field).toString();
+ String componentDescriptor =
+ namingLens.lookupDescriptor(instanceField.field.type).toString();
+ String componentSignature =
+ instanceField.getGenericSignature().toRenamedString(namingLens, isTypeMissing);
+ writer.visitRecordComponent(componentName, componentDescriptor, componentSignature);
+ }
+ }
+
for (InnerClassAttribute entry : clazz.getInnerClasses()) {
entry.write(writer, namingLens, options);
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 7ddb042..0f94efa 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -222,6 +222,14 @@
private void parseClassMappings(ProguardMap.Builder mapBuilder) throws IOException {
while (hasLine()) {
skipWhitespace();
+ if (isCommentLineWithJsonBrace()) {
+ // TODO(b/179665169): Parse the mapping information without doing anything with it, since we
+ // at this point do not have a global context.
+ MappingInformation.fromJsonObject(parseJsonInComment(), diagnosticsHandler, lineNo);
+ // Skip reading the rest of the line.
+ lineOffset = line.length();
+ nextLine();
+ }
String before = parseType(false);
skipWhitespace();
// Workaround for proguard map files that contain entries for package-info.java files.
@@ -528,7 +536,7 @@
}
}
- private class ParseException extends RuntimeException {
+ public class ParseException extends RuntimeException {
private final int lineNo;
private final int lineOffset;
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 9161f4f..e8fed84 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -15,13 +15,13 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.CfOrDexInstruction;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
@@ -87,12 +87,15 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.R8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.ir.desugar.NonEmptyCfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
-import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -113,7 +116,6 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.IteratorUtils;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
@@ -126,7 +128,6 @@
import com.android.tools.r8.utils.collections.ProgramFieldSet;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -384,19 +385,28 @@
private final GraphReporter graphReporter;
+ private static final class LambdaInfo {
+ final ProgramMethod context;
+ final DexCallSite callsite;
+ final LambdaDescriptor descriptor;
+
+ public LambdaInfo(ProgramMethod context, DexCallSite callsite, LambdaDescriptor descriptor) {
+ this.context = context;
+ this.callsite = callsite;
+ this.descriptor = descriptor;
+ }
+ }
+
+ private final CfInstructionDesugaringCollection desugaring;
private final LambdaRewriter lambdaRewriter;
+ private final List<LambdaInfo> lambdasForDesugaring = new ArrayList<>();
+
private final BackportedMethodRewriter backportRewriter;
- private final NestBasedAccessDesugaring nestBasedAccessRewriter;
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
- private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses =
- new IdentityHashMap<>();
- private final ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites =
- ProgramMethodMap.create();
private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>();
private final Map<DexMethod, ProgramMethod> methodsWithTwrCloseResource = new IdentityHashMap<>();
- private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create();
Enqueuer(
@@ -439,9 +449,11 @@
failedFieldResolutionTargets = SetUtils.newIdentityHashSet(0);
liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
liveFields = new LiveFieldsSet(graphReporter::registerField);
+ desugaring =
+ mode.isInitialTreeShaking()
+ ? new NonEmptyCfInstructionDesugaringCollection(appView)
+ : CfInstructionDesugaringCollection.empty();
lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
- nestBasedAccessRewriter =
- options.shouldDesugarNests() ? new NestBasedAccessDesugaring(appView) : null;
backportRewriter =
options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
twrCloseResourceRewriter =
@@ -968,19 +980,10 @@
return;
}
- DexEncodedMethod contextMethod = context.getDefinition();
if (lambdaRewriter != null) {
- assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
- CfCode code = contextMethod.getCode().asCfCode();
- if (code != null) {
- LambdaClass lambdaClass = lambdaRewriter.createLambdaClass(descriptor, context);
- lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context));
- lambdaCallSites
- .computeIfAbsent(context, k -> new IdentityHashMap<>())
- .put(callSite, lambdaClass);
- if (lambdaClass.descriptor.interfaces.contains(appView.dexItemFactory().serializableType)) {
- classesWithSerializableLambdas.add(context.getHolder());
- }
+ assert context.getDefinition().getCode().isCfCode() : "Unexpected input type with lambdas";
+ if (context.getDefinition().getCode().isCfCode()) {
+ lambdasForDesugaring.add(new LambdaInfo(context, callSite, descriptor));
}
} else {
markLambdaAsInstantiated(descriptor, context);
@@ -3066,8 +3069,6 @@
registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
}
if (mode.isInitialTreeShaking()) {
- registerInvokeAnalysis(
- appView.getInvokeSpecialBridgeSynthesizer().getEnqueuerInvokeAnalysis());
// This is simulating the effect of the "root set" applied rules.
// This is done only in the initial pass, in subsequent passes the "rules" are reapplied
// by iterating the instances.
@@ -3167,6 +3168,10 @@
private static class SyntheticAdditions {
+ private final ProcessorContext processorContext;
+ private Map<DexMethod, MethodProcessingContext> methodProcessingContexts =
+ new IdentityHashMap<>();
+
List<ProgramMethod> desugaredMethods = new LinkedList<>();
Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations =
@@ -3186,6 +3191,15 @@
// Subset of synthesized classes that need to be added to the main-dex file.
Set<DexProgramClass> mainDexTypes = Sets.newIdentityHashSet();
+ SyntheticAdditions(ProcessorContext processorContext) {
+ this.processorContext = processorContext;
+ }
+
+ MethodProcessingContext getMethodContext(ProgramMethod method) {
+ return methodProcessingContexts.computeIfAbsent(
+ method.getReference(), k -> processorContext.createMethodProcessingContext(method));
+ }
+
boolean isEmpty() {
boolean empty =
desugaredMethods.isEmpty()
@@ -3289,9 +3303,8 @@
// First part of synthesis is to create and register all reachable synthetic additions.
// In particular these additions are order independent, i.e., it does not matter which are
// registered first and no dependencies may exist among them.
- SyntheticAdditions additions = new SyntheticAdditions();
+ SyntheticAdditions additions = new SyntheticAdditions(appView.createProcessorContext());
desugar(additions);
- synthesizeInvokeSpecialBridges(additions);
synthesizeInterfaceMethodBridges(additions);
synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
@@ -3319,55 +3332,17 @@
}
private void desugar(SyntheticAdditions additions) throws ExecutionException {
- ThreadUtils.processItems(pendingDesugaring, this::desugar, executorService);
+ R8CfInstructionDesugaringEventConsumer desugaringEventConsumer =
+ CfInstructionDesugaringEventConsumer.createForR8();
+ ThreadUtils.processItems(
+ pendingDesugaring,
+ method -> desugaring.desugar(method, desugaringEventConsumer),
+ executorService);
+ desugaringEventConsumer.finalizeDesugaring(appView);
Iterables.addAll(additions.desugaredMethods, pendingDesugaring);
pendingDesugaring.clear();
}
- private void desugar(ProgramMethod method) {
- Code code = method.getDefinition().getCode();
- if (!code.isCfCode()) {
- appView
- .options()
- .reporter
- .error(
- new StringDiagnostic(
- "Unsupported attempt to desugar non-CF code",
- method.getOrigin(),
- method.getPosition()));
- return;
- }
-
- CfCode cfCode = code.asCfCode();
- List<CfInstruction> desugaredInstructions =
- ListUtils.flatMap(
- cfCode.getInstructions(),
- instruction -> {
- // TODO(b/177810578): Migrate other cf-to-cf based desugaring here, and assert that
- // that at most one instruction desugarer applies to each instruction.
- if (nestBasedAccessRewriter != null) {
- List<CfInstruction> replacement =
- nestBasedAccessRewriter.desugarInstruction(instruction, method, null);
- if (replacement != null) {
- return replacement;
- }
- }
- return null;
- },
- null);
- if (desugaredInstructions != null) {
- cfCode.setInstructions(desugaredInstructions);
- } else {
- assert false : "Expected code to be desugared";
- }
- }
-
- private void synthesizeInvokeSpecialBridges(SyntheticAdditions additions) {
- SortedProgramMethodSet bridges =
- appView.getInvokeSpecialBridgeSynthesizer().insertBridgesForR8();
- bridges.forEach(additions::addLiveMethod);
- }
-
private void synthesizeInterfaceMethodBridges(SyntheticAdditions additions) {
for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
DexProgramClass holder = bridge.getHolder();
@@ -3380,26 +3355,31 @@
private void synthesizeBackports(SyntheticAdditions additions) {
for (ProgramMethod method : methodsWithBackports.values()) {
- backportRewriter.desugar(method, appInfo, additions::addLiveMethod);
+ backportRewriter.desugar(
+ method, appInfo, additions.getMethodContext(method), additions::addLiveMethod);
}
}
private void synthesizeTwrCloseResource(SyntheticAdditions additions) {
for (ProgramMethod method : methodsWithTwrCloseResource.values()) {
- twrCloseResourceRewriter.rewriteCf(method, additions::addLiveMethod);
+ twrCloseResourceRewriter.rewriteCf(
+ method, additions::addLiveMethod, additions.getMethodContext(method));
}
}
private void synthesizeLambdas(SyntheticAdditions additions) {
- if (lambdaRewriter == null || lambdaClasses.isEmpty()) {
- assert lambdaCallSites.isEmpty();
- assert classesWithSerializableLambdas.isEmpty();
+ if (lambdasForDesugaring.isEmpty()) {
return;
}
- for (Pair<LambdaClass, ProgramMethod> lambdaClassAndContext : lambdaClasses.values()) {
+ assert lambdaRewriter != null;
+ ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites = ProgramMethodMap.create();
+ Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
+ for (LambdaInfo lambdaInfo : lambdasForDesugaring) {
// Add all desugared classes to the application, main-dex list, and mark them instantiated.
- LambdaClass lambdaClass = lambdaClassAndContext.getFirst();
- ProgramMethod context = lambdaClassAndContext.getSecond();
+ ProgramMethod context = lambdaInfo.context;
+ LambdaClass lambdaClass =
+ lambdaRewriter.createLambdaClass(
+ lambdaInfo.descriptor, context, additions.getMethodContext(context));
DexProgramClass programClass = lambdaClass.getLambdaProgramClass();
additions.addInstantiatedClass(programClass, context);
// Mark the instance constructor targeted and live.
@@ -3408,6 +3388,14 @@
ProgramMethod method = new ProgramMethod(programClass, constructor);
markMethodAsTargeted(method, reason);
markDirectStaticOrConstructorMethodAsLive(method, reason);
+ // Populate method -> info mapping for method rewriting.
+ lambdaCallSites
+ .computeIfAbsent(context, k -> new IdentityHashMap<>())
+ .put(lambdaInfo.callsite, lambdaClass);
+ // Populate set of types with serialized lambda method for removal.
+ if (lambdaInfo.descriptor.interfaces.contains(appView.dexItemFactory().serializableType)) {
+ classesWithSerializableLambdas.add(context.getHolder());
+ }
}
// Rewrite all of the invoke-dynamic instructions to lambda class instantiations.
@@ -3420,9 +3408,7 @@
}
// Clear state before next fixed point iteration.
- lambdaClasses.clear();
- lambdaCallSites.clear();
- classesWithSerializableLambdas.clear();
+ lambdasForDesugaring.clear();
}
private void finalizeLibraryMethodOverrideInformation() {
@@ -4054,11 +4040,9 @@
}
private void traceNonDesugaredCode(ProgramMethod method) {
- if (getMode().isInitialTreeShaking()) {
- if (nestBasedAccessRewriter != null && nestBasedAccessRewriter.needsDesugaring(method)) {
- pendingDesugaring.add(method);
- return;
- }
+ if (getMode().isInitialTreeShaking() && desugaring.needsDesugaring(method)) {
+ pendingDesugaring.add(method);
+ return;
}
traceCode(method);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 14f9cf8..6c07af2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
@@ -16,7 +17,6 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
@@ -276,15 +276,15 @@
public DexProgramClass createClass(
SyntheticKind kind,
- DexProgramClass context,
+ UniqueContext context,
DexItemFactory factory,
- Supplier<String> syntheticIdSupplier,
Consumer<SyntheticProgramClassBuilder> fn) {
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
- SynthesizingContext outerContext = getSynthesizingContext(context);
+ SynthesizingContext outerContext = getSynthesizingContext(context.getClassContext());
DexType type =
- SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), factory);
+ SyntheticNaming.createInternalType(
+ kind, outerContext, context.getSyntheticSuffix(), factory);
SyntheticProgramClassBuilder classBuilder =
new SyntheticProgramClassBuilder(type, outerContext, factory);
fn.accept(classBuilder);
@@ -305,6 +305,7 @@
return clazz;
}
+ // TODO(b/172194101): Make this take a unique context.
public DexProgramClass createFixedClass(
SyntheticKind kind,
DexProgramClass context,
@@ -338,31 +339,13 @@
/** Create a single synthetic method item. */
public ProgramMethod createMethod(
SyntheticKind kind,
- ProgramDefinition context,
+ UniqueContext context,
DexItemFactory factory,
Consumer<SyntheticMethodBuilder> fn) {
- return createMethod(kind, context, factory, fn, this::getNextSyntheticId);
+ return createMethod(kind, context.getClassContext(), factory, fn, context::getSyntheticSuffix);
}
- // TODO(b/172194101): Remove this once the uniqueness is a property of the context.
- public ProgramMethod createMethod(
- SyntheticKind kind,
- ProgramDefinition context,
- DexItemFactory factory,
- Consumer<SyntheticMethodBuilder> fn,
- MethodProcessingId methodProcessingId) {
- return createMethod(
- kind,
- context,
- factory,
- fn,
- methodProcessingId != null
- ? methodProcessingId::getFullyQualifiedIdAndIncrement
- : this::getNextSyntheticId);
- }
-
- // TODO(b/172194101): Remove/private this once the uniqueness is a property of the context.
- public ProgramMethod createMethod(
+ private ProgramMethod createMethod(
SyntheticKind kind,
ProgramDefinition context,
DexItemFactory factory,
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 118a5b6..afbafe0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -44,7 +44,6 @@
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -101,7 +100,15 @@
public enum DesugarState {
OFF,
- ON
+ ON;
+
+ public boolean isOff() {
+ return this == OFF;
+ }
+
+ public boolean isOn() {
+ return this == ON;
+ }
}
public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V15;
@@ -479,10 +486,7 @@
}
public boolean shouldDesugarNests() {
- if (testing.enableForceNestBasedAccessDesugaringForTest) {
- return true;
- }
- return !canUseNestBasedAccess();
+ return testing.enableForceNestBasedAccessDesugaringForTest || !canUseNestBasedAccess();
}
public boolean canUseRecords() {
@@ -1222,6 +1226,10 @@
options.testing.enableExperimentalMissingClassesReporting = true;
}
+ public static void allowExperimentClassFileVersion(InternalOptions options) {
+ options.reportedExperimentClassFileVersion.set(true);
+ }
+
public static int NO_LIMIT = -1;
// Force writing the specified bytes as the DEX version content.
@@ -1234,7 +1242,7 @@
public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
- public BiConsumer<ProgramMethod, MethodProcessingId> methodProcessingIdConsumer = null;
+ public Consumer<String> processingContextsConsumer = null;
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
repackagingConfigurationFactory =
@@ -1269,7 +1277,6 @@
public boolean addCallEdgesForLibraryInvokes = false;
public boolean allowCheckDiscardedErrors = false;
- public boolean allowDexInputForTesting = false;
public boolean allowInjectedAnnotationMethods = false;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index e6b0085..a6f800b 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -114,6 +114,12 @@
return result != null ? result : defaultValue;
}
+ public static <T> ArrayList<T> newArrayList(ForEachable<T> forEachable) {
+ ArrayList<T> list = new ArrayList<>();
+ forEachable.forEach(list::add);
+ return list;
+ }
+
public static <T> Optional<T> removeFirstMatch(List<T> list, Predicate<T> element) {
int index = firstIndexMatching(list, element);
if (index >= 0) {
diff --git a/src/main/java/com/android/tools/r8/utils/SupplierUtils.java b/src/main/java/com/android/tools/r8/utils/SupplierUtils.java
new file mode 100644
index 0000000..13a02cb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SupplierUtils.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, 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.utils;
+
+import java.util.function.Supplier;
+
+public class SupplierUtils {
+
+ public static <T> Supplier<T> nonThreadSafeMemoize(Supplier<T> supplier) {
+ Box<T> box = new Box<>();
+ return () -> box.computeIfAbsent(supplier);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 400ef0c..e7b9b00 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -122,6 +122,12 @@
zip(zipFile, basePath, Arrays.asList(filesToZip));
}
+ public static List<Path> unzip(Path zipFile, Path outDirectory) throws IOException {
+ return unzip(zipFile.toString(), outDirectory.toFile(), (entry) -> true).stream()
+ .map(File::toPath)
+ .collect(Collectors.toList());
+ }
+
public static List<File> unzip(String zipFile, File outDirectory) throws IOException {
return unzip(zipFile, outDirectory, (entry) -> true);
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index 6c388fb..ff0e4ba 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -65,7 +65,7 @@
@Override
public void visitDexString(DexString string) {
- visitInt(string.hashCode());
+ hash.putBytes(string.content);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index 4a24331..a8b8a49 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -66,8 +66,9 @@
ProcessResult d8Result = runOnArtRaw(compileWithD8(app), main);
ProcessResult r8NonShakenResult =
runOnArtRaw(compileWithR8(app, "-dontshrink\n-dontobfuscate\n"), main);
- ProcessResult r8ShakenResult = runOnArtRaw(
- compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"), main);
+ ProcessResult r8ShakenResult =
+ runOnArtRaw(
+ compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"), main);
Assert.assertEquals(javaResult.stdout, d8Result.stdout);
Assert.assertEquals(javaResult.stdout, r8NonShakenResult.stdout);
Assert.assertEquals(javaResult.stdout, r8ShakenResult.stdout);
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 8698d61..9951a04 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -153,6 +153,11 @@
return addClasspath(ToolHelper.getClassPathForTests());
}
+ public JvmTestBuilder enablePreview() {
+ addVmArguments("--enable-preview");
+ return self();
+ }
+
public JvmTestBuilder addVmArguments(Collection<String> arguments) {
vmArguments.addAll(arguments);
return self();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
index 9ac01a0..a9907fc 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -53,9 +53,10 @@
public void invokeCustomWithShrinking() throws Throwable {
test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
.withMinApiLevel(AndroidApiLevel.P.getLevel())
- .withBuilderTransformation(builder ->
- builder.addProguardConfigurationFiles(
- Paths.get(ToolHelper.EXAMPLES_ANDROID_P_DIR, "invokecustom/keep-rules.txt")))
+ .withBuilderTransformation(
+ builder ->
+ builder.addProguardConfigurationFiles(
+ Paths.get(ToolHelper.EXAMPLES_ANDROID_P_DIR, "invokecustom/keep-rules.txt")))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 8a004a9..e13ddd2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -19,7 +19,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
@@ -104,10 +103,10 @@
}
}
- private R8Command.Builder addInputFile(R8Command.Builder builder) throws NoSuchFileException {
+ private R8Command.Builder addInputFile(R8Command.Builder builder) {
if (input == Input.DX) {
// If input is DEX code, use the tool helper to add the DEX sources as R8 disallows them.
- ToolHelper.getAppBuilder(builder).addProgramFiles(getInputFile());
+ ToolHelper.getAppBuilder(builder).addProgramFiles(getOriginalDexFile());
} else {
builder.addProgramFiles(getInputFile());
}
@@ -169,7 +168,6 @@
protected void configure(InternalOptions options) {
options.lineNumberOptimization = LineNumberOptimization.OFF;
- options.testing.allowDexInputForTesting = input == Input.DX;
}
private boolean shouldCompileFail() {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
index 9e48fd0..0690fa9 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesKotlinTest.java
@@ -20,6 +20,7 @@
@Override
protected void configure(InternalOptions options) {
+ super.configure(options);
if (output == Output.CF) {
// Class inliner is not supported with CF backend yet.
options.enableClassInlining = false;
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index 0d21319..1d6cb9a 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -7,16 +7,17 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -29,13 +30,19 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class R8RunSmaliTestsTest {
+public class R8RunSmaliTestsTest extends TestBase {
@Rule
public ExpectedException thrown = ExpectedException.none();
private static final String SMALI_DIR = ToolHelper.SMALI_BUILD_DIR;
+ private static Map<String, Set<String>> missingClasses =
+ ImmutableMap.of(
+ "try-catch", ImmutableSet.of("test.X"),
+ "type-confusion-regression5", ImmutableSet.of("jok", "jol"),
+ "bad-codegen", ImmutableSet.of("java.util.LTest"));
+
// Tests where the original smali code fails on Art, but runs after R8 processing.
private static Map<DexVm.Version, List<String>> originalFailingOnArtVersions = ImmutableMap.of(
Version.V5_1_1, ImmutableList.of(
@@ -164,24 +171,25 @@
@Test
public void SmaliTest() throws Exception {
Path originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName);
- String outputPath = temp.getRoot().getCanonicalPath();
- R8Command.Builder builder = R8Command.builder()
- .addProguardConfiguration(ImmutableList.of("-keep class * { *; }"), Origin.unknown())
- .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
- .setOutput(Paths.get(outputPath), OutputMode.DexIndexed);
- ToolHelper.getAppBuilder(builder).addProgramFiles(originalDexFile);
+ Path outputPath = temp.getRoot().toPath().resolve("classes.dex");
if (failingOnX8.contains(directoryName)) {
thrown.expect(CompilationFailedException.class);
}
- R8.run(builder.build());
+
+ testForR8(Backend.DEX)
+ .addKeepAllClassesRule()
+ .addProgramDexFileData(Files.readAllBytes(originalDexFile))
+ .addDontWarn(missingClasses.getOrDefault(directoryName, Collections.emptySet()))
+ .compile()
+ .writeToZip(outputPath);
if (!ToolHelper.artSupported()) {
return;
}
String mainClass = "Test";
- String generated = outputPath + "/classes.dex";
+ String generated = outputPath.toString();
String output = "";
DexVm.Version dexVmVersion = ToolHelper.getDexVm().getVersion();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index a80cc32..cbd2755 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -715,18 +715,30 @@
protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
AndroidApp app) throws Exception {
- return computeAppViewWithClassHierachy(
- app,
- factory ->
- buildConfigForRules(
- factory,
- Collections.singletonList(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
+ return computeAppViewWithClassHierachy(app, null);
}
private static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
+ return computeAppViewWithClassHierachy(app, keepConfig, null);
+ }
+
+ private static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
+ AndroidApp app,
+ Function<DexItemFactory, ProguardConfiguration> keepConfig,
+ Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
DexItemFactory dexItemFactory = new DexItemFactory();
+ if (keepConfig == null) {
+ keepConfig =
+ factory ->
+ buildConfigForRules(
+ factory, ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})));
+ }
InternalOptions options = new InternalOptions(keepConfig.apply(dexItemFactory), new Reporter());
+ if (optionsConsumer != null) {
+ optionsConsumer.accept(options);
+ }
DexApplication dexApplication = readApplicationForDexOutput(app, options);
AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(dexApplication.toDirect());
appView.setAppServices(AppServices.builder(appView).build());
@@ -735,11 +747,7 @@
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(AndroidApp app)
throws Exception {
- return computeAppViewWithLiveness(
- app,
- factory ->
- buildConfigForRules(
- factory, ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
+ return computeAppViewWithLiveness(app, null, null);
}
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
@@ -752,7 +760,16 @@
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
- AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierachy(app, keepConfig);
+ return computeAppViewWithLiveness(app, keepConfig, null);
+ }
+
+ protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
+ AndroidApp app,
+ Function<DexItemFactory, ProguardConfiguration> keepConfig,
+ Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
+ AppView<AppInfoWithClassHierarchy> appView =
+ computeAppViewWithClassHierachy(app, keepConfig, optionsConsumer);
// Run the tree shaker to compute an instance of AppInfoWithLiveness.
ExecutorService executor = Executors.newSingleThreadExecutor();
SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index 1e75de5..63b1982 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -96,6 +96,11 @@
return addMainDexListClassReferences(ListUtils.map(classes, Reference::classFromClass));
}
+ public T addMainDexListFiles(Path... files) {
+ builder.addMainDexListFiles(files);
+ return self();
+ }
+
public T addMainDexListFiles(Collection<Path> files) {
builder.addMainDexListFiles(files);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 64c7680..80f3234 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.TriFunction;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -290,6 +291,11 @@
return new CodeInspector(app);
}
+ public CodeInspector inspector(Consumer<InternalOptions> debugOptionsConsumer)
+ throws IOException, ExecutionException {
+ return new CodeInspector(app, debugOptionsConsumer);
+ }
+
public <E extends Throwable> CR inspect(ThrowingConsumer<CodeInspector, E> consumer)
throws IOException, ExecutionException, E {
consumer.accept(inspector());
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index c5811f9..e3570be 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -197,6 +197,10 @@
return withApiFilter(api -> api.getLevel() < endExclusive.getLevel());
}
+ public TestParametersBuilder withApiLevelsWithoutNativeMultiDex() {
+ return withApiLevelsEndingAtExcluding(AndroidApiLevel.L);
+ }
+
public TestParametersBuilder withCustomRuntime(TestRuntime runtime) {
assert getUnfilteredAvailableRuntimes().noneMatch(r -> r == runtime);
customRuntimes.add(runtime);
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index dbac9f1..816e207 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -114,13 +114,17 @@
return addKeepRules("-dontwarn " + className);
}
- public T addDontWarn(String... classes) {
+ public T addDontWarn(Collection<String> classes) {
for (String clazz : classes) {
addKeepRules("-dontwarn " + clazz);
}
return self();
}
+ public T addDontWarn(String... classes) {
+ return addDontWarn(Arrays.asList(classes));
+ }
+
@Deprecated
public T addDontWarnCompanionClass(Class<?> clazz) {
return addDontWarn(clazz.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
@@ -146,12 +150,6 @@
return addDontWarn("java.nio.file.**");
}
- // TODO(b/176133676): Investigate why there are missing class references to org.jetbrains
- @Deprecated
- public T addDontWarnJetBrains() {
- return addDontWarn("org.jetbrains.**");
- }
-
public T addDontWarnJetBrainsAnnotations() {
return addDontWarnJetBrainsNotNullAnnotation().addDontWarnJetBrainsNullableAnnotation();
}
@@ -164,18 +162,6 @@
return addDontWarn("org.jetbrains.annotations.Nullable");
}
- // TODO(b/176133676): Should not report missing classes for Kotlin classes.
- @Deprecated
- public T addDontWarnKotlin() {
- return addDontWarn("kotlin.**");
- }
-
- // TODO(b/176133676): Should not report missing classes for Kotlin metadata.
- @Deprecated
- public T addDontWarnKotlinMetadata() {
- return addDontWarn("kotlin.Metadata");
- }
-
public T addIgnoreWarnings() {
return addKeepRules("-ignorewarnings");
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2ef51ed..c906281 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -860,6 +860,12 @@
return reflectJar;
}
+ public static Path getKotlinAnnotationJar(KotlinCompiler kotlinc) {
+ Path annotationJar = kotlinc.getFolder().resolve("annotations-13.0.jar");
+ assert Files.exists(annotationJar) : "Expected annotation jar";
+ return annotationJar;
+ }
+
public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) {
if (minSdk.getLevel() >= AndroidApiLevel.N.getLevel()) {
return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar");
@@ -889,8 +895,8 @@
super(parentFolder);
}
- protected void after() {
- } // instead of remove, do nothing
+ @Override
+ protected void after() {} // instead of remove, do nothing
}
// For non-Linux platforms create the temporary directory in the repository root to simplify
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index 55d4763..4ac6ddf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -124,12 +124,12 @@
.addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
.addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
+ .addProgramFiles(ToolHelper.getKotlinAnnotationJar(kotlinParameters.getCompiler()))
.addKeepMainRule(PKG + ".MainKt")
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages()
- .addDontWarnJetBrains();
+ .allowDiagnosticWarningMessages();
KeepRuleConsumer keepRuleConsumer = null;
if (desugarLibrary) {
keepRuleConsumer = createKeepRuleConsumer(parameters);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
index 92183a7..ccd5d40 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+import static com.android.tools.r8.utils.InternalOptions.TestingOptions;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +42,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
index 33a3b57..b2fb0f5 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +42,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
index 1d1e785..02e75d3 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,7 +55,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
index 1bb0a1a..d567625 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,7 +52,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
index d4a804b..6e5cc6b 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -4,8 +4,15 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.TestRuntime.getCheckedInJdk8;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.JavaCompilerTool;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.nio.file.Files;
@@ -16,6 +23,8 @@
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import org.junit.Assume;
+import org.junit.rules.TemporaryFolder;
/**
* Records are compiled using: third_party/openjdk/jdk-15/linux/bin/javac --release 15
@@ -30,6 +39,27 @@
return Paths.get(ToolHelper.TESTS_BUILD_DIR, EXAMPLE_FOLDER, RECORD_FOLDER + ".jar");
}
+ // TODO(b/169645628): Consider if that keep rule should be required or not.
+ public static final String RECORD_KEEP_RULE =
+ "-keepattributes *\n" + "-keep class * extends java.lang.Record { private final <fields>; }";
+
+ public static void setJdk15Library(R8FullTestBuilder builder, TemporaryFolder temp)
+ throws IOException {
+ Assume.assumeFalse(ToolHelper.isWindows());
+ // TODO(b/169645628): Add JDK-15 runtime jar instead. As a temporary solution we use the jdk 8
+ // runtime with additional stubs.
+ // We use jdk-8 for compilation because in jdk-9 and higher we would need to deal with the
+ // module patching logic.
+ Path recordStubs =
+ JavaCompilerTool.create(getCheckedInJdk8(), temp)
+ .addSourceFiles(Paths.get("src", "test", "javaStubs", "Record.java"))
+ .addSourceFiles(Paths.get("src", "test", "javaStubs", "ObjectMethods.java"))
+ .addSourceFiles(Paths.get("src", "test", "javaStubs", "TypeDescriptor.java"))
+ .addSourceFiles(Paths.get("src", "test", "javaStubs", "RecordComponent.java"))
+ .compile();
+ builder.addLibraryFiles(ToolHelper.getJava8RuntimeJar()).addLibraryFiles(recordStubs);
+ }
+
public static byte[][] getProgramData(String mainClassSimpleName) {
byte[][] bytes = classDataFromPrefix(RECORD_FOLDER + "/" + mainClassSimpleName);
assert bytes.length > 0 : "Did not find any program data for " + mainClassSimpleName;
@@ -68,4 +98,13 @@
}
return result.toArray(new byte[0][0]);
}
+
+ public static void assertRecordsAreRecords(Path output) throws IOException {
+ CodeInspector inspector = new CodeInspector(output, opt -> opt.testing.canUseRecords = true);
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ if (clazz.getDexProgramClass().superType.toString().equals("java.lang.Record")) {
+ assertTrue(clazz.getDexProgramClass().isRecord());
+ }
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
index c0ca829..e8ef3a4 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +44,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index 35ce924..39e503c 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -4,10 +4,14 @@
package com.android.tools.r8.desugar.records;
+import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +43,28 @@
public void testJvm() throws Exception {
testForJvm()
.addProgramClassFileData(PROGRAM_DATA)
- .addVmArguments("--enable-preview")
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8Cf() throws Exception {
+ Path output =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRules(RECORD_KEEP_RULE)
+ .addKeepMainRule(MAIN_TYPE)
+ .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .addOptionsModification(opt -> opt.testing.canUseRecords = true)
+ .compile()
+ .writeToZip();
+ RecordTestUtils.assertRecordsAreRecords(output);
+ testForJvm()
+ .addRunClasspathFiles(output)
+ .enablePreview()
.run(parameters.getRuntime(), MAIN_TYPE)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
index 48b29e7..ee2e4e1 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
@@ -43,7 +43,7 @@
assumeTrue(backend == Backend.CF);
testForJvm()
.addRunClasspathFiles(Sealed.jar())
- .addVmArguments("--enable-preview")
+ .enablePreview()
.run(TestRuntime.getCheckedInJdk15(), Sealed.Main.typeName())
.assertSuccessWithOutputLines("R8 compiler", "D8 compiler");
}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 603a0b1..b1d9990 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -288,7 +288,7 @@
assertEquals(0, result.exitCode);
// Process the application and expect the same result on Art.
- AndroidApp processedApp = processApplication(application);
+ AndroidApp processedApp = ToolHelper.runR8(application, null);
assertEquals(result.stdout, runArt(processedApp, Main.class.getCanonicalName()));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
index d0242b4..d4e1e43 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
@@ -5,14 +5,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
+import com.google.common.collect.Sets;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -29,7 +29,7 @@
List<String> additionalProguardConfiguration =
ImmutableList.of(
ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS + "GmsCore_proguard.config");
- Map<String, IntSet> methodProcessingIds = new ConcurrentHashMap<>();
+ Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
AndroidApp app1 =
buildAndTreeShakeFromDeployJar(
CompilationMode.RELEASE,
@@ -38,17 +38,12 @@
GMSCORE_LATEST_MAX_SIZE,
additionalProguardConfiguration,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) ->
- assertTrue(
- methodProcessingIds
- .computeIfAbsent(
- method.toSourceString(), ignore -> new IntOpenHashSet(4))
- .add(methodProcessingId.getPrimaryId()));
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id));
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
});
-
+ Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
AndroidApp app2 =
buildAndTreeShakeFromDeployJar(
CompilationMode.RELEASE,
@@ -57,23 +52,20 @@
GMSCORE_LATEST_MAX_SIZE,
additionalProguardConfiguration,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) -> {
- String key = method.toSourceString();
- IntSet ids = methodProcessingIds.get(key);
- assertNotNull(ids);
- assertTrue(ids.remove(methodProcessingId.getPrimaryId()));
- if (ids.isEmpty()) {
- methodProcessingIds.remove(key);
- }
+ options.testing.processingContextsConsumer =
+ id -> {
+ assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
};
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
});
// Verify that the result of the two compilations was the same.
+ assertEquals(
+ Collections.emptySet(),
+ Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
assertIdenticalApplications(app1, app2);
- assertTrue(methodProcessingIds.isEmpty());
assertEquals(proguardMap1, proguardMap2);
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
index 98bc7ce..2c2adbf 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -6,16 +6,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
+import com.google.common.collect.Sets;
import java.io.File;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
@@ -27,11 +27,9 @@
@Test
public void buildFromDeployJar() throws Exception {
- // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
File tempFolder = temp.newFolder();
-
File app1Zip = new File(tempFolder, "app1.zip");
- Map<String, IntSet> methodProcessingIds = new ConcurrentHashMap<>();
+ Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
AndroidApp app1 =
buildFromDeployJar(
CompilerUnderTest.R8,
@@ -39,19 +37,15 @@
GMSCoreCompilationTestBase.GMSCORE_V10_DIR,
false,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) ->
- assertTrue(
- methodProcessingIds
- .computeIfAbsent(
- method.toSourceString(), ignore -> new IntOpenHashSet(4))
- .add(methodProcessingId.getPrimaryId()));
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id));
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
},
() -> new ArchiveConsumer(app1Zip.toPath(), true));
File app2Zip = new File(tempFolder, "app2.zip");
+ Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
AndroidApp app2 =
buildFromDeployJar(
CompilerUnderTest.R8,
@@ -59,15 +53,10 @@
GMSCoreCompilationTestBase.GMSCORE_V10_DIR,
false,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) -> {
- String key = method.toSourceString();
- IntSet ids = methodProcessingIds.get(key);
- assertNotNull(ids);
- assertTrue(ids.remove(methodProcessingId.getPrimaryId()));
- if (ids.isEmpty()) {
- methodProcessingIds.remove(key);
- }
+ options.testing.processingContextsConsumer =
+ id -> {
+ assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
};
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
@@ -75,9 +64,11 @@
() -> new ArchiveConsumer(app2Zip.toPath(), true));
// Verify that the result of the two compilations was the same.
+ assertEquals(
+ Collections.emptySet(),
+ Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
assertIdenticalApplications(app1, app2);
assertIdenticalZipFiles(app1Zip, app2Zip);
- assertTrue(methodProcessingIds.isEmpty());
assertEquals(proguardMap1, proguardMap2);
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
index 198ae76..dc78be2 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
@@ -3,15 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
+import static com.android.tools.r8.utils.AssertionUtils.assertNotNull;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApp;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
+import com.google.common.collect.Sets;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
@@ -24,8 +24,7 @@
@Test
public void buildAndTreeShakeFromDeployJar() throws Exception {
- // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
- Map<String, IntSet> methodProcessingIds = new ConcurrentHashMap<>();
+ Map<String, String> idsRoundOne = new ConcurrentHashMap<>();
AndroidApp app1 =
buildAndTreeShakeFromDeployJar(
CompilationMode.RELEASE,
@@ -33,16 +32,12 @@
false,
GMSCORE_V10_MAX_SIZE,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) ->
- assertTrue(
- methodProcessingIds
- .computeIfAbsent(
- method.toSourceString(), ignore -> new IntOpenHashSet(4))
- .add(methodProcessingId.getPrimaryId()));
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id));
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap1 = proguardMap);
});
+ Map<String, String> idsRoundTwo = new ConcurrentHashMap<>();
AndroidApp app2 =
buildAndTreeShakeFromDeployJar(
CompilationMode.RELEASE,
@@ -50,23 +45,20 @@
false,
GMSCORE_V10_MAX_SIZE,
options -> {
- options.testing.methodProcessingIdConsumer =
- (method, methodProcessingId) -> {
- String key = method.toSourceString();
- IntSet ids = methodProcessingIds.get(key);
- assertNotNull(ids);
- assertTrue(ids.remove(methodProcessingId.getPrimaryId()));
- if (ids.isEmpty()) {
- methodProcessingIds.remove(key);
- }
+ options.testing.processingContextsConsumer =
+ id -> {
+ assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
};
options.proguardMapConsumer =
ToolHelper.consumeString(proguardMap -> this.proguardMap2 = proguardMap);
});
// Verify that the result of the two compilations was the same.
+ assertEquals(
+ Collections.emptySet(),
+ Sets.symmetricDifference(idsRoundOne.keySet(), idsRoundTwo.keySet()));
assertIdenticalApplications(app1, app2);
- assertTrue(methodProcessingIds.isEmpty());
assertEquals(proguardMap1, proguardMap2);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
index ce5cbce..6f24c38 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.function.Consumer;
@@ -56,9 +57,11 @@
this.className = className;
}
+ public void configure(InternalOptions options) {}
+
@Before
public void setup() throws Exception {
- appView = computeAppViewWithLiveness(app);
+ appView = computeAppViewWithLiveness(app, null, this::configure);
}
public void buildAndCheckIR(String methodName, Consumer<IRCode> irInspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index ac86504..024dd38 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -227,7 +227,7 @@
}
@Override
- public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+ public void scheduleDesugaredMethodForProcessing(ProgramMethod method) {
throw new Unreachable();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
index 9c1a2b1..eaa9941 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer.BuilderState;
+import com.android.tools.r8.utils.InternalOptions;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -41,8 +42,11 @@
StringConcatenationTestClass.class);
}
+ @Override
+ public void configure(InternalOptions options) {}
+
@Test
- public void testUnusedBuilder() throws Exception {
+ public void testUnusedBuilder() {
buildAndCheckIR(
"unusedBuilder",
checkOptimizerStates(appView, optimizer -> {
@@ -58,7 +62,7 @@
}
@Test
- public void testTrivialSequence() throws Exception {
+ public void testTrivialSequence() {
buildAndCheckIR(
"trivialSequence",
checkOptimizerStates(appView, optimizer -> {
@@ -73,7 +77,7 @@
}
@Test
- public void testBuilderWithInitialValue() throws Exception {
+ public void testBuilderWithInitialValue() {
buildAndCheckIR(
"builderWithInitialValue",
checkOptimizerStates(appView, optimizer -> {
@@ -88,7 +92,7 @@
}
@Test
- public void testBuilderWithCapacity() throws Exception {
+ public void testBuilderWithCapacity() {
buildAndCheckIR(
"builderWithCapacity",
checkOptimizerStates(appView, optimizer -> {
@@ -103,7 +107,7 @@
}
@Test
- public void testNonStringArgs() throws Exception {
+ public void testNonStringArgs() {
buildAndCheckIR(
"nonStringArgs",
checkOptimizerStates(appView, optimizer -> {
@@ -118,7 +122,7 @@
}
@Test
- public void testTypeConversion() throws Exception {
+ public void testTypeConversion() {
buildAndCheckIR(
"typeConversion",
checkOptimizerStates(appView, optimizer -> {
@@ -133,7 +137,7 @@
}
@Test
- public void testTypeConversion_withPhis() throws Exception {
+ public void testTypeConversion_withPhis() {
buildAndCheckIR(
"typeConversion_withPhis",
checkOptimizerStates(appView, optimizer -> {
@@ -149,7 +153,7 @@
@Ignore("TODO(b/113859361): passed to another builder should be an eligible case.")
@Test
- public void testNestedBuilders_appendBuilderItself() throws Exception {
+ public void testNestedBuilders_appendBuilderItself() {
buildAndCheckIR(
"nestedBuilders_appendBuilderItself",
checkOptimizerStates(appView, optimizer -> {
@@ -167,7 +171,7 @@
@Ignore("TODO(b/113859361): merge builder.")
@Test
- public void testNestedBuilders_appendBuilderResult() throws Exception {
+ public void testNestedBuilders_appendBuilderResult() {
buildAndCheckIR(
"nestedBuilders_appendBuilderResult",
checkOptimizerStates(appView, optimizer -> {
@@ -185,7 +189,7 @@
@Ignore("TODO(b/113859361): merge builder.")
@Test
- public void testNestedBuilders_conditional() throws Exception {
+ public void testNestedBuilders_conditional() {
buildAndCheckIR(
"nestedBuilders_conditional",
checkOptimizerStates(appView, optimizer -> {
@@ -203,7 +207,7 @@
@Ignore("TODO(b/113859361): merge builder.")
@Test
- public void testConcatenatedBuilders_init() throws Exception {
+ public void testConcatenatedBuilders_init() {
buildAndCheckIR(
"concatenatedBuilders_init",
checkOptimizerStates(appView, optimizer -> {
@@ -221,7 +225,7 @@
@Ignore("TODO(b/113859361): merge builder.")
@Test
- public void testConcatenatedBuilders_append() throws Exception {
+ public void testConcatenatedBuilders_append() {
buildAndCheckIR(
"concatenatedBuilders_append",
checkOptimizerStates(appView, optimizer -> {
@@ -239,7 +243,7 @@
@Ignore("TODO(b/113859361): merge builder.")
@Test
- public void testConcatenatedBuilders_conditional() throws Exception {
+ public void testConcatenatedBuilders_conditional() {
final Set<String> expectedConstStrings = new HashSet<>();
expectedConstStrings.add("Hello,R8");
expectedConstStrings.add("D8");
@@ -259,7 +263,7 @@
}
@Test
- public void testSimplePhi() throws Exception {
+ public void testSimplePhi() {
buildAndCheckIR(
"simplePhi",
checkOptimizerStates(appView, optimizer -> {
@@ -269,7 +273,7 @@
}
@Test
- public void testPhiAtInit() throws Exception {
+ public void testPhiAtInit() {
int expectedNumOfNewBuilder = 2;
boolean expectToMeetToString = false;
if (parameters.isDexRuntime()
@@ -293,7 +297,7 @@
}
@Test
- public void testPhiWithDifferentInits() throws Exception {
+ public void testPhiWithDifferentInits() {
buildAndCheckIR(
"phiWithDifferentInits",
checkOptimizerStates(appView, optimizer -> {
@@ -308,7 +312,7 @@
}
@Test
- public void testConditionalPhiWithoutAppend() throws Exception {
+ public void testConditionalPhiWithoutAppend() {
buildAndCheckIR(
"conditionalPhiWithoutAppend",
checkOptimizerStates(appView, optimizer -> {
@@ -323,7 +327,7 @@
}
@Test
- public void testLoop() throws Exception {
+ public void testLoop() {
buildAndCheckIR(
"loop",
checkOptimizerStates(appView, optimizer -> {
@@ -338,7 +342,7 @@
}
@Test
- public void testLoopWithBuilder() throws Exception {
+ public void testLoopWithBuilder() {
buildAndCheckIR(
"loopWithBuilder",
checkOptimizerStates(appView, optimizer -> {
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 9f92a95..f53dae8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -38,7 +38,9 @@
private void test(ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception {
testForR8(parameters.getBackend())
.addLibraryFiles(
- ToolHelper.getMostRecentAndroidJar(), ToolHelper.getKotlinStdlibJar(kotlinc))
+ ToolHelper.getMostRecentAndroidJar(),
+ ToolHelper.getKotlinStdlibJar(kotlinc),
+ ToolHelper.getKotlinAnnotationJar(kotlinc))
.addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
@@ -49,14 +51,12 @@
@Test
public void testAsIs() throws Exception {
- test(
- builder ->
- builder.addDontWarnJetBrains().noMinification().noOptimization().noTreeShaking());
+ test(builder -> builder.noMinification().noOptimization().noTreeShaking());
}
@Test
public void testDontShrinkAndDontOptimize() throws Exception {
- test(builder -> builder.addDontWarnJetBrains().noOptimization().noTreeShaking());
+ test(builder -> builder.noOptimization().noTreeShaking());
}
@Test
@@ -65,7 +65,6 @@
builder ->
builder
.addKeepRules("-keep,allowobfuscation class **.*KClasses*")
- .addDontWarnJetBrains()
.noTreeShaking()
.addOptionsModification(
o -> {
@@ -78,12 +77,12 @@
@Test
public void testDontShrinkAndDontObfuscate() throws Exception {
- test(builder -> builder.addDontWarnJetBrains().noMinification().noTreeShaking());
+ test(builder -> builder.noMinification().noTreeShaking());
}
@Test
public void testDontShrink() throws Exception {
- test(builder -> builder.addDontWarnJetBrains().noTreeShaking());
+ test(TestShrinkerBuilder::noTreeShaking);
}
@Test
@@ -92,7 +91,6 @@
builder ->
builder
.addKeepRules("-keep,allowobfuscation class **.*KClasses*")
- .addDontWarnJetBrains()
.noTreeShaking());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
index 71bd275..b4bca5c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -96,6 +96,7 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib { *; }")
.addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib { *; }")
.addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib$Comp { *; }")
@@ -112,7 +113,6 @@
" void staticInternal() -> staticInternalReference"))
.addKeepRuntimeVisibleAnnotations()
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.allowAccessModification()
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index ebe0dac..cb63498 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -97,6 +97,7 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
/// Keep the annotations
.addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassAndEnum")
.addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassArr")
@@ -119,7 +120,6 @@
ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
ProguardKeepAttributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS)
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.compile()
.inspect(this::inspect)
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
index 989512b..a8ccc76 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
@@ -62,10 +62,10 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepAllClassesRule()
.addKeepAllAttributes()
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.compile()
.writeToZip();
Path output =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
index d969f34..9c138bf 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -62,10 +62,10 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepAllClassesRule()
.addKeepAllAttributes()
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.compile()
.writeToZip();
Path output =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index bc8051d..564cd64 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -63,12 +63,12 @@
public void testMetadataForLib() throws Exception {
Path outputJar =
testForR8(parameters.getBackend())
- .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+ .addClasspathFiles(
+ ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc))
.addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion))
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addDontWarnJetBrainsAnnotations()
- .addDontWarnKotlin()
.compile()
.inspect(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index d583ea7..5b7d4e6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -95,7 +95,6 @@
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addDontWarn(PKG + ".**")
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.allowDiagnosticWarningMessages()
// -dontoptimize so that basic code structure is kept.
.noOptimization()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
index e03a72c..b31c838 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -92,10 +92,10 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.compile()
.inspect(this::inspect)
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
index 569d2b7..1d6a41f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
@@ -89,10 +89,10 @@
public void testMissing() throws Exception {
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepRules("-keep class " + LIB_CLASS_NAME)
.addKeepRuntimeVisibleAnnotations()
.addDontWarnJetBrainsNotNullAnnotation()
- .addDontWarnKotlin()
.compile()
.inspect(inspector -> inspect(inspector, true));
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 6d4391f..2de9622 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -70,9 +70,9 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepRules("-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); }")
.addKeepRuntimeVisibleAnnotations()
- .addDontWarnKotlinMetadata()
.noMinification()
.compile()
.inspect(this::checkPruned)
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index ae80118..3732458 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -86,11 +86,11 @@
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
+ .addProgramFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
- .addDontWarnJetBrains()
.compile()
.writeToZip(foo.toPath())
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexRulesAndListD8.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexRulesAndListD8.java
new file mode 100644
index 0000000..d80f45c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexRulesAndListD8.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2018, 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.maindexlist;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MainDexRulesAndListD8 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withApiLevelsWithoutNativeMultiDex().build();
+ }
+
+ public MainDexRulesAndListD8(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private static Path testDir;
+ private static Path mainDexRules;
+ private static Path mainDexList;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ testDir = getStaticTemp().newFolder().toPath();
+ mainDexRules = testDir.resolve("main-dex-rules");
+ mainDexList = testDir.resolve("main-dex-list");
+ FileUtils.writeTextFile(mainDexRules, ImmutableList.of("-keep class " + A.class.getTypeName()));
+ FileUtils.writeTextFile(
+ mainDexList, ImmutableList.of(B.class.getTypeName().replace('.', '/') + ".class"));
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path result =
+ testForD8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(getClass())
+ .addMainDexRulesFiles(mainDexRules)
+ .addMainDexListFiles(mainDexList)
+ .debug()
+ .compile()
+ .writeToZip();
+ List<Path> dexFiles =
+ ZipUtils.unzip(result, testDir).stream().sorted().collect(Collectors.toList());
+ assertEquals(
+ classNamesFromDexFile(dexFiles.get(0)).stream().sorted().collect(Collectors.toList()),
+ ImmutableList.of(A.class.getTypeName(), B.class.getTypeName()));
+ assertEquals(
+ classNamesFromDexFile(dexFiles.get(1)).stream().sorted().collect(Collectors.toList()),
+ ImmutableList.of(C.class.getTypeName()));
+ }
+
+ static class A {}
+
+ static class B {}
+
+ static class C {}
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 4996911..756cf01 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -29,7 +29,6 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Assume;
@@ -374,7 +373,7 @@
}
@Test
- public void memberRebindingTest() throws IOException, ExecutionException {
+ public void memberRebindingTest() throws IOException {
Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles());
Path out = Paths.get(temp.getRoot().getCanonicalPath());
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodParameterTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodParameterTest.java
new file mode 100644
index 0000000..21e18bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodParameterTest.java
@@ -0,0 +1,80 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.missingclasses.MissingClassReferencedFromNestMemberAttributeTest.Main;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collection;
+import org.junit.Test;
+
+/**
+ * If a method reference that refers to a missing class does not resolve to a definition, then the
+ * enclosing method is to be blamed.
+ */
+public class MissingClassReferencedFromInvokeVirtualToAbsentMethodParameterTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromInvokeVirtualToAbsentMethodParameterTest(
+ TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain(), diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(Main.class)), TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(MissingClass.class)),
+ TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addIgnoreWarnings()),
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addMain() {
+ return builder ->
+ builder.addProgramClassFileData(getProgramClassFileData()).addKeepMainRule(Main.class);
+ }
+
+ static Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(transformer(Main.class).removeMethodsWithName("get").transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new Main().get(null);
+ }
+
+ /** Removed by transformer. */
+ public void get(MissingClass mc) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodReturnTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodReturnTest.java
new file mode 100644
index 0000000..d40d191
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToAbsentMethodReturnTest.java
@@ -0,0 +1,81 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collection;
+import org.junit.Test;
+
+/**
+ * If a method reference that refers to a missing class does not resolve to a definition, then the
+ * enclosing method is to be blamed.
+ */
+public class MissingClassReferencedFromInvokeVirtualToAbsentMethodReturnTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromInvokeVirtualToAbsentMethodReturnTest(
+ TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain(), diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(Main.class)), TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(MissingClass.class)),
+ TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addIgnoreWarnings()),
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addMain() {
+ return builder ->
+ builder.addProgramClassFileData(getProgramClassFileData()).addKeepMainRule(Main.class);
+ }
+
+ static Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(transformer(Main.class).removeMethodsWithName("get").transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new Main().get();
+ }
+
+ /** Removed by transformer. */
+ public MissingClass get() {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodParameterTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodParameterTest.java
new file mode 100644
index 0000000..f5b82ad
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodParameterTest.java
@@ -0,0 +1,68 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+/**
+ * If a method reference that refers to a missing class resolves to a definition, then the method
+ * definition is to be blamed, and not the enclosing method.
+ */
+public class MissingClassReferencedFromInvokeVirtualToPresentMethodParameterTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "get",
+ ImmutableList.of(Reference.classFromClass(MissingClass.class)),
+ null);
+
+ public MissingClassReferencedFromInvokeVirtualToPresentMethodParameterTest(
+ TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new Main().get(null);
+ }
+
+ public void get(MissingClass mc) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodReturnTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodReturnTest.java
new file mode 100644
index 0000000..b675c8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInvokeVirtualToPresentMethodReturnTest.java
@@ -0,0 +1,70 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Collections;
+import org.junit.Test;
+
+/**
+ * If a method reference that refers to a missing class resolves to a definition, then the method
+ * definition is to be blamed, and not the enclosing method.
+ */
+public class MissingClassReferencedFromInvokeVirtualToPresentMethodReturnTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "get",
+ Collections.emptyList(),
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromInvokeVirtualToPresentMethodReturnTest(
+ TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new Main().get();
+ }
+
+ public MissingClass get() {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodParameterTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodParameterTest.java
new file mode 100644
index 0000000..a86e57a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodParameterTest.java
@@ -0,0 +1,75 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+/** If a method definition refers to a missing class, then the method definition is to be blamed. */
+public class MissingClassReferencedFromKeptMethodParameterTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "get",
+ ImmutableList.of(Reference.classFromClass(MissingClass.class)),
+ null);
+
+ public MissingClassReferencedFromKeptMethodParameterTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ this::addKeepMethodRule);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::addKeepMethodRule));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::addKeepMethodRule));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(this::addKeepMethodRule));
+ }
+
+ private void addKeepMethodRule(R8FullTestBuilder builder) {
+ builder.addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void get(" + MissingClass.class.getTypeName() + ");",
+ "}");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+
+ public static void get(MissingClass mc) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodReturnTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodReturnTest.java
new file mode 100644
index 0000000..f211d14
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptMethodReturnTest.java
@@ -0,0 +1,77 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Collections;
+import org.junit.Test;
+
+/** If a method definition refers to a missing class, then the method definition is to be blamed. */
+public class MissingClassReferencedFromKeptMethodReturnTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ Reference.method(
+ Reference.classFromClass(Main.class),
+ "get",
+ Collections.emptyList(),
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromKeptMethodReturnTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ this::addKeepMethodRule);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::addKeepMethodRule));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::addKeepMethodRule));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(this::addKeepMethodRule));
+ }
+
+ private void addKeepMethodRule(R8FullTestBuilder builder) {
+ builder.addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static " + MissingClass.class.getTypeName() + " get();",
+ "}");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+
+ public static MissingClass get() {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index 668a8a0..4e56011 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -3,13 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.naming.ProguardMapReader.ParseException;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
@@ -196,4 +200,67 @@
Assert.assertEquals(s, result);
}
}
+
+ @Test()
+ public void testCommentLineBeforeAnyClassMappings() throws IOException {
+ String mapping =
+ StringUtils.lines(
+ "# {'id':'some.namespace.here','unknownField':'Hi There'}", "foo.bar.baz -> a:");
+ ClassNameMapper.mapperFromString(mapping);
+ }
+
+ // TODO(b/179666867): Should not fail.
+ @Test(expected = ParseException.class)
+ public void testCommentLineOnClassMapping() throws IOException {
+ ClassNameMapper.mapperFromString("foo.bar.qux -> b: # Some comment here");
+ }
+
+ // TODO(b/179666867): Should not fail.
+ @Test(expected = ParseException.class)
+ public void testJsonCommentLineOnClassMapping() throws IOException {
+ ClassNameMapper.mapperFromString(
+ "foo.bar.baz -> a: # {'id':'same.class.namespace.here','frame':'foo'}");
+ }
+
+ // TODO(b/179666867): Should not fail.
+ @Test(expected = ParseException.class)
+ public void testCommentLinesOnMethodMappingFiles() throws IOException {
+ ClassNameMapper.mapperFromString(
+ StringUtils.lines(
+ "foo.bar.qux -> b:",
+ " 1:10:void error(com.android.tools.r8.Diagnostic) -> error # Some comment here"));
+ }
+
+ // TODO(b/179666867): Should not fail.
+ @Test(expected = ParseException.class)
+ public void testJsonCommentLinesOnMethodMappingFiles() throws IOException {
+ ClassNameMapper.mapperFromString(
+ StringUtils.lines(
+ "foo.bar.qux -> b:",
+ " 1:10:void error(com.android.tools.r8.Diagnostic) -> error #"
+ + " {'id':'same.frame.namespace.here','frame':'bar'}"));
+ }
+
+ @Test()
+ public void testUnknownNamespaceComments() throws IOException {
+ String mappingWithComments =
+ StringUtils.lines(
+ "foo.bar.baz -> a:",
+ "# {'id':'some.other.namespace.here','fileName':'Class.kt'}",
+ " 1:10:void error(com.android.tools.r8.Diagnostic) -> error",
+ "# {'id':'some.line.namespace.here','fileName':'Class.kt'}",
+ "foo.bar.qux -> b:",
+ "# {'id':'some.final.namespace.thing','foo':'Hello World'}");
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ ClassNameMapper.mapperFromString(mappingWithComments, testDiagnosticMessages);
+ testDiagnosticMessages.assertOnlyInfos();
+ testDiagnosticMessages.assertInfosMatch(
+ ImmutableList.of(
+ diagnosticMessage(
+ containsString("Could not find a handler for some.other.namespace.here")),
+ diagnosticMessage(
+ containsString("Could not find a handler for some.line.namespace.here")),
+ diagnosticMessage(
+ containsString("Could not find a handler for some.final.namespace.thing"))));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 2f1a112..869deb0 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -197,9 +197,6 @@
if (optionsConsumer != null) {
optionsConsumer.accept(options);
}
- if (frontend == Frontend.DEX) {
- options.testing.allowDexInputForTesting = true;
- }
})
.allowStdoutMessages()
.applyIf(testBuilderConsumer != null, testBuilderConsumer);
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 1112ccd..073a085 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -62,7 +63,7 @@
classWireFieldLabel(),
classTest(defaultEnumValueInAnnotation))
.addProgramClasses(TestClass.class)
- .addDontWarnKotlin()
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()))
.addDontWarnJetBrainsAnnotations()
.addKeepClassAndMembersRules(
"com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 10962c8..000eb7e 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -225,14 +225,12 @@
*/
public DexEncodedMethod oneMethodApplication(String returnType, List<String> parameters,
int locals, String... instructions) {
- InternalOptions options = new InternalOptions();
-
// Build a one class application.
AndroidApp application = singleMethodApplication(
returnType, parameters, locals, instructions);
// Process the application with R8.
- AndroidApp processdApplication = null;
+ AndroidApp processdApplication;
try {
processdApplication = processApplication(application);
} catch (CompilationFailedException e) {
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 7b61e06..2f098bf 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -554,6 +554,11 @@
});
}
+ public ClassFileTransformer removeMethodsWithName(String nameToRemove) {
+ return removeMethods(
+ (access, name, descriptor, signature, exceptions) -> name.equals(nameToRemove));
+ }
+
public ClassFileTransformer renameMethod(MethodPredicate predicate, String newName) {
return addClassTransformer(
new ClassTransformer() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 5f67ef0..c20ccd6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -87,6 +87,10 @@
this(Collections.singletonList(file), null, null);
}
+ public CodeInspector(Path file, Consumer<InternalOptions> optionsConsumer) throws IOException {
+ this(Collections.singletonList(file), null, optionsConsumer);
+ }
+
public CodeInspector(Collection<Path> files) throws IOException {
this(files, null, null);
}
diff --git a/src/test/javaStubs/ObjectMethods.java b/src/test/javaStubs/ObjectMethods.java
new file mode 100644
index 0000000..21aff6a
--- /dev/null
+++ b/src/test/javaStubs/ObjectMethods.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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 java.lang.runtime;
+
+public class ObjectMethods {}
diff --git a/src/test/javaStubs/Record.java b/src/test/javaStubs/Record.java
new file mode 100644
index 0000000..fcf63e6
--- /dev/null
+++ b/src/test/javaStubs/Record.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, 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 java.lang;
+
+public abstract class Record {
+ protected Record() {}
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract String toString();
+}
diff --git a/src/test/javaStubs/RecordComponent.java b/src/test/javaStubs/RecordComponent.java
new file mode 100644
index 0000000..b3e0560
--- /dev/null
+++ b/src/test/javaStubs/RecordComponent.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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 java.lang.reflect;
+
+public class RecordComponent {}
diff --git a/src/test/javaStubs/TypeDescriptor.java b/src/test/javaStubs/TypeDescriptor.java
new file mode 100644
index 0000000..643d1e0
--- /dev/null
+++ b/src/test/javaStubs/TypeDescriptor.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2021, 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 java.lang.invoke;
+
+public interface TypeDescriptor {
+ String descriptorString();
+}
diff --git a/tools/utils.py b/tools/utils.py
index a55ea58..dcfa4f8 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -455,7 +455,7 @@
cmd = [jdk.GetJavaExecutable(), '-jar', R8_JAR, 'dexsegments']
cmd.extend(dex_files)
PrintCmd(cmd)
- output = subprocess.check_output(cmd)
+ output = subprocess.check_output(cmd).decode('utf-8')
matches = DEX_SEGMENTS_RESULT_PATTERN.findall(output)