Merge commit 'df524d261735db40ca981db5a83bd5b4bb9bd4f6' into dev-release
diff --git a/build.gradle b/build.gradle
index 121279f..f4881c2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -337,6 +337,7 @@
"kotlin/kotlin-compiler-1.3.11",
"kotlin/kotlin-compiler-1.3.41",
"kotlin/kotlin-compiler-1.3.72",
+ "kotlin/kotlin-compiler-1.4.20",
"kotlinx-coroutines-1.3.6",
"openjdk/openjdk-rt-1.8",
"openjdk/desugar_jdk_libs",
@@ -2006,6 +2007,10 @@
systemProperty 'slow_tests', project.property('slow_tests')
}
+ if (project.hasProperty('desugar_jdk_libs')) {
+ systemProperty 'desugar_jdk_libs', project.property('desugar_jdk_libs')
+ }
+
if (project.hasProperty('one_line_per_test')) {
beforeTest { desc ->
println "Start executing test ${desc.name} [${desc.className}]"
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 5600b2e..0ba3777 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.1.2",
+ "version": "1.1.3",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -242,6 +242,7 @@
"shrinker_config": [
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { private void readObject(java.io.ObjectInputStream); private void writeObject(java.io.ObjectOutputStream); private void readObjectNoData(); private static final java.io.ObjectStreamField[] serialPersistentFields; private static final long serialVersionUID;}",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
"-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
"-keeppackagenames j$.**",
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 99fc6db..d938092 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
+import com.android.tools.r8.shaking.EnqueuerResult;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -94,7 +95,8 @@
.build(executor);
Enqueuer enqueuer =
EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo);
- AppInfoWithLiveness appInfo = enqueuer.traceApplication(rootSet, executor, timing);
+ EnqueuerResult enqueuerResult = enqueuer.traceApplication(rootSet, executor, timing);
+ AppInfoWithLiveness appInfo = enqueuerResult.getAppInfo();
RootSetBuilder.writeSeeds(
appInfo, System.out, type -> descriptors.contains(type.toDescriptorString()));
} catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 283e461..126fb87 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -32,7 +32,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
@@ -88,6 +87,7 @@
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.Mode;
import com.android.tools.r8.shaking.EnqueuerFactory;
+import com.android.tools.r8.shaking.EnqueuerResult;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.MainDexListBuilder;
import com.android.tools.r8.shaking.MissingClasses;
@@ -603,13 +603,10 @@
new SubtypingInfo(appView),
keptGraphConsumer,
prunedTypes);
- appView.setAppInfo(
- enqueuer.traceApplication(
- appView.rootSet(),
- executorService,
- timing));
+ EnqueuerResult enqueuerResult =
+ enqueuer.traceApplication(appView.rootSet(), executorService, timing);
+ appView.setAppInfo(enqueuerResult.getAppInfo());
// Rerunning the enqueuer should not give rise to any method rewritings.
- assert enqueuer.buildGraphLens() == null;
appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
shrinker.rewriteDeadBuilderReferencesFromDynamicMethods(
@@ -977,14 +974,10 @@
classMergingEnqueuerExtensionBuilder.attach(enqueuer);
}
+ EnqueuerResult enqueuerResult =
+ enqueuer.traceApplication(appView.rootSet(), executorService, timing);
AppView<AppInfoWithLiveness> appViewWithLiveness =
- appView.setAppInfo(
- enqueuer.traceApplication(
- appView.rootSet(),
- executorService,
- timing));
- NestedGraphLens lens = enqueuer.buildGraphLens();
- appView.rewriteWithLens(lens);
+ appView.setAppInfo(enqueuerResult.getAppInfo());
if (InternalOptions.assertionsEnabled()) {
// Register the dead proto types. These are needed to verify that no new missing types are
// reported and that no dead proto types are referenced in the generated application.
diff --git a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
index 339f961..faf9046 100644
--- a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
@@ -264,15 +263,11 @@
if (constant.isConstNumber()) {
return new ConstNumber(stackValue, constant.asConstNumber().getRawValue());
} else if (constant.isConstString()) {
- return new ConstString(
- stackValue, constant.asConstString().getValue(), ThrowingInfo.NO_THROW);
+ return new ConstString(stackValue, constant.asConstString().getValue());
} else if (constant.isDexItemBasedConstString()) {
DexItemBasedConstString computedConstant = constant.asDexItemBasedConstString();
return new DexItemBasedConstString(
- stackValue,
- computedConstant.getItem(),
- computedConstant.getNameComputationInfo(),
- ThrowingInfo.NO_THROW);
+ stackValue, computedConstant.getItem(), computedConstant.getNameComputationInfo());
} else if (constant.isConstClass()) {
return new ConstClass(stackValue, constant.asConstClass().getValue());
} else {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index e7ae429..42844fd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -141,6 +141,18 @@
return false;
}
+ public CfInvokeDynamic asInvokeDynamic() {
+ return null;
+ }
+
+ public boolean isInvokeDynamic() {
+ return false;
+ }
+
+ public boolean isInvokeSpecial() {
+ return false;
+ }
+
public CfLabel asLabel() {
return null;
}
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 70fe54d..5cc4435 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
@@ -175,6 +175,7 @@
!method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
}
+ @Override
public boolean isInvokeSpecial() {
return opcode == Opcodes.INVOKESPECIAL;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index e65d095..88ffb82 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -44,6 +44,16 @@
}
@Override
+ public boolean isInvokeDynamic() {
+ return true;
+ }
+
+ @Override
+ public CfInvokeDynamic asInvokeDynamic() {
+ return this;
+ }
+
+ @Override
public int getCompareToId() {
return Opcodes.INVOKEDYNAMIC;
}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionContext.java b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionContext.java
new file mode 100644
index 0000000..268c7cc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionContext.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.diagnostic;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+
+/** A context that references a missing definition in the program, classpath, or library. */
+@Keep
+public interface MissingDefinitionContext {
+
+ /** The class context from which a missing definition is referenced. */
+ ClassReference getClassReference();
+
+ /** The origin of the context. */
+ Origin getOrigin();
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.java
new file mode 100644
index 0000000..ea8def0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionInfo.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 com.android.tools.r8.diagnostic;
+
+import com.android.tools.r8.Keep;
+import java.util.Collection;
+
+/**
+ * Information about the contexts that references an item that was not part of the compilation unit.
+ */
+@Keep
+public interface MissingDefinitionInfo {
+
+ /** The contexts from which this missing definition was referenced. */
+ Collection<MissingDefinitionContext> getReferencedFromContexts();
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionsDiagnostic.java b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionsDiagnostic.java
new file mode 100644
index 0000000..b3111be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/MissingDefinitionsDiagnostic.java
@@ -0,0 +1,23 @@
+// 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.diagnostic;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import java.util.Collection;
+
+/**
+ * Information about items that are not part of the compilation unit, but which are referenced from
+ * a reachable program location.
+ */
+@Keep
+public interface MissingDefinitionsDiagnostic extends Diagnostic {
+
+ /**
+ * Returns a collection containing information about each of the missing definitions, along with
+ * contextual information describing where these missing definitions are referenced from.
+ */
+ Collection<MissingDefinitionInfo> getMissingDefinitions();
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java
new file mode 100644
index 0000000..3406c0d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java
@@ -0,0 +1,103 @@
+// 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.diagnostic.internal;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+public class MissingClassAccessContexts {
+
+ private ImmutableSet<ClassReference> classContexts;
+ private ImmutableSet<FieldReference> fieldContexts;
+ private ImmutableSet<MethodReference> methodContexts;
+
+ private MissingClassAccessContexts(
+ ImmutableSet<ClassReference> classContexts,
+ ImmutableSet<FieldReference> fieldContexts,
+ ImmutableSet<MethodReference> methodContexts) {
+ this.classContexts = classContexts;
+ this.fieldContexts = fieldContexts;
+ this.methodContexts = methodContexts;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ String getReferencedFromMessageSuffix(ClassReference missingClass) {
+ if (!fieldContexts.isEmpty()) {
+ return " (referenced from: "
+ + FieldReferenceUtils.toSourceString(fieldContexts.iterator().next())
+ + ")";
+ }
+ if (!methodContexts.isEmpty()) {
+ return " (referenced from: "
+ + MethodReferenceUtils.toSourceString(methodContexts.iterator().next())
+ + ")";
+ }
+ // TODO(b/175543745): The legacy reporting is context insensitive, and therefore uses the
+ // missing classes as their own context. Once legacy reporting is removed, this should be
+ // simplified to taking the first context.
+ Optional<ClassReference> classContext =
+ classContexts.stream().filter(not(missingClass::equals)).findFirst();
+ return classContext
+ .map(classReference -> " (referenced from: " + classReference.getTypeName() + ")")
+ .orElse("");
+ }
+
+ static class Builder {
+
+ private final Set<DexReference> contexts = Sets.newIdentityHashSet();
+
+ Builder addAll(Set<DexReference> contexts) {
+ this.contexts.addAll(contexts);
+ return this;
+ }
+
+ // TODO(b/179249745): Sort on demand in getReferencedFromMessageSuffix() instead.
+ MissingClassAccessContexts build() {
+ // Sort the contexts for deterministic reporting.
+ List<DexType> classContexts = new ArrayList<>();
+ List<DexField> fieldContexts = new ArrayList<>();
+ List<DexMethod> methodContexts = new ArrayList<>();
+ contexts.forEach(
+ context -> context.apply(classContexts::add, fieldContexts::add, methodContexts::add));
+ Collections.sort(classContexts);
+ Collections.sort(fieldContexts);
+ Collections.sort(methodContexts);
+
+ // Build immutable sets (which preserve insertion order) from the sorted lists, mapping each
+ // DexType, DexField, and DexMethod to ClassReference, FieldReference, and MethodReference,
+ // respectively.
+ return new MissingClassAccessContexts(
+ toImmutableSet(classContexts, DexType::asClassReference),
+ toImmutableSet(fieldContexts, DexField::asFieldReference),
+ toImmutableSet(methodContexts, DexMethod::asMethodReference));
+ }
+
+ private <S, T> ImmutableSet<T> toImmutableSet(List<S> list, Function<S, T> fn) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ list.forEach(element -> builder.add(fn.apply(element)));
+ return builder.build();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
new file mode 100644
index 0000000..044b1d5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
@@ -0,0 +1,145 @@
+// 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.diagnostic.internal;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
+import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedMap;
+
+public class MissingDefinitionsDiagnosticImpl implements MissingDefinitionsDiagnostic {
+
+ private final boolean fatal;
+ private final SortedMap<ClassReference, MissingClassAccessContexts> missingClasses;
+
+ private MissingDefinitionsDiagnosticImpl(
+ boolean fatal, SortedMap<ClassReference, MissingClassAccessContexts> missingClasses) {
+ assert !missingClasses.isEmpty();
+ this.fatal = fatal;
+ this.missingClasses = missingClasses;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Deprecated
+ public Set<ClassReference> getMissingClasses() {
+ return missingClasses.keySet();
+ }
+
+ @Override
+ public Collection<MissingDefinitionInfo> getMissingDefinitions() {
+ throw new Unimplemented();
+ }
+
+ /** A missing class(es) failure can generally not be attributed to a single origin. */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** A missing class(es) failure can generally not be attributed to a single position. */
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return fatal ? getFatalDiagnosticMessage() : getNonFatalDiagnosticMessage();
+ }
+
+ private String getFatalDiagnosticMessage() {
+ if (missingClasses.size() == 1) {
+ StringBuilder builder =
+ new StringBuilder(
+ "Compilation can't be completed because the following class is missing: ");
+ writeMissingClass(builder, missingClasses.entrySet().iterator().next());
+ return builder.append(".").toString();
+ }
+
+ StringBuilder builder =
+ new StringBuilder("Compilation can't be completed because the following ")
+ .append(missingClasses.size())
+ .append(" classes are missing:");
+ missingClasses.forEach(
+ (missingClass, contexts) ->
+ writeMissingClass(
+ builder.append(System.lineSeparator()).append("- "), missingClass, contexts));
+ return builder.toString();
+ }
+
+ private String getNonFatalDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder();
+ Iterator<Entry<ClassReference, MissingClassAccessContexts>> missingClassesIterator =
+ missingClasses.entrySet().iterator();
+
+ // The diagnostic is always non-empty.
+ assert missingClassesIterator.hasNext();
+
+ // Write first line.
+ writeMissingClass(builder.append("Missing class "), missingClassesIterator.next());
+
+ // Write remaining lines with line separator before.
+ missingClassesIterator.forEachRemaining(
+ missingClassInfo ->
+ writeMissingClass(
+ builder.append(System.lineSeparator()).append("Missing class "), missingClassInfo));
+
+ return builder.toString();
+ }
+
+ private static void writeMissingClass(
+ StringBuilder builder, Entry<ClassReference, MissingClassAccessContexts> missingClassInfo) {
+ writeMissingClass(builder, missingClassInfo.getKey(), missingClassInfo.getValue());
+ }
+
+ private static void writeMissingClass(
+ StringBuilder builder, ClassReference missingClass, MissingClassAccessContexts contexts) {
+ builder
+ .append(missingClass.getTypeName())
+ .append(contexts.getReferencedFromMessageSuffix(missingClass));
+ }
+
+ public static class Builder {
+
+ private boolean fatal;
+ private ImmutableSortedMap.Builder<ClassReference, MissingClassAccessContexts>
+ missingClassesBuilder =
+ ImmutableSortedMap.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
+
+ public Builder addMissingClasses(Map<DexType, Set<DexReference>> missingClasses) {
+ missingClasses.forEach(
+ (missingClass, contexts) ->
+ missingClassesBuilder.put(
+ Reference.classFromDescriptor(missingClass.toDescriptorString()),
+ MissingClassAccessContexts.builder().addAll(contexts).build()));
+ return this;
+ }
+
+ public Builder setFatal(boolean fatal) {
+ this.fatal = fatal;
+ return this;
+ }
+
+ public MissingDefinitionsDiagnostic build() {
+ return new MissingDefinitionsDiagnosticImpl(fatal, missingClassesBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java b/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java
index e96cb83..89bcb10 100644
--- a/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java
+++ b/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java
@@ -62,7 +62,7 @@
public boolean validate(InternalOptions options) {
assert options.testing.allowUnnecessaryDontWarnWildcards
|| validateNoUnnecessaryDontWarnWildcards();
- assert options.testing.allowUnusedDontWarnRules || validateNoUnusedDontWarnPatterns();
+ assert options.testing.allowUnusedDontWarnRules || validateNoUnusedDontWarnPatterns(options);
return true;
}
@@ -79,9 +79,10 @@
return true;
}
- public boolean validateNoUnusedDontWarnPatterns() {
+ public boolean validateNoUnusedDontWarnPatterns(InternalOptions options) {
for (ProguardClassNameList dontWarnPattern : dontWarnPatterns) {
assert matchedDontWarnPatterns.containsKey(dontWarnPattern)
+ || options.testing.allowedUnusedDontWarnPatterns.contains(dontWarnPattern.toString())
: "Unexpected unused rule -dontwarn " + dontWarnPattern.toString();
}
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 70d7cdb..68921ca 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -338,6 +338,10 @@
return self();
}
+ public B setPublic() {
+ return setPublic(true);
+ }
+
public B setPublic(boolean value) {
if (value) {
flags.setPublic();
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 4d45d55..4247d74 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX;
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
-import static com.android.tools.r8.ir.conversion.CfSourceCode.canThrowHelper;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import com.android.tools.r8.cf.CfPrinter;
@@ -803,7 +802,7 @@
// Check the exceptional edge prior to evaluating the instruction. The local state is stable
// at this point as store operations are not throwing and the current stack does not
// affect the exceptional transfer (the exception edge is always a singleton stack).
- if (canThrowHelper(instruction, appView.options().isGeneratingClassFiles())) {
+ if (instruction.canThrow()) {
assert !instruction.isStore();
builder.verifyExceptionEdges();
}
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 08d2880..2b45551 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -788,6 +788,10 @@
this.innerClasses = innerClasses;
}
+ public boolean hasEnclosingMethodAttribute() {
+ return enclosingMethod != null;
+ }
+
public EnclosingMethodAttribute getEnclosingMethodAttribute() {
return enclosingMethod;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 18b0434..ed0339d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
+import static com.android.tools.r8.ir.desugar.LambdaClass.LAMBDA_INSTANCE_FIELD_NAME;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -293,6 +294,7 @@
createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX);
public final DexString thisName = createString("this");
+ public final DexString lambdaInstanceFieldName = createString(LAMBDA_INSTANCE_FIELD_NAME);
// As much as possible, R8 should rely on the content of the static enum field, using
// enumMembers.isValuesFieldCandidate or checking the object state in the optimization info.
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index b369871..0bdb208 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -332,6 +332,10 @@
.withItem(m -> m.rewrittenTarget);
}
+ public Handle toAsmHandle() {
+ return toAsmHandle(NamingLens.getIdentityLens());
+ }
+
public Handle toAsmHandle(NamingLens lens) {
String owner;
String name;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0fdd6ad..11e5957 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -209,6 +209,10 @@
predicate, method -> consumer.accept(new ProgramMethod(this, method)));
}
+ public Iterable<ProgramMethod> programMethods() {
+ return Iterables.concat(directProgramMethods(), virtualProgramMethods());
+ }
+
public Iterable<ProgramMethod> directProgramMethods() {
return Iterables.transform(directMethods(), method -> new ProgramMethod(this, method));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 3c17c60..52a57bf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
@@ -1253,8 +1252,7 @@
AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull());
Value outValue = code.createValue(type, local);
- ConstString instruction =
- new ConstString(outValue, value, ThrowingInfo.defaultForConstString(appView.options()));
+ ConstString instruction = new ConstString(outValue, value);
if (!instruction.instructionInstanceCanThrow()) {
return instruction;
}
@@ -1346,11 +1344,7 @@
TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull());
Value outValue = code.createValue(type, local);
DexItemBasedConstString instruction =
- new DexItemBasedConstString(
- outValue,
- value,
- nameComputationInfo,
- ThrowingInfo.defaultForConstString(appView.options()));
+ new DexItemBasedConstString(outValue, value, nameComputationInfo);
// DexItemBasedConstString cannot throw.
assert !instruction.instructionInstanceCanThrow();
return instruction;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index a92dc75..1dc1f4f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -134,6 +134,12 @@
default boolean hasNoSignature() {
return !hasSignature();
}
+
+ default boolean isInvalid() {
+ return false;
+ }
+
+ DexDefinitionSignature<T> toInvalid();
}
public static class FormalTypeParameter {
@@ -207,6 +213,12 @@
}
@Override
+ public InvalidClassSignature toInvalid() {
+ // Since we could create the structure we are also able to generate a string for it.
+ return new InvalidClassSignature(toString());
+ }
+
+ @Override
public boolean isClassSignature() {
return true;
}
@@ -216,6 +228,10 @@
return this;
}
+ public List<FormalTypeParameter> getFormalTypeParameters() {
+ return formalTypeParameters;
+ }
+
public void visit(GenericSignatureVisitor visitor) {
visitor.visitFormalTypeParameters(formalTypeParameters);
visitor.visitSuperClass(superClassSignature);
@@ -244,6 +260,42 @@
}
}
+ private static class InvalidClassSignature extends ClassSignature {
+
+ private final String genericSignatureString;
+
+ InvalidClassSignature(String genericSignatureString) {
+ super(EMPTY_TYPE_PARAMS, NO_FIELD_TYPE_SIGNATURE, EMPTY_SUPER_INTERFACES);
+ this.genericSignatureString = genericSignatureString;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignatureString;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignatureString;
+ }
+
+ @Override
+ public InvalidClassSignature toInvalid() {
+ assert false : "Should not invoke toInvalid on an invalid signature";
+ return this;
+ }
+
+ @Override
+ public void visit(GenericSignatureVisitor visitor) {
+ assert false : "Should not visit an invalid signature";
+ }
+
+ @Override
+ public boolean isInvalid() {
+ return true;
+ }
+ }
+
public abstract static class TypeSignature {
public boolean isFieldTypeSignature() {
@@ -354,6 +406,43 @@
public static FieldTypeSignature noSignature() {
return NO_FIELD_TYPE_SIGNATURE;
}
+
+ @Override
+ public InvalidFieldTypeSignature toInvalid() {
+ return new InvalidFieldTypeSignature(toString());
+ }
+ }
+
+ private static class InvalidFieldTypeSignature extends FieldTypeSignature {
+
+ private final String genericSignature;
+
+ public InvalidFieldTypeSignature(String genericSignature) {
+ super(WildcardIndicator.NONE);
+ this.genericSignature = genericSignature;
+ }
+
+ @Override
+ public FieldTypeSignature asArgument(WildcardIndicator indicator) {
+ assert false : "Should not be called for an invalid signature";
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignature;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignature;
+ }
+
+ @Override
+ public InvalidFieldTypeSignature toInvalid() {
+ assert false : " Should not be called for an invalid signature";
+ return this;
+ }
}
static final class StarFieldTypeSignature extends FieldTypeSignature {
@@ -436,10 +525,6 @@
return argument;
}
- public boolean isNoSignature() {
- return this == NO_FIELD_TYPE_SIGNATURE;
- }
-
@Override
public ArrayTypeSignature toArrayTypeSignature() {
return new ArrayTypeSignature(this);
@@ -538,7 +623,7 @@
return new ArrayTypeSignature(this);
}
- public String getTypeVariable() {
+ public String typeVariable() {
return typeVariable;
}
}
@@ -674,6 +759,47 @@
public String toString() {
return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
}
+
+ @Override
+ public InvalidMethodTypeSignature toInvalid() {
+ return new InvalidMethodTypeSignature(toString());
+ }
+ }
+
+ private static class InvalidMethodTypeSignature extends MethodTypeSignature {
+
+ private final String genericSignature;
+
+ public InvalidMethodTypeSignature(String genericSignature) {
+ super(EMPTY_TYPE_PARAMS, EMPTY_TYPE_SIGNATURES, ReturnType.VOID, EMPTY_TYPE_SIGNATURES);
+ this.genericSignature = genericSignature;
+ }
+
+ @Override
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ return genericSignature;
+ }
+
+ @Override
+ public String toString() {
+ return genericSignature;
+ }
+
+ @Override
+ public boolean isInvalid() {
+ return true;
+ }
+
+ @Override
+ public void visit(GenericSignatureVisitor visitor) {
+ assert false : "Should not visit an invalid signature";
+ }
+
+ @Override
+ public InvalidMethodTypeSignature toInvalid() {
+ assert false : "Should not be called for an invalid signature";
+ return this;
+ }
}
public static ClassSignature parseClassSignature(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
new file mode 100644
index 0000000..a7979f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -0,0 +1,305 @@
+// 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.graph;
+
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_APPLICATION_COUNT;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_INTERFACE_COUNT;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_SUPER_TYPE;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED;
+import static com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult.VALID;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class GenericSignatureCorrectnessHelper {
+
+ private enum Mode {
+ VERIFY,
+ MARK_AS_INVALID;
+
+ public boolean doNotVerify() {
+ return markAsInvalid();
+ }
+
+ public boolean markAsInvalid() {
+ return this == MARK_AS_INVALID;
+ }
+ }
+
+ public enum SignatureEvaluationResult {
+ INVALID_SUPER_TYPE,
+ INVALID_INTERFACE_COUNT,
+ INVALID_APPLICATION_COUNT,
+ INVALID_TYPE_VARIABLE_UNDEFINED,
+ VALID;
+
+ boolean isValid() {
+ return this == VALID;
+ }
+
+ boolean isInvalid() {
+ return this != VALID;
+ }
+ }
+
+ private final AppView<?> appView;
+ private final Mode mode;
+
+ private GenericSignatureCorrectnessHelper(AppView<?> appView, Mode mode) {
+ this.appView = appView;
+ this.mode = mode;
+ }
+
+ public static GenericSignatureCorrectnessHelper createForInitialCheck(AppView<?> appView) {
+ return new GenericSignatureCorrectnessHelper(appView, Mode.MARK_AS_INVALID);
+ }
+
+ public static GenericSignatureCorrectnessHelper createForVerification(AppView<?> appView) {
+ return new GenericSignatureCorrectnessHelper(appView, Mode.VERIFY);
+ }
+
+ public void run() {
+ appView.appInfo().classes().forEach(this::evaluateSignaturesForClass);
+ }
+
+ public SignatureEvaluationResult evaluateSignaturesForClass(DexProgramClass clazz) {
+ GenericSignatureContextEvaluator genericSignatureContextEvaluator =
+ new GenericSignatureContextEvaluator(appView, clazz, mode);
+ ClassSignature classSignature = clazz.getClassSignature();
+ SignatureEvaluationResult result = VALID;
+ if (classSignature.hasNoSignature() || !classSignature.isInvalid()) {
+ result = genericSignatureContextEvaluator.evaluateClassSignature(classSignature);
+ if (result.isInvalid() && mode.markAsInvalid()) {
+ clazz.setClassSignature(classSignature.toInvalid());
+ }
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ SignatureEvaluationResult methodResult =
+ evaluate(
+ method::getGenericSignature,
+ genericSignatureContextEvaluator::visitMethodSignature,
+ method::setGenericSignature);
+ if (result.isValid() && methodResult.isInvalid()) {
+ result = methodResult;
+ }
+ }
+ for (DexEncodedField field : clazz.fields()) {
+ SignatureEvaluationResult fieldResult =
+ evaluate(
+ field::getGenericSignature,
+ genericSignatureContextEvaluator::visitFieldTypeSignature,
+ field::setGenericSignature);
+ if (result.isValid() && fieldResult.isInvalid()) {
+ result = fieldResult;
+ }
+ }
+ return result;
+ }
+
+ private <T extends DexDefinitionSignature<?>> SignatureEvaluationResult evaluate(
+ Supplier<T> getter, Function<T, SignatureEvaluationResult> evaluate, Consumer<T> setter) {
+ T signature = getter.get();
+ if (signature.hasNoSignature() || signature.isInvalid()) {
+ // Already marked as invalid, do nothing
+ return VALID;
+ }
+ SignatureEvaluationResult signatureResult = evaluate.apply(signature);
+ assert signatureResult.isValid() || mode.doNotVerify();
+ if (signatureResult.isInvalid() && mode.doNotVerify()) {
+ setter.accept((T) signature.toInvalid());
+ }
+ return signatureResult;
+ }
+
+ private static class GenericSignatureContextEvaluator {
+
+ private final AppView<?> appView;
+ private final DexProgramClass context;
+ private final Set<String> classFormalTypeParameters = new HashSet<>();
+ private final Set<String> methodTypeArguments = new HashSet<>();
+ private final Mode mode;
+
+ public GenericSignatureContextEvaluator(
+ AppView<?> appView, DexProgramClass context, Mode mode) {
+ this.appView = appView;
+ this.context = context;
+ this.mode = mode;
+ }
+
+ private SignatureEvaluationResult evaluateClassSignature(ClassSignature classSignature) {
+ classSignature
+ .getFormalTypeParameters()
+ .forEach(param -> classFormalTypeParameters.add(param.name));
+ if (classSignature.hasNoSignature()) {
+ return VALID;
+ }
+ SignatureEvaluationResult signatureEvaluationResult =
+ evaluateFormalTypeParameters(classSignature.formalTypeParameters);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ if ((context.superType != appView.dexItemFactory().objectType
+ && context.superType != classSignature.superClassSignature().type())
+ || (context.superType == appView.dexItemFactory().objectType
+ && classSignature.superClassSignature().hasNoSignature())) {
+ assert mode.doNotVerify();
+ return INVALID_SUPER_TYPE;
+ }
+ signatureEvaluationResult =
+ evaluateTypeArgumentsAppliedToType(
+ classSignature.superClassSignature().typeArguments(), context.superType);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ List<ClassTypeSignature> superInterfaces = classSignature.superInterfaceSignatures();
+ if (context.interfaces.size() != superInterfaces.size()) {
+ assert mode.doNotVerify();
+ return INVALID_INTERFACE_COUNT;
+ }
+ DexType[] actualInterfaces = context.interfaces.values;
+ for (int i = 0; i < actualInterfaces.length; i++) {
+ signatureEvaluationResult =
+ evaluateTypeArgumentsAppliedToType(
+ superInterfaces.get(i).typeArguments(), actualInterfaces[i]);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult visitMethodSignature(MethodTypeSignature methodSignature) {
+ methodSignature
+ .getFormalTypeParameters()
+ .forEach(param -> methodTypeArguments.add(param.name));
+ SignatureEvaluationResult evaluateResult =
+ evaluateFormalTypeParameters(methodSignature.getFormalTypeParameters());
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ evaluateResult = evaluateTypeArguments(methodSignature.typeSignatures);
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ evaluateResult = evaluateTypeArguments(methodSignature.throwsSignatures);
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ ReturnType returnType = methodSignature.returnType();
+ if (!returnType.isVoidDescriptor()) {
+ evaluateResult = evaluateTypeArgument(returnType.typeSignature());
+ if (evaluateResult.isInvalid()) {
+ return evaluateResult;
+ }
+ }
+ methodTypeArguments.clear();
+ return evaluateResult;
+ }
+
+ private SignatureEvaluationResult evaluateTypeArguments(List<TypeSignature> typeSignatures) {
+ for (TypeSignature typeSignature : typeSignatures) {
+ SignatureEvaluationResult signatureEvaluationResult = evaluateTypeArgument(typeSignature);
+ if (signatureEvaluationResult.isInvalid()) {
+ return signatureEvaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ return evaluateTypeArgument(fieldSignature);
+ }
+
+ private SignatureEvaluationResult evaluateFormalTypeParameters(
+ List<FormalTypeParameter> typeParameters) {
+ for (FormalTypeParameter typeParameter : typeParameters) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeParameter(typeParameter);
+ if (evaluationResult.isInvalid()) {
+ return evaluationResult;
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult evaluateTypeParameter(FormalTypeParameter typeParameter) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeArgument(typeParameter.classBound);
+ if (evaluationResult.isInvalid()) {
+ return evaluationResult;
+ }
+ if (typeParameter.interfaceBounds != null) {
+ for (FieldTypeSignature interfaceBound : typeParameter.interfaceBounds) {
+ evaluationResult = evaluateTypeArgument(interfaceBound);
+ if (evaluationResult != VALID) {
+ return evaluationResult;
+ }
+ }
+ }
+ return VALID;
+ }
+
+ private SignatureEvaluationResult evaluateTypeArgument(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return VALID;
+ }
+ FieldTypeSignature fieldTypeSignature = typeSignature.asFieldTypeSignature();
+ if (fieldTypeSignature.hasNoSignature()) {
+ return VALID;
+ }
+ if (fieldTypeSignature.isTypeVariableSignature()) {
+ // This is in an applied position, just check that the variable is registered.
+ String typeVariable = fieldTypeSignature.asTypeVariableSignature().typeVariable();
+ if (classFormalTypeParameters.contains(typeVariable)
+ || methodTypeArguments.contains(typeVariable)) {
+ return VALID;
+ }
+ assert mode.doNotVerify();
+ return INVALID_TYPE_VARIABLE_UNDEFINED;
+ }
+ if (fieldTypeSignature.isArrayTypeSignature()) {
+ return evaluateTypeArgument(fieldTypeSignature.asArrayTypeSignature().elementSignature());
+ }
+ assert fieldTypeSignature.isClassTypeSignature();
+ return evaluateTypeArguments(fieldTypeSignature.asClassTypeSignature());
+ }
+
+ private SignatureEvaluationResult evaluateTypeArguments(ClassTypeSignature classTypeSignature) {
+ return evaluateTypeArgumentsAppliedToType(
+ classTypeSignature.typeArguments, classTypeSignature.type());
+ }
+
+ private SignatureEvaluationResult evaluateTypeArgumentsAppliedToType(
+ List<FieldTypeSignature> typeArguments, DexType type) {
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ SignatureEvaluationResult evaluationResult = evaluateTypeArgument(typeArgument);
+ if (evaluationResult.isInvalid()) {
+ assert mode.doNotVerify();
+ return evaluationResult;
+ }
+ }
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz == null) {
+ // We do not know if the application of arguments works or not.
+ return VALID;
+ }
+ if (typeArguments.size() != clazz.classSignature.getFormalTypeParameters().size()) {
+ assert mode.doNotVerify();
+ return INVALID_APPLICATION_COUNT;
+ }
+ return VALID;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index f68675f..a670da0 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -148,7 +148,7 @@
} else {
assert fieldTypeSignature.isClassTypeSignature();
ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
- if (classTypeSignature.isNoSignature()) {
+ if (classTypeSignature.hasNoSignature()) {
return;
}
String renamedString = namingLens.lookupDescriptor(classTypeSignature.type).toString();
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index fd77b73..d20833f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -57,14 +57,14 @@
}
public ClassSignature rewrite(ClassSignature classSignature) {
- if (classSignature.hasNoSignature()) {
+ if (classSignature.hasNoSignature() || classSignature.isInvalid()) {
return classSignature;
}
return new ClassSignatureRewriter().run(classSignature);
}
public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
- if (fieldTypeSignature.hasNoSignature()) {
+ if (fieldTypeSignature.hasNoSignature() || fieldTypeSignature.isInvalid()) {
return fieldTypeSignature;
}
FieldTypeSignature rewrittenSignature = new TypeSignatureRewriter().run(fieldTypeSignature);
@@ -72,7 +72,7 @@
}
public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
- if (methodTypeSignature.hasNoSignature()) {
+ if (methodTypeSignature.hasNoSignature() || methodTypeSignature.isInvalid()) {
return methodTypeSignature;
}
return new MethodTypeSignatureRewriter().run(methodTypeSignature);
@@ -117,7 +117,7 @@
classSignature.visit(this);
if (rewrittenTypeParameters.isEmpty()
&& rewrittenSuperInterfaces.isEmpty()
- && rewrittenSuperClass.isNoSignature()
+ && rewrittenSuperClass.hasNoSignature()
&& rewrittenSuperClass.type == factory.objectType) {
return ClassSignature.noSignature();
}
@@ -255,7 +255,7 @@
}
assert fieldTypeSignature.isClassTypeSignature();
ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
- if (classTypeSignature.isNoSignature()) {
+ if (classTypeSignature.hasNoSignature()) {
return classTypeSignature;
}
return new ClassTypeSignatureRewriter(false).run(classTypeSignature);
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 56d3f52..a296543 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_INSTANCE_FIELD_NAME;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Invoke.Type;
@@ -640,14 +639,15 @@
private boolean isD8R8SynthesizedField(DexField field, AppView<?> appView) {
// TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized
// instead of relying on field names.
- if (field.match(appView.dexItemFactory().objectMembers.clinitField)) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ if (field.match(dexItemFactory.objectMembers.clinitField)) {
return true;
}
if (field.getName().toSourceString().equals(CLASS_ID_FIELD_NAME)) {
return true;
}
if (appView.getSyntheticItems().isNonLegacySynthetic(field.getHolderType())
- && field.getName().toSourceString().equals(LAMBDA_INSTANCE_FIELD_NAME)) {
+ && field.getName() == dexItemFactory.lambdaInstanceFieldName) {
return true;
}
return false;
@@ -660,6 +660,10 @@
private final Map<DexType, DexType> arrayTypeCache = new ConcurrentHashMap<>();
+ public NonIdentityGraphLens(AppView<?> appView) {
+ this(appView.dexItemFactory(), appView.graphLens());
+ }
+
public NonIdentityGraphLens(DexItemFactory dexItemFactory, GraphLens previousLens) {
this.dexItemFactory = dexItemFactory;
this.previousLens = previousLens;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
index 81627e9..272f1e9 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramPackage.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Iterator;
@@ -47,6 +48,10 @@
return packageDescriptor;
}
+ public String getPackageName() {
+ return DescriptorUtils.getJavaTypeFromBinaryName(packageDescriptor);
+ }
+
public void forEachClass(Consumer<DexProgramClass> consumer) {
forEach(consumer);
}
@@ -67,4 +72,9 @@
public Iterator<DexProgramClass> iterator() {
return classes.iterator();
}
+
+ @Override
+ public String toString() {
+ return "ProgramPackage(" + getPackageName() + ")";
+ }
}
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 ab3673e..1053b49 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
@@ -18,7 +18,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRCodeUtils;
@@ -45,7 +44,6 @@
private final RawMessageInfoDecoder decoder;
private final RawMessageInfoEncoder encoder;
private final ProtoReferences references;
- private final ThrowingInfo throwingInfo;
private final TypeElement objectArrayType;
private final TypeElement stringType;
@@ -58,7 +56,6 @@
this.decoder = decoder;
this.encoder = new RawMessageInfoEncoder(appView.dexItemFactory());
this.references = references;
- this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
// Types.
this.objectArrayType =
@@ -154,9 +151,7 @@
private void rewriteInfoArgumentToNewMessageInfo(
IRCode code, Value infoValue, ProtoMessageInfo protoMessageInfo) {
infoValue.definition.replace(
- new ConstString(
- code.createValue(stringType), encoder.encodeInfo(protoMessageInfo), throwingInfo),
- code);
+ new ConstString(code.createValue(stringType), encoder.encodeInfo(protoMessageInfo)), code);
}
private void rewriteObjectsArgumentToNewMessageInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/LiveProtoFieldObject.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/LiveProtoFieldObject.java
index 308ea07..4727869 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/LiveProtoFieldObject.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/LiveProtoFieldObject.java
@@ -34,10 +34,9 @@
code.createValue(TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
ThrowingInfo throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
if (appView.options().isMinifying()) {
- return new DexItemBasedConstString(
- value, field, FieldNameComputationInfo.forFieldName(), throwingInfo);
+ return new DexItemBasedConstString(value, field, FieldNameComputationInfo.forFieldName());
}
- return new ConstString(value, field.name, throwingInfo);
+ return new ConstString(value, field.name);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 24b17fb..eb00100 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -381,7 +381,7 @@
// Map/required fields cannot be removed. Therefore, we mark such fields as both read and
// written such that we cannot optimize any field reads or writes.
enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
- worklist.enqueueMarkInstanceFieldAsReachableAction(
+ worklist.enqueueMarkFieldAsReachableAction(
valueStorage, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
valueStorageIsLive = true;
} else {
@@ -446,7 +446,7 @@
// Unconditionally register the hazzer and one-of proto fields as written from
// dynamicMethod().
if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.getReference(), dynamicMethod)) {
- worklist.enqueueMarkInstanceFieldAsReachableAction(
+ worklist.enqueueMarkFieldAsReachableAction(
newlyLiveField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
@@ -566,7 +566,7 @@
}
if (enqueuer.registerReflectiveFieldWrite(oneOfField.getReference(), dynamicMethod)) {
- worklist.enqueueMarkInstanceFieldAsReachableAction(
+ worklist.enqueueMarkFieldAsReachableAction(
oneOfField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
index 88ea973..f2d8962 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -78,11 +77,7 @@
Value returnedValue =
code.createValue(stringClassType(appView, definitelyNotNull()), debugLocalInfo);
DexItemBasedConstString instruction =
- new DexItemBasedConstString(
- returnedValue,
- item,
- nameComputationInfo,
- ThrowingInfo.defaultForConstString(appView.options()));
+ new DexItemBasedConstString(returnedValue, item, nameComputationInfo);
assert !instruction.instructionInstanceCanThrow();
return instruction;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index e494c43..cd0c9e9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -73,9 +72,7 @@
.isTrue();
Value returnedValue =
code.createValue(stringClassType(appView, definitelyNotNull()), debugLocalInfo);
- ConstString instruction =
- new ConstString(
- returnedValue, string, ThrowingInfo.defaultForConstString(appView.options()));
+ ConstString instruction = new ConstString(returnedValue, string);
assert !instruction.instructionInstanceCanThrow();
return instruction;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 93b95e7..f873fd8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
@@ -27,12 +26,10 @@
public class ConstString extends ConstInstruction {
private final DexString value;
- private final ThrowingInfo throwingInfo;
- public ConstString(Value dest, DexString value, ThrowingInfo throwingInfo) {
+ public ConstString(Value dest, DexString value) {
super(dest);
this.value = value;
- this.throwingInfo = throwingInfo;
}
@Override
@@ -53,7 +50,7 @@
public static ConstString copyOf(Value newValue, ConstString original) {
assert newValue != original.outValue();
- return new ConstString(newValue, original.getValue(), original.throwingInfo);
+ return new ConstString(newValue, original.getValue());
}
public Value dest() {
@@ -98,7 +95,7 @@
@Override
public boolean instructionTypeCanThrow() {
- return throwingInfo == ThrowingInfo.CAN_THROW;
+ return true;
}
@Override
@@ -118,9 +115,6 @@
@Override
public boolean instructionInstanceCanThrow() {
- if (throwingInfo == ThrowingInfo.NO_THROW) {
- return false;
- }
// The const-string instruction can be a throwing instruction in DEX, if decode() fails.
try {
value.toString();
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 9c209c9..4c8c646 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
@@ -27,17 +26,12 @@
private final DexReference item;
private final NameComputationInfo<?> nameComputationInfo;
- private final ThrowingInfo throwingInfo;
public DexItemBasedConstString(
- Value dest,
- DexReference item,
- NameComputationInfo<?> nameComputationInfo,
- ThrowingInfo throwingInfo) {
+ Value dest, DexReference item, NameComputationInfo<?> nameComputationInfo) {
super(dest);
this.item = item;
this.nameComputationInfo = nameComputationInfo;
- this.throwingInfo = throwingInfo;
}
@Override
@@ -58,8 +52,7 @@
public static DexItemBasedConstString copyOf(Value newValue, DexItemBasedConstString original) {
assert newValue != original.outValue();
- return new DexItemBasedConstString(
- newValue, original.getItem(), original.nameComputationInfo, original.throwingInfo);
+ return new DexItemBasedConstString(newValue, original.getItem(), original.nameComputationInfo);
}
public DexReference getItem() {
@@ -118,7 +111,7 @@
@Override
public boolean instructionTypeCanThrow() {
- return throwingInfo == ThrowingInfo.CAN_THROW;
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 36eeadf..cbc8c6b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.origin.Origin;
@@ -1148,7 +1147,7 @@
public ConstString createStringConstant(
AppView<?> appView, DexString value, DebugLocalInfo local) {
Value out = createValue(TypeElement.stringClassType(appView, definitelyNotNull()), local);
- return new ConstString(out, value, ThrowingInfo.defaultForConstString(appView.options()));
+ return new ConstString(out, value);
}
public Phi createPhi(BasicBlock block, TypeElement type) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 5df390f..50ba68b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -293,28 +293,13 @@
@Override
public void clear() {}
- // Utility method that treats constant strings as not throwing in the case of having CF output.
- // This is the only instruction that differ in throwing between DEX and CF. If we find more
- // consider rewriting CfInstruction.canThrow to take in options.
- private boolean canThrowHelper(CfInstruction instruction) {
- return canThrowHelper(instruction, internalOutputMode.isGeneratingClassFiles());
- }
-
- public static boolean canThrowHelper(CfInstruction instruction, boolean isGeneratingClassFiles) {
- if (isGeneratingClassFiles
- && (instruction.isConstString() || instruction.isDexItemBasedConstString())) {
- return false;
- }
- return instruction.canThrow();
- }
-
@Override
public int traceInstruction(int instructionIndex, IRBuilder builder) {
CfInstruction instruction = code.getInstructions().get(instructionIndex);
AppView<?> appView = builder.appView;
assert appView.options().isGeneratingClassFiles()
== internalOutputMode.isGeneratingClassFiles();
- if (canThrowHelper(instruction)) {
+ if (instruction.canThrow()) {
TryHandlerList tryHandlers = getTryHandlers(instructionIndex, appView.dexItemFactory());
if (!tryHandlers.isEmpty()) {
// Ensure the block starts at the start of the try-range (don't enqueue, not a target).
@@ -537,7 +522,7 @@
assert currentBlockInfo != null;
setLocalVariableLists();
- if (canThrowHelper(instruction)) {
+ if (instruction.canThrow()) {
Snapshot exceptionTransfer =
state.getSnapshot().exceptionTransfer(builder.appView.dexItemFactory().throwableType);
for (int target : currentBlockInfo.exceptionalSuccessors) {
@@ -858,7 +843,7 @@
@Override
public boolean verifyCurrentInstructionCanThrow() {
return isCurrentlyGeneratingMethodSynchronization()
- || canThrowHelper(code.getInstructions().get(currentInstructionIndex));
+ || code.getInstructions().get(currentInstructionIndex).canThrow();
}
@Override
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 1a25cb1..fb9c5d3 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
@@ -38,12 +38,17 @@
: new DefaultClassConverter(appView, converter, methodProcessor);
}
- public void convertClasses(ExecutorService executorService) throws ExecutionException {
- internalConvertClasses(executorService);
+ public ClassConverterResult convertClasses(ExecutorService executorService)
+ throws ExecutionException {
+ ClassConverterResult.Builder resultBuilder = ClassConverterResult.builder();
+ internalConvertClasses(resultBuilder, executorService);
notifyAllClassesConverted();
+ return resultBuilder.build();
}
- private void internalConvertClasses(ExecutorService executorService) throws ExecutionException {
+ private void internalConvertClasses(
+ ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
+ throws ExecutionException {
List<DexProgramClass> classes = appView.appInfo().classes();
while (!classes.isEmpty()) {
Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
@@ -62,7 +67,8 @@
// Process the wave and wait for all IR processing to complete.
D8CfInstructionDesugaringEventConsumer desugaringEventConsumer =
- CfInstructionDesugaringEventConsumer.createForD8();
+ CfInstructionDesugaringEventConsumer.createForD8(
+ resultBuilder::addSynthesizedLambdaClass, methodProcessor);
methodProcessor.newWave();
ThreadUtils.processItems(
wave, clazz -> convertClass(clazz, desugaringEventConsumer), executorService);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverterResult.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverterResult.java
new file mode 100644
index 0000000..4a07e14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverterResult.java
@@ -0,0 +1,51 @@
+// 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.conversion;
+
+import com.android.tools.r8.ir.desugar.LambdaClass;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class ClassConverterResult {
+
+ private final List<LambdaClass> synthesizedLambdaClassesWithDeterministicOrder;
+
+ private ClassConverterResult(List<LambdaClass> synthesizedLambdaClassesWithDeterministicOrder) {
+ this.synthesizedLambdaClassesWithDeterministicOrder =
+ synthesizedLambdaClassesWithDeterministicOrder;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public void forEachSynthesizedLambdaClassWithDeterministicOrdering(
+ Consumer<LambdaClass> consumer) {
+ synthesizedLambdaClassesWithDeterministicOrder.forEach(consumer);
+ }
+
+ public List<LambdaClass> getSynthesizedLambdaClasses() {
+ return synthesizedLambdaClassesWithDeterministicOrder;
+ }
+
+ public static class Builder {
+
+ private final List<LambdaClass> synthesizedLambdaClasses = new ArrayList<>();
+
+ public Builder addSynthesizedLambdaClass(LambdaClass lambdaClass) {
+ synchronized (synthesizedLambdaClasses) {
+ synthesizedLambdaClasses.add(lambdaClass);
+ }
+ return this;
+ }
+
+ public ClassConverterResult build() {
+ synthesizedLambdaClasses.sort(Comparator.comparing(LambdaClass::getType));
+ return new ClassConverterResult(synthesizedLambdaClasses);
+ }
+ }
+}
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 2d3247e..bee8182 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
@@ -8,7 +8,7 @@
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.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
@@ -75,6 +75,11 @@
executorService));
}
+ public D8MethodProcessor scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) {
+ methods.forEach(this::scheduleDesugaredMethodForProcessing);
+ return this;
+ }
+
@Override
public CallSiteInformation getCallSiteInformation() {
throw new Unreachable("Invalid attempt to obtain call-site information in D8");
@@ -86,7 +91,7 @@
}
public void processMethod(
- ProgramMethod method, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
+ ProgramMethod method, CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
converter.convertMethod(
method,
desugaringEventConsumer,
@@ -94,6 +99,10 @@
processorContext.createMethodProcessingContext(method));
}
+ public void processDesugaredMethod(ProgramMethod method) {
+ processMethod(method, CfInstructionDesugaringEventConsumer.createForDesugaredCode());
+ }
+
public boolean verifyNoPendingMethodProcessing() {
assert futures.isEmpty();
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 1bee2d4..93c61ab 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1272,15 +1272,13 @@
}
private ThrowingInfo throwingInfoForConstStrings() {
- return appView.options().isGeneratingClassFiles()
- ? ThrowingInfo.NO_THROW
- : ThrowingInfo.CAN_THROW;
+ return ThrowingInfo.CAN_THROW;
}
public void addConstString(int dest, DexString string) {
TypeElement typeLattice = TypeElement.stringClassType(appView, definitelyNotNull());
ThrowingInfo throwingInfo = throwingInfoForConstStrings();
- add(new ConstString(writeRegister(dest, typeLattice, throwingInfo), string, throwingInfo));
+ add(new ConstString(writeRegister(dest, typeLattice, throwingInfo), string));
}
public void addDexItemBasedConstString(
@@ -1288,7 +1286,7 @@
TypeElement typeLattice = TypeElement.stringClassType(appView, definitelyNotNull());
ThrowingInfo throwingInfo = throwingInfoForConstStrings();
Value out = writeRegister(dest, typeLattice, throwingInfo);
- add(new DexItemBasedConstString(out, item, nameComputationInfo, throwingInfo));
+ add(new DexItemBasedConstString(out, item, nameComputationInfo));
}
public void addDiv(NumericType type, int dest, int left, int right) {
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 7a45467..db7c135 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
@@ -5,6 +5,7 @@
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
+import static com.android.tools.r8.ir.desugar.lambda.D8LambdaDesugaring.synthesizeAccessibilityBridgesForLambdaClasses;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
@@ -43,6 +44,7 @@
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.CfInstructionDesugaringCollection;
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;
@@ -51,12 +53,10 @@
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor;
-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.lambda.LambdaDeserializationMethodRemover;
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;
import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization;
@@ -101,6 +101,7 @@
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.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.SupplierUtils;
@@ -130,15 +131,13 @@
private final Timing timing;
private final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
+ private final CfInstructionDesugaringCollection desugaring;
private final FieldAccessAnalysis fieldAccessAnalysis;
private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
private final StringConcatRewriter stringConcatRewriter;
private final StringOptimizer stringOptimizer;
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;
private final BackportedMethodRewriter backportedMethodRewriter;
@@ -213,17 +212,22 @@
.collect(Collectors.toList());
if (options.isDesugaredLibraryCompilation()) {
// Specific L8 Settings, performs all desugaring including L8 specific desugaring.
- // The following desugaring are required for L8 specific desugaring:
+ //
+ // The following desugarings are required for L8 specific desugaring:
// - DesugaredLibraryRetargeter for retarget core library members.
// - InterfaceMethodRewriter for emulated interfaces,
- // - LambdaRewriter since InterfaceMethodDesugaring does not support invokeCustom rewriting,
+ // - Lambda desugaring since interface method desugaring does not support invoke-custom
+ // rewriting,
// - DesugaredLibraryAPIConverter to duplicate APIs.
+ //
// The following desugaring are present so all desugaring is performed cf to cf in L8, and
// the second L8 phase can just run with Desugar turned off:
// - InterfaceMethodRewriter for non L8 specific interface method desugaring,
// - TwrCloseResourceRewriter,
- // - NestBaseAccessDesugaring.
- assert options.desugarState == DesugarState.ON;
+ // - nest based access desugaring,
+ // - invoke-special desugaring.
+ assert options.desugarState.isOn();
+ this.desugaring = CfInstructionDesugaringCollection.create(appView);
this.desugaredLibraryRetargeter =
options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
? null
@@ -232,7 +236,6 @@
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
: new InterfaceMethodRewriter(appView, this);
- this.lambdaRewriter = new LambdaRewriter(appView);
this.desugaredLibraryAPIConverter =
new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
this.backportedMethodRewriter = new BackportedMethodRewriter(appView);
@@ -240,9 +243,6 @@
TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(appView.options())
? new TwrCloseResourceRewriter(appView)
: null;
- this.invokeSpecialToSelfDesugaring = new InvokeSpecialToSelfDesugaring(appView);
- this.d8NestBasedAccessDesugaring =
- options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.covariantReturnTypeAnnotationTransformer = null;
this.dynamicTypeOptimization = null;
this.classInliner = null;
@@ -264,10 +264,10 @@
this.assumeInserter = null;
return;
}
- this.lambdaRewriter =
- (options.desugarState == DesugarState.ON && !appView.enableWholeProgramOptimizations())
- ? new LambdaRewriter(appView)
- : null;
+ this.desugaring =
+ appView.enableWholeProgramOptimizations()
+ ? CfInstructionDesugaringCollection.empty()
+ : CfInstructionDesugaringCollection.create(appView);
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled()
? new InterfaceMethodRewriter(appView, this)
@@ -321,8 +321,6 @@
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
? new ServiceLoaderRewriter(appViewWithLiveness)
@@ -348,9 +346,6 @@
this.identifierNameStringMarker = null;
this.devirtualizer = null;
this.typeChecker = null;
- this.invokeSpecialToSelfDesugaring = new InvokeSpecialToSelfDesugaring(appView);
- this.d8NestBasedAccessDesugaring =
- options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.desugaredLibraryAPIConverter =
appView.rewritePrefix.isRewriting()
? new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS)
@@ -375,31 +370,19 @@
this(AppView.createForD8(appInfo), timing, printer);
}
- private void removeLambdaDeserializationMethods() {
- if (lambdaRewriter != null) {
- lambdaRewriter.removeLambdaDeserializationMethods(appView.appInfo().classes());
- }
- }
-
private void synthesizeBridgesForNestBasedAccessesOnClasspath(
- MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
- if (d8NestBasedAccessDesugaring != null) {
- d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
- methodProcessor, executorService);
- }
+ D8MethodProcessor methodProcessor, ExecutorService executorService)
+ throws ExecutionException {
+ desugaring.withD8NestBasedAccessDesugaring(
+ d8NestBasedAccessDesugaring ->
+ d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
+ methodProcessor, executorService));
+ methodProcessor.awaitMethodProcessing();
}
private void reportNestDesugarDependencies() {
- if (d8NestBasedAccessDesugaring != null) {
- d8NestBasedAccessDesugaring.reportDesugarDependencies();
- }
- }
-
- private void synthesizeLambdaClasses(ExecutorService executorService) throws ExecutionException {
- if (lambdaRewriter != null) {
- assert !appView.enableWholeProgramOptimizations();
- lambdaRewriter.finalizeLambdaDesugaringForD8(this, executorService);
- }
+ desugaring.withD8NestBasedAccessDesugaring(
+ D8NestBasedAccessDesugaring::reportDesugarDependencies);
}
private void staticizeClasses(OptimizationFeedback feedback, ExecutorService executorService)
@@ -462,7 +445,7 @@
public void convert(AppView<AppInfo> appView, ExecutorService executor)
throws ExecutionException {
- removeLambdaDeserializationMethods();
+ LambdaDeserializationMethodRemover.run(appView);
workaroundAbstractMethodOnNonAbstractClassVerificationBug(
executor, OptimizationFeedbackIgnore.getInstance());
DexApplication application = appView.appInfo().app();
@@ -471,10 +454,8 @@
convertClasses(executor);
reportNestDesugarDependencies();
-
- // Synthesize lambda classes and commit to the app in full.
- synthesizeLambdaClasses(executor);
processTwrCloseResourceUtilityMethods();
+
if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
appView.setAppInfo(
new AppInfo(
@@ -503,11 +484,22 @@
private void convertClasses(ExecutorService executorService) throws ExecutionException {
D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executorService);
- ClassConverter classConverter = ClassConverter.create(appView, this, methodProcessor);
- classConverter.convertClasses(executorService);
+ ClassConverterResult classConverterResult =
+ ClassConverter.create(appView, this, methodProcessor).convertClasses(executorService);
+ // The synthesis of accessibility bridges in lambda desugaring and nest based access desugaring
+ // will schedule and await the processing of synthesized methods.
synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
- methodProcessor.awaitMethodProcessing();
+ synthesizeAccessibilityBridgesForLambdaClasses(appView, classConverterResult, methodProcessor);
+ methodProcessor
+ .scheduleDesugaredMethodsForProcessing(
+ IterableUtils.flatMap(
+ classConverterResult.getSynthesizedLambdaClasses(),
+ lambdaClass -> lambdaClass.getLambdaProgramClass().programMethods()))
+ .awaitMethodProcessing();
+
+ // There should be no outstanding method processing.
+ methodProcessor.verifyNoPendingMethodProcessing();
}
void convertMethods(
@@ -550,7 +542,7 @@
void convertMethod(
ProgramMethod method,
- D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
DexEncodedMethod definition = method.getDefinition();
@@ -673,8 +665,8 @@
public DexApplication optimize(
AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
throws ExecutionException {
- // Lambda rewriting happens in the enqueuer.
- assert lambdaRewriter == null;
+ // Desugaring happens in the enqueuer.
+ assert desugaring.isEmpty();
DexApplication application = appView.appInfo().app();
@@ -1173,22 +1165,14 @@
boolean didDesugar = false;
Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
SupplierUtils.nonThreadSafeMemoize(appView::appInfoForDesugaring);
- if (lambdaRewriter != null) {
- didDesugar |=
- lambdaRewriter.desugarLambdas(method, lazyAppInfo.get(), methodProcessingContext) > 0;
+ if (desugaring.needsDesugaring(method)) {
+ desugaring.desugar(method, methodProcessingContext, desugaringEventConsumer);
+ didDesugar = true;
}
if (backportedMethodRewriter != null) {
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;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index d91b447..c12c6f0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -91,7 +91,7 @@
if (!needsDesugaring) {
needsDesugaring =
interfaceMethodRewriter != null
- && interfaceMethodRewriter.needsRewriting(method, invokeType, appView);
+ && interfaceMethodRewriter.needsRewriting(method, invokeType);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
index fec0919..845793f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchRemover.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
@@ -50,13 +49,11 @@
private final AppView<?> appView;
private final IdentifierNameStringMarker identifierNameStringMarker;
private final ClassTypeElement stringType;
- private final ThrowingInfo throwingInfo;
StringSwitchRemover(AppView<?> appView, IdentifierNameStringMarker identifierNameStringMarker) {
this.appView = appView;
this.identifierNameStringMarker = identifierNameStringMarker;
this.stringType = TypeElement.stringClassType(appView, definitelyNotNull());
- this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
}
void run(IRCode code) {
@@ -222,7 +219,7 @@
BasicBlock previous = null;
for (Entry<DexString, BasicBlock> entry : structure.entrySet()) {
ConstString constStringInstruction =
- new ConstString(code.createValue(stringType), entry.getKey(), throwingInfo);
+ new ConstString(code.createValue(stringType), entry.getKey());
constStringInstruction.setPosition(position);
InvokeVirtual invokeInstruction =
new InvokeVirtual(
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
index 08a7934..22b1d92 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
-import java.util.List;
+import java.util.Collection;
/** Interface for desugaring a single class-file instruction. */
public interface CfInstructionDesugaring {
@@ -15,10 +16,12 @@
* 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(
+ Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
- CfInstructionDesugaringEventConsumer consumer,
- ProgramMethod context);
+ FreshLocalProvider freshLocalProvider,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext);
/**
* Returns true if the given instruction needs desugaring.
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
index bf63ace..21ac7ae 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
+import com.android.tools.r8.utils.ThrowingConsumer;
/**
* Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
@@ -14,13 +18,35 @@
*/
public abstract class CfInstructionDesugaringCollection {
+ public static CfInstructionDesugaringCollection create(AppView<?> appView) {
+ if (appView.options().desugarState.isOn()) {
+ return new NonEmptyCfInstructionDesugaringCollection(appView);
+ }
+ // TODO(b/145775365): invoke-special desugaring is mandatory, since we currently can't map
+ // invoke-special instructions that require desugaring into IR.
+ if (appView.options().isGeneratingClassFiles()) {
+ return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(appView);
+ }
+ return empty();
+ }
+
public static CfInstructionDesugaringCollection empty() {
- return new EmptyCfInstructionDesugaringCollection();
+ return EmptyCfInstructionDesugaringCollection.getInstance();
}
/** Desugars the instructions in the given method. */
- public abstract void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer);
+ public abstract void desugar(
+ ProgramMethod method,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringEventConsumer eventConsumer);
+
+ public boolean isEmpty() {
+ return false;
+ }
/** Returns true if the given method needs desugaring. */
public abstract boolean needsDesugaring(ProgramMethod method);
+
+ public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring(
+ ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
}
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
index 0b01594..4041efd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -6,47 +6,90 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
+import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
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 abstract class CfInstructionDesugaringEventConsumer
+ implements InvokeSpecialToSelfDesugaringEventConsumer,
+ LambdaDesugaringEventConsumer,
+ NestBasedAccessDesugaringEventConsumer {
- public static D8CfInstructionDesugaringEventConsumer createForD8() {
- return new D8CfInstructionDesugaringEventConsumer();
+ public static D8CfInstructionDesugaringEventConsumer createForD8(
+ Consumer<LambdaClass> lambdaClassConsumer, D8MethodProcessor methodProcessor) {
+ return new D8CfInstructionDesugaringEventConsumer(lambdaClassConsumer, methodProcessor);
}
- public static R8CfInstructionDesugaringEventConsumer createForR8() {
- return new R8CfInstructionDesugaringEventConsumer();
+ public static R8CfInstructionDesugaringEventConsumer createForR8(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return new R8CfInstructionDesugaringEventConsumer(appView);
}
public static CfInstructionDesugaringEventConsumer createForDesugaredCode() {
return new CfInstructionDesugaringEventConsumer() {
+
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
assert false;
}
+
+ @Override
+ public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ assert false;
+ }
+
+ @Override
+ public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ assert false;
+ }
};
}
- public abstract void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info);
-
public static class D8CfInstructionDesugaringEventConsumer
extends CfInstructionDesugaringEventConsumer {
+ private final Consumer<LambdaClass> lambdaClassConsumer;
+ private final D8MethodProcessor methodProcessor;
+
private final Map<DexReference, InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges =
new LinkedHashMap<>();
+ private D8CfInstructionDesugaringEventConsumer(
+ Consumer<LambdaClass> lambdaClassConsumer, D8MethodProcessor methodProcessor) {
+ this.lambdaClassConsumer = lambdaClassConsumer;
+ this.methodProcessor = methodProcessor;
+ }
+
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
synchronized (pendingInvokeSpecialBridges) {
@@ -55,6 +98,26 @@
}
}
+ @Override
+ public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
+ lambdaClassConsumer.accept(lambdaClass);
+ }
+
+ @Override
+ public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
+ @Override
+ public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
+ @Override
+ public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
public List<ProgramMethod> finalizeDesugaring(AppView<?> appView) {
List<ProgramMethod> needsReprocessing = new ArrayList<>();
finalizeInvokeSpecialDesugaring(appView, needsReprocessing::add);
@@ -98,8 +161,17 @@
public static class R8CfInstructionDesugaringEventConsumer
extends CfInstructionDesugaringEventConsumer {
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+
+ private final Map<LambdaClass, ProgramMethod> synthesizedLambdaClasses =
+ new IdentityHashMap<>();
private final List<InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new ArrayList<>();
+ public R8CfInstructionDesugaringEventConsumer(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.appView = appView;
+ }
+
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
synchronized (pendingInvokeSpecialBridges) {
@@ -107,7 +179,37 @@
}
}
- public void finalizeDesugaring(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ @Override
+ public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
+ synchronized (synthesizedLambdaClasses) {
+ synthesizedLambdaClasses.put(lambdaClass, context);
+ }
+ }
+
+ @Override
+ public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ // Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
+ // in the input code, and thus nothing needs to be done.
+ }
+
+ @Override
+ public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ // Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
+ // in the input code, and thus nothing needs to be done.
+ }
+
+ @Override
+ public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ // Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
+ // in the input code, and thus nothing needs to be done.
+ }
+
+ public void finalizeDesugaring() {
+ finalizeInvokeSpecialDesugaring();
+ finalizeLambdaDesugaring();
+ }
+
+ private void finalizeInvokeSpecialDesugaring() {
Collections.sort(pendingInvokeSpecialBridges);
pendingInvokeSpecialBridges.forEach(
info ->
@@ -115,5 +217,22 @@
.getDefinition()
.setCode(info.getVirtualMethodCode(), appView));
}
+
+ private void finalizeLambdaDesugaring() {
+ Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
+ synthesizedLambdaClasses.forEach(
+ (lambdaClass, context) -> {
+ lambdaClass.target.ensureAccessibilityIfNeeded();
+
+ // Populate set of types with serialized lambda method for removal.
+ if (lambdaClass.descriptor.interfaces.contains(
+ appView.dexItemFactory().serializableType)) {
+ classesWithSerializableLambdas.add(context.getHolder());
+ }
+ });
+
+ // Remove all '$deserializeLambda$' methods which are not supported by desugaring.
+ LambdaDeserializationMethodRemover.run(appView, classesWithSerializableLambdas);
+ }
}
}
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
index 8c56b4d..60b3537 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -4,17 +4,44 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
+import com.android.tools.r8.utils.ThrowingConsumer;
public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
+ private static final EmptyCfInstructionDesugaringCollection INSTANCE =
+ new EmptyCfInstructionDesugaringCollection();
+
+ private EmptyCfInstructionDesugaringCollection() {}
+
+ /** Intentionally package-private, prefer {@link CfInstructionDesugaringCollection#empty()}. */
+ static EmptyCfInstructionDesugaringCollection getInstance() {
+ return INSTANCE;
+ }
+
@Override
- public void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ public void desugar(
+ ProgramMethod method,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
// Intentionally empty.
}
@Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
public boolean needsDesugaring(ProgramMethod method) {
return false;
}
+
+ @Override
+ public <T extends Throwable> void withD8NestBasedAccessDesugaring(
+ ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) {
+ // Intentionally empty.
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/FreshLocalProvider.java b/src/main/java/com/android/tools/r8/ir/desugar/FreshLocalProvider.java
new file mode 100644
index 0000000..6c62b59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/FreshLocalProvider.java
@@ -0,0 +1,10 @@
+// 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;
+
+public interface FreshLocalProvider {
+
+ int getFreshLocal(int requiredRegisters);
+}
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 6ffc8e2..7d041f6 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
@@ -68,6 +68,7 @@
import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.Origin;
@@ -78,6 +79,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -236,7 +238,7 @@
return emulatedInterfaces.containsKey(itf);
}
- public boolean needsRewriting(DexMethod method, Type invokeType, AppView<?> appView) {
+ public boolean needsRewriting(DexMethod method, Type invokeType) {
if (invokeType == SUPER || invokeType == STATIC || invokeType == DIRECT) {
DexClass clazz = appView.appInfo().definitionFor(method.getHolderType());
if (clazz != null && clazz.isInterface()) {
@@ -307,7 +309,15 @@
methodProcessingContext);
break;
case INVOKE_SUPER:
- rewriteInvokeSuper(instruction.asInvokeSuper(), instructions, context);
+ rewriteInvokeSuper(
+ instruction.asInvokeSuper(),
+ code,
+ blocks,
+ instructions,
+ affectedValues,
+ blocksToRemove,
+ methodProcessor,
+ methodProcessingContext);
break;
case INVOKE_INTERFACE:
case INVOKE_VIRTUAL:
@@ -373,7 +383,7 @@
// This can be a private instance method call. Note that the referenced
// method is expected to be in the current class since it is private, but desugaring
// may move some methods or their code into other classes.
- assert needsRewriting(method, DIRECT, appView);
+ assert needsRewriting(method, DIRECT);
instructions.replaceCurrentInstruction(
new InvokeStatic(
directTarget.getDefinition().isPrivateMethod()
@@ -387,7 +397,7 @@
appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, method);
if (virtualTarget != null) {
// This is a invoke-direct call to a virtual method.
- assert needsRewriting(method, DIRECT, appView);
+ assert needsRewriting(method, DIRECT);
instructions.replaceCurrentInstruction(
new InvokeStatic(
defaultAsMethodOfCompanionClass(virtualTarget),
@@ -470,7 +480,7 @@
.setStaticTarget(invokedMethod, true)
.setStaticSource(m)
.build()));
- assert needsRewriting(invokedMethod, STATIC, appView);
+ assert needsRewriting(invokedMethod, STATIC);
instructions.replaceCurrentInstruction(
new InvokeStatic(
newProgramMethod.getReference(), invoke.outValue(), invoke.arguments()));
@@ -492,41 +502,42 @@
.appInfoForDesugaring()
.resolveMethodOnInterface(clazz, invokedMethod)
.asSingleResolution();
- if (resolutionResult != null && resolutionResult.getResolvedMethod().isStatic()) {
- assert needsRewriting(invokedMethod, STATIC, appView);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()),
- invoke.outValue(),
- invoke.arguments()));
+ if (clazz.isInterface()
+ && rewriteInvokeToThrow(
+ invoke,
+ resolutionResult,
+ code,
+ blockIterator,
+ instructions,
+ affectedValues,
+ blocksToRemove,
+ methodProcessor,
+ methodProcessingContext)) {
+ assert needsRewriting(invoke.getInvokedMethod(), STATIC);
return;
}
- // Replace by throw new IncompatibleClassChangeError/NoSuchMethodError.
- UtilityMethodForCodeOptimizations throwMethod =
- resolutionResult == null
- ? UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
- appView, methodProcessingContext)
- : UtilityMethodsForCodeOptimizations.synthesizeThrowIncompatibleClassChangeErrorMethod(
- appView, methodProcessingContext);
- throwMethod.optimize(methodProcessor);
+ assert resolutionResult != null;
+ assert resolutionResult.getResolvedMethod().isStatic();
+ assert needsRewriting(invokedMethod, STATIC);
- InvokeStatic throwInvoke =
- InvokeStatic.builder()
- .setMethod(throwMethod.getMethod())
- .setFreshOutValue(appView, code)
- .setPosition(invoke)
- .build();
- instructions.previous();
- instructions.add(throwInvoke);
- instructions.next();
- assert needsRewriting(invokedMethod, STATIC, appView);
- instructions.replaceCurrentInstructionWithThrow(
- appView, code, blockIterator, throwInvoke.outValue(), blocksToRemove, affectedValues);
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()),
+ invoke.outValue(),
+ invoke.arguments()));
}
private void rewriteInvokeSuper(
- InvokeSuper invoke, InstructionListIterator instructions, ProgramMethod context) {
+ InvokeSuper invoke,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ InstructionListIterator instructions,
+ Set<Value> affectedValues,
+ Set<BasicBlock> blocksToRemove,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ ProgramMethod context = code.context();
DexMethod invokedMethod = invoke.getInvokedMethod();
DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
if (clazz == null) {
@@ -537,6 +548,23 @@
return;
}
+ SingleResolutionResult resolutionResult =
+ appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution();
+ if (clazz.isInterface()
+ && rewriteInvokeToThrow(
+ invoke,
+ resolutionResult,
+ code,
+ blockIterator,
+ instructions,
+ affectedValues,
+ blocksToRemove,
+ methodProcessor,
+ methodProcessingContext)) {
+ assert needsRewriting(invoke.getInvokedMethod(), SUPER);
+ return;
+ }
+
if (clazz.isInterface() && !clazz.isLibraryClass()) {
// NOTE: we intentionally don't desugar super calls into interface methods
// coming from android.jar since it is only possible in case v24+ version
@@ -547,7 +575,7 @@
//
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
- assert needsRewriting(invokedMethod, SUPER, appView);
+ assert needsRewriting(invokedMethod, SUPER);
DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
instructions.replaceCurrentInstruction(
new InvokeStatic(
@@ -563,7 +591,7 @@
if (target != null && target.getDefinition().isDefaultMethod()) {
DexClass holder = target.getHolder();
if (holder.isLibraryClass() && holder.isInterface()) {
- assert needsRewriting(invokedMethod, SUPER, appView);
+ assert needsRewriting(invokedMethod, SUPER);
instructions.replaceCurrentInstruction(
new InvokeStatic(
defaultAsMethodOfCompanionClass(target),
@@ -593,11 +621,11 @@
factory.protoWithDifferentFirstParameter(
originalCompanionMethod.proto, emulatedItf),
originalCompanionMethod.name);
- assert needsRewriting(invokedMethod, SUPER, appView);
+ assert needsRewriting(invokedMethod, SUPER);
instructions.replaceCurrentInstruction(
new InvokeStatic(companionMethod, invoke.outValue(), invoke.arguments()));
} else {
- assert needsRewriting(invokedMethod, SUPER, appView);
+ assert needsRewriting(invokedMethod, SUPER);
instructions.replaceCurrentInstruction(
new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
}
@@ -624,12 +652,66 @@
if (resolution != null
&& (resolution.getResolvedHolder().isLibraryClass()
|| appView.options().isDesugaredLibraryCompilation())) {
- assert needsRewriting(invokedMethod, VIRTUAL, appView);
+ assert needsRewriting(invokedMethod, VIRTUAL);
rewriteCurrentInstructionToEmulatedInterfaceCall(
emulatedItf, invokedMethod, invoke, instructions);
}
}
+ private boolean rewriteInvokeToThrow(
+ InvokeMethod invoke,
+ SingleResolutionResult resolutionResult,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ InstructionListIterator instructions,
+ Set<Value> affectedValues,
+ Set<BasicBlock> blocksToRemove,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ MethodSynthesizerConsumer methodSynthesizerConsumer;
+ if (resolutionResult == null) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
+ } else if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
+ } else {
+ return false;
+ }
+
+ // Replace by throw new SomeException.
+ UtilityMethodForCodeOptimizations throwMethod =
+ methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
+ throwMethod.optimize(methodProcessor);
+
+ InvokeStatic throwInvoke =
+ InvokeStatic.builder()
+ .setMethod(throwMethod.getMethod())
+ .setFreshOutValue(appView, code)
+ .setPosition(invoke)
+ .build();
+ instructions.previous();
+
+ // Split the block before the invoke instruction, and position the block iterator at the newly
+ // created throw block (this involves rewinding the block iterator back over the blocks created
+ // as a result of critical edge splitting, if any).
+ BasicBlock throwBlock = instructions.splitCopyCatchHandlers(code, blockIterator, options);
+ IteratorUtils.previousUntil(blockIterator, block -> block == throwBlock);
+ blockIterator.next();
+
+ // Insert the `SomeException e = throwSomeException()` invoke before the goto
+ // instruction.
+ instructions.previous();
+ instructions.add(throwInvoke);
+
+ // Insert the `throw e` instruction in the newly created throw block.
+ InstructionListIterator throwBlockIterator = throwBlock.listIterator(code);
+ throwBlockIterator.next();
+ throwBlockIterator.replaceCurrentInstructionWithThrow(
+ appView, code, blockIterator, throwInvoke.outValue(), blocksToRemove, affectedValues);
+ return true;
+ }
+
private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
// Here we try to avoid doing the expensive look-up on all invokes.
if (!emulatedMethods.contains(invokedMethod.name)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 477d8d8..076c0c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer.emptyForcefullyMovedLambdaMethodConsumer;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
@@ -30,15 +33,15 @@
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
-import com.android.tools.r8.utils.OptionalBool;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -57,10 +60,14 @@
*/
public final class LambdaClass {
+ public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
+ public static final String JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
+ public static final String R8_LAMBDA_ACCESSOR_METHOD_PREFIX = "$r8$lambda$";
+
private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
final AppView<?> appView;
- final LambdaRewriter rewriter;
+ final LambdaInstructionDesugaring desugaring;
public final DexType type;
public LambdaDescriptor descriptor;
public final DexMethod constructor;
@@ -71,17 +78,17 @@
// Considered final but is set after due to circularity in allocation.
private DexProgramClass clazz = null;
- LambdaClass(
+ public LambdaClass(
SyntheticProgramClassBuilder builder,
AppView<?> appView,
- LambdaRewriter rewriter,
+ LambdaInstructionDesugaring desugaring,
ProgramMethod accessedFrom,
LambdaDescriptor descriptor) {
- assert rewriter != null;
+ assert desugaring != null;
assert descriptor != null;
this.type = builder.getType();
this.appView = appView;
- this.rewriter = rewriter;
+ this.desugaring = desugaring;
this.descriptor = descriptor;
DexItemFactory factory = builder.getFactory();
@@ -97,7 +104,7 @@
? null
: factory.createMethod(type, constructorProto, factory.classConstructorMethodName);
this.lambdaField =
- !stateless ? null : factory.createField(type, type, rewriter.instanceFieldName);
+ !stateless ? null : factory.createField(type, type, factory.lambdaInstanceFieldName);
// Synthesize the program class one all fields are set.
synthesizeLambdaClass(builder);
@@ -108,7 +115,11 @@
return clazz;
}
- void setClass(DexProgramClass clazz) {
+ public DexType getType() {
+ return type;
+ }
+
+ public void setClass(DexProgramClass clazz) {
assert this.clazz == null;
assert clazz != null;
assert type == clazz.type;
@@ -256,7 +267,8 @@
// Creates a delegation target for this particular lambda class. Note that we
// should always be able to create targets for the lambdas we support.
private Target createTarget(ProgramMethod accessedFrom) {
- if (descriptor.delegatesToLambdaImplMethod()) {
+ if (appView.options().canAccessModifyLambdaImplementationMethods(appView)
+ && descriptor.delegatesToLambdaImplMethod()) {
return createLambdaImplMethodTarget(accessedFrom);
}
@@ -339,7 +351,7 @@
assert descriptor.implHandle.type.isInvokeInstance() ||
descriptor.implHandle.type.isInvokeDirect();
- if (!descriptor.needsAccessor(accessedFrom)) {
+ if (!descriptor.needsAccessor(appView, accessedFrom)) {
return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL);
}
// We need to generate an accessor method in `accessedFrom` class/interface
@@ -371,7 +383,7 @@
private Target createStaticMethodTarget(ProgramMethod accessedFrom) {
assert descriptor.implHandle.type.isInvokeStatic();
- if (!descriptor.needsAccessor(accessedFrom)) {
+ if (!descriptor.needsAccessor(appView, accessedFrom)) {
return new NoAccessorMethodTarget(Invoke.Type.STATIC);
}
@@ -395,7 +407,7 @@
assert implHandle != null;
assert implHandle.type.isInvokeConstructor();
- if (!descriptor.needsAccessor(accessedFrom)) {
+ if (!descriptor.needsAccessor(appView, accessedFrom)) {
return new NoAccessorMethodTarget(Invoke.Type.DIRECT);
}
@@ -418,14 +430,14 @@
// Create targets for interface methods.
private Target createInterfaceMethodTarget(ProgramMethod accessedFrom) {
assert descriptor.implHandle.type.isInvokeInterface();
- assert !descriptor.needsAccessor(accessedFrom);
+ assert !descriptor.needsAccessor(appView, accessedFrom);
return new NoAccessorMethodTarget(Invoke.Type.INTERFACE);
}
private DexString generateUniqueLambdaMethodName() {
return appView
.dexItemFactory()
- .createString(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
+ .createString(R8_LAMBDA_ACCESSOR_METHOD_PREFIX + descriptor.uniqueId);
}
// Represents information about the method lambda class need to delegate the call to. It may
@@ -447,15 +459,23 @@
}
// Ensure access of the referenced symbol(s).
- abstract ProgramMethod ensureAccessibility(boolean allowMethodModification);
+ abstract ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer);
+
+ public final void ensureAccessibilityIfNeeded() {
+ ensureAccessibilityIfNeeded(emptyForcefullyMovedLambdaMethodConsumer(), emptyConsumer());
+ }
// Ensure access of the referenced symbol(s).
- public ProgramMethod ensureAccessibilityIfNeeded(boolean allowMethodModification) {
+ public final void ensureAccessibilityIfNeeded(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
if (!hasEnsuredAccessibility) {
- accessibilityBridge = ensureAccessibility(allowMethodModification);
+ accessibilityBridge =
+ ensureAccessibility(forcefullyMovedLambdaMethodConsumer, needsProcessingConsumer);
hasEnsuredAccessibility = true;
}
- return accessibilityBridge;
}
boolean isInterface() {
@@ -463,6 +483,13 @@
}
}
+ public abstract class D8SpecificTarget extends Target {
+ D8SpecificTarget(DexMethod callTarget, Type invokeType) {
+ super(callTarget, invokeType);
+ assert !appView.enableWholeProgramOptimizations();
+ }
+ }
+
// Used for targeting methods referenced directly without creating accessors.
private final class NoAccessorMethodTarget extends Target {
@@ -471,13 +498,15 @@
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
return null;
}
}
// Used for static private lambda$ methods. Only needs access relaxation.
- private final class StaticLambdaImplTarget extends Target {
+ private final class StaticLambdaImplTarget extends D8SpecificTarget {
final ProgramMethod target;
@@ -487,11 +516,14 @@
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
// We already found the static method to be called, just relax its accessibility.
- target.getDefinition().accessFlags.unsetPrivate();
+ MethodAccessFlags flags = target.getAccessFlags();
+ flags.unsetPrivate();
if (target.getHolder().isInterface()) {
- target.getDefinition().accessFlags.setPublic();
+ flags.setPublic();
}
return null;
}
@@ -499,14 +531,16 @@
// Used for instance private lambda$ methods on interfaces which need to be converted to public
// static methods. They can't remain instance methods as they will end up on the companion class.
- private class InterfaceLambdaImplTarget extends Target {
+ private class InterfaceLambdaImplTarget extends D8SpecificTarget {
InterfaceLambdaImplTarget(DexMethod staticMethod) {
super(staticMethod, Type.STATIC);
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
DexMethod implMethod = descriptor.implHandle.asMethod();
@@ -523,9 +557,7 @@
MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
newAccessFlags.setStatic();
newAccessFlags.unsetPrivate();
- // Always make the method public to provide access when r8 minification is
- // allowed to move the lambda class accessing this method to another package
- // (-allowaccessmodification).
+ // Always make the method public to provide access.
newAccessFlags.setPublic();
DexEncodedMethod newMethod =
new DexEncodedMethod(
@@ -537,14 +569,24 @@
encodedMethod.getCode(),
true);
newMethod.copyMetadata(encodedMethod);
- rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
+ forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
+ encodedMethod.method, callTarget);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
newMethod.getCode(), callTarget.getArity(), appView);
return newMethod;
});
if (replacement != null) {
- return new ProgramMethod(implMethodHolder, replacement);
+ // Since we've copied the code object from an existing method, the code should already be
+ // processed, and thus we don't need to schedule it for processing in D8.
+ assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode();
+ assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode();
+ ProgramMethod newMethod = new ProgramMethod(implMethodHolder, replacement);
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ assert appView.options().isGeneratingClassFiles();
+ needsProcessingConsumer.accept(newMethod);
+ }
+ return newMethod;
}
// The method might already have been moved by another invoke-dynamic targeting it.
// If so, it must be defined on the holder.
@@ -565,33 +607,30 @@
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
return null;
}
}
// Used for instance private lambda$ methods which need to be converted to public methods.
- private class InstanceLambdaImplTarget extends Target {
+ private class InstanceLambdaImplTarget extends D8SpecificTarget {
InstanceLambdaImplTarget(DexMethod staticMethod) {
super(staticMethod, Type.VIRTUAL);
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
// When compiling with whole program optimization, check that we are not inplace modifying.
- assert !(appView.enableWholeProgramOptimizations() && allowMethodModification);
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
DexMethod implMethod = descriptor.implHandle.asMethod();
DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
- return allowMethodModification
- ? modifyLambdaImplementationMethod(implMethod, implMethodHolder)
- : createSyntheticAccessor(implMethod, implMethodHolder);
- }
- private ProgramMethod modifyLambdaImplementationMethod(
- DexMethod implMethod, DexProgramClass implMethodHolder) {
DexEncodedMethod replacement =
implMethodHolder
.getMethodCollection()
@@ -614,11 +653,21 @@
encodedMethod.getCode(),
true);
newMethod.copyMetadata(encodedMethod);
- rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
+ forcefullyMovedLambdaMethodConsumer.acceptForcefullyMovedLambdaMethod(
+ encodedMethod.method, callTarget);
return newMethod;
});
if (replacement != null) {
- return new ProgramMethod(implMethodHolder, replacement);
+ // Since we've copied the code object from an existing method, the code should already be
+ // processed, and thus we don't need to schedule it for processing in D8.
+ assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode();
+ assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode();
+ ProgramMethod newMethod = new ProgramMethod(implMethodHolder, replacement);
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ assert appView.options().isGeneratingClassFiles();
+ needsProcessingConsumer.accept(newMethod);
+ }
+ return newMethod;
}
// The method might already have been moved by another invoke-dynamic targeting it.
// If so, it must be defined on the holder.
@@ -627,45 +676,6 @@
assert modified.getDefinition().isNonPrivateVirtualMethod();
return modified;
}
-
- private ProgramMethod createSyntheticAccessor(
- DexMethod implMethod, DexProgramClass implMethodHolder) {
- // The accessor might already have been created by another invoke-dynamic targeting it.
- ProgramMethod existing = implMethodHolder.lookupProgramMethod(callTarget);
- if (existing != null) {
- assert existing.getAccessFlags().isSynthetic();
- assert existing.getAccessFlags().isPublic();
- assert existing.getDefinition().isVirtualMethod();
- return existing;
- }
- MethodAccessFlags accessorFlags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC, false);
-
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(callTarget)
- .setReceiver(implMethod.holder)
- .setTargetReceiver(implMethod.holder)
- .setTarget(implMethod)
- .setInvokeType(Type.DIRECT)
- .setIsInterface(false);
-
- DexEncodedMethod accessorEncodedMethod =
- new DexEncodedMethod(
- callTarget,
- accessorFlags,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- new SynthesizedCode(
- forwardSourceCodeBuilder::build,
- registry -> registry.registerInvokeDirect(implMethod)),
- true);
- accessorEncodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
-
- implMethodHolder.addVirtualMethod(accessorEncodedMethod);
- return new ProgramMethod(implMethodHolder, accessorEncodedMethod);
- }
}
// Used for instance/static methods or constructors accessed via
@@ -677,7 +687,9 @@
}
@Override
- ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+ ProgramMethod ensureAccessibility(
+ ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+ Consumer<ProgramMethod> needsProcessingConsumer) {
// Create a static accessor with proper accessibility.
DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
assert accessorClass != null;
@@ -693,23 +705,23 @@
// Always make the method public to provide access when r8 minification is allowed to move
// the lambda class accessing this method to another package (-allowaccessmodification).
- MethodAccessFlags accessorFlags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC,
- false);
-
- DexEncodedMethod accessorEncodedMethod =
- new DexEncodedMethod(
- callTarget,
- accessorFlags,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
- true);
-
- accessorClass.addDirectMethod(accessorEncodedMethod);
- return new ProgramMethod(accessorClass, accessorEncodedMethod);
+ ProgramMethod accessorMethod =
+ new ProgramMethod(
+ accessorClass,
+ new DexEncodedMethod(
+ callTarget,
+ MethodAccessFlags.createPublicStaticSynthetic(),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
+ true));
+ accessorClass.addDirectMethod(accessorMethod.getDefinition());
+ if (appView.options().isDesugaredLibraryCompilation()
+ || appView.options().isGeneratingDex()) {
+ needsProcessingConsumer.accept(accessorMethod);
+ }
+ return accessorMethod;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index f661256..3cc480d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.ir.desugar.LambdaClass.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -177,10 +180,14 @@
return targetHolder == type;
}
+ public boolean canAccessModifyLambdaImplementationMethods(AppView<?> appView) {
+ return appView.enableWholeProgramOptimizations();
+ }
+
/** If the lambda delegates to lambda$ method. */
public boolean delegatesToLambdaImplMethod() {
- DexString methodName = implHandle.asMethod().name;
- return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX);
+ String methodName = implHandle.asMethod().getName().toString();
+ return methodName.startsWith(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX);
}
/** Is a stateless lambda, i.e. lambda does not capture any values */
@@ -189,8 +196,9 @@
}
/** Checks if call site needs a accessor when referenced from `accessedFrom`. */
- boolean needsAccessor(ProgramMethod accessedFrom) {
- if (delegatesToLambdaImplMethod()) {
+ boolean needsAccessor(AppView<?> appView, ProgramMethod accessedFrom) {
+ if (appView.options().canAccessModifyLambdaImplementationMethods(appView)
+ && delegatesToLambdaImplMethod()) {
return false;
}
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
deleted file mode 100644
index b12b2a7..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright (c) 2017, 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.CfFieldInstruction;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfInvokeDynamic;
-import com.android.tools.r8.cf.code.CfLoad;
-import com.android.tools.r8.cf.code.CfNew;
-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;
-import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexField;
-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.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import org.objectweb.asm.Opcodes;
-
-/**
- * Lambda desugaring rewriter.
- *
- * <p>Performs lambda instantiation point matching, lambda class generation, and instruction
- * patching.
- */
-public class LambdaRewriter {
-
- static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
- public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
-
- private final AppView<?> appView;
-
- final DexString instanceFieldName;
-
- private final LambdaRewriterLens.Builder lensBuilder = LambdaRewriterLens.builder();
- private final Set<DexMethod> forcefullyMovedMethods = Sets.newIdentityHashSet();
-
- // Maps lambda class type into lambda class representation.
- // NOTE: synchronize concurrent access on `knownLambdaClasses`.
- private final List<LambdaClass> knownLambdaClasses = new ArrayList<>();
-
- private final Map<DexMethod, Integer> methodIds = new ConcurrentHashMap<>();
-
- public LambdaRewriter(AppView<?> appView) {
- this.appView = appView;
- this.instanceFieldName = appView.dexItemFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
- }
-
- void forcefullyMoveMethod(DexMethod from, DexMethod to) {
- lensBuilder.move(from, to);
- forcefullyMovedMethods.add(from);
- }
-
- public Set<DexMethod> getForcefullyMovedMethods() {
- return forcefullyMovedMethods;
- }
-
- private void synthesizeAccessibilityBridgesForLambdaClassesD8(
- Collection<LambdaClass> lambdaClasses, IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
- SortedProgramMethodSet nonDexAccessibilityBridges = SortedProgramMethodSet.create();
- List<LambdaClass> sortedLambdaClasses = new ArrayList<>(lambdaClasses);
- sortedLambdaClasses.sort((x, y) -> x.type.compareTo(y.type));
- for (LambdaClass lambdaClass : sortedLambdaClasses) {
- // This call may cause originalMethodSignatures to be updated.
- ProgramMethod accessibilityBridge = lambdaClass.target.ensureAccessibilityIfNeeded(true);
- if (accessibilityBridge != null
- && !accessibilityBridge.getDefinition().getCode().isDexCode()) {
- nonDexAccessibilityBridges.add(accessibilityBridge);
- }
- }
- if (!nonDexAccessibilityBridges.isEmpty()) {
- converter.processMethodsConcurrently(nonDexAccessibilityBridges, executorService);
- }
- }
-
- /**
- * Detect and desugar lambdas and method references found in the code.
- *
- * <p>NOTE: this method can be called concurrently for several different methods.
- */
- public int desugarLambdas(
- ProgramMethod method,
- AppInfoWithClassHierarchy appInfo,
- MethodProcessingContext methodProcessingContext) {
- return desugarLambdas(
- method,
- callsite -> {
- LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callsite, appInfo, method);
- if (descriptor == null) {
- return null;
- }
- return createLambdaClass(descriptor, method, methodProcessingContext);
- });
- }
-
- // Same as above, but where lambdas are always known to exist for the call sites.
- public static int desugarLambdas(
- ProgramMethod method, Function<DexCallSite, LambdaClass> callSites) {
- CfCode code = method.getDefinition().getCode().asCfCode();
- List<CfInstruction> instructions = code.getInstructions();
- Supplier<List<CfInstruction>> lazyNewInstructions =
- Suppliers.memoize(() -> new ArrayList<>(instructions));
- int replaced = 0;
- int maxTemp = 0;
- int newInstructionsDelta = 0;
- for (int i = 0; i < instructions.size(); i++) {
- CfInstruction instruction = instructions.get(i);
- if (instruction instanceof CfInvokeDynamic) {
- LambdaClass lambdaClass = callSites.apply(((CfInvokeDynamic) instruction).getCallSite());
- if (lambdaClass == null) {
- continue;
- }
- int newInstructionsIndex = i + newInstructionsDelta;
- if (lambdaClass.isStateless()) {
- CfFieldInstruction getStaticLambdaInstance =
- new CfFieldInstruction(
- Opcodes.GETSTATIC, lambdaClass.lambdaField, lambdaClass.lambdaField);
- lazyNewInstructions.get().set(newInstructionsIndex, getStaticLambdaInstance);
- } else {
- List<CfInstruction> replacement = new ArrayList<>();
- int arguments = lambdaClass.descriptor.captures.size();
- int temp = code.getMaxLocals();
- for (int j = arguments - 1; j >= 0; j--) {
- ValueType type = ValueType.fromDexType(lambdaClass.descriptor.captures.values[j]);
- replacement.add(new CfStore(type, temp));
- temp += type.requiredRegisters();
- }
- maxTemp = Math.max(temp, maxTemp);
- replacement.add(new CfNew(lambdaClass.type));
- replacement.add(new CfStackInstruction(Opcode.Dup));
- for (int j = 0; j < arguments; j++) {
- ValueType type = ValueType.fromDexType(lambdaClass.descriptor.captures.values[j]);
- temp -= type.requiredRegisters();
- replacement.add(new CfLoad(type, temp));
- }
- replacement.add(new CfInvoke(Opcodes.INVOKESPECIAL, lambdaClass.constructor, false));
- List<CfInstruction> newInstructions = lazyNewInstructions.get();
- newInstructions.remove(newInstructionsIndex);
- newInstructions.addAll(newInstructionsIndex, replacement);
- newInstructionsDelta += replacement.size() - 1;
- }
- ++replaced;
- }
- }
- if (maxTemp > 0) {
- assert maxTemp > code.getMaxLocals();
- code.setMaxLocals(maxTemp);
- }
- if (replaced > 0) {
- code.setInstructions(lazyNewInstructions.get());
- }
- return replaced;
- }
-
- /** Remove lambda deserialization methods. */
- public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
- for (DexProgramClass clazz : classes) {
- clazz.removeMethod(appView.dexItemFactory().deserializeLambdaMethod);
- }
- }
-
- /** Generates lambda classes and adds them to the builder. */
- public void finalizeLambdaDesugaringForD8(IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
- synthesizeAccessibilityBridgesForLambdaClassesD8(
- knownLambdaClasses, converter, executorService);
- fixup();
- optimizeSynthesizedClasses(converter, executorService);
- }
-
- private void optimizeSynthesizedClasses(IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
- converter.optimizeSynthesizedClasses(
- knownLambdaClasses.stream()
- .map(LambdaClass::getLambdaProgramClass)
- .collect(ImmutableSet.toImmutableSet()),
- executorService);
- }
-
- // Creates a lambda class corresponding to the lambda descriptor and context.
- public LambdaClass createLambdaClass(
- LambdaDescriptor descriptor, ProgramMethod accessedFrom, MethodProcessingContext context) {
- Box<LambdaClass> box = new Box<>();
- DexProgramClass clazz =
- appView
- .getSyntheticItems()
- .createClass(
- SyntheticNaming.SyntheticKind.LAMBDA,
- context.createUniqueContext(),
- appView.dexItemFactory(),
- builder ->
- box.set(new LambdaClass(builder, appView, this, accessedFrom, descriptor)));
- // Immediately set the actual program class on the lambda.
- LambdaClass lambdaClass = box.get();
- lambdaClass.setClass(clazz);
- synchronized (knownLambdaClasses) {
- knownLambdaClasses.add(lambdaClass);
- }
- return lambdaClass;
- }
-
- public Collection<LambdaClass> getKnownLambdaClasses() {
- return Collections.unmodifiableList(knownLambdaClasses);
- }
-
- public NestedGraphLens fixup() {
- LambdaRewriterLens lens = lensBuilder.build(appView.graphLens(), appView.dexItemFactory());
- if (lens == null) {
- return null;
- }
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
- if (enclosingMethod != null) {
- if (enclosingMethod.getEnclosingMethod() != null) {
- DexMethod mappedEnclosingMethod = lens.lookupMethod(enclosingMethod.getEnclosingMethod());
- if (mappedEnclosingMethod != enclosingMethod.getEnclosingMethod()) {
- clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(mappedEnclosingMethod));
- }
- } else {
- assert enclosingMethod.getEnclosingClass() != null;
- DexType mappedEnclosingClass = lens.lookupType(enclosingMethod.getEnclosingClass());
- if (mappedEnclosingClass != enclosingMethod.getEnclosingClass()) {
- clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(mappedEnclosingClass));
- }
- }
- }
- }
- // Return lens without method map (but still retaining originalMethodSignatures), as the
- // generated lambdas classes are generated with the an invoke to the new method, so no
- // code rewriting is required.
- return lens.withoutMethodMap();
- }
-
- static class LambdaRewriterLens extends NestedGraphLens {
-
- LambdaRewriterLens(
- Map<DexType, DexType> typeMap,
- Map<DexMethod, DexMethod> methodMap,
- BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
- BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
- GraphLens previousLens,
- DexItemFactory dexItemFactory) {
- super(
- typeMap,
- methodMap,
- fieldMap,
- originalMethodSignatures,
- previousLens,
- dexItemFactory);
- }
-
- @Override
- protected boolean isLegitimateToHaveEmptyMappings() {
- return true;
- }
-
- private LambdaRewriterLens withoutMethodMap() {
- methodMap.clear();
- return this;
- }
-
- public static LambdaRewriterLens.Builder builder() {
- return new LambdaRewriterLens.Builder();
- }
-
- public static class Builder extends NestedGraphLens.Builder {
- public LambdaRewriterLens build(GraphLens previousLens, DexItemFactory dexItemFactory) {
- if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
- return null;
- }
- assert typeMap.isEmpty();
- assert fieldMap.isEmpty();
- return new LambdaRewriterLens(
- typeMap,
- methodMap,
- fieldMap,
- originalMethodSignatures,
- previousLens,
- dexItemFactory);
- }
- }
- }
-}
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
index 6a11e86..c34e32c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -4,19 +4,26 @@
package com.android.tools.r8.ir.desugar;
+
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
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.lambda.LambdaInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -24,26 +31,40 @@
private final AppView<?> appView;
private final List<CfInstructionDesugaring> desugarings = new ArrayList<>();
- private final InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring;
+
private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
- public NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
+ 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);
+ this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
+ desugarings.add(new LambdaInstructionDesugaring(appView));
+ desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+ if (nestBasedAccessDesugaring != null) {
+ desugarings.add(nestBasedAccessDesugaring);
}
}
+ // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
+ // This should be removed once we can represent invoke-special instructions in the IR.
+ private NonEmptyCfInstructionDesugaringCollection(
+ AppView<?> appView, InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring) {
+ this.appView = appView;
+ this.nestBasedAccessDesugaring = null;
+ desugarings.add(invokeSpecialToSelfDesugaring);
+ }
+
+ static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
+ assert appView.options().desugarState.isOff();
+ assert appView.options().isGeneratingClassFiles();
+ return new NonEmptyCfInstructionDesugaringCollection(
+ appView, new InvokeSpecialToSelfDesugaring(appView));
+ }
+
@Override
- public void desugar(ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ public void desugar(
+ ProgramMethod method,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
Code code = method.getDefinition().getCode();
if (!code.isCfCode()) {
appView
@@ -58,30 +79,60 @@
}
CfCode cfCode = code.asCfCode();
+
+ // Tracking of temporary locals used for instruction desugaring. The desugaring of each
+ // instruction is assumed to use locals only for the duration of the instruction, such that any
+ // temporarily used locals will be free again at the next instruction to be desugared.
+ IntBox maxLocalsForCode = new IntBox(cfCode.getMaxLocals());
+ IntBox maxLocalsForInstruction = new IntBox(cfCode.getMaxLocals());
+
List<CfInstruction> desugaredInstructions =
ListUtils.flatMap(
cfCode.getInstructions(),
- instruction -> desugarInstruction(instruction, consumer, method),
+ instruction -> {
+ Collection<CfInstruction> replacement =
+ desugarInstruction(
+ instruction,
+ maxLocalsForInstruction::getAndIncrement,
+ eventConsumer,
+ method,
+ methodProcessingContext);
+ if (replacement != null) {
+ // Record if we increased the max number of locals for the method, and reset the
+ // next temporary locals register.
+ maxLocalsForCode.setMax(maxLocalsForInstruction.getAndSet(cfCode.getMaxLocals()));
+ } else {
+ // The next temporary locals register should be unchanged.
+ assert maxLocalsForInstruction.get() == cfCode.getMaxLocals();
+ }
+ return replacement;
+ },
null);
if (desugaredInstructions != null) {
+ assert maxLocalsForCode.get() >= cfCode.getMaxLocals();
cfCode.setInstructions(desugaredInstructions);
+ cfCode.setMaxLocals(maxLocalsForCode.get());
} else {
assert false : "Expected code to be desugared";
}
}
- private List<CfInstruction> desugarInstruction(
+ private Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
- CfInstructionDesugaringEventConsumer consumer,
- ProgramMethod context) {
+ FreshLocalProvider freshLocalProvider,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
// 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);
+ Collection<CfInstruction> replacement =
+ desugaring.desugarInstruction(
+ instruction, freshLocalProvider, eventConsumer, context, methodProcessingContext);
if (replacement != null) {
- assert verifyNoOtherDesugaringNeeded(instruction, context, iterator);
+ assert verifyNoOtherDesugaringNeeded(
+ instruction, context, methodProcessingContext, iterator);
return replacement;
}
}
@@ -115,16 +166,31 @@
private static boolean verifyNoOtherDesugaringNeeded(
CfInstruction instruction,
ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
Iterator<CfInstructionDesugaring> iterator) {
assert IteratorUtils.nextUntil(
iterator,
desugaring ->
desugaring.desugarInstruction(
instruction,
+ requiredRegisters -> {
+ assert false;
+ return 0;
+ },
CfInstructionDesugaringEventConsumer.createForDesugaredCode(),
- context)
+ context,
+ methodProcessingContext)
!= null)
== null;
return true;
}
+
+ @Override
+ public <T extends Throwable> void withD8NestBasedAccessDesugaring(
+ ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T {
+ if (nestBasedAccessDesugaring != null) {
+ assert nestBasedAccessDesugaring instanceof D8NestBasedAccessDesugaring;
+ consumer.accept((D8NestBasedAccessDesugaring) nestBasedAccessDesugaring);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
index 4b33d2f..514ee97 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/StringConcatRewriter.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -441,11 +440,7 @@
@Override
Value getOrCreateValue() {
Value value = code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
- appendInstruction(
- new ConstString(
- value,
- factory.createString(str),
- ThrowingInfo.defaultForConstString(appView.options())));
+ appendInstruction(new ConstString(value, factory.createString(str)));
return value;
}
}
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
index 2d99341..a4f4b9b 100644
--- 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
@@ -6,9 +6,9 @@
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.Code;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -16,10 +16,10 @@
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.desugar.FreshLocalProvider;
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.Collection;
import java.util.List;
import org.objectweb.asm.Opcodes;
@@ -28,17 +28,15 @@
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()) {
+ if (instruction.isInvokeSpecial()) {
return needsDesugaring(instruction.asInvoke(), context) != null;
}
return false;
@@ -66,46 +64,23 @@
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(
+ public Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
- CfInstructionDesugaringEventConsumer consumer,
- ProgramMethod context) {
- if (instruction.isInvoke()) {
- return desugarInvokeInstruction(instruction.asInvoke(), consumer, context);
+ FreshLocalProvider freshLocalProvider,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ if (instruction.isInvokeSpecial()) {
+ return desugarInvokeInstruction(instruction.asInvoke(), eventConsumer, context);
}
return null;
}
private List<CfInstruction> desugarInvokeInstruction(
- CfInvoke invoke, CfInstructionDesugaringEventConsumer consumer, ProgramMethod context) {
+ CfInvoke invoke,
+ InvokeSpecialToSelfDesugaringEventConsumer eventConsumer,
+ ProgramMethod context) {
ProgramMethod method = needsDesugaring(invoke, context);
if (method == null) {
return null;
@@ -119,13 +94,13 @@
// 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);
+ DexMethod bridgeMethod = ensureInvokeSpecialBridge(method, eventConsumer);
return ImmutableList.of(
new CfInvoke(Opcodes.INVOKESPECIAL, bridgeMethod, invoke.isInterface()));
}
private DexMethod ensureInvokeSpecialBridge(
- ProgramMethod method, CfInstructionDesugaringEventConsumer consumer) {
+ ProgramMethod method, InvokeSpecialToSelfDesugaringEventConsumer eventConsumer) {
DexMethod bridgeReference = getInvokeSpecialBridgeReference(method);
DexProgramClass clazz = method.getHolder();
synchronized (clazz.getMethodCollection()) {
@@ -144,7 +119,7 @@
// Add the newly created direct method to its holder.
clazz.addDirectMethod(newDirectMethod.getDefinition());
- consumer.acceptInvokeSpecialBridgeInfo(
+ eventConsumer.acceptInvokeSpecialBridgeInfo(
new InvokeSpecialBridgeInfo(newDirectMethod, method, virtualMethodCode));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaringEventConsumer.java
new file mode 100644
index 0000000..b706a7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaringEventConsumer.java
@@ -0,0 +1,10 @@
+// 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;
+
+public interface InvokeSpecialToSelfDesugaringEventConsumer {
+
+ void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java
new file mode 100644
index 0000000..2d89fce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/D8LambdaDesugaring.java
@@ -0,0 +1,56 @@
+// 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.lambda;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.ir.conversion.ClassConverterResult;
+import com.android.tools.r8.ir.conversion.D8MethodProcessor;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+public class D8LambdaDesugaring {
+
+ public static void synthesizeAccessibilityBridgesForLambdaClasses(
+ AppView<?> appView,
+ ClassConverterResult classConverterResult,
+ D8MethodProcessor methodProcessor)
+ throws ExecutionException {
+ Map<DexMethod, DexMethod> forcefullyMovedLambdaMethods = new IdentityHashMap<>();
+ ProgramMethodSet seenAccessibilityBridges = ProgramMethodSet.createConcurrent();
+ classConverterResult.forEachSynthesizedLambdaClassWithDeterministicOrdering(
+ lambdaClass -> {
+ // Collect the accessibility bridges that require processing. Note that we cannot schedule
+ // the methods for processing directly here, since that would lead to concurrent IR
+ // processing meanwhile we update the program (insert bridges on existing classes).
+ lambdaClass.target.ensureAccessibilityIfNeeded(
+ forcefullyMovedLambdaMethods::put, seenAccessibilityBridges::add);
+ });
+ methodProcessor
+ .scheduleDesugaredMethodsForProcessing(seenAccessibilityBridges)
+ .awaitMethodProcessing();
+ rewriteEnclosingMethodAttributes(appView, forcefullyMovedLambdaMethods);
+ }
+
+ private static void rewriteEnclosingMethodAttributes(
+ AppView<?> appView, Map<DexMethod, DexMethod> forcefullyMovedLambdaMethods) {
+ if (forcefullyMovedLambdaMethods.isEmpty()) {
+ return;
+ }
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.hasEnclosingMethodAttribute()) {
+ DexMethod enclosingMethod = clazz.getEnclosingMethodAttribute().getEnclosingMethod();
+ DexMethod rewrittenEnclosingMethod = forcefullyMovedLambdaMethods.get(enclosingMethod);
+ if (rewrittenEnclosingMethod != null) {
+ clazz.setEnclosingMethodAttribute(new EnclosingMethodAttribute(rewrittenEnclosingMethod));
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/ForcefullyMovedLambdaMethodConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/ForcefullyMovedLambdaMethodConsumer.java
new file mode 100644
index 0000000..1925f61
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/ForcefullyMovedLambdaMethodConsumer.java
@@ -0,0 +1,16 @@
+// 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.lambda;
+
+import com.android.tools.r8.graph.DexMethod;
+
+public interface ForcefullyMovedLambdaMethodConsumer {
+
+ void acceptForcefullyMovedLambdaMethod(DexMethod from, DexMethod to);
+
+ static ForcefullyMovedLambdaMethodConsumer emptyForcefullyMovedLambdaMethodConsumer() {
+ return (from, to) -> {};
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDeserializationMethodRemover.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDeserializationMethodRemover.java
new file mode 100644
index 0000000..d496b33
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDeserializationMethodRemover.java
@@ -0,0 +1,30 @@
+// 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.lambda;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import java.util.Collection;
+
+public class LambdaDeserializationMethodRemover {
+
+ /** Remove lambda deserialization methods. */
+ public static void run(AppView<AppInfo> appView) {
+ if (appView.options().desugarState.isOn()) {
+ run(appView, appView.appInfo().classes());
+ }
+ }
+
+ /** Remove lambda deserialization methods. */
+ public static void run(AppView<?> appView, Collection<DexProgramClass> classes) {
+ assert appView.options().desugarState.isOn() || classes.isEmpty();
+ DexMethod reference = appView.dexItemFactory().deserializeLambdaMethod;
+ for (DexProgramClass clazz : classes) {
+ clazz.removeMethod(reference);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDesugaringEventConsumer.java
new file mode 100644
index 0000000..2d5fe08
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaDesugaringEventConsumer.java
@@ -0,0 +1,13 @@
+// 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.lambda;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.LambdaClass;
+
+public interface LambdaDesugaringEventConsumer {
+
+ void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
new file mode 100644
index 0000000..54477eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.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.lambda;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+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.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LambdaClass;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.utils.Box;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import org.objectweb.asm.Opcodes;
+
+public class LambdaInstructionDesugaring implements CfInstructionDesugaring {
+
+ private final AppView<?> appView;
+
+ public LambdaInstructionDesugaring(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ if (instruction.isInvokeDynamic()) {
+ return desugarInvokeDynamicInstruction(
+ instruction.asInvokeDynamic(),
+ freshLocalProvider,
+ eventConsumer,
+ context,
+ methodProcessingContext);
+ }
+ return null;
+ }
+
+ private Collection<CfInstruction> desugarInvokeDynamicInstruction(
+ CfInvokeDynamic invoke,
+ FreshLocalProvider freshLocalProvider,
+ LambdaDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ LambdaClass lambdaClass = createLambdaClass(invoke, context, methodProcessingContext);
+ if (lambdaClass == null) {
+ return null;
+ }
+
+ eventConsumer.acceptLambdaClass(lambdaClass, context);
+
+ if (lambdaClass.isStateless()) {
+ return ImmutableList.of(
+ new CfFieldInstruction(
+ Opcodes.GETSTATIC, lambdaClass.lambdaField, lambdaClass.lambdaField));
+ }
+
+ DexTypeList captureTypes = lambdaClass.descriptor.captures;
+ Deque<CfInstruction> replacement = new ArrayDeque<>(3 + captureTypes.size() * 2);
+ replacement.add(new CfNew(lambdaClass.getType()));
+ replacement.add(new CfStackInstruction(Opcode.Dup));
+ captureTypes.forEach(
+ captureType -> {
+ ValueType valueType = ValueType.fromDexType(captureType);
+ int freshLocal = freshLocalProvider.getFreshLocal(valueType.requiredRegisters());
+ replacement.addFirst(new CfStore(valueType, freshLocal));
+ replacement.addLast(new CfLoad(valueType, freshLocal));
+ });
+ replacement.add(new CfInvoke(Opcodes.INVOKESPECIAL, lambdaClass.constructor, false));
+ return replacement;
+ }
+
+ // Creates a lambda class corresponding to the lambda descriptor and context.
+ private LambdaClass createLambdaClass(
+ CfInvokeDynamic invoke,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ LambdaDescriptor descriptor =
+ LambdaDescriptor.tryInfer(invoke.getCallSite(), appView.appInfoForDesugaring(), context);
+ if (descriptor == null) {
+ return null;
+ }
+
+ Box<LambdaClass> box = new Box<>();
+ DexProgramClass clazz =
+ appView
+ .getSyntheticItems()
+ .createClass(
+ SyntheticNaming.SyntheticKind.LAMBDA,
+ methodProcessingContext.createUniqueContext(),
+ appView.dexItemFactory(),
+ builder -> box.set(new LambdaClass(builder, appView, this, context, descriptor)));
+ // Immediately set the actual program class on the lambda.
+ LambdaClass lambdaClass = box.get();
+ lambdaClass.setClass(clazz);
+ return lambdaClass;
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return instruction.isInvokeDynamic()
+ && LambdaDescriptor.tryInfer(
+ instruction.asInvokeDynamic().getCallSite(),
+ appView.appInfoForDesugaring(),
+ context)
+ != null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index 9fcf485..2518219 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -15,6 +15,8 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.utils.ThreadUtils;
@@ -30,7 +32,7 @@
*/
public class D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
- public D8NestBasedAccessDesugaring(AppView<?> appView) {
+ D8NestBasedAccessDesugaring(AppView<?> appView) {
super(appView);
}
@@ -64,30 +66,47 @@
Iterables.addAll(classpathClassesInNests, nest.getClasspathMembers());
});
- NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
+ NestBasedAccessDesugaringEventConsumer eventConsumer =
+ new NestBasedAccessDesugaringEventConsumer() {
+
+ @Override
+ public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
+ @Override
+ public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+
+ @Override
+ public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+ }
+ };
ThreadUtils.processItems(
classpathClassesInNests,
- clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, bridgeConsumer),
+ clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, eventConsumer),
executorService);
}
public void synthesizeBridgesForNestBasedAccessesOnClasspath(
- DexClasspathClass clazz, NestBridgeConsumer bridgeConsumer) {
+ DexClasspathClass clazz, NestBasedAccessDesugaringEventConsumer eventConsumer) {
clazz.forEachClasspathMethod(
method ->
method.registerCodeReferencesForDesugaring(
- new NestBasedAccessDesugaringUseRegistry(method, bridgeConsumer)));
+ new NestBasedAccessDesugaringUseRegistry(method, eventConsumer)));
}
private class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
- private final NestBridgeConsumer bridgeConsumer;
+ private final NestBasedAccessDesugaringEventConsumer eventConsumer;
private final ClasspathMethod context;
NestBasedAccessDesugaringUseRegistry(
- ClasspathMethod context, NestBridgeConsumer bridgeConsumer) {
+ ClasspathMethod context, NestBasedAccessDesugaringEventConsumer eventConsumer) {
super(appView.dexItemFactory());
- this.bridgeConsumer = bridgeConsumer;
+ this.eventConsumer = eventConsumer;
this.context = context;
}
@@ -95,7 +114,7 @@
DexClassAndField field =
reference.lookupMemberOnClass(appView.definitionForHolder(reference));
if (field != null && needsDesugaring(field, context)) {
- ensureFieldAccessBridge(field, isGet, bridgeConsumer);
+ ensureFieldAccessBridge(field, isGet, eventConsumer);
}
}
@@ -106,7 +125,7 @@
DexClassAndMethod method =
reference.lookupMemberOnClass(appView.definitionForHolder(reference));
if (method != null && needsDesugaring(method, context)) {
- ensureMethodBridge(method, bridgeConsumer);
+ ensureMethodBridge(method, eventConsumer);
}
}
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
deleted file mode 100644
index 953801f..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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.nest;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
-
-public class D8NestBridgeConsumer extends NestBridgeConsumer {
-
- private final MethodProcessor methodProcessor;
-
- public D8NestBridgeConsumer(MethodProcessor methodProcessor) {
- this.methodProcessor = methodProcessor;
- }
-
- @Override
- public void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge) {
- methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
- }
-
- @Override
- public void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge) {
- methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
- }
-
- @Override
- public void acceptMethodBridge(ProgramMethod target, ProgramMethod 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 86aba1e..af0a140 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
@@ -10,9 +10,9 @@
import com.android.tools.r8.cf.code.CfFieldInstruction;
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.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.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
@@ -31,13 +31,13 @@
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.desugar.FreshLocalProvider;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -65,11 +65,20 @@
private final DexItemFactory dexItemFactory;
private final Map<DexType, DexType> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
- public NestBasedAccessDesugaring(AppView<?> appView) {
+ NestBasedAccessDesugaring(AppView<?> appView) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
}
+ public static NestBasedAccessDesugaring create(AppView<?> appView) {
+ if (appView.options().shouldDesugarNests()) {
+ return appView.enableWholeProgramOptimizations()
+ ? new NestBasedAccessDesugaring(appView)
+ : new D8NestBasedAccessDesugaring(appView);
+ }
+ return null;
+ }
+
void forEachNest(Consumer<Nest> consumer) {
forEachNest(consumer, emptyConsumer());
}
@@ -133,62 +142,26 @@
&& member.getHolder().getNestHost() == context.getHolder().getNestHost();
}
- public boolean desugar(ProgramMethod method) {
- return desugar(method, null);
- }
-
- public boolean desugar(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
- if (!method.getHolder().isInANest()) {
- return false;
- }
-
- 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, method, bridgeConsumer),
- null);
- if (desugaredInstructions != null) {
- cfCode.setInstructions(desugaredInstructions);
- return true;
- }
- return false;
- }
-
@Override
- public List<CfInstruction> desugarInstruction(
+ public Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
- CfInstructionDesugaringEventConsumer consumer,
- ProgramMethod context) {
- return desugarInstruction(instruction, context, null);
- }
-
- public List<CfInstruction> desugarInstruction(
- CfInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ FreshLocalProvider freshLocalProvider,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
if (instruction.isFieldInstruction()) {
- return desugarFieldInstruction(instruction.asFieldInstruction(), context, bridgeConsumer);
+ return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
}
if (instruction.isInvoke()) {
- return desugarInvokeInstruction(instruction.asInvoke(), context, bridgeConsumer);
+ return desugarInvokeInstruction(instruction.asInvoke(), context, eventConsumer);
}
return null;
}
private List<CfInstruction> desugarFieldInstruction(
- CfFieldInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ CfFieldInstruction instruction,
+ ProgramMethod context,
+ NestBasedAccessDesugaringEventConsumer eventConsumer) {
// Since we only need to desugar accesses to private fields, and all accesses to private
// fields must be accessing the private field directly on its holder, we can lookup the
// field on the holder instead of resolving the field.
@@ -198,13 +171,15 @@
return null;
}
- DexMethod bridge = ensureFieldAccessBridge(field, instruction.isFieldGet(), bridgeConsumer);
+ DexMethod bridge = ensureFieldAccessBridge(field, instruction.isFieldGet(), eventConsumer);
return ImmutableList.of(
new CfInvoke(Opcodes.INVOKESTATIC, bridge, field.getHolder().isInterface()));
}
private List<CfInstruction> desugarInvokeInstruction(
- CfInvoke invoke, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ CfInvoke invoke,
+ ProgramMethod context,
+ NestBasedAccessDesugaringEventConsumer eventConsumer) {
DexMethod invokedMethod = invoke.getMethod();
if (!invokedMethod.getHolderType().isClassType()) {
return null;
@@ -219,7 +194,7 @@
return null;
}
- DexMethod bridge = ensureMethodBridge(target, bridgeConsumer);
+ DexMethod bridge = ensureMethodBridge(target, eventConsumer);
if (target.getDefinition().isInstanceInitializer()) {
assert !invoke.isInterface();
return ImmutableList.of(
@@ -236,9 +211,9 @@
}
DexMethod ensureFieldAccessBridge(
- DexClassAndField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
+ DexClassAndField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
if (field.isProgramField()) {
- return ensureFieldAccessBridge(field.asProgramField(), isGet, bridgeConsumer);
+ return ensureFieldAccessBridge(field.asProgramField(), isGet, eventConsumer);
}
if (field.isClasspathField()) {
return getFieldAccessBridgeReference(field, isGet);
@@ -248,15 +223,19 @@
}
private DexMethod ensureFieldAccessBridge(
- ProgramField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
+ ProgramField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
synchronized (field.getHolder().getMethodCollection()) {
ProgramMethod bridge = field.getHolder().lookupProgramMethod(bridgeReference);
if (bridge == null) {
bridge = DexEncodedMethod.createFieldAccessorBridge(field, isGet, bridgeReference);
bridge.getHolder().addDirectMethod(bridge.getDefinition());
- if (bridgeConsumer != null) {
- bridgeConsumer.acceptFieldBridge(field, bridge, isGet);
+ if (eventConsumer != null) {
+ if (isGet) {
+ eventConsumer.acceptNestFieldGetBridge(field, bridge);
+ } else {
+ eventConsumer.acceptNestFieldPutBridge(field, bridge);
+ }
}
}
return bridge.getReference();
@@ -293,9 +272,10 @@
return dexItemFactory.createString(prefix + field.getName().toString());
}
- DexMethod ensureMethodBridge(DexClassAndMethod method, NestBridgeConsumer bridgeConsumer) {
+ DexMethod ensureMethodBridge(
+ DexClassAndMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
if (method.isProgramMethod()) {
- return ensureMethodBridge(method.asProgramMethod(), bridgeConsumer);
+ return ensureMethodBridge(method.asProgramMethod(), eventConsumer);
}
if (method.isClasspathMethod()) {
return getMethodBridgeReference(method);
@@ -304,7 +284,8 @@
throw reportIncompleteNest(method.asLibraryMethod());
}
- private DexMethod ensureMethodBridge(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
+ private DexMethod ensureMethodBridge(
+ ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
DexMethod bridgeReference = getMethodBridgeReference(method);
synchronized (method.getHolder().getMethodCollection()) {
ProgramMethod bridge = method.getHolder().lookupProgramMethod(bridgeReference);
@@ -317,8 +298,8 @@
: definition.toStaticForwardingBridge(
method.getHolder(), bridgeReference, dexItemFactory);
bridge.getHolder().addDirectMethod(bridge.getDefinition());
- if (bridgeConsumer != null) {
- bridgeConsumer.acceptMethodBridge(method, bridge);
+ if (eventConsumer != null) {
+ eventConsumer.acceptNestMethodBridge(method, bridge);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
new file mode 100644
index 0000000..b5afce1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
@@ -0,0 +1,17 @@
+// 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.nest;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface NestBasedAccessDesugaringEventConsumer {
+
+ void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge);
+
+ void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge);
+
+ void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
deleted file mode 100644
index e6a8a03..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.nest;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
-
-public abstract class NestBridgeConsumer {
-
- public static D8NestBridgeConsumer createForD8(MethodProcessor methodProcessor) {
- return new D8NestBridgeConsumer(methodProcessor);
- }
-
- public abstract void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge);
-
- public abstract void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge);
-
- public abstract void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge);
-
- public final void acceptFieldBridge(ProgramField target, ProgramMethod bridge, boolean isGet) {
- if (isGet) {
- acceptFieldGetBridge(target, bridge);
- } else {
- acceptFieldPutBridge(target, bridge);
- }
- }
-}
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 0713d06..d01470e 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
@@ -39,7 +39,6 @@
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.Binop;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
@@ -3634,9 +3633,7 @@
private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
TypeElement typeLattice = TypeElement.stringClassType(appView, definitelyNotNull());
Value value = code.createValue(typeLattice);
- ThrowingInfo throwingInfo =
- options.isGeneratingClassFiles() ? ThrowingInfo.NO_THROW : ThrowingInfo.CAN_THROW;
- iterator.add(new ConstString(value, dexItemFactory.createString(s), throwingInfo));
+ iterator.add(new ConstString(value, dexItemFactory.createString(s)));
return value;
}
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 61b0f30..687a9bf 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
@@ -22,6 +22,12 @@
public class UtilityMethodsForCodeOptimizations {
+ public interface MethodSynthesizerConsumer {
+
+ UtilityMethodForCodeOptimizations synthesizeMethod(
+ AppView<?> appView, MethodProcessingContext methodProcessingContext);
+ }
+
public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod(
AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
@@ -77,6 +83,32 @@
options, method);
}
+ public static UtilityMethodForCodeOptimizations synthesizeThrowIllegalAccessErrorMethod(
+ AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ InternalOptions options = appView.options();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexProto proto = dexItemFactory.createProto(dexItemFactory.illegalAccessErrorType);
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ ProgramMethod syntheticMethod =
+ syntheticItems.createMethod(
+ SyntheticNaming.SyntheticKind.THROW_IAE,
+ methodProcessingContext.createUniqueContext(),
+ dexItemFactory,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setCode(method -> getThrowIllegalAccessErrorCodeTemplate(method, options))
+ .setProto(proto));
+ return new UtilityMethodForCodeOptimizations(syntheticMethod);
+ }
+
+ private static CfCode getThrowIllegalAccessErrorCodeTemplate(
+ DexMethod method, InternalOptions options) {
+ return CfUtilityMethodsForCodeOptimizations
+ .CfUtilityMethodsForCodeOptimizationsTemplates_throwIllegalAccessError(options, method);
+ }
+
public static UtilityMethodForCodeOptimizations synthesizeThrowIncompatibleClassChangeErrorMethod(
AppView<?> appView, MethodProcessingContext methodProcessingContext) {
InternalOptions options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 88eedce..7ad0bdb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.ir.analysis.value.SingleStringValue;
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
@@ -120,11 +119,7 @@
if (isNameInvoke) {
Value newValue =
code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
- iterator.replaceCurrentInstruction(
- new ConstString(
- newValue,
- nameValue.getDexString(),
- ThrowingInfo.defaultForConstString(appView.options())));
+ iterator.replaceCurrentInstruction(new ConstString(newValue, nameValue.getDexString()));
newValue.addAffectedValuesTo(affectedValues);
continue;
}
@@ -155,11 +150,7 @@
Value newValue =
code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
- iterator.replaceCurrentInstruction(
- new ConstString(
- newValue,
- nameValue.getDexString(),
- ThrowingInfo.defaultForConstString(appView.options())));
+ iterator.replaceCurrentInstruction(new ConstString(newValue, nameValue.getDexString()));
newValue.addAffectedValuesTo(affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 3864fb8..5fdaf51 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -593,8 +593,7 @@
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()),
invoke.getLocalInfo());
- it.replaceCurrentInstruction(
- new ConstString(dummy, factory.createString(DUMMY), throwingInfo));
+ it.replaceCurrentInstruction(new ConstString(dummy, factory.createString(DUMMY)));
} else {
it.removeOrReplaceByDebugLocalRead();
}
@@ -614,8 +613,7 @@
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
affectedValues.addAll(outValue.affectedValues());
- it.replaceCurrentInstruction(
- new ConstString(stringValue, factory.createString(element), throwingInfo));
+ it.replaceCurrentInstruction(new ConstString(stringValue, factory.createString(element)));
simplifiedBuilders.add(builder);
numberOfBuildersSimplified++;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 3c6c003..48f2199 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.ir.analysis.escape.EscapeAnalysisConfiguration;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
@@ -49,7 +48,6 @@
private final AppView<?> appView;
private final DexItemFactory factory;
- private final ThrowingInfo throwingInfo;
private int numberOfSimplifiedOperations = 0;
private final Object2IntMap<ClassNameMapping> numberOfComputedNames;
@@ -61,7 +59,6 @@
public StringOptimizer(AppView<?> appView) {
this.appView = appView;
this.factory = appView.dexItemFactory();
- this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
if (Log.ENABLED && Log.isLoggingEnabledFor(StringOptimizer.class)) {
numberOfComputedNames = new Object2IntArrayMap<>();
numberOfDeferredComputationOfNames = new Object2IntArrayMap<>();
@@ -186,8 +183,7 @@
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
affectedValues.addAll(invoke.outValue().affectedValues());
- it.replaceCurrentInstruction(
- new ConstString(stringValue, factory.createString(sub), throwingInfo));
+ it.replaceCurrentInstruction(new ConstString(stringValue, factory.createString(sub)));
numberOfSimplifiedOperations++;
continue;
}
@@ -203,7 +199,7 @@
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
affectedValues.addAll(invoke.outValue().affectedValues());
- it.replaceCurrentInstruction(new ConstString(newOutValue, resultString, throwingInfo));
+ it.replaceCurrentInstruction(new ConstString(newOutValue, resultString));
numberOfSimplifiedOperations++;
continue;
}
@@ -379,10 +375,7 @@
if (mayBeRenamed) {
deferred =
new DexItemBasedConstString(
- invoke.outValue(),
- baseType,
- ClassNameComputationInfo.create(NAME, arrayDepth),
- throwingInfo);
+ invoke.outValue(), baseType, ClassNameComputationInfo.create(NAME, arrayDepth));
logDeferredNameComputation(NAME);
} else {
name = NAME.map(descriptor, holder, factory, arrayDepth);
@@ -408,8 +401,7 @@
new DexItemBasedConstString(
invoke.outValue(),
baseType,
- ClassNameComputationInfo.create(CANONICAL_NAME, arrayDepth),
- throwingInfo);
+ ClassNameComputationInfo.create(CANONICAL_NAME, arrayDepth));
logDeferredNameComputation(CANONICAL_NAME);
} else {
name = CANONICAL_NAME.map(descriptor, holder, factory, arrayDepth);
@@ -431,8 +423,7 @@
new DexItemBasedConstString(
invoke.outValue(),
baseType,
- ClassNameComputationInfo.create(SIMPLE_NAME, arrayDepth),
- throwingInfo);
+ ClassNameComputationInfo.create(SIMPLE_NAME, arrayDepth));
logDeferredNameComputation(SIMPLE_NAME);
} else {
name = SIMPLE_NAME.map(descriptor, holder, factory, arrayDepth);
@@ -445,7 +436,7 @@
Value stringValue =
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
- ConstString constString = new ConstString(stringValue, name, throwingInfo);
+ ConstString constString = new ConstString(stringValue, name);
it.replaceCurrentInstruction(constString);
logHistogramOfNames(name);
} else if (deferred != null) {
@@ -531,8 +522,7 @@
Value nullStringValue =
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
- ConstString nullString =
- new ConstString(nullStringValue, factory.createString("null"), throwingInfo);
+ ConstString nullString = new ConstString(nullStringValue, factory.createString("null"));
it.replaceCurrentInstruction(nullString);
numberOfSimplifiedConversions++;
} else if (inType.nullability().isDefinitelyNotNull()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
index 831a0b3..33f1c04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -33,6 +33,7 @@
public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
factory.createSynthesizedType("Ljava/lang/ClassCastException;");
+ factory.createSynthesizedType("Ljava/lang/IllegalAccessError;");
factory.createSynthesizedType("Ljava/lang/IncompatibleClassChangeError;");
factory.createSynthesizedType("Ljava/lang/NoSuchMethodError;");
}
@@ -75,6 +76,29 @@
ImmutableList.of());
}
+ public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_throwIllegalAccessError(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 0,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/lang/IllegalAccessError;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/IllegalAccessError;"),
+ options.itemFactory.createProto(options.itemFactory.voidType),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow()),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode
CfUtilityMethodsForCodeOptimizationsTemplates_throwIncompatibleClassChangeError(
InternalOptions options, DexMethod method) {
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 1c6adbb..3a25d00 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -265,9 +265,7 @@
if (!method.hasClassFileVersion()) {
// In this case bridges have been introduced for the Cf back-end,
// which do not have class file version.
- assert options.testing.enableForceNestBasedAccessDesugaringForTest
- || options.isDesugaredLibraryCompilation()
- || options.cfToCfDesugar
+ assert options.isDesugaredLibraryCompilation() || options.cfToCfDesugar
: "Expected class file version for " + method.method.toSourceString();
assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(
options.classFileVersionAfterDesugaring(InternalOptions.SUPPORTED_CF_VERSION));
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 44d6ede..fdb9472 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -30,8 +30,7 @@
public class KotlinMetadataRewriter {
// Due to a bug with nested classes and the lookup of RequirementVersion, we bump all metadata
- // versions to 1.4 if compiled with kotlin 1.3 (1.1.16). For more information, see b/161885097 for
- // more information.
+ // versions to 1.4 if compiled with kotlin 1.3 (1.1.16). For more information, see b/161885097.
private static final int[] METADATA_VERSION_1_4 = new int[] {1, 4, 0};
private static final class WriteMetadataFieldInfo {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 2168235..2d9a19d 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.FieldInstruction;
@@ -54,12 +53,10 @@
private final AppView<AppInfoWithLiveness> appView;
private final Object2BooleanMap<DexReference> identifierNameStrings;
- private final ThrowingInfo throwingInfo;
public IdentifierNameStringMarker(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
this.identifierNameStrings = appView.appInfo().identifierNameStrings;
- this.throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
}
public void decoupleIdentifierNameStringsInFields(
@@ -161,8 +158,7 @@
// Prepare $decoupled just before $fieldPut
Value newIn = code.createValue(in.getType(), in.getLocalInfo());
DexItemBasedConstString decoupled =
- new DexItemBasedConstString(
- newIn, itemBasedString, ClassNameComputationInfo.none(), throwingInfo);
+ new DexItemBasedConstString(newIn, itemBasedString, ClassNameComputationInfo.none());
decoupled.setPosition(fieldPut.getPosition());
// If the current block has catch handler, split into two blocks.
// Because const-string we're about to add is also a throwing instr, we need to split
@@ -227,10 +223,7 @@
Value newIn = code.createValue(in.getType(), in.getLocalInfo());
DexItemBasedConstString decoupled =
new DexItemBasedConstString(
- newIn,
- identifierLookupResult.getReference(),
- ClassNameComputationInfo.none(),
- throwingInfo);
+ newIn, identifierLookupResult.getReference(), ClassNameComputationInfo.none());
changes[identifierPosition] = newIn;
if (in.numberOfAllUsers() == 1) {
@@ -295,8 +288,7 @@
// Prepare $decoupled just before $invoke
Value newIn = code.createValue(in.getType(), in.getLocalInfo());
DexItemBasedConstString decoupled =
- new DexItemBasedConstString(
- newIn, itemBasedString, ClassNameComputationInfo.none(), throwingInfo);
+ new DexItemBasedConstString(newIn, itemBasedString, ClassNameComputationInfo.none());
decoupled.setPosition(invoke.getPosition());
changes[i] = newIn;
// If the current block has catch handler, split into two blocks.
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 191dc0a..ca181fd 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -72,7 +72,7 @@
}
private Node createNode(DexDefinition definition) {
- Node node = new Node();
+ Node node = new Node(definition);
nodes.put(definition, node);
return node;
}
@@ -172,7 +172,12 @@
public Iterable<DexProgramClass> computeClassesToRepackage() {
WorkList<Node> worklist = WorkList.newIdentityWorkList(pinnedNodes);
while (worklist.hasNext()) {
- worklist.addIfNotSeen(worklist.next().getNeighbors());
+ Node pinnedNode = worklist.next();
+ for (Node neighbor : pinnedNode.getNeighbors()) {
+ // Mark all the immediate neighbors as ineligible for repackaging and continue the tracing
+ // from the neighbors.
+ worklist.addIfNotSeen(neighbor);
+ }
}
Set<Node> pinnedNodes = worklist.getSeenSet();
List<DexProgramClass> classesToRepackage = new ArrayList<>();
@@ -186,8 +191,13 @@
static class Node {
+ private final DexDefinition definitionForDebugging;
private final Set<Node> neighbors = Sets.newConcurrentHashSet();
+ Node(DexDefinition definitionForDebugging) {
+ this.definitionForDebugging = definitionForDebugging;
+ }
+
public void addNeighbor(Node neighbor) {
neighbors.add(neighbor);
neighbor.neighbors.add(this);
@@ -196,5 +206,10 @@
public Set<Node> getNeighbors() {
return neighbors;
}
+
+ @Override
+ public String toString() {
+ return "Node(" + definitionForDebugging.getReference().toSourceString() + ")";
+ }
}
}
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 e8fed84..2f7edb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -53,7 +53,6 @@
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
@@ -91,10 +90,7 @@
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.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
@@ -126,7 +122,6 @@
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
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.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
@@ -308,7 +303,7 @@
* are required so that invokes can find the method. If a method is only a target but not live,
* its implementation may be removed and it may be marked abstract.
*/
- private final SetWithReason<DexEncodedMethod> targetedMethods;
+ private final LiveMethodsSet targetedMethods;
/** Set of methods that have invalid resolutions or lookups. */
private final Set<DexMethod> failedMethodResolutionTargets;
@@ -370,7 +365,8 @@
* A map from annotation classes to annotations that need to be processed should the classes ever
* become live.
*/
- private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
+ private final Map<DexType, Map<DexAnnotation, List<ProgramDefinition>>> deferredAnnotations =
+ new IdentityHashMap<>();
/** Map of active if rules to speed up aapt2 generated keep rules. */
private Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
@@ -385,21 +381,7 @@
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 TwrCloseResourceRewriter twrCloseResourceRewriter;
@@ -427,7 +409,7 @@
this.mode = mode;
this.options = options;
this.useRegistryFactory = createUseRegistryFactory();
- this.workList = EnqueuerWorklist.createWorklist();
+ this.workList = EnqueuerWorklist.createWorklist(this);
this.proguardCompatibilityActionsBuilder =
mode.isInitialTreeShaking() && options.forceProguardCompatibility
? ProguardCompatibilityActions.builder()
@@ -441,7 +423,7 @@
shrinker -> registerAnalysis(shrinker.createEnqueuerAnalysis()));
}
- targetedMethods = new SetWithReason<>(graphReporter::registerMethod);
+ targetedMethods = new LiveMethodsSet(graphReporter::registerMethod);
// This set is only populated in edge cases due to multiple default interface methods.
// The set is generally expected to be empty and in the unlikely chance it is not, it will
// likely contain two methods. Thus the default capacity of 2.
@@ -451,9 +433,8 @@
liveFields = new LiveFieldsSet(graphReporter::registerField);
desugaring =
mode.isInitialTreeShaking()
- ? new NonEmptyCfInstructionDesugaringCollection(appView)
+ ? CfInstructionDesugaringCollection.create(appView)
: CfInstructionDesugaringCollection.empty();
- lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
backportRewriter =
options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
twrCloseResourceRewriter =
@@ -545,6 +526,14 @@
deadProtoTypeCandidates.add(clazz);
}
+ public boolean addLiveMethod(ProgramMethod method, KeepReason reason) {
+ return liveMethods.add(method, reason);
+ }
+
+ public boolean addTargetedMethod(ProgramMethod method, KeepReason reason) {
+ return targetedMethods.add(method, reason);
+ }
+
private void recordCompilerSynthesizedTypeReference(DexType type) {
DexClass clazz = appInfo().definitionFor(type);
if (clazz == null) {
@@ -785,7 +774,7 @@
graphReporter.reportCompatKeepDefaultInitializer(defaultInitializer));
}
if (clazz.isExternalizable(appView)) {
- enqueueMarkMethodLiveAction(defaultInitializer, defaultInitializer, witness);
+ workList.enqueueMarkMethodLiveAction(defaultInitializer, defaultInitializer, witness);
}
}
}
@@ -867,21 +856,10 @@
clazz = superClass;
}
if (clazz.hasDefaultInitializer()) {
- enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), clazz, reason);
+ workList.enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), clazz, reason);
}
}
- // Utility to avoid adding to the worklist if already live.
- private boolean enqueueMarkMethodLiveAction(
- ProgramMethod method, ProgramDefinition context, KeepReason reason) {
- if (liveMethods.add(method, reason)) {
- assert !method.getDefinition().getOptimizationInfo().forceInline();
- workList.enqueueMarkMethodLiveAction(method, context);
- return true;
- }
- return false;
- }
-
private void compatEnqueueHolderIfDependentNonStaticMember(
DexProgramClass holder, Set<ProguardKeepRuleBase> compatRules) {
if (!forceProguardCompatibility || compatRules == null) {
@@ -980,16 +958,11 @@
return;
}
- if (lambdaRewriter != null) {
- 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);
- transitionMethodsForInstantiatedLambda(descriptor);
- callSites.computeIfAbsent(callSite, ignore -> ProgramMethodSet.create()).add(context);
- }
+ assert options.desugarState.isOff();
+
+ markLambdaAsInstantiated(descriptor, context);
+ transitionMethodsForInstantiatedLambda(descriptor);
+ callSites.computeIfAbsent(callSite, ignore -> ProgramMethodSet.create()).add(context);
// For call sites representing a lambda, we link the targeted method
// or field as if it were referenced from the current method.
@@ -1441,8 +1414,8 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Must trace the types from the field reference even if it does not exist.
+ traceFieldReference(fieldReference, resolutionResult, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1473,7 +1446,7 @@
markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
- workList.enqueueMarkInstanceFieldAsReachableAction(
+ workList.enqueueMarkFieldAsReachableAction(
field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
}
@@ -1493,8 +1466,8 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Must trace the types from the field reference even if it does not exist.
+ traceFieldReference(fieldReference, resolutionResult, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1526,7 +1499,7 @@
}
KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
- workList.enqueueMarkInstanceFieldAsReachableAction(field, currentMethod, reason);
+ workList.enqueueMarkFieldAsReachableAction(field, currentMethod, reason);
}
void traceStaticFieldRead(DexField field, ProgramMethod currentMethod) {
@@ -1545,8 +1518,8 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Must trace the types from the field reference even if it does not exist.
+ traceFieldReference(fieldReference, resolutionResult, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
// Record field reference for generated extension registry shrinking.
@@ -1593,7 +1566,7 @@
markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
- markStaticFieldAsLive(field, currentMethod);
+ markFieldAsLive(field, currentMethod);
}
void traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1612,8 +1585,8 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Must trace the types from the field reference even if it does not exist.
+ traceFieldReference(fieldReference, resolutionResult, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1658,7 +1631,7 @@
markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
- markStaticFieldAsLive(field, currentMethod);
+ markFieldAsLive(field, currentMethod);
}
private DexMethod getInvokeSuperTarget(DexMethod method, ProgramMethod currentMethod) {
@@ -1787,8 +1760,8 @@
if (enclosingMethod != null) {
recordMethodReference(enclosingMethod, clazz, missingClassConsumer);
} else {
- recordTypeReference(
- enclosingMethodAttribute.getEnclosingClass(), clazz, missingClassConsumer);
+ DexType enclosingClass = enclosingMethodAttribute.getEnclosingClass();
+ recordTypeReference(enclosingClass, clazz, missingClassConsumer);
}
}
@@ -1838,14 +1811,19 @@
enqueueFirstNonSerializableClassInitializer(clazz, reason);
}
- processAnnotations(clazz, clazz);
+ processAnnotations(clazz);
// If this type has deferred annotations, we have to process those now, too.
if (clazz.isAnnotation()) {
- Set<DexAnnotation> annotations = deferredAnnotations.remove(clazz.type);
- if (annotations != null && !annotations.isEmpty()) {
- assert annotations.stream().allMatch(a -> a.annotation.type == clazz.type);
- annotations.forEach(annotation -> processAnnotation(clazz, clazz, annotation));
+ Map<DexAnnotation, List<ProgramDefinition>> annotations =
+ deferredAnnotations.remove(clazz.getType());
+ if (annotations != null) {
+ assert annotations.keySet().stream()
+ .allMatch(a -> a.getAnnotationType() == clazz.getType());
+ annotations.forEach(
+ (annotation, annotatedItems) ->
+ annotatedItems.forEach(
+ annotatedItem -> processAnnotation(annotatedItem, annotation)));
}
}
@@ -1938,35 +1916,33 @@
enqueueKeepRuleInstantiatedType(holder, reasons, instanceInitializer.getDefinition());
}
- private void processAnnotations(DexProgramClass holder, ProgramDefinition annotatedItem) {
- processAnnotations(holder, annotatedItem, annotatedItem.getDefinition().annotations());
+ private void processAnnotations(ProgramDefinition annotatedItem) {
+ processAnnotations(annotatedItem, annotatedItem.getDefinition().annotations());
}
- private void processAnnotations(
- DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotationSet annotations) {
- processAnnotations(holder, annotatedItem, annotations.annotations);
+ private void processAnnotations(ProgramDefinition annotatedItem, DexAnnotationSet annotations) {
+ processAnnotations(annotatedItem, annotations.annotations);
}
- private void processAnnotations(
- DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotation[] annotations) {
+ private void processAnnotations(ProgramDefinition annotatedItem, DexAnnotation[] annotations) {
for (DexAnnotation annotation : annotations) {
- processAnnotation(holder, annotatedItem, annotation);
+ processAnnotation(annotatedItem, annotation);
}
}
- private void processAnnotation(
- DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotation annotation) {
- assert annotatedItem == holder || annotatedItem.asProgramMember().getHolder() == holder;
- assert !holder.isDexClass() || holder.asDexClass().isProgramClass();
+ private void processAnnotation(ProgramDefinition annotatedItem, DexAnnotation annotation) {
DexType type = annotation.getAnnotationType();
- recordTypeReference(type, annotatedItem);
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = definitionFor(type, annotatedItem);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
if (!shouldKeepAnnotation(appView, annotatedItem.getDefinition(), annotation, isLive)) {
// Remember this annotation for later.
if (!annotationTypeIsLibraryClass) {
- deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
+ Map<DexAnnotation, List<ProgramDefinition>> deferredAnnotationsForAnnotationType =
+ deferredAnnotations.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
+ deferredAnnotationsForAnnotationType
+ .computeIfAbsent(annotation, ignore -> new ArrayList<>())
+ .add(annotatedItem);
}
return;
}
@@ -2171,7 +2147,7 @@
// Method). In a class, that would lead to a verification error.
if (encodedMethod.isNonPrivateVirtualMethod()
&& virtualMethodsTargetedByInvokeDirect.add(encodedMethod.method)) {
- enqueueMarkMethodLiveAction(method, context, reason);
+ workList.enqueueMarkMethodLiveAction(method, context, reason);
}
}
@@ -2277,30 +2253,6 @@
missingClassesBuilder.legacyAddNewMissingClass(clazz);
}
- private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
- DexEncodedMethod definition = method.getDefinition();
- DexProgramClass holder = method.getHolder();
- if (!targetedMethods.add(definition, reason)) {
- // Already targeted.
- return;
- }
- markReferencedTypesAsLive(method);
- processAnnotations(holder, method);
- definition.parameterAnnotationsList.forEachAnnotation(
- annotation -> processAnnotation(holder, method, annotation));
-
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Method `%s` is targeted.", method);
- }
- if (forceProguardCompatibility) {
- // Keep targeted default methods in compatibility mode. The tree pruner will otherwise make
- // these methods abstract, whereas Proguard does not (seem to) touch their code.
- if (!definition.isAbstract() && holder.isInterface()) {
- markMethodAsLiveWithCompatRule(method);
- }
- }
- }
-
/**
* Adds the class to the set of instantiated classes and marks its fields and methods live
* depending on the currently seen invokes and field reads.
@@ -2611,7 +2563,7 @@
// TODO(b/120959039): Should the reason this field is reachable come from the set?
KeepReason reason = KeepReason.reachableFromLiveType(clazz.type);
for (ProgramField field : reachableFields) {
- markInstanceFieldAsLive(field, clazz, reason);
+ markFieldAsLive(field, clazz, reason);
}
}
clazz = getProgramClassOrNull(clazz.superType, clazz);
@@ -2655,94 +2607,95 @@
}
}
- private void markFieldAsTargeted(ProgramField field) {
- markTypeAsLive(field.getHolder(), field);
- markTypeAsLive(field.getType(), field);
+ private void markFieldAsLive(ProgramField field, ProgramMethod context) {
+ markFieldAsLive(field, context, KeepReason.fieldReferencedIn(context));
}
- private void markFieldAsTargeted(DexField field, ProgramMethod context) {
+ private void markFieldAsLive(ProgramField field, ProgramDefinition context, KeepReason reason) {
+ // This field might be an instance field reachable from a static context, e.g. a getStatic that
+ // resolves to an instance field. We have to keep the instance field nonetheless, as otherwise
+ // we might unmask a shadowed static field and hence change semantics.
+ if (!liveFields.add(field, reason)) {
+ // Already live.
+ return;
+ }
+
+ // Mark the field as targeted.
+ if (field.getAccessFlags().isStatic()) {
+ traceFieldDefinition(field);
+ markDirectAndIndirectClassInitializersAsLive(field.getHolder());
+ } else if (!reachableInstanceFields
+ .getOrDefault(field.getHolder(), ProgramFieldSet.empty())
+ .contains(field)) {
+ traceFieldDefinition(field);
+ }
+
+ // Add all dependent members to the workqueue.
+ enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
+
+ checkMemberForSoftPinning(field);
+
+ // Notify analyses.
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
+ }
+
+ // Package protected due to entry point from worklist.
+ void markFieldAsReachable(ProgramField field, ProgramDefinition context, KeepReason reason) {
+ // We might have a instance field access that is dispatched to a static field. In such case,
+ // we have to keep the static field, so that the dispatch fails at runtime in the same way that
+ // it did before. We have to keep the field even if the receiver has no live inhabitants, as
+ // field resolution happens before the receiver is inspected.
+ if (field.getDefinition().isStatic()
+ || objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
+ field.getHolder())) {
+ markFieldAsLive(field, context, reason);
+ }
+
+ if (liveFields.contains(field)
+ || !reachableInstanceFields
+ .computeIfAbsent(field.getHolder(), ignore -> ProgramFieldSet.create())
+ .add(field)) {
+ // Already reachable.
+ graphReporter.registerField(field.getDefinition(), reason);
+ return;
+ }
+
+ traceFieldDefinition(field);
+ }
+
+ private void traceFieldDefinition(ProgramField field) {
+ markTypeAsLive(field.getHolder(), field);
+ markTypeAsLive(field.getType(), field);
+ processAnnotations(field);
+ }
+
+ private void traceFieldReference(
+ DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+ assert resolutionResult.isFailedOrUnknownResolution();
markTypeAsLive(field.getHolderType(), context);
markTypeAsLive(field.getType(), context);
}
- private void markStaticFieldAsLive(ProgramField field, ProgramMethod context) {
- markStaticFieldAsLive(field, context, KeepReason.fieldReferencedIn(context));
- }
-
- private void markStaticFieldAsLive(
- ProgramField field, ProgramDefinition context, KeepReason reason) {
- // Mark the field type and holder live here, so that they exist at runtime.
- markFieldAsTargeted(field);
-
- markDirectAndIndirectClassInitializersAsLive(field.getHolder());
-
- // This field might be an instance field reachable from a static context, e.g. a getStatic that
- // resolves to an instance field. We have to keep the instance field nonetheless, as otherwise
- // we might unmask a shadowed static field and hence change semantics.
- if (field.getDefinition().isStatic()) {
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Adding static field `%s` to live set.", field);
- }
- } else {
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Adding instance field `%s` to live set (static context).", field);
- }
- }
- processAnnotations(field.getHolder(), field);
- liveFields.add(field, reason);
-
- // Add all dependent members to the workqueue.
- enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
-
- checkMemberForSoftPinning(field);
-
- // Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
- }
-
- private void markInstanceFieldAsLive(
- ProgramField field, ProgramDefinition context, KeepReason reason) {
- markFieldAsTargeted(field);
-
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Adding instance field `%s` to live set.", field);
- }
- processAnnotations(field.getHolder(), field);
- liveFields.add(field, reason);
-
- // Add all dependent members to the workqueue.
- enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
-
- checkMemberForSoftPinning(field);
-
- // Notify analyses.
- analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
- }
-
private void markDirectStaticOrConstructorMethodAsLive(ProgramMethod method, KeepReason reason) {
- if (!enqueueMarkMethodLiveAction(method, method, reason)) {
- // Already marked live.
- return;
- }
- // Should already have marked the type live previously.
- assert method.getDefinition().isClassInitializer() || verifyMethodIsTargeted(method);
- assert verifyTypeIsLive(method.getHolder());
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Method `%s` has become live due to direct invoke", method);
+ if (workList.enqueueMarkMethodLiveAction(method, method, reason)) {
+ assert workList.enqueueAssertAction(
+ () -> {
+ // Should have marked the holder type live.
+ assert method.getDefinition().isClassInitializer() || verifyMethodIsTargeted(method);
+ assert verifyTypeIsLive(method.getHolder());
+ });
+ } else {
+ assert method.getDefinition().isClassInitializer() || verifyMethodIsTargeted(method);
+ assert workList.enqueueAssertAction(() -> verifyTypeIsLive(method.getHolder()));
}
}
private void markVirtualMethodAsLive(ProgramMethod method, KeepReason reason) {
- assert method != null;
// Only explicit keep rules or reflective use should make abstract methods live.
assert !method.getDefinition().isAbstract()
|| reason.isDueToKeepRule()
|| reason.isDueToReflectiveUse();
- if (enqueueMarkMethodLiveAction(method, method, reason)) {
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method);
- }
- }
+ workList.enqueueMarkMethodLiveAction(method, method, reason);
}
public boolean isFieldReferenced(DexEncodedField field) {
@@ -2795,6 +2748,10 @@
return targetedMethods.contains(method);
}
+ public boolean isMethodTargeted(ProgramMethod method) {
+ return isMethodTargeted(method.getDefinition());
+ }
+
public boolean isTypeLive(DexClass clazz) {
return clazz.isProgramClass()
? isTypeLive(clazz.asProgramClass())
@@ -2814,32 +2771,6 @@
liveTypes.items.forEach(consumer);
}
- // Package protected due to entry point from worklist.
- void markInstanceFieldAsReachable(
- ProgramField field, ProgramDefinition context, KeepReason reason) {
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
- }
-
- // We might have a instance field access that is dispatched to a static field. In such case,
- // we have to keep the static field, so that the dispatch fails at runtime in the same way that
- // it did before. We have to keep the field even if the receiver has no live inhabitants, as
- // field resolution happens before the receiver is inspected.
- if (field.getDefinition().isStatic()) {
- markStaticFieldAsLive(field, context, reason);
- } else if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
- field.getHolder())) {
- markInstanceFieldAsLive(field, context, reason);
- } else {
- markFieldAsTargeted(field);
-
- // Add the field to the reachable set if the type later becomes instantiated.
- reachableInstanceFields
- .computeIfAbsent(field.getHolder(), ignore -> ProgramFieldSet.create())
- .add(field);
- }
- }
-
private void markVirtualMethodAsReachable(
DexMethod method, boolean interfaceInvoke, ProgramDefinition context, KeepReason reason) {
if (method.holder.isArrayType()) {
@@ -2939,7 +2870,7 @@
LookupLambdaTarget target, Function<ProgramMethod, KeepReasonWitness> reason) {
ProgramMethod implementationMethod = target.getImplementationMethod().asProgramMethod();
if (implementationMethod != null) {
- enqueueMarkMethodLiveAction(
+ workList.enqueueMarkMethodLiveAction(
implementationMethod, implementationMethod, reason.apply(implementationMethod));
}
}
@@ -3051,11 +2982,8 @@
return builder.build(previousMainDexInfo);
}
- public AppInfoWithLiveness traceApplication(
- RootSet rootSet,
- ExecutorService executorService,
- Timing timing)
- throws ExecutionException {
+ public EnqueuerResult traceApplication(
+ RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
this.rootSet = rootSet;
// Translate the result of root-set computation into enqueuer actions.
if (appView.options().getProguardConfiguration() != null
@@ -3119,15 +3047,7 @@
// no AppInfo is returned.
return null;
}
- AppInfoWithLiveness appInfoWithLiveness = createAppInfo(appInfo);
- if (options.testing.enqueuerInspector != null) {
- options.testing.enqueuerInspector.accept(appInfoWithLiveness, mode);
- }
- return appInfoWithLiveness;
- }
-
- public NestedGraphLens buildGraphLens() {
- return lambdaRewriter != null ? lambdaRewriter.fixup() : null;
+ return createEnqueuerResult(appInfo);
}
private void keepClassWithRules(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
@@ -3174,12 +3094,6 @@
List<ProgramMethod> desugaredMethods = new LinkedList<>();
- Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations =
- new IdentityHashMap<>();
-
- ProgramMethodMap<Set<DexField>> syntheticStaticFieldReadsByContext =
- ProgramMethodMap.createLinked();
-
Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>();
Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
@@ -3188,9 +3102,6 @@
List<Pair<ProgramMethod, Consumer<KeepMethodInfo.Joiner>>> liveMethodsWithKeepActions =
new ArrayList<>();
- // 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;
}
@@ -3203,18 +3114,12 @@
boolean isEmpty() {
boolean empty =
desugaredMethods.isEmpty()
- && syntheticInstantiations.isEmpty()
&& liveMethods.isEmpty()
&& syntheticClasspathClasses.isEmpty();
- assert !empty || (liveMethodsWithKeepActions.isEmpty() && mainDexTypes.isEmpty());
+ assert !empty || liveMethodsWithKeepActions.isEmpty();
return empty;
}
- void addInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {
- assert !syntheticInstantiations.containsKey(clazz.type);
- syntheticInstantiations.put(clazz.type, new Pair<>(clazz, context));
- }
-
void addClasspathClass(DexClasspathClass clazz) {
DexClasspathClass old = syntheticClasspathClasses.put(clazz.type, clazz);
assert old == null;
@@ -3237,11 +3142,6 @@
appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
}
- void amendMainDexClasses(MainDexInfo mainDexInfo) {
- assert !isEmpty();
- mainDexTypes.forEach(mainDexInfo::addSyntheticClass);
- }
-
void enqueueWorkItems(Enqueuer enqueuer) {
assert !isEmpty();
assert enqueuer.mode.isInitialTreeShaking();
@@ -3255,45 +3155,13 @@
liveMethodsWithKeepActions.forEach(
item -> enqueuer.keepInfo.joinMethod(item.getFirst(), item.getSecond()));
- for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
- syntheticInstantiations.values()) {
- enqueuer.workList.enqueueMarkInstantiatedAction(
- clazzAndContext.getFirst(),
- clazzAndContext.getSecond(),
- InstantiationReason.SYNTHESIZED_CLASS,
- fakeReason);
- }
- syntheticStaticFieldReadsByContext.forEach(
- (context, fields) ->
- fields.forEach(
- field -> enqueuer.workList.enqueueTraceStaticFieldRead(field, context)));
for (ProgramMethod liveMethod : liveMethods.values()) {
assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition());
enqueuer.markMethodAsTargeted(liveMethod, fakeReason);
- enqueuer.enqueueMarkMethodLiveAction(liveMethod, liveMethod, fakeReason);
+ enqueuer.workList.enqueueMarkMethodLiveAction(liveMethod, liveMethod, fakeReason);
}
enqueuer.liveNonProgramTypes.addAll(syntheticClasspathClasses.values());
}
-
- void registerStatelessLambdaInstanceFieldReads(
- ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites) {
- lambdaCallSites.forEach(this::registerStatelessLambdaInstanceFieldReads);
- }
-
- private void registerStatelessLambdaInstanceFieldReads(
- ProgramMethod context, Map<DexCallSite, LambdaClass> callSites) {
- Set<DexField> syntheticStaticFieldReadsInContext = null;
- for (LambdaClass lambdaClass : callSites.values()) {
- if (lambdaClass.isStateless()) {
- if (syntheticStaticFieldReadsInContext == null) {
- syntheticStaticFieldReadsInContext =
- syntheticStaticFieldReadsByContext.computeIfAbsent(
- context, ignore -> Sets.newLinkedHashSet());
- }
- syntheticStaticFieldReadsInContext.add(lambdaClass.lambdaField);
- }
- }
- }
}
private void synthesize() throws ExecutionException {
@@ -3306,7 +3174,6 @@
SyntheticAdditions additions = new SyntheticAdditions(appView.createProcessorContext());
desugar(additions);
synthesizeInterfaceMethodBridges(additions);
- synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
synthesizeBackports(additions);
synthesizeTwrCloseResource(additions);
@@ -3322,7 +3189,6 @@
additions.amendApplication(appBuilder);
return appBuilder.build();
});
- additions.amendMainDexClasses(appInfo.getMainDexInfo());
appView.setAppInfo(appInfo);
subtypingInfo = new SubtypingInfo(appView);
@@ -3332,13 +3198,17 @@
}
private void desugar(SyntheticAdditions additions) throws ExecutionException {
+ if (pendingDesugaring.isEmpty()) {
+ return;
+ }
R8CfInstructionDesugaringEventConsumer desugaringEventConsumer =
- CfInstructionDesugaringEventConsumer.createForR8();
+ CfInstructionDesugaringEventConsumer.createForR8(appView);
ThreadUtils.processItems(
pendingDesugaring,
- method -> desugaring.desugar(method, desugaringEventConsumer),
+ method ->
+ desugaring.desugar(method, additions.getMethodContext(method), desugaringEventConsumer),
executorService);
- desugaringEventConsumer.finalizeDesugaring(appView);
+ desugaringEventConsumer.finalizeDesugaring();
Iterables.addAll(additions.desugaredMethods, pendingDesugaring);
pendingDesugaring.clear();
}
@@ -3367,50 +3237,6 @@
}
}
- private void synthesizeLambdas(SyntheticAdditions additions) {
- if (lambdasForDesugaring.isEmpty()) {
- return;
- }
- 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.
- 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.
- DexEncodedMethod constructor = programClass.lookupDirectMethod(lambdaClass.constructor);
- KeepReason reason = KeepReason.instantiatedIn(context);
- 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.
- lambdaCallSites.forEach(this::rewriteLambdaCallSites);
- additions.registerStatelessLambdaInstanceFieldReads(lambdaCallSites);
-
- // Remove all '$deserializeLambda$' methods which are not supported by desugaring.
- for (DexProgramClass clazz : classesWithSerializableLambdas) {
- clazz.removeMethod(appView.dexItemFactory().deserializeLambdaMethod);
- }
-
- // Clear state before next fixed point iteration.
- lambdasForDesugaring.clear();
- }
-
private void finalizeLibraryMethodOverrideInformation() {
for (DexProgramClass liveType : liveTypes.getItems()) {
for (DexEncodedMethod method : liveType.virtualMethods()) {
@@ -3430,13 +3256,7 @@
return true;
}
- private AppInfoWithLiveness createAppInfo(AppInfoWithClassHierarchy appInfo) {
- // Once all tracing is done, we generate accessor methods for lambdas.
- // These are assumed to be simple forwarding or access flag updates, thus no further tracing
- // is needed. These cannot be generated as part of lambda synthesis as changing a direct method
- // to a static method will invalidate the reachable method sets for tracing methods.
- ensureLambdaAccessibility();
-
+ private EnqueuerResult createEnqueuerResult(AppInfoWithClassHierarchy appInfo) {
// Compute the set of dead proto types.
deadProtoTypeCandidates.removeIf(this::isTypeLive);
Set<DexType> deadProtoTypes =
@@ -3532,42 +3352,10 @@
lockCandidates,
initClassReferences);
appInfo.markObsolete();
- return appInfoWithLiveness;
- }
-
- private void ensureLambdaAccessibility() {
- if (lambdaRewriter == null) {
- return;
+ if (options.testing.enqueuerInspector != null) {
+ options.testing.enqueuerInspector.accept(appInfoWithLiveness, mode);
}
- lambdaRewriter
- .getKnownLambdaClasses()
- .forEach(
- lambda -> {
- DexProgramClass synthesizedClass = lambda.getLambdaProgramClass();
- assert synthesizedClass != null;
- assert liveTypes.contains(synthesizedClass);
- if (synthesizedClass == null) {
- return;
- }
- DexMethod method = lambda.descriptor.getMainMethod();
- if (!liveMethods.contains(synthesizedClass.lookupMethod(method))) {
- return;
- }
- ProgramMethod accessor = lambda.target.ensureAccessibilityIfNeeded(false);
- if (accessor != null) {
- liveMethods.add(accessor, graphReporter.fakeReportShouldNotBeUsed());
- }
- });
- unpinLambdaMethods();
- }
-
- // TODO(b/157700141): Determine if this is the right way to allow modification of pinned lambdas.
- private void unpinLambdaMethods() {
- assert lambdaRewriter != null;
- for (DexMethod method : lambdaRewriter.getForcefullyMovedMethods()) {
- keepInfo.unsafeUnpinMethod(method);
- rootSet.prune(method);
- }
+ return new EnqueuerResult(appInfoWithLiveness);
}
private boolean verifyReferences(DexApplication app) {
@@ -3647,12 +3435,6 @@
desugaredLibraryWrapperAnalysis.generateWrappers(additions::addClasspathClass);
}
- private void rewriteLambdaCallSites(
- ProgramMethod context, Map<DexCallSite, LambdaClass> callSites) {
- assert !callSites.isEmpty();
- int replaced = LambdaRewriter.desugarLambdas(context, callSites::get);
- assert replaced == callSites.size();
- }
private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
Set<R> toDescriptorSet(Set<D> set) {
@@ -3878,7 +3660,7 @@
singleTargetHolder.isInterface(),
singleTarget,
graphReporter.fakeReportShouldNotBeUsed());
- enqueueMarkMethodLiveAction(
+ workList.enqueueMarkMethodLiveAction(
singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed());
}
action.getAction().accept(builder);
@@ -3930,9 +3712,9 @@
// Package protected due to entry point from worklist.
void markFieldAsKept(ProgramField field, KeepReason reason) {
if (field.getDefinition().isStatic()) {
- markStaticFieldAsLive(field, field, reason);
+ markFieldAsLive(field, field, reason);
} else {
- markInstanceFieldAsReachable(field, field, reason);
+ workList.enqueueMarkFieldAsReachableAction(field, field, reason);
}
}
@@ -4004,15 +3786,17 @@
// Package protected due to entry point from worklist.
void markMethodAsLive(ProgramMethod method, ProgramDefinition context) {
- DexProgramClass holder = method.getHolder();
- DexEncodedMethod definition = method.getDefinition();
+ assert liveMethods.contains(method);
- assert liveMethods.contains(definition);
+ DexEncodedMethod definition = method.getDefinition();
+ assert !definition.getOptimizationInfo().forceInline();
if (definition.isStatic()) {
markDirectAndIndirectClassInitializersAsLive(method.getHolder());
}
+ traceNonDesugaredCode(method);
+
ProgramMethodSet superCallTargets = superInvokeDependencies.get(method.getDefinition());
if (superCallTargets != null) {
for (ProgramMethod superCallTarget : superCallTargets) {
@@ -4023,12 +3807,6 @@
markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method));
}
}
- markParameterAndReturnTypesAsLive(method);
- processAnnotations(holder, method);
- definition.parameterAnnotationsList.forEachAnnotation(
- annotation -> processAnnotation(holder, method, annotation));
-
- traceNonDesugaredCode(method);
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(definition));
@@ -4039,6 +3817,34 @@
analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
}
+ private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
+ if (!targetedMethods.add(method, reason)) {
+ // Already targeted.
+ return;
+ }
+
+ if (!liveMethods.contains(method)) {
+ traceMethodDefinitionExcludingCode(method);
+ }
+
+ if (forceProguardCompatibility) {
+ // Keep targeted default methods in compatibility mode. The tree pruner will otherwise make
+ // these methods abstract, whereas Proguard does not (seem to) touch their code.
+ if (!method.getAccessFlags().isAbstract() && method.getHolder().isInterface()) {
+ markMethodAsLiveWithCompatRule(method);
+ }
+ }
+ }
+
+ void traceMethodDefinitionExcludingCode(ProgramMethod method) {
+ markReferencedTypesAsLive(method);
+ processAnnotations(method);
+ method
+ .getDefinition()
+ .getParameterAnnotations()
+ .forEachAnnotation(annotation -> processAnnotation(method, annotation));
+ }
+
private void traceNonDesugaredCode(ProgramMethod method) {
if (getMode().isInitialTreeShaking() && desugaring.needsDesugaring(method)) {
pendingDesugaring.add(method);
@@ -4069,7 +3875,7 @@
}
private void markReferencedTypesAsLive(ProgramMethod method) {
- markTypeAsLive(method.getHolderType(), method);
+ markTypeAsLive(method.getHolder(), method);
markParameterAndReturnTypesAsLive(method);
}
@@ -4134,7 +3940,8 @@
}
private void markMethodAsLiveWithCompatRule(ProgramMethod method) {
- enqueueMarkMethodLiveAction(method, method, graphReporter.reportCompatKeepMethod(method));
+ workList.enqueueMarkMethodLiveAction(
+ method, method, graphReporter.reportCompatKeepMethod(method));
}
private void handleReflectiveBehavior(ProgramMethod method) {
@@ -4652,7 +4459,7 @@
: fieldAccessInfoCollection.extend(
fieldReference, new FieldAccessInfoImpl(fieldReference));
fieldAccessInfo.setReadFromAnnotation();
- markStaticFieldAsLive(field, context, KeepReason.referencedInAnnotation(annotationHolder));
+ markFieldAsLive(field, context, KeepReason.referencedInAnnotation(annotationHolder));
// When an annotation has a field of an enum type the JVM will use the values() method on
// that enum class if the field is referenced.
if (options.isGeneratingClassFiles() && field.getHolder().isEnum()) {
@@ -4661,7 +4468,7 @@
}
} else {
// There is no dispatch on annotations, so only keep what is directly referenced.
- markInstanceFieldAsReachable(
+ workList.enqueueMarkFieldAsReachableAction(
field, context, KeepReason.referencedInAnnotation(annotationHolder));
}
return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerResult.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerResult.java
new file mode 100644
index 0000000..746cf59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerResult.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 com.android.tools.r8.shaking;
+
+public class EnqueuerResult {
+
+ private final AppInfoWithLiveness appInfo;
+
+ EnqueuerResult(AppInfoWithLiveness appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public AppInfoWithLiveness getAppInfo() {
+ return appInfo;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 01e415b..65081d5 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
+import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -21,6 +23,19 @@
public abstract void run(Enqueuer enqueuer);
}
+ static class AssertAction extends EnqueuerAction {
+ private final Action assertion;
+
+ AssertAction(Action assertion) {
+ this.assertion = assertion;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ assertion.execute();
+ }
+ }
+
static class MarkReachableDirectAction extends EnqueuerAction {
private final DexMethod target;
// TODO(b/175854431): Avoid pushing context on worklist.
@@ -55,13 +70,13 @@
}
}
- static class MarkInstanceFieldAsReachableAction extends EnqueuerAction {
+ static class MarkFieldAsReachableAction extends EnqueuerAction {
private final ProgramField field;
// TODO(b/175854431): Avoid pushing context on worklist.
private final ProgramDefinition context;
private final KeepReason reason;
- public MarkInstanceFieldAsReachableAction(
+ public MarkFieldAsReachableAction(
ProgramField field, ProgramDefinition context, KeepReason reason) {
this.field = field;
this.context = context;
@@ -70,7 +85,7 @@
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markInstanceFieldAsReachable(field, context, reason);
+ enqueuer.markFieldAsReachable(field, context, reason);
}
}
@@ -219,6 +234,19 @@
}
}
+ static class TraceMethodDefinitionExcludingCodeAction extends EnqueuerAction {
+ private final ProgramMethod method;
+
+ TraceMethodDefinitionExcludingCodeAction(ProgramMethod method) {
+ this.method = method;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.traceMethodDefinitionExcludingCode(method);
+ }
+ }
+
static class TraceNewInstanceAction extends EnqueuerAction {
private final DexType type;
// TODO(b/175854431): Avoid pushing context on worklist.
@@ -251,12 +279,15 @@
}
}
+ private final Enqueuer enqueuer;
private final Queue<EnqueuerAction> queue = new ArrayDeque<>();
- private EnqueuerWorklist() {}
+ private EnqueuerWorklist(Enqueuer enqueuer) {
+ this.enqueuer = enqueuer;
+ }
- public static EnqueuerWorklist createWorklist() {
- return new EnqueuerWorklist();
+ public static EnqueuerWorklist createWorklist(Enqueuer enqueuer) {
+ return new EnqueuerWorklist(enqueuer);
}
public boolean isEmpty() {
@@ -267,6 +298,13 @@
return queue.poll();
}
+ boolean enqueueAssertAction(Action assertion) {
+ if (InternalOptions.assertionsEnabled()) {
+ queue.add(new AssertAction(assertion));
+ }
+ return true;
+ }
+
void enqueueMarkReachableDirectAction(
DexMethod method, ProgramDefinition context, KeepReason reason) {
queue.add(new MarkReachableDirectAction(method, context, reason));
@@ -276,9 +314,9 @@
queue.add(new MarkReachableSuperAction(method, from));
}
- public void enqueueMarkInstanceFieldAsReachableAction(
+ public void enqueueMarkFieldAsReachableAction(
ProgramField field, ProgramDefinition context, KeepReason reason) {
- queue.add(new MarkInstanceFieldAsReachableAction(field, context, reason));
+ queue.add(new MarkFieldAsReachableAction(field, context, reason));
}
// TODO(b/142378367): Context is the containing method that is cause of the instantiation.
@@ -305,8 +343,16 @@
queue.add(new MarkInterfaceInstantiatedAction(clazz, reason));
}
- void enqueueMarkMethodLiveAction(ProgramMethod method, ProgramDefinition context) {
- queue.add(new MarkMethodLiveAction(method, context));
+ boolean enqueueMarkMethodLiveAction(
+ ProgramMethod method, ProgramDefinition context, KeepReason reason) {
+ if (enqueuer.addLiveMethod(method, reason)) {
+ queue.add(new MarkMethodLiveAction(method, context));
+ if (!enqueuer.isMethodTargeted(method)) {
+ queue.add(new TraceMethodDefinitionExcludingCodeAction(method));
+ }
+ return true;
+ }
+ return false;
}
void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) {
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index dee668f..aa49727 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -8,6 +8,8 @@
import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.getRetargetPackageAndClassPrefixDescriptor;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
@@ -123,8 +125,8 @@
Map<DexType, Set<DexReference>> missingClassesToBeReported =
getMissingClassesToBeReported(appView);
if (!missingClassesToBeReported.isEmpty()) {
- MissingClassesDiagnostic diagnostic =
- new MissingClassesDiagnostic.Builder()
+ MissingDefinitionsDiagnostic diagnostic =
+ MissingDefinitionsDiagnosticImpl.builder()
.addMissingClasses(missingClassesToBeReported)
.setFatal(!options.ignoreMissingClasses)
.build();
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java b/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
deleted file mode 100644
index 0f4e97b..0000000
--- a/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
+++ /dev/null
@@ -1,228 +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.shaking;
-
-import static com.android.tools.r8.utils.PredicateUtils.not;
-
-import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.Keep;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.Position;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.FieldReferenceUtils;
-import com.android.tools.r8.utils.MethodReferenceUtils;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.function.Function;
-
-@Keep
-public class MissingClassesDiagnostic implements Diagnostic {
-
- private static class MissingClassAccessContexts {
-
- private ImmutableSet<ClassReference> classContexts;
- private ImmutableSet<FieldReference> fieldContexts;
- private ImmutableSet<MethodReference> methodContexts;
-
- private MissingClassAccessContexts(
- ImmutableSet<ClassReference> classContexts,
- ImmutableSet<FieldReference> fieldContexts,
- ImmutableSet<MethodReference> methodContexts) {
- this.classContexts = classContexts;
- this.fieldContexts = fieldContexts;
- this.methodContexts = methodContexts;
- }
-
- static Builder builder() {
- return new Builder();
- }
-
- String getReferencedFromMessageSuffix(ClassReference missingClass) {
- if (!fieldContexts.isEmpty()) {
- return " (referenced from: "
- + FieldReferenceUtils.toSourceString(fieldContexts.iterator().next())
- + ")";
- }
- if (!methodContexts.isEmpty()) {
- return " (referenced from: "
- + MethodReferenceUtils.toSourceString(methodContexts.iterator().next())
- + ")";
- }
- // TODO(b/175543745): The legacy reporting is context insensitive, and therefore uses the
- // missing classes as their own context. Once legacy reporting is removed, this should be
- // simplified to taking the first context.
- Optional<ClassReference> classContext =
- classContexts.stream().filter(not(missingClass::equals)).findFirst();
- return classContext
- .map(classReference -> " (referenced from: " + classReference.getTypeName() + ")")
- .orElse("");
- }
-
- static class Builder {
-
- private final Set<DexReference> contexts = Sets.newIdentityHashSet();
-
- Builder addAll(Set<DexReference> contexts) {
- this.contexts.addAll(contexts);
- return this;
- }
-
- // TODO(b/179249745): Sort on demand in getReferencedFromMessageSuffix() instead.
- MissingClassAccessContexts build() {
- // Sort the contexts for deterministic reporting.
- List<DexType> classContexts = new ArrayList<>();
- List<DexField> fieldContexts = new ArrayList<>();
- List<DexMethod> methodContexts = new ArrayList<>();
- contexts.forEach(
- context -> context.apply(classContexts::add, fieldContexts::add, methodContexts::add));
- Collections.sort(classContexts);
- Collections.sort(fieldContexts);
- Collections.sort(methodContexts);
-
- // Build immutable sets (which preserve insertion order) from the sorted lists, mapping each
- // DexType, DexField, and DexMethod to ClassReference, FieldReference, and MethodReference,
- // respectively.
- return new MissingClassAccessContexts(
- toImmutableSet(classContexts, DexType::asClassReference),
- toImmutableSet(fieldContexts, DexField::asFieldReference),
- toImmutableSet(methodContexts, DexMethod::asMethodReference));
- }
-
- private <S, T> ImmutableSet<T> toImmutableSet(List<S> list, Function<S, T> fn) {
- ImmutableSet.Builder<T> builder = ImmutableSet.builder();
- list.forEach(element -> builder.add(fn.apply(element)));
- return builder.build();
- }
- }
- }
-
- private final boolean fatal;
- private final SortedMap<ClassReference, MissingClassAccessContexts> missingClasses;
-
- private MissingClassesDiagnostic(
- boolean fatal, SortedMap<ClassReference, MissingClassAccessContexts> missingClasses) {
- assert !missingClasses.isEmpty();
- this.fatal = fatal;
- this.missingClasses = missingClasses;
- }
-
- public Set<ClassReference> getMissingClasses() {
- return missingClasses.keySet();
- }
-
- /** A missing class(es) failure can generally not be attributed to a single origin. */
- @Override
- public Origin getOrigin() {
- return Origin.unknown();
- }
-
- /** A missing class(es) failure can generally not be attributed to a single position. */
- @Override
- public Position getPosition() {
- return Position.UNKNOWN;
- }
-
- @Override
- public String getDiagnosticMessage() {
- return fatal ? getFatalDiagnosticMessage() : getNonFatalDiagnosticMessage();
- }
-
- private String getFatalDiagnosticMessage() {
- if (missingClasses.size() == 1) {
- StringBuilder builder =
- new StringBuilder(
- "Compilation can't be completed because the following class is missing: ");
- writeMissingClass(builder, missingClasses.entrySet().iterator().next());
- return builder.append(".").toString();
- }
-
- StringBuilder builder =
- new StringBuilder("Compilation can't be completed because the following ")
- .append(missingClasses.size())
- .append(" classes are missing:");
- missingClasses.forEach(
- (missingClass, contexts) ->
- writeMissingClass(
- builder.append(System.lineSeparator()).append("- "), missingClass, contexts));
- return builder.toString();
- }
-
- private String getNonFatalDiagnosticMessage() {
- StringBuilder builder = new StringBuilder();
- Iterator<Entry<ClassReference, MissingClassAccessContexts>> missingClassesIterator =
- missingClasses.entrySet().iterator();
-
- // The diagnostic is always non-empty.
- assert missingClassesIterator.hasNext();
-
- // Write first line.
- writeMissingClass(builder.append("Missing class "), missingClassesIterator.next());
-
- // Write remaining lines with line separator before.
- missingClassesIterator.forEachRemaining(
- missingClassInfo ->
- writeMissingClass(
- builder.append(System.lineSeparator()).append("Missing class "), missingClassInfo));
-
- return builder.toString();
- }
-
- private static void writeMissingClass(
- StringBuilder builder, Entry<ClassReference, MissingClassAccessContexts> missingClassInfo) {
- writeMissingClass(builder, missingClassInfo.getKey(), missingClassInfo.getValue());
- }
-
- private static void writeMissingClass(
- StringBuilder builder, ClassReference missingClass, MissingClassAccessContexts contexts) {
- builder
- .append(missingClass.getTypeName())
- .append(contexts.getReferencedFromMessageSuffix(missingClass));
- }
-
- public static class Builder {
-
- private boolean fatal;
- private ImmutableSortedMap.Builder<ClassReference, MissingClassAccessContexts>
- missingClassesBuilder =
- ImmutableSortedMap.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
-
- public MissingClassesDiagnostic.Builder addMissingClasses(
- Map<DexType, Set<DexReference>> missingClasses) {
- missingClasses.forEach(
- (missingClass, contexts) ->
- missingClassesBuilder.put(
- Reference.classFromDescriptor(missingClass.toDescriptorString()),
- MissingClassAccessContexts.builder().addAll(contexts).build()));
- return this;
- }
-
- public MissingClassesDiagnostic.Builder setFatal(boolean fatal) {
- this.fatal = fatal;
- return this;
- }
-
- public MissingClassesDiagnostic build() {
- return new MissingClassesDiagnostic(fatal, missingClassesBuilder.build());
- }
- }
-}
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 6c07af2..73b61b3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -4,7 +4,6 @@
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;
import com.android.tools.r8.graph.DexApplication;
@@ -12,6 +11,7 @@
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
@@ -137,16 +137,6 @@
appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexInfo()));
}
- // Internal synthetic id creation helpers.
-
- private synchronized String getNextSyntheticId() {
- if (nextSyntheticId == INVALID_ID_AFTER_SYNTHETIC_FINALIZATION) {
- throw new InternalCompilerError(
- "Unexpected attempt to synthesize classes after synthetic finalization.");
- }
- return Integer.toString(nextSyntheticId++);
- }
-
// Predicates and accessors.
@Override
@@ -225,6 +215,13 @@
return isSyntheticClass(clazz.type);
}
+ // TODO(b/180091213): Implement this and remove client provided the oracle.
+ public Set<DexReference> getSynthesizingContexts(
+ DexProgramClass clazz, Function<DexProgramClass, Set<DexReference>> oracle) {
+ assert isSyntheticClass(clazz);
+ return oracle.apply(clazz);
+ }
+
// The compiler should not inspect the kind of a synthetic, so this provided only as a assertion
// utility.
public boolean verifySyntheticLambdaProperty(
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index 0b53962..930443a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -54,7 +54,7 @@
if (method != rewritten && !lens.isSimpleRenaming(method.holder, rewritten.holder)) {
// If the referenced item is rewritten, it should be moved to another holder as the
// synthetic holder is no longer part of the synthetic collection.
- assert method.holder != rewritten.holder;
+ assert method.holder != rewritten.holder : "The synthetic method reference should have moved";
assert SyntheticNaming.verifyNotInternalSynthetic(rewritten.holder);
return null;
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c433ec0..63772a2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -30,6 +30,7 @@
STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true),
+ THROW_IAE("ThrowIAE", true),
THROW_ICCE("ThrowICCE", true),
THROW_NSME("ThrowNSME", true),
TWR_CLOSE_RESOURCE("TwrCloseResource", true),
diff --git a/src/main/java/com/android/tools/r8/utils/ForEachable.java b/src/main/java/com/android/tools/r8/utils/ForEachable.java
index 4c8ae53..63ed027 100644
--- a/src/main/java/com/android/tools/r8/utils/ForEachable.java
+++ b/src/main/java/com/android/tools/r8/utils/ForEachable.java
@@ -8,4 +8,9 @@
public interface ForEachable<T> {
void forEach(Consumer<T> consumer);
+
+ default void forEachWithIndex(IntObjConsumer<T> consumer) {
+ IntBox index = new IntBox();
+ forEach(element -> consumer.accept(index.getAndIncrement(), element));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/IntBox.java b/src/main/java/com/android/tools/r8/utils/IntBox.java
index ebb92ca..9a31ebd 100644
--- a/src/main/java/com/android/tools/r8/utils/IntBox.java
+++ b/src/main/java/com/android/tools/r8/utils/IntBox.java
@@ -14,19 +14,52 @@
set(initialValue);
}
+ public void decrement(int i) {
+ assert i > 0;
+ value -= i;
+ }
+
+ public int decrementAndGet(int i) {
+ decrement(i);
+ return get();
+ }
+
public int get() {
return value;
}
public int getAndIncrement() {
- return value++;
+ return getAndIncrement(1);
+ }
+
+ public int getAndIncrement(int i) {
+ int previous = value;
+ increment(i);
+ return previous;
+ }
+
+ public int getAndSet(int value) {
+ int previous = this.value;
+ set(value);
+ return previous;
}
public void increment() {
- value++;
+ increment(1);
+ }
+
+ public void increment(int i) {
+ assert i >= 0;
+ value += i;
}
public void set(int value) {
this.value = value;
}
+
+ public void setMax(int value) {
+ if (value > get()) {
+ set(value);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/IntConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/IntConsumerUtils.java
new file mode 100644
index 0000000..c701cdd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntConsumerUtils.java
@@ -0,0 +1,14 @@
+// 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.utils;
+
+import java.util.function.IntConsumer;
+
+public class IntConsumerUtils {
+
+ public static IntConsumer emptyIntConsumer() {
+ return ignore -> {};
+ }
+}
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 afbafe0..d515b20 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -76,6 +76,7 @@
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -486,7 +487,7 @@
}
public boolean shouldDesugarNests() {
- return testing.enableForceNestBasedAccessDesugaringForTest || !canUseNestBasedAccess();
+ return !canUseNestBasedAccess();
}
public boolean canUseRecords() {
@@ -1323,6 +1324,7 @@
System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
// TODO(b/177333791): Set to true
public boolean checkForNotExpandingMainDexTracingResult = false;
+ public Set<String> allowedUnusedDontWarnPatterns = new HashSet<>();
public boolean allowConflictingSyntheticTypes = false;
@@ -1333,8 +1335,6 @@
// TODO(b/144781417): This is disabled by default as some test apps appear to have such classes.
public boolean allowNonAbstractClassesWithAbstractMethods = true;
- // Flag to turn on/off JDK11+ nest-access control even when not required (Cf backend)
- public boolean enableForceNestBasedAccessDesugaringForTest = false;
public boolean verifyKeptGraphInfo = false;
public boolean readInputStackMaps = true;
@@ -1395,6 +1395,15 @@
}
/**
+ * Allow access modification of synthetic lambda implementation methods in D8 to avoid generating
+ * an excessive amount of accessibility bridges. In R8, the lambda implementation methods are
+ * inlined into the synthesized accessibility bridges, thus we don't allow access modification.
+ */
+ public boolean canAccessModifyLambdaImplementationMethods(AppView<?> appView) {
+ return !appView.enableWholeProgramOptimizations();
+ }
+
+ /**
* Dex2Oat issues a warning for abstract methods on non-abstract classes, so we never allow this.
*
* <p>Note that having an invoke instruction that targets an abstract method on a non-abstract
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 a6f800b..10cd22a 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -22,11 +22,12 @@
* as the singleton list containing {@code v} (i.e., no changes should be made to the given
* element).
*/
- public static <T> List<T> flatMap(List<T> list, Function<T, List<T>> fn, List<T> defaultValue) {
+ public static <T> List<T> flatMap(
+ List<T> list, Function<T, Collection<T>> fn, List<T> defaultValue) {
List<T> result = null;
for (int i = 0; i < list.size(); i++) {
T element = list.get(i);
- List<T> replacement = fn.apply(element);
+ Collection<T> replacement = fn.apply(element);
if (replacement == null) {
if (result != null) {
result.add(element);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
index 09b3c27..8f12b15 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
@@ -19,6 +19,8 @@
void forEachKey(Consumer<? super K> consumer);
+ void forEachValue(Consumer<? super V> consumer);
+
Set<K> getKeys(V value);
Set<V> getValues(K key);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
index c217e91..8f29df9 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
@@ -58,6 +58,11 @@
}
@Override
+ public void forEachValue(Consumer<? super V> consumer) {
+ inverse.keySet().forEach(consumer);
+ }
+
+ @Override
public V get(Object key) {
return backing.get(key);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java
index 552a406..66b2375 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java
@@ -58,6 +58,11 @@
}
@Override
+ public void forEachValue(Consumer<? super V> consumer) {
+ inverse.keySet().forEach(consumer);
+ }
+
+ @Override
public Set<V> get(Object key) {
return backing.get(key);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
index 8e2edb5..830138a 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -61,6 +61,11 @@
}
@Override
+ public void forEachValue(Consumer<? super V> consumer) {
+ backing.values().forEach(consumer);
+ }
+
+ @Override
public V get(Object key) {
return backing.get(key);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
index f2c10c3..f91f7df 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
@@ -42,6 +42,11 @@
}
@Override
+ public void forEachValue(Consumer<? super V> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
public V get(Object key) {
return null;
}
diff --git a/src/test/examples/shaking1/print-mapping-cf.ref b/src/test/examples/shaking1/print-mapping-cf.ref
index 87a7bc0..a7e0b8e 100644
--- a/src/test/examples/shaking1/print-mapping-cf.ref
+++ b/src/test/examples/shaking1/print-mapping-cf.ref
@@ -3,3 +3,4 @@
1:1:java.lang.String method():17:17 -> a
1:1:void main(java.lang.String[]):8:8 -> main
1:1:void <init>(java.lang.String):12:12 -> <init>
+ 1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index bee542f..8e94e5e 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -27,22 +27,37 @@
public class KotlinCompilerTool {
+ public enum KotlinCompilerVersion {
+ KOTLINC_1_3_72("kotlin-compiler-1.3.72"),
+ KOTLINC_1_4_20("kotlin-compiler-1.4.20");
+
+ private final String folder;
+
+ KotlinCompilerVersion(String folder) {
+ this.folder = folder;
+ }
+ }
+
public static final class KotlinCompiler {
private final String name;
private final Path lib;
private final Path compiler;
+ private final KotlinCompilerVersion compilerVersion;
- public KotlinCompiler(String name) {
- this.name = name;
- this.lib = Paths.get(ToolHelper.THIRD_PARTY_DIR, "kotlin", name, "kotlinc", "lib");
+ public KotlinCompiler(KotlinCompilerVersion compilerVersion) {
+ this.lib =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "kotlin", compilerVersion.folder, "kotlinc", "lib");
this.compiler = lib.resolve("kotlin-compiler.jar");
+ this.compilerVersion = compilerVersion;
+ this.name = compilerVersion.name();
}
- public KotlinCompiler(String name, Path compiler) {
- this.name = name;
+ public KotlinCompiler(String name, Path compiler, KotlinCompilerVersion compilerVersion) {
this.compiler = compiler;
this.lib = null;
+ this.compilerVersion = compilerVersion;
+ this.name = name;
}
public Path getCompiler() {
@@ -53,6 +68,14 @@
return lib;
}
+ public boolean is(KotlinCompilerVersion version) {
+ return compilerVersion == version;
+ }
+
+ public KotlinCompilerVersion getCompilerVersion() {
+ return compilerVersion;
+ }
+
@Override
public String toString() {
return name;
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index 38202a6..f018f74 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
@@ -111,6 +112,22 @@
sharedFolder, ignore -> new KotlinCompileMemoizer(sources));
}
+ public ThrowableConsumer<R8TestCompileResult> assertUnusedKeepRuleForKotlinMetadata(
+ boolean condition) {
+ return compileResult -> {
+ if (!condition) {
+ return;
+ }
+ compileResult
+ .getDiagnosticMessages()
+ .assertInfoThatMatches(
+ diagnosticMessage(
+ containsString(
+ "Proguard configuration rule does not match anything: `-keep class"
+ + " kotlin.Metadata")));
+ };
+ }
+
public static class KotlinCompileMemoizer {
private final Collection<Path> sources;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 389b0fa..e169df0 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -167,7 +167,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 35, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 37, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -176,7 +176,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
.run();
}
@@ -190,7 +190,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 35, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 36, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 83313a3..e1d8658 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -309,6 +309,20 @@
return self();
}
+ public T allowUnusedDontWarnKotlinReflectJvmInternal() {
+ addOptionsModification(
+ options ->
+ options.testing.allowedUnusedDontWarnPatterns.add("kotlin.reflect.jvm.internal.**"));
+ return self();
+ }
+
+ public T allowUnusedDontWarnKotlinReflectJvmInternal(boolean condition) {
+ if (condition) {
+ allowUnusedDontWarnKotlinReflectJvmInternal();
+ }
+ return self();
+ }
+
public T allowUnusedDontWarnPatterns() {
return addOptionsModification(options -> options.testing.allowUnusedDontWarnRules = true);
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index cbd2755..1b4ca35 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.EnqueuerFactory;
+import com.android.tools.r8.shaking.EnqueuerResult;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
@@ -778,12 +779,12 @@
appView, subtypingInfo, appView.options().getProguardConfiguration().getRules())
.build(executor);
appView.setRootSet(rootSet);
- AppInfoWithLiveness appInfoWithLiveness =
+ EnqueuerResult enqueuerResult =
EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo)
.traceApplication(rootSet, executor, Timing.empty());
// We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
// due to liveness.
- return appView.setAppInfo(appInfoWithLiveness);
+ return appView.setAppInfo(enqueuerResult.getAppInfo());
}
protected static DexType buildType(Class<?> clazz, DexItemFactory factory) {
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 4a7d5c7..34cc0b0 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyThrowingConsumer;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
@@ -44,6 +45,22 @@
return self();
}
+ public <T extends Throwable> RR applyIf(boolean condition, ThrowingConsumer<RR, T> thenConsumer)
+ throws T {
+ return applyIf(condition, thenConsumer, emptyThrowingConsumer());
+ }
+
+ public <S extends Throwable, T extends Throwable> RR applyIf(
+ boolean condition, ThrowingConsumer<RR, S> thenConsumer, ThrowingConsumer<RR, T> elseConsumer)
+ throws S, T {
+ if (condition) {
+ thenConsumer.accept(self());
+ } else {
+ elseConsumer.accept(self());
+ }
+ return self();
+ }
+
public <S> S map(Function<RR, S> fn) {
return fn.apply(self());
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 816e207..c0cd551 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -4,7 +4,6 @@
package com.android.tools.r8;
-import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
@@ -106,8 +105,11 @@
return addKeepRules(Arrays.asList(rules));
}
- public T addDontWarn(Class<?> clazz) {
- return addDontWarn(clazz.getTypeName());
+ public T addDontWarn(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ addDontWarn(clazz.getTypeName());
+ }
+ return self();
}
public T addDontWarn(String className) {
@@ -125,11 +127,6 @@
return addDontWarn(Arrays.asList(classes));
}
- @Deprecated
- public T addDontWarnCompanionClass(Class<?> clazz) {
- return addDontWarn(clazz.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
- }
-
public T addDontWarnGoogle() {
return addDontWarn("com.google.**");
}
@@ -247,7 +244,11 @@
}
public T addKeepPackageNamesRule(Package pkg) {
- return addKeepRules("-keeppackagenames " + pkg.getName());
+ return addKeepPackageNamesRule(pkg.getName());
+ }
+
+ public T addKeepPackageNamesRule(String packageName) {
+ return addKeepRules("-keeppackagenames " + packageName);
}
public T addKeepMainRule(Class<?> mainClass) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index c906281..6c395bf 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
import static org.junit.Assert.assertEquals;
@@ -135,7 +137,8 @@
public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
public static final String JAVA_8_RUNTIME = "third_party/openjdk/openjdk-rt-1.8/rt.jar";
- public static final String DESUGAR_JDK_LIBS = "third_party/openjdk/desugar_jdk_libs/libjava.jar";
+ public static final String DESUGAR_JDK_LIBS =
+ System.getProperty("desugar_jdk_libs", "third_party/openjdk/desugar_jdk_libs/libjava.jar");
public static final String CORE_LAMBDA_STUBS =
"third_party/core-lambda-stubs/core-lambda-stubs.jar";
public static final String JSR223_RI_JAR = "third_party/jsr223-api-1.0/jsr223-api-1.0.jar";
@@ -2133,11 +2136,15 @@
}
public static KotlinCompiler getKotlinC_1_3_72() {
- return new KotlinCompiler("kotlin-compiler-1.3.72");
+ return new KotlinCompiler(KOTLINC_1_3_72);
+ }
+
+ public static KotlinCompiler getKotlinC_1_4_20() {
+ return new KotlinCompiler(KOTLINC_1_4_20);
}
public static KotlinCompiler[] getKotlinCompilers() {
- return new KotlinCompiler[] {getKotlinC_1_3_72()};
+ return new KotlinCompiler[] {getKotlinC_1_3_72(), getKotlinC_1_4_20()};
}
public static void disassemble(AndroidApp app, PrintStream ps)
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
index 98421ae..d6a696c 100644
--- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -7,11 +7,13 @@
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
@@ -78,6 +80,11 @@
assertThat(clazz, isPresent());
AnnotationSubject sourceDebugExtensions =
clazz.annotation("dalvik.annotation.SourceDebugExtension");
- assertThat(sourceDebugExtensions, isPresent());
+ // TODO(b/179866574): This is somehow not present
+ if (kotlinCompiler.is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+ assertThat(sourceDebugExtensions, not(isPresent()));
+ } else {
+ assertThat(sourceDebugExtensions, isPresent());
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
index 7aa4205..3dd6878 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -119,7 +119,8 @@
Path classPathAfter =
kotlinc(
parameters.getRuntime().asCf(),
- new KotlinCompiler("r8ProcessedKotlinc", r8ProcessedKotlinc),
+ new KotlinCompiler(
+ "r8ProcessedKotlinc", r8ProcessedKotlinc, kotlinCompiler.getCompilerVersion()),
KotlinTargetVersion.JAVA_8)
.addSourceFiles(HELLO_KT)
.setOutputPath(temp.newFolder().toPath())
diff --git a/src/test/java/com/android/tools/r8/d8/ThrowingConstStringTest.java b/src/test/java/com/android/tools/r8/d8/ThrowingConstStringTest.java
new file mode 100644
index 0000000..314d32d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/ThrowingConstStringTest.java
@@ -0,0 +1,73 @@
+// 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.d8;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ThrowingConstStringTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello!");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public ThrowingConstStringTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path cfout =
+ testForD8(Backend.CF)
+ .addInnerClasses(ThrowingConstStringTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.testing.forceIRForCfToCfDesugar = true)
+ .compile()
+ .inspect(
+ inspector -> {
+ MethodSubject method = inspector.clazz(TestClass.class).mainMethod();
+ InstructionSubject constString =
+ method.iterateInstructions(InstructionSubject::isConstString).next();
+ assertTrue(
+ "ConstString is not covered by try range",
+ method
+ .streamTryCatches()
+ .anyMatch(
+ tryCatch ->
+ tryCatch.getRange().includes(constString.getOffset(method))));
+ })
+ .writeToZip();
+ testForD8(parameters.getBackend())
+ .addProgramFiles(cfout)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ synchronized (TestClass.class) {
+ String constant = "Hello!";
+ System.out.println(constant);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a5a1a8a..6dc126c 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -2188,7 +2188,7 @@
private static boolean isLambdaMethod(VmMirror mirror, Location location) {
String methodName = mirror.getMethodName(location.classID, location.methodID);
- return methodName.startsWith("lambda$");
+ return methodName.startsWith("lambda$") || methodName.startsWith("$private$lambda$");
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaTest.java b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
index f9e6c34..749c4b4 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -84,7 +84,6 @@
checkMethod(debuggeeClass, initialMethodName),
checkLine(SOURCE_FILE, 20),
stepInto(INTELLIJ_FILTER),
- config.isCfRuntime() ? LambdaTest::doNothing : stepInto(INTELLIJ_FILTER),
checkMethod(debuggeeClass, "returnOne"),
checkLine(SOURCE_FILE, 28),
checkNoLocal(),
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
index 7a97972..477af87 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
@@ -51,7 +51,7 @@
this.parameters = parameters;
}
- private void runDebugger(DebugTestConfig config) throws Throwable {
+ private void runDebugger(DebugTestConfig config, boolean isR8) throws Throwable {
MethodReference main = Reference.methodFromMethod(CLASS.getMethod("main", String[].class));
Command checkThisLambda =
conditional(
@@ -83,7 +83,9 @@
stepInto(INTELLIJ_FILTER),
checkLine(17),
// When desugaring, the LambdaClass will change this to a static (later moved to companion).
- checkThisLambda,
+ parameters.canUseDefaultAndStaticInterfaceMethods() && isR8
+ ? checkThisDefaultMethod
+ : checkThisLambda,
run());
}
@@ -92,7 +94,7 @@
assumeTrue(parameters.isCfRuntime());
JvmTestBuilder builder = testForJvm().addTestClasspath();
builder.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(builder.debugConfig());
+ runDebugger(builder.debugConfig(), false);
}
@Test
@@ -111,7 +113,7 @@
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutput(EXPECTED)
.inspect(inspector -> assertThat(inspector.clazz(CLASS), isPresent()));
- runDebugger(compileResult.debugConfig());
+ runDebugger(compileResult.debugConfig(), true);
}
@Test
@@ -168,7 +170,7 @@
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutput(EXPECTED);
- runDebugger(compiledResult.debugConfig());
+ runDebugger(compiledResult.debugConfig(), false);
Path dissasemble1 = temp.newFolder().toPath().resolve("disassemble1.txt");
Path dissasemble2 = temp.newFolder().toPath().resolve("disassemble2.txt");
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java
index bf507de..de06aad 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarInstanceLambdaWithReadsTest.java
@@ -3,12 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar;
+import static com.android.tools.r8.ir.desugar.LambdaClass.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX;
+import static com.android.tools.r8.ir.desugar.LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -41,7 +44,7 @@
.addProgramClasses(Main.class, A.class, B.class, Consumer.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkJustOneLambdaImplementationMethod);
+ .inspect(inspector -> checkNumberOfLambdaMethods(inspector, false));
}
@Test
@@ -54,13 +57,27 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkJustOneLambdaImplementationMethod);
+ .inspect(inspector -> checkNumberOfLambdaMethods(inspector, true));
}
- private void checkJustOneLambdaImplementationMethod(CodeInspector inspector) {
+ private void checkNumberOfLambdaMethods(CodeInspector inspector, boolean isR8) {
+ // When generating DEX, only R8 synthesizes an accessor for the javac-generated lambda$ method.
+ List<FoundMethodSubject> lambdaAccessorMethods =
+ inspector
+ .clazz(Main.class)
+ .allMethods(m -> m.getOriginalName().startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX));
+ assertEquals(
+ BooleanUtils.intValue(parameters.isDexRuntime() && isR8), lambdaAccessorMethods.size());
+
+ // When generating DEX, R8 will inline the javac-generated lambda$ method into the synthesized
+ // $r8$lambda$ accessor method.
List<FoundMethodSubject> lambdaImplementationMethods =
- inspector.clazz(Main.class).allMethods(m -> m.getOriginalName().startsWith("lambda$"));
- assertEquals(1, lambdaImplementationMethods.size());
+ inspector
+ .clazz(Main.class)
+ .allMethods(m -> m.getOriginalName().startsWith(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX));
+ assertEquals(
+ 1 - BooleanUtils.intValue(parameters.isDexRuntime() && isR8),
+ lambdaImplementationMethods.size());
}
private interface Consumer {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
index 5d84439..72549c5 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
@@ -32,7 +32,7 @@
private List<String> EXPECTED_JAVAC_RESULT =
ImmutableList.of("Hello from inside lambda$test$0", "Hello from inside lambda$testStatic$1");
- private List<String> EXPECTED_DESUGARED_RESULT =
+ private List<String> EXPECTED_D8_DESUGARED_RESULT =
ImmutableList.of(
"Hello from inside lambda$test$0$DesugarLambdaWithAnonymousClass$TestClass",
"Hello from inside lambda$testStatic$1");
@@ -106,7 +106,7 @@
r -> r.assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT))
.applyIf(
DesugarTestConfiguration::isDesugared,
- r -> r.assertSuccessWithOutputLines(EXPECTED_DESUGARED_RESULT));
+ r -> r.assertSuccessWithOutputLines(EXPECTED_D8_DESUGARED_RESULT));
}
@Test
@@ -124,8 +124,7 @@
.addKeepMainRule(TestClass.class)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkEnclosingMethod)
- .assertSuccessWithOutputLines(
- parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_DESUGARED_RESULT);
+ .assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT);
assertFalse(parameters.isDexRuntime());
} catch (AssertionError e) {
assertTrue(parameters.isDexRuntime());
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
index f272f26..62c36fd 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
@@ -31,7 +31,7 @@
private List<String> EXPECTED_JAVAC_RESULT =
ImmutableList.of("Hello from inside lambda$test$0", "Hello from inside lambda$testStatic$1");
- private List<String> EXPECTED_DESUGARED_RESULT =
+ private List<String> EXPECTED_D8_DESUGARED_RESULT =
ImmutableList.of(
"Hello from inside lambda$test$0$DesugarLambdaWithLocalClass$TestClass",
"Hello from inside lambda$testStatic$1");
@@ -104,7 +104,7 @@
r -> r.assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT))
.applyIf(
DesugarTestConfiguration::isDesugared,
- r -> r.assertSuccessWithOutputLines(EXPECTED_DESUGARED_RESULT));
+ r -> r.assertSuccessWithOutputLines(EXPECTED_D8_DESUGARED_RESULT));
}
@Test
@@ -120,8 +120,7 @@
.addKeepMainRule(TestClass.class)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkEnclosingMethod)
- .assertSuccessWithOutputLines(
- parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_JAVAC_RESULT);
+ .assertSuccessWithOutputLines(EXPECTED_JAVAC_RESULT);
}
public interface MyConsumer<T> {
diff --git a/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
new file mode 100644
index 0000000..bdcfd8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
@@ -0,0 +1,71 @@
+// 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.desugar;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LambdaMissingInterfaceTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public LambdaMissingInterfaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ClassWithLambda.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(MissingInterface.class)
+ .enableInliningAnnotations()
+ .compile()
+ .addRunClasspathClasses(MissingInterface.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(AbstractMethodError.class);
+ }
+
+ interface MissingInterface {
+
+ void bar(int x);
+ }
+
+ public static class ClassWithLambda {
+
+ @NeverInline
+ public static void callWithLambda() {
+ Main.foo(System.out::println);
+ }
+ }
+
+ public static class Main {
+
+ private static int argCount;
+
+ @NeverInline
+ public static void foo(MissingInterface i) {
+ i.bar(argCount);
+ }
+
+ public static void main(String[] args) {
+ argCount = args.length;
+ ClassWithLambda.callWithLambda();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java
new file mode 100644
index 0000000..b38eac1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java
@@ -0,0 +1,124 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConcurrentHashMapFileSerializationTest extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("2", "2", "v1", "v1", "v2", "v2");
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ // TODO(b/134732760): Skip Android 4.4.4 due to missing libjavacrypto.
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withDexRuntime(Version.V4_0_4)
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withAllApiLevels()
+ .build());
+ }
+
+ public ConcurrentHashMapFileSerializationTest(
+ boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testMapSerializationD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(ConcurrentHashMapFileSerializationTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .withArtFrameworks()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testMapSerializationR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(Backend.DEX)
+ .addInnerClasses(ConcurrentHashMapFileSerializationTest.class)
+ .addKeepMainRule(Executor.class)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .withArtFrameworks()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+ static class Executor {
+ public static void main(String[] args) throws Exception {
+ chmTest();
+ }
+
+ private static void chmTest() throws IOException, ClassNotFoundException {
+ ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+ map.put("k1", "v1");
+ map.put("k2", "v2");
+ File file = new File("testTemp");
+
+ FileOutputStream fos = new FileOutputStream(file);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(map);
+ oos.close();
+ fos.close();
+
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ ConcurrentHashMap<String, String> newMap =
+ (ConcurrentHashMap<String, String>) ois.readObject();
+ fis.close();
+ ois.close();
+
+ System.out.println(map.size());
+ System.out.println(newMap.size());
+ System.out.println(map.get("k1"));
+ System.out.println(newMap.get("k1"));
+ System.out.println(map.get("k2"));
+ System.out.println(newMap.get("k2"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java
new file mode 100644
index 0000000..623cfd0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java
@@ -0,0 +1,119 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MyMapFileSerializationTest extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("2", "2", "v1", "v1", "v2", "v2");
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ // TODO(b/134732760): Skip Android 4.4.4 due to missing libjavacrypto.
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withDexRuntime(Version.V4_0_4)
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withAllApiLevels()
+ .build());
+ }
+
+ public MyMapFileSerializationTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testMapSerializationD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(MyMapFileSerializationTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .withArtFrameworks()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testMapSerializationR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(Backend.DEX)
+ .addInnerClasses(MyMapFileSerializationTest.class)
+ .addKeepMainRule(Executor.class)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .withArtFrameworks()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+ static class Executor {
+ public static void main(String[] args) throws Exception {
+ MyMap<String, String> map = new MyMap<>();
+ map.put("k1", "v1");
+ map.put("k2", "v2");
+ File file = new File("testTemp");
+
+ FileOutputStream fos = new FileOutputStream(file);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(map);
+ oos.close();
+ fos.close();
+
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ MyMap<String, String> newMap = (MyMap<String, String>) ois.readObject();
+ fis.close();
+ ois.close();
+
+ System.out.println(map.size());
+ System.out.println(newMap.size());
+ System.out.println(map.get("k1"));
+ System.out.println(newMap.get("k1"));
+ System.out.println(map.get("k2"));
+ System.out.println(newMap.get("k2"));
+ }
+ }
+
+ static class MyMap<R, T> extends ConcurrentHashMap<R, T> {}
+}
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 4ac6ddf..2d3c243 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
@@ -4,10 +4,10 @@
package com.android.tools.r8.desugar.desugaredlibrary.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
@@ -24,6 +24,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
+import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -129,17 +130,18 @@
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages();
+ .allowDiagnosticWarningMessages()
+ .allowUnusedDontWarnKotlinReflectJvmInternal(
+ kotlinParameters.getCompiler().is(KOTLINC_1_4_20));
KeepRuleConsumer keepRuleConsumer = null;
if (desugarLibrary) {
keepRuleConsumer = createKeepRuleConsumer(parameters);
testBuilder.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer);
}
- final R8TestCompileResult compileResult =
+ R8TestCompileResult compileResult =
testBuilder
.compile()
- .assertAllWarningMessagesMatch(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
+ .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib);
if (desugarLibrary) {
assertNotNull(keepRuleConsumer);
compileResult.addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java
new file mode 100644
index 0000000..721e41d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedInstanceLambdaMethodTest.java
@@ -0,0 +1,115 @@
+// 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.desugar.lambdas;
+
+import static com.android.tools.r8.ir.desugar.LambdaClass.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class ExplicitCallToJavacGeneratedInstanceLambdaMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ public ExplicitCallToJavacGeneratedInstanceLambdaMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ result -> result.assertSuccessWithOutputLines("Hello world!", "Hello world!"),
+ result -> result.assertFailureWithErrorThatThrows(NoSuchMethodError.class));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ private byte[] getProgramClassFileData() throws IOException {
+ Method lambdaMethod =
+ Stream.of(I.class.getDeclaredMethods())
+ .filter(x -> x.getName().contains(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX))
+ .findFirst()
+ .get();
+ return transformer(I.class)
+ .transformMethodInsnInMethod(
+ "test",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (name.equals("lambdaMethod")) {
+ visitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, owner, lambdaMethod.getName(), descriptor, isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .removeMethodsWithName("lambdaMethod")
+ .setVersion(CfVersion.V9)
+ .transform();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().test();
+ }
+ }
+
+ interface I {
+
+ default void test() {
+ FunctionalInterface f = () -> greet();
+ f.m();
+ lambdaMethod(); // Changed to lambda$test$0() by transformer.
+ }
+
+ default void greet() {
+ System.out.println("Hello world!");
+ }
+
+ // Removed by transformer.
+ default void lambdaMethod() {}
+ }
+
+ static class A implements I {}
+
+ interface FunctionalInterface {
+
+ void m();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java
new file mode 100644
index 0000000..f57ef59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/ExplicitCallToJavacGeneratedStaticLambdaMethodTest.java
@@ -0,0 +1,107 @@
+// 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.desugar.lambdas;
+
+import static com.android.tools.r8.ir.desugar.LambdaClass.JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ExplicitCallToJavacGeneratedStaticLambdaMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ public ExplicitCallToJavacGeneratedStaticLambdaMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ private byte[] getProgramClassFileData() throws IOException {
+ Method lambdaMethod =
+ Stream.of(I.class.getDeclaredMethods())
+ .filter(x -> x.getName().contains(JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX))
+ .findFirst()
+ .get();
+ return transformer(I.class)
+ .transformMethodInsnInMethod(
+ "test",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (name.equals("lambdaMethod")) {
+ visitor.visitMethodInsn(
+ opcode, owner, lambdaMethod.getName(), descriptor, isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .removeMethodsWithName("lambdaMethod")
+ .setVersion(CfVersion.V9)
+ .transform();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().test();
+ }
+ }
+
+ interface I {
+
+ default void test() {
+ FunctionalInterface f = () -> System.out.println("Hello world!");
+ f.m();
+ lambdaMethod(); // Changed to lambda$test$0() by transformer.
+ }
+
+ // Removed by transformer.
+ static void lambdaMethod() {}
+ }
+
+ static class A implements I {}
+
+ interface FunctionalInterface {
+
+ void m();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
new file mode 100644
index 0000000..7d0b2fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest.java
@@ -0,0 +1,114 @@
+// 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.desugar.lambdas;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ public LambdaPrivateInstanceInterfaceMethodWithNonLambdaCallSiteTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, FunctionalInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ private byte[] getProgramClassFileData() throws IOException, NoSuchMethodException {
+ return transformer(I.class)
+ .transformInvokeDynamicInsnInMethod(
+ "test",
+ (name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments, visitor) -> {
+ visitor.visitInvokeDynamicInsn(
+ name,
+ descriptor,
+ bootstrapMethodHandle,
+ bootstrapMethodArguments.get(0),
+ new Handle(
+ Opcodes.H_INVOKESPECIAL, binaryName(I.class), "privateMethod", "()V", true),
+ bootstrapMethodArguments.get(2));
+ })
+ .transformMethodInsnInMethod(
+ "test",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (name.equals("privateMethod")) {
+ assert opcode == Opcodes.INVOKEINTERFACE;
+ visitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, owner, name, descriptor, isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .setPrivate(I.class.getDeclaredMethod("privateMethod"))
+ .setVersion(CfVersion.V9)
+ .transform();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().test();
+ }
+ }
+
+ interface I {
+
+ default void test() {
+ FunctionalInterface f = this::privateMethod;
+ f.m();
+ privateMethod();
+ }
+
+ default void privateMethod() {
+ System.out.println("Hello world!");
+ }
+ }
+
+ static class A implements I {}
+
+ interface FunctionalInterface {
+
+ void m();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 5a788bf..a756d95 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -66,8 +67,14 @@
return testForR8(TestBase.getStaticTemp(), Backend.CF)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
.addKeepRuleFiles(MAIN_KEEP)
- .addOptionsModification(
- options -> options.testing.enableForceNestBasedAccessDesugaringForTest = desugar)
+ .applyIf(
+ desugar,
+ builder ->
+ builder.addOptionsModification(
+ options -> {
+ options.desugarState = DesugarState.ON;
+ options.cfToCfDesugar = true;
+ }))
.compile()
.inspect(inspector -> assertNests(inspector, desugar))
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index a9e05a3..78904fd 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -25,10 +25,10 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
-import com.android.tools.r8.shaking.MissingClassesDiagnostic;
import java.nio.file.Path;
import java.util.List;
import org.hamcrest.Matcher;
@@ -128,10 +128,10 @@
} else {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ .assertErrorsMatch(diagnosticType(MissingDefinitionsDiagnosticImpl.class));
- MissingClassesDiagnostic diagnostic =
- (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ MissingDefinitionsDiagnosticImpl diagnostic =
+ (MissingDefinitionsDiagnosticImpl) diagnostics.getErrors().get(0);
assertEquals(1, diagnostic.getMissingClasses().size());
assertEquals(
"nesthostexample.BasicNestHostWithInnerClassMethods",
@@ -167,10 +167,10 @@
} else {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ .assertErrorsMatch(diagnosticType(MissingDefinitionsDiagnosticImpl.class));
- MissingClassesDiagnostic diagnostic =
- (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ MissingDefinitionsDiagnosticImpl diagnostic =
+ (MissingDefinitionsDiagnosticImpl) diagnostics.getErrors().get(0);
assertEquals(1, diagnostic.getMissingClasses().size());
assertEquals(
"nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass",
@@ -201,15 +201,16 @@
diagnostics -> {
diagnostics.assertOnlyWarnings();
if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
- diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ diagnostics.assertWarningsMatch(
+ diagnosticType(MissingDefinitionsDiagnosticImpl.class));
} else {
diagnostics.assertWarningsMatch(
- diagnosticType(MissingClassesDiagnostic.class),
+ diagnosticType(MissingDefinitionsDiagnosticImpl.class),
diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
}
- MissingClassesDiagnostic diagnostic =
- (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+ MissingDefinitionsDiagnosticImpl diagnostic =
+ (MissingDefinitionsDiagnosticImpl) diagnostics.getWarnings().get(0);
assertEquals(1, diagnostic.getMissingClasses().size());
assertEquals(
"nesthostexample.BasicNestHostWithInnerClassMethods",
@@ -234,15 +235,16 @@
diagnostics -> {
diagnostics.assertOnlyWarnings();
if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
- diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ diagnostics.assertWarningsMatch(
+ diagnosticType(MissingDefinitionsDiagnosticImpl.class));
} else {
diagnostics.assertWarningsMatch(
- diagnosticType(MissingClassesDiagnostic.class),
+ diagnosticType(MissingDefinitionsDiagnosticImpl.class),
diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
}
- MissingClassesDiagnostic diagnostic =
- (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+ MissingDefinitionsDiagnosticImpl diagnostic =
+ (MissingDefinitionsDiagnosticImpl) diagnostics.getWarnings().get(0);
assertNotNull(diagnostic);
assertEquals(1, diagnostic.getMissingClasses().size());
assertEquals(
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 ccd5d40..c68ec40 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
@@ -55,7 +55,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
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 b2fb0f5..811d24a 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
@@ -55,7 +55,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
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 02e75d3..fc7a247 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
@@ -68,7 +68,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
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 d567625..eca975d 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
@@ -65,7 +65,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
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 6e5cc6b..245607b 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
@@ -8,7 +8,6 @@
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;
@@ -43,8 +42,7 @@
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 {
+ public static Path[] getJdk15LibraryFiles(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.
@@ -57,7 +55,7 @@
.addSourceFiles(Paths.get("src", "test", "javaStubs", "TypeDescriptor.java"))
.addSourceFiles(Paths.get("src", "test", "javaStubs", "RecordComponent.java"))
.compile();
- builder.addLibraryFiles(ToolHelper.getJava8RuntimeJar()).addLibraryFiles(recordStubs);
+ return new Path[] {recordStubs, ToolHelper.getJava8RuntimeJar()};
}
public static byte[][] getProgramData(String mainClassSimpleName) {
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 e8ef3a4..6d97f29 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
@@ -57,7 +57,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
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 39e503c..ff540d1 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
@@ -56,7 +56,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepRules(RECORD_KEEP_RULE)
.addKeepMainRule(MAIN_TYPE)
- .apply(builder -> RecordTestUtils.setJdk15Library(builder, temp))
+ .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp))
.addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
.addOptionsModification(opt -> opt.testing.canUseRecords = true)
.compile()
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
index 4b1bb9e..9f20b7d 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.shaking.MissingClassesDiagnostic;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -40,7 +40,7 @@
.setDiagnosticsLevelModifier(
(level, diagnostic) -> {
if (level == DiagnosticsLevel.WARNING
- && diagnostic instanceof MissingClassesDiagnostic
+ && diagnostic instanceof MissingDefinitionsDiagnosticImpl
&& diagnostic.getDiagnosticMessage().startsWith(MISSING_CLASS_MESSAGE_PREFIX)) {
return DiagnosticsLevel.INFO;
}
@@ -65,7 +65,7 @@
.setDiagnosticsLevelModifier(
(level, diagnostic) -> {
if (level == DiagnosticsLevel.WARNING
- && diagnostic instanceof MissingClassesDiagnostic
+ && diagnostic instanceof MissingDefinitionsDiagnosticImpl
&& diagnostic.getDiagnosticMessage().startsWith(MISSING_CLASS_MESSAGE_PREFIX)) {
return DiagnosticsLevel.ERROR;
}
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index ae244b5..2eb88a1 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.shaking.MissingClassesDiagnostic;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.io.IOException;
@@ -82,10 +82,11 @@
diagnostics -> {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ .assertErrorsMatch(
+ diagnosticType(MissingDefinitionsDiagnosticImpl.class));
- MissingClassesDiagnostic diagnostic =
- (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ MissingDefinitionsDiagnosticImpl diagnostic =
+ (MissingDefinitionsDiagnosticImpl) diagnostics.getErrors().get(0);
assertEquals(1, diagnostic.getMissingClasses().size());
assertEquals(
MissingException.class.getTypeName(),
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
new file mode 100644
index 0000000..db76bf9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
@@ -0,0 +1,244 @@
+// 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.graph.genericsignature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
+import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper.SignatureEvaluationResult;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignatureCorrectnessHelperTests extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignatureCorrectnessHelperTests(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testAllValid() throws Exception {
+ AppView<AppInfoWithClassHierarchy> appView =
+ computeAppViewWithClassHierachy(
+ buildInnerClasses(GenericSignatureCorrectnessHelperTests.class)
+ .addLibraryFile(ToolHelper.getJava8RuntimeJar())
+ .build());
+ GenericSignatureCorrectnessHelper check =
+ GenericSignatureCorrectnessHelper.createForVerification(appView);
+ check.run();
+ }
+
+ @Test
+ public void testMissingTypeArgumentInClassBound() throws Exception {
+ runTest(
+ ImmutableList.of(Base.class),
+ ImmutableList.of(
+ transformer(ClassWithClassBound.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithClassBound.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMissingTypeArgumentInInterfaceBound() throws Exception {
+ runTest(
+ ImmutableList.of(I.class, J.class),
+ ImmutableList.of(
+ transformer(ClassWithInterfaceBound.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithInterfaceBound.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMembersHavingInvalidTypeReference() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithMembersHavingInvalidTypeReference.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithMembersHavingInvalidTypeReference.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testMethodHavingInvalidTypeReferences() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithMethodMissingTypeParameters.class)
+ .setGenericSignature(
+ MethodPredicate.onName("test"),
+ existing -> {
+ // Replace the generic type parameter T with R.
+ return existing.replace("<T:", "<R:");
+ })
+ .transform()),
+ ClassWithMethodMissingTypeParameters.class,
+ SignatureEvaluationResult.INVALID_TYPE_VARIABLE_UNDEFINED);
+ }
+
+ @Test
+ public void testIncorrectNumberOfSuperInterfaces() throws Exception {
+ runTest(
+ ImmutableList.of(),
+ ImmutableList.of(
+ transformer(ClassWithInvalidNumberOfSuperInterfaces.class)
+ .setImplements(I.class)
+ .transform()),
+ ClassWithInvalidNumberOfSuperInterfaces.class,
+ SignatureEvaluationResult.INVALID_INTERFACE_COUNT);
+ }
+
+ @Test
+ public void testMissingArgument() throws Exception {
+ runTest(
+ ImmutableList.of(J.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidArgumentCount.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type argument <TT;> with nothing
+ return existing.replace("<TT;>", "");
+ })
+ .transform()),
+ ClassWithInvalidArgumentCount.class,
+ SignatureEvaluationResult.INVALID_APPLICATION_COUNT);
+ }
+
+ @Test
+ public void testTooManyArguments() throws Exception {
+ runTest(
+ ImmutableList.of(J.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidArgumentCount.class)
+ .setGenericSignature(
+ existing -> {
+ // Replace the generic type argument <TT;> with nothing
+ return existing.replace("<TT;>", "<TT;TT;>");
+ })
+ .transform()),
+ ClassWithInvalidArgumentCount.class,
+ SignatureEvaluationResult.INVALID_APPLICATION_COUNT);
+ }
+
+ @Test
+ public void testClassWithInvalidSuperType() throws Exception {
+ runTest(
+ ImmutableList.of(Base.class, OtherBase.class),
+ ImmutableList.of(
+ transformer(ClassWithInvalidSuperType.class)
+ .setSuper(DescriptorUtils.javaTypeToDescriptor(OtherBase.class.getTypeName()))
+ .transform()),
+ ClassWithInvalidSuperType.class,
+ SignatureEvaluationResult.INVALID_SUPER_TYPE);
+ }
+
+ private void runTest(
+ List<Class<?>> classes,
+ List<byte[]> transformations,
+ Class<?> classToVerify,
+ SignatureEvaluationResult expected)
+ throws Exception {
+ AppView<AppInfoWithClassHierarchy> appView =
+ computeAppViewWithClassHierachy(
+ buildClasses(classes)
+ .addClassProgramData(transformations)
+ .addLibraryFile(ToolHelper.getJava8RuntimeJar())
+ .build());
+ GenericSignatureCorrectnessHelper check =
+ GenericSignatureCorrectnessHelper.createForInitialCheck(appView);
+ DexProgramClass clazz =
+ appView
+ .definitionFor(
+ appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.javaTypeToDescriptor(classToVerify.getTypeName())))
+ .asProgramClass();
+ assertNotNull(clazz);
+ assertEquals(expected, check.evaluateSignaturesForClass(clazz));
+ }
+
+ public interface I {}
+
+ public interface J<T> {
+ <R extends Object & I & J<Integer>> R foo(T foo) throws CustomException;
+ }
+
+ public static class Base<T> {}
+
+ public static class CustomException extends Exception {}
+
+ public static class Empty {}
+
+ public static class ClassWithClassBound<T extends Base<T /* R */>> {}
+
+ public static class ClassWithInterfaceBound<T extends I & J<T /* R */>> {}
+
+ public abstract static class ClassWithMembersHavingInvalidTypeReference<T /* R */> {
+
+ T t;
+
+ public abstract T testReturn();
+
+ public abstract void testParameter(T t);
+ }
+
+ public abstract static class ClassOverridingTypeArgument<T> {
+
+ public abstract <T> T test();
+ }
+
+ public abstract static class ClassWithMethodMissingTypeParameters {
+
+ public abstract <T /* R */> T test(T foo);
+ }
+
+ public abstract static class ClassWithInvalidNumberOfSuperInterfaces<T>
+ implements I, J<T> /* I */ {}
+
+ public abstract static class ClassWithInvalidArgumentCount<T>
+ implements J<T> /* J and J<T,T> */ {}
+
+ public static class OtherBase<T> {}
+
+ public abstract static class ClassWithInvalidSuperType<T> extends Base<T> /* OtherBase<T> */ {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 73f1561..c141654 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
+import com.android.tools.r8.shaking.EnqueuerResult;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
@@ -76,7 +77,9 @@
Timing timing = Timing.empty();
Enqueuer enqueuer =
EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo);
- appView.setAppInfo(enqueuer.traceApplication(appView.rootSet(), executorService, timing));
+ EnqueuerResult enqueuerResult =
+ enqueuer.traceApplication(appView.rootSet(), executorService, timing);
+ appView.setAppInfo(enqueuerResult.getAppInfo());
return new TestApplication(appView, method, additionalCode);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
index c1b1f8b..b8ad8d9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
@@ -18,6 +18,10 @@
}
}
+ public static IllegalAccessError throwIllegalAccessError() {
+ throw new IllegalAccessError();
+ }
+
public static IncompatibleClassChangeError throwIncompatibleClassChangeError() {
throw new IncompatibleClassChangeError();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 0fb02d7..33848bb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.Code;
@@ -49,14 +50,20 @@
private final List<Path> classpath = new ArrayList<>();
private final List<Path> extraClasspath = new ArrayList<>();
+ protected final TestParameters testParameters;
+
// Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation.
- protected AbstractR8KotlinTestBase(KotlinTestParameters kotlinParameters) {
- this(kotlinParameters, false);
+ protected AbstractR8KotlinTestBase(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ this(parameters, kotlinParameters, false);
}
protected AbstractR8KotlinTestBase(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
super(kotlinParameters);
+ this.testParameters = parameters;
this.allowAccessModification = allowAccessModification;
}
@@ -237,18 +244,19 @@
}
// Build with R8
- return testForR8(Backend.DEX)
+ return testForR8(testParameters.getBackend())
.addProgramFiles(classpath)
.addKeepMainRule(mainClass)
.allowAccessModification(allowAccessModification)
.allowDiagnosticWarningMessages()
.enableProguardTestOptions()
.noMinification()
+ .setMinApi(testParameters.getApiLevel())
.apply(configuration)
.compile()
.assertAllWarningMessagesMatch(
containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
- .run(mainClass)
+ .run(testParameters.getRuntime(), mainClass)
.assertSuccessWithOutput(javaResult.stdout);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 608b92b..3b554fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -4,13 +4,15 @@
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
-import com.android.tools.r8.KotlinTestParametersCollection;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
@@ -37,13 +39,15 @@
@RunWith(Parameterized.class)
public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}")
- public static KotlinTestParametersCollection data() {
- return getKotlinTestParameters().withAllCompilersAndTargetVersions().build();
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public KotlinClassInlinerTest(KotlinTestParameters kotlinParameters) {
- super(kotlinParameters, true);
+ public KotlinClassInlinerTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(parameters, kotlinParameters, true);
}
private static boolean isLambda(DexClass clazz) {
@@ -203,6 +207,8 @@
@Test
public void testDataClass() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72) && testParameters.isDexRuntime());
String mainClassName = "class_inliner_data_class.MainKt";
runTest(
"class_inliner_data_class",
@@ -234,6 +240,7 @@
String... params) {
assertNotNull(clazz);
MethodSignature signature = new MethodSignature(methodName, "void", params);
+ // TODO(b/179866251): Allow for CF code here.
DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
return Stream.concat(
filterInstructionKind(code, NewInstance.class)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index 1598bb8..ecf67ae 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -24,16 +25,19 @@
@RunWith(Parameterized.class)
public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public KotlinClassStaticizerTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
@@ -64,7 +68,10 @@
ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
assertThat(utilClass, isPresent());
- assertTrue(utilClass.allMethods().stream().allMatch(FoundMethodSubject::isStatic));
+ // TODO(b/179951488): The <init> is not removed in CF
+ if (testParameters.isDexRuntime()) {
+ assertTrue(utilClass.allMethods().stream().allMatch(FoundMethodSubject::isStatic));
+ }
});
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index e74d082..b7b1f1b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -22,6 +23,7 @@
@RunWith(Parameterized.class)
public class KotlinDuplicateAnnotationTest extends AbstractR8KotlinTestBase {
+
private static final String FOLDER = "duplicate_annotation";
private static final String MAIN = FOLDER + ".MainKt";
private static final String KEEP_RULES = StringUtils.lines(
@@ -37,7 +39,7 @@
@Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
@@ -48,7 +50,7 @@
TestParameters parameters,
KotlinTestParameters kotlinParameters,
boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ super(parameters, kotlinParameters, allowAccessModification);
this.parameters = parameters;
}
@@ -58,6 +60,8 @@
@Test
public void test_dex() {
+ // TODO(b/179860027): Make it run on all tests.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
assumeTrue("test DEX", parameters.isDexRuntime());
try {
testForR8(parameters.getBackend())
@@ -76,6 +80,8 @@
@Test
public void test_cf() throws Exception {
+ // TODO(b/179860027): Make it run on all tests.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
assumeTrue("test CF", parameters.isCfRuntime());
testForR8(parameters.getBackend())
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index dae8a7d..068ac79 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -20,26 +22,28 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class KotlinIntrinsicsInlineTest extends AbstractR8KotlinTestBase {
+public class KotlinIntrinsicsInlineTest extends KotlinTestBase {
private static final String FOLDER = "intrinsics";
private static final String MAIN = FOLDER + ".InlineKt";
@Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
private final TestParameters parameters;
+ private final boolean allowAccessModification;
public KotlinIntrinsicsInlineTest(
TestParameters parameters,
KotlinTestParameters kotlinParameters,
boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ super(kotlinParameters);
this.parameters = parameters;
+ this.allowAccessModification = allowAccessModification;
}
private static final KotlinCompileMemoizer compiledJars =
@@ -48,6 +52,8 @@
@Test
public void b139432507() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification);
testForR8(parameters.getBackend())
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
.addKeepRules(
@@ -81,12 +87,16 @@
@Test
public void b139432507_isSupported() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification);
assumeTrue("Different inlining behavior on CF backend", parameters.isDexRuntime());
testSingle("isSupported");
}
@Test
public void b139432507_containsArray() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification);
assumeTrue("Different inlining behavior on CF backend", parameters.isDexRuntime());
testSingle("containsArray");
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index fa3b505..e928379 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -21,16 +22,19 @@
@RunWith(Parameterized.class)
public class KotlinUnusedArgumentsInLambdasTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public KotlinUnusedArgumentsInLambdasTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
index 226fb8b..4da0c68 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -3,13 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -22,13 +25,15 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class KotlinUnusedSingletonTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
@@ -37,12 +42,16 @@
"void java.io.PrintStream.println(java.lang.Object)";
public KotlinUnusedSingletonTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
public void b110196118() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
final String mainClassName = "unused_singleton.MainKt";
final String moduleName = "unused_singleton.TestModule";
runTest(
@@ -66,6 +75,7 @@
// The method provideGreeting() is no longer being invoked -- i.e., we have been able
// to determine that the class initialization of the enclosing class is trivial.
ClassSubject module = inspector.clazz(moduleName);
+ // TODO(b/179897889): Should probably check for module being present.
assertThat(main, isPresent());
assertEquals(
0,
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 f53dae8..b0dffa9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
@@ -22,7 +23,7 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, {1}")
+ @Parameterized.Parameters(name = "{0}, target: {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().build(),
@@ -45,8 +46,11 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
+ .allowUnusedProguardConfigurationRules(kotlinc.is(KOTLINC_1_4_20))
.apply(consumer)
- .compile();
+ .compile()
+ .apply(assertUnusedKeepRuleForKotlinMetadata(kotlinc.is(KOTLINC_1_4_20)));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index a405400..8773698 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -9,7 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KotlinTestParameters;
-import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -66,16 +66,19 @@
.addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
.addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public R8KotlinAccessorTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
@@ -331,7 +334,13 @@
TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
String mainClass =
addMainToClasspath("accessors.AccessorKt", "accessor_accessPropertyFromCompanionClass");
- runTest("accessors", mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ "accessors",
+ mainClass,
+ builder -> {
+ builder.addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc));
+ builder.noClassStaticizing();
+ })
.inspect(
inspector -> {
// The classes are removed entirely as a result of member value propagation, inlining,
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index b2f0927..b992613 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.kotlin;
+import static org.junit.Assume.assumeTrue;
+
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -42,20 +45,25 @@
private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public R8KotlinDataClassTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
public void test_dataclass_gettersOnly() throws Exception {
+ // TODO(b/179866251): Allow for CF code.
+ assumeTrue(testParameters.isDexRuntime());
final String mainClassName = "dataclass.MainGettersOnlyKt";
final MethodSignature testMethodSignature =
new MethodSignature("testDataClassGetters", "void", Collections.emptyList());
@@ -100,6 +108,8 @@
@Test
public void test_dataclass_componentOnly() throws Exception {
+ // TODO(b/179866251): Allow for CF code.
+ assumeTrue(testParameters.isDexRuntime());
final String mainClassName = "dataclass.MainComponentOnlyKt";
final MethodSignature testMethodSignature =
new MethodSignature("testAllDataClassComponentFunctions", "void", Collections.emptyList());
@@ -143,6 +153,8 @@
@Test
public void test_dataclass_componentPartial() throws Exception {
+ // TODO(b/179866251): Allow for CF code.
+ assumeTrue(testParameters.isDexRuntime());
final String mainClassName = "dataclass.MainComponentPartialKt";
final MethodSignature testMethodSignature =
new MethodSignature("testSomeDataClassComponentFunctions", "void", Collections.emptyList());
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 11d1ea2..9d9fcee 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
+import static org.junit.Assume.assumeTrue;
+
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -23,20 +27,25 @@
private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public R8KotlinIntrinsicsTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
public void testParameterNullCheckIsInlined() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
final String extraRules = keepClassMethod("intrinsics.IntrinsicsKt",
new MethodSignature("expectsNonNullParameters",
"java.lang.String", Lists.newArrayList("java.lang.String", "java.lang.String")));
@@ -67,7 +76,8 @@
"checkParameterIsNotNull",
"void",
Lists.newArrayList("java.lang.Object", "java.lang.String")),
- !allowAccessModification)
+ // TODO(b/179866251): This is also different on CF
+ !allowAccessModification || testParameters.isCfRuntime())
.build());
});
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index edd2c8f..c7325b0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -93,16 +94,19 @@
o.enableClassStaticizer = false;
};
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public R8KotlinPropertiesTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index c9fd417..b3537a9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -21,16 +24,19 @@
private static final String FOLDER = "non_null";
private static final String STRING = "java.lang.String";
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public SimplifyIfNotNullKotlinTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
@@ -56,13 +62,16 @@
long paramNullCheckCount =
countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
// One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
- assertEquals(2, ifzCount);
+ // TODO(b/179951729): Not the same amount of ifz on CF and DEX.
+ assertEquals(testParameters.isCfRuntime() ? 1 : 2, ifzCount);
assertEquals(0, paramNullCheckCount);
});
}
@Test
public void test_example2() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification);
final TestKotlinClass ex2 = new TestKotlinClass("non_null.Example2Kt");
final MethodSignature testMethodSignature =
new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING));
@@ -83,7 +92,8 @@
long paramNullCheckCount =
countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
// ?: in aOrDefault
- assertEquals(1, ifzCount);
+ // TODO(b/179951729): Not the same amount of ifz on CF and DEX.
+ assertEquals(testParameters.isCfRuntime() ? 0 : 1, ifzCount);
assertEquals(allowAccessModification ? 0 : 1, paramNullCheckCount);
});
}
@@ -109,7 +119,8 @@
testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
// !! operator inside explicit null check should be gone.
// One explicit null-check as well as 4 bar? accesses.
- assertEquals(5, ifzCount);
+ // TODO(b/179951729): Not the same amount of ifz on CF and DEX.
+ assertEquals(testParameters.isCfRuntime() ? 0 : 5, ifzCount);
});
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 1241446..70ae682 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.kotlin.coroutines;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -65,11 +67,15 @@
@Test
public void runKotlinxCoroutinesTests_smoke() throws Exception {
+ // TODO(b/179860018): Make run for 1.4.20
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
runTestsInJar(compileTestSources(BASE_LIBRARY), BASE_LIBRARY);
}
@Test
public void runKotlinxCoroutinesTests_r8() throws Exception {
+ // TODO(b/179860018): Make run for 1.4.20
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
Path baseJar =
testForR8(parameters.getBackend())
.addProgramFiles(BASE_LIBRARY)
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 655ddb5..9d9f42a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -6,12 +6,12 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.DescriptorUtils;
import java.nio.file.Path;
import java.util.Collection;
@@ -20,7 +20,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class KotlinLambdaMergerValidationTest extends AbstractR8KotlinTestBase {
+public class KotlinLambdaMergerValidationTest extends KotlinTestBase {
private final TestParameters parameters;
@@ -33,7 +33,7 @@
public KotlinLambdaMergerValidationTest(
TestParameters parameters, KotlinTestParameters kotlinParameters) {
- super(kotlinParameters, false);
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
index 61f417e..7aaeade 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -6,10 +6,10 @@
import static org.hamcrest.CoreMatchers.equalTo;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -17,7 +17,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class KotlinLambdaMergingDebugTest extends AbstractR8KotlinTestBase {
+public class KotlinLambdaMergingDebugTest extends KotlinTestBase {
private final TestParameters parameters;
private static final String FOLDER = "reprocess_merged_lambdas_kstyle";
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
index 8258582..96135ae 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.lambda;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
@@ -15,16 +16,19 @@
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public KotlinLambdaMergingWithReprocessingTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 087a81b..2700f5e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.lambda;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.Collection;
@@ -14,16 +15,19 @@
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public KotlinLambdaMergingWithSmallInliningBudgetTest(
- KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
- super(kotlinParameters, allowAccessModification);
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(parameters, kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 79cef64..67bb203 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -10,12 +10,12 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -29,7 +29,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class LambdaSplitByCodeCorrectnessTest extends AbstractR8KotlinTestBase {
+public class LambdaSplitByCodeCorrectnessTest extends KotlinTestBase {
private final TestParameters parameters;
private final boolean splitGroup;
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index 3765187..e9324e9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -5,11 +5,14 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertNotNull;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -19,7 +22,7 @@
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
-public abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase {
+public abstract class KotlinMetadataTestBase extends KotlinTestBase {
public KotlinMetadataTestBase(KotlinTestParameters kotlinParameters) {
super(kotlinParameters);
@@ -64,4 +67,12 @@
TestCase.assertEquals(expected, actual);
}
}
+
+ public static void verifyExpectedWarningsFromKotlinReflectAndStdLib(
+ TestCompileResult<?, ?> compileResult) {
+ compileResult.assertAllWarningMessagesMatch(
+ anyOf(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."),
+ equalTo("Resource 'META-INF/versions/9/module-info.class' already exists.")));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 8111567..7afbd44 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -60,9 +60,9 @@
.addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
.compile()
- .assertAllWarningMessagesMatch(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
.run(parameters.getRuntime(), mainClassName);
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(mainClassName);
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 3732458..fcb64b6 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
@@ -4,7 +4,7 @@
package com.android.tools.r8.kotlin.reflection;
-import static org.hamcrest.CoreMatchers.equalTo;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
@@ -12,6 +12,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -91,9 +92,10 @@
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
+ .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.is(KOTLINC_1_4_20))
.compile()
.writeToZip(foo.toPath())
- .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
.run(parameters.getRuntime(), PKG + ".SimpleReflectKt")
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java
new file mode 100644
index 0000000..0af31a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java
@@ -0,0 +1,62 @@
+// 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.ClassReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromClassAnnotationTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromClassAnnotationTest(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(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ @MissingRuntimeAnnotation
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java
new file mode 100644
index 0000000..0918d76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java
@@ -0,0 +1,76 @@
+// 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.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import org.junit.Test;
+
+public class MissingClassReferencedFromClassAnnotationWithDataTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromClassAnnotationWithDataTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ addRuntimeAnnotation());
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addRuntimeAnnotation().andThen(addDontWarn(Main.class)));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addRuntimeAnnotation().andThen(addDontWarn(MissingClass.class)));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addRuntimeAnnotation().andThen(addIgnoreWarnings()));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addRuntimeAnnotation() {
+ return builder ->
+ builder
+ .addProgramClasses(RuntimeAnnotation.class)
+ .addKeepClassRules(RuntimeAnnotation.class)
+ .addKeepRuntimeVisibleAnnotations();
+ }
+
+ @RuntimeAnnotation(data = MissingClass.class)
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface RuntimeAnnotation {
+ Class<?> data();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java
new file mode 100644
index 0000000..7788831
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java
@@ -0,0 +1,67 @@
+// 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.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromFieldAnnotationTest extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(Reference.classFromClass(Main.class), "FIELD", Reference.INT);
+
+ public MissingClassReferencedFromFieldAnnotationTest(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(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ @MissingRuntimeAnnotation static int FIELD;
+
+ public static void main(String[] args) {
+ int ignore = FIELD;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java
new file mode 100644
index 0000000..189b49e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java
@@ -0,0 +1,65 @@
+// 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.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromMethodAnnotationTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromMethodAnnotationTest(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(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ @MissingRuntimeAnnotation
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java
new file mode 100644
index 0000000..0017aa7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java
@@ -0,0 +1,64 @@
+// 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.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromParameterAnnotationTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromParameterAnnotationTest(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(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ public static void main(@MissingRuntimeAnnotation String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index dafb7b4..3968b75 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -14,15 +14,17 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.diagnostic.internal.MissingDefinitionsDiagnosticImpl;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.MissingClassesDiagnostic;
import com.android.tools.r8.utils.FieldReferenceUtils;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.Function;
import org.junit.runner.RunWith;
@@ -39,6 +41,9 @@
int field;
}
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface MissingRuntimeAnnotation {}
+
interface MissingInterface {}
private final TestParameters parameters;
@@ -129,11 +134,11 @@
void inspectDiagnosticsWithIgnoreWarnings(
TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
- MissingClassesDiagnostic diagnostic =
+ MissingDefinitionsDiagnosticImpl diagnostic =
diagnostics
.assertOnlyWarnings()
.assertWarningsCount(1)
- .assertAllWarningsMatch(diagnosticType(MissingClassesDiagnostic.class))
+ .assertAllWarningsMatch(diagnosticType(MissingDefinitionsDiagnosticImpl.class))
.getWarning(0);
assertEquals(ImmutableSet.of(getMissingClassReference()), diagnostic.getMissingClasses());
assertEquals(expectedDiagnosticMessage, diagnostic.getDiagnosticMessage());
@@ -173,11 +178,11 @@
void inspectDiagnosticsWithNoRules(
TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
- MissingClassesDiagnostic diagnostic =
+ MissingDefinitionsDiagnosticImpl diagnostic =
diagnostics
.assertOnlyErrors()
.assertErrorsCount(1)
- .assertAllErrorsMatch(diagnosticType(MissingClassesDiagnostic.class))
+ .assertAllErrorsMatch(diagnosticType(MissingDefinitionsDiagnosticImpl.class))
.getError(0);
assertEquals(ImmutableSet.of(getMissingClassReference()), diagnostic.getMissingClasses());
assertEquals(expectedDiagnosticMessage, diagnostic.getDiagnosticMessage());
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
index 2c3f2bb..2a19802 100644
--- a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -19,10 +20,11 @@
protected final boolean minification;
AbstractR8KotlinNamingTestBase(
+ TestParameters parameters,
KotlinTestParameters kotlinParameters,
boolean allowAccessModification,
boolean minification) {
- super(kotlinParameters, allowAccessModification);
+ super(parameters, kotlinParameters, allowAccessModification);
this.minification = minification;
}
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index 5d9e78b..3aff7c9 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -3,15 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.kotlin.TestKotlinClass;
@@ -37,19 +40,21 @@
public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
private static final String FOLDER = "intrinsics_identifiers";
- @Parameters(name = "{0}, allowAccessModification: {1}, minification: {2}")
+ @Parameters(name = "{0}, {1}, allowAccessModification: {2}, minification: {3}")
public static Collection<Object[]> data() {
return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values(),
BooleanUtils.values());
}
public KotlinIntrinsicsIdentifierTest(
+ TestParameters parameters,
KotlinTestParameters kotlinParameters,
boolean allowAccessModification,
boolean minification) {
- super(kotlinParameters, allowAccessModification, minification);
+ super(parameters, kotlinParameters, allowAccessModification, minification);
}
private static final KotlinCompileMemoizer compiledJars =
@@ -58,6 +63,8 @@
@Test
public void test_example1() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
TestKotlinClass ex1 = new TestKotlinClass("intrinsics_identifiers.Example1Kt");
String targetClassName = "ToBeRenamedClass";
String targetFieldName = "toBeRenamedField";
@@ -67,6 +74,8 @@
@Test
public void test_example2() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
TestKotlinClass ex2 = new TestKotlinClass("intrinsics_identifiers.Example2Kt");
String targetClassName = "AnotherClass";
String targetFieldName = "anotherField";
@@ -76,6 +85,8 @@
@Test
public void test_example3() throws Exception {
+ // TODO(b/179866251): Update tests.
+ assumeTrue(kotlinc.is(KOTLINC_1_3_72));
TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
String mainClassName = ex3.getClassName();
TestCompileResult<?, ?> result =
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
index 2836f61..f59b75d 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming.retrace;
+import static com.android.tools.r8.ir.desugar.LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileName;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
@@ -52,7 +53,7 @@
private int expectedActualStackTraceHeight() {
// In DEX release the entire lambda is inlined.
if (parameters.isDexRuntime()) {
- return mode == CompilationMode.RELEASE ? 1 : 5;
+ return mode == CompilationMode.RELEASE ? 1 : 6;
}
// In CF release it is not and in debug there is no lambda desugaring thus the shorter stack.
return mode == CompilationMode.RELEASE ? 2 : 4;
@@ -60,19 +61,22 @@
private boolean isSynthesizedLambdaFrame(StackTraceLine line) {
// TODO(141287349): The mapping should not map the external name to the internal name!
- return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className));
+ return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className))
+ || line.methodName.startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX);
}
- private void checkLambdaFrame(StackTrace retracedStackTrace) {
+ private void checkLambdaFrames(StackTrace retracedStackTrace) {
StackTrace lambdaFrames = retracedStackTrace.filter(this::isSynthesizedLambdaFrame);
- assertEquals(1, lambdaFrames.size());
- if (lambdaFrames.get(0).hasLineNumber()) {
- assertEquals(mode == CompilationMode.RELEASE ? 0 : 2, lambdaFrames.get(0).lineNumber);
+ assertEquals(2, lambdaFrames.size());
+
+ StackTraceLine syntheticLambdaClassFrame = lambdaFrames.get(1);
+ if (syntheticLambdaClassFrame.hasLineNumber()) {
+ assertEquals(mode == CompilationMode.RELEASE ? 0 : 2, syntheticLambdaClassFrame.lineNumber);
}
// Proguard retrace will take the class name until the first $ to construct the file
// name, so for "-$$Lambda$...", the file name becomes "-.java".
- // TODO(b/141287349): Format the class name of desugard lambda classes.
- // assertEquals("-.java", lambdaFrames.get(0).fileName);
+ // TODO(b/141287349): Format the class name of desugared lambda classes.
+ // assertEquals("-.java", syntheticLambdaClassFrame.fileName);
}
private void checkIsSame(StackTrace actualStackTrace, StackTrace retracedStackTrace) {
@@ -85,7 +89,7 @@
retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
isSame(expectedStackTrace));
// Check the frame from the lambda class.
- checkLambdaFrame(retracedStackTrace);
+ checkLambdaFrames(retracedStackTrace);
}
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
@@ -96,12 +100,12 @@
if (parameters.isCfRuntime()) {
assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace));
} else {
- // With the frame from the lambda class filtered out the stack trace is the same.
+ // With the frames from the lambda class filtered out the stack trace is the same.
assertThat(
retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
isSameExceptForFileName(expectedStackTrace));
// Check the frame from the lambda class.
- checkLambdaFrame(retracedStackTrace);
+ checkLambdaFrames(retracedStackTrace);
}
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
@@ -117,7 +121,7 @@
retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
isSameExceptForFileNameAndLineNumber(expectedStackTrace));
// Check the frame from the lambda class.
- checkLambdaFrame(retracedStackTrace);
+ checkLambdaFrames(retracedStackTrace);
}
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
diff --git a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
index 541ca47..1527d17 100644
--- a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
@@ -49,8 +49,7 @@
.addProgramClasses(TestClass.class)
.run(parameters.getRuntime(), TestClass.class);
- if (isInterface
- || (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm().equals(CfVm.JDK8))) {
+ if (isInterface || parameters.isCfRuntime(CfVm.JDK8)) {
// JDK 8 allows mismatched method references in this case.
result.assertSuccessWithOutput(EXPECTED);
} else {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
new file mode 100644
index 0000000..622b8fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
@@ -0,0 +1,89 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageLambdaMissingInterfaceTest extends RepackageTestBase {
+
+ public RepackageLambdaMissingInterfaceTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8WithoutRepackaging() throws Exception {
+ runTest(false)
+ .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), AbstractMethodError.class)
+ .assertSuccessWithOutputLinesIf(parameters.isCfRuntime(), "0");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult r8TestRunResult = runTest(true);
+ if (parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V4_4_4)) {
+ r8TestRunResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ } else {
+ r8TestRunResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+ }
+
+ private R8TestRunResult runTest(boolean repackage) throws Exception {
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(ClassWithLambda.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .applyIf(repackage, this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(MissingInterface.class)
+ .noClassInlining()
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/179889105): This should probably not be repackaged.
+ assertThat(ClassWithLambda.class, isRepackagedIf(inspector, repackage));
+ })
+ .addRunClasspathClasses(MissingInterface.class)
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ interface MissingInterface {
+
+ void bar(int x);
+ }
+
+ public static class ClassWithLambda {
+
+ @NeverInline
+ public static void callWithLambda() {
+ Main.foo(System.out::println);
+ }
+ }
+
+ public static class Main {
+
+ private static int argCount;
+
+ @NeverInline
+ public static void foo(MissingInterface i) {
+ i.bar(argCount);
+ }
+
+ public static void main(String[] args) {
+ argCount = args.length;
+ ClassWithLambda.callWithLambda();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java
new file mode 100644
index 0000000..e6f8e93
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingMemberReferenceTest.java
@@ -0,0 +1,80 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageMissingMemberReferenceTest extends RepackageTestBase {
+
+ private final String EXPECTED = "MissingReference::doSomething";
+
+ public RepackageMissingMemberReferenceTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8WithoutRepackaging() throws Exception {
+ runTest(false).assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult r8TestRunResult = runTest(true);
+ if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion() == Version.V8_1_0) {
+ r8TestRunResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ r8TestRunResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+ }
+
+ private R8TestRunResult runTest(boolean repackage) throws Exception {
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(ClassWithMissingReferenceInCode.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .applyIf(repackage, this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(MissingReference.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/179889105): This should probably not be repackaged.
+ assertThat(
+ ClassWithMissingReferenceInCode.class, isRepackagedIf(inspector, repackage));
+ })
+ .addRunClasspathClasses(MissingReference.class)
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ static class MissingReference {
+ public static void doSomething() {
+ System.out.println("MissingReference::doSomething");
+ }
+ }
+
+ public static class ClassWithMissingReferenceInCode {
+
+ @NeverInline
+ public static void test() {
+ MissingReference.doSomething();
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ ClassWithMissingReferenceInCode.test();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java
new file mode 100644
index 0000000..8f08c58
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperInterfaceTestTest.java
@@ -0,0 +1,83 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageMissingSuperInterfaceTestTest extends RepackageTestBase {
+
+ public RepackageMissingSuperInterfaceTestTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8WithoutRepackaging() throws Exception {
+ runTest(false).assertSuccessWithOutputLines("ClassImplementingMissingInterface::bar");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult r8TestRunResult = runTest(true);
+ if (parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V4_4_4)) {
+ r8TestRunResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ } else {
+ r8TestRunResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+ }
+
+ private R8TestRunResult runTest(boolean repackage) throws Exception {
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(ClassImplementingMissingInterface.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .applyIf(repackage, this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(MissingInterface.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/179889105): This should probably not be repackaged.
+ assertThat(
+ ClassImplementingMissingInterface.class, isRepackagedIf(inspector, repackage));
+ })
+ .addRunClasspathClasses(MissingInterface.class)
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ private interface MissingInterface {
+
+ void bar();
+ }
+
+ @NeverClassInline
+ public static class ClassImplementingMissingInterface implements MissingInterface {
+
+ @Override
+ @NeverInline
+ public void bar() {
+ System.out.println("ClassImplementingMissingInterface::bar");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new ClassImplementingMissingInterface().bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
new file mode 100644
index 0000000..cb2ce41
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
@@ -0,0 +1,110 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageMissingSuperTypeTest extends RepackageTestBase {
+
+ public RepackageMissingSuperTypeTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8WithoutRepackaging() throws Exception {
+ runTest(false)
+ .assertSuccessWithOutputLines(
+ "ClassWithSuperCall::foo",
+ "MissingSuperType::foo",
+ "ClassWithoutSuperCall::foo",
+ "MissingSuperType::foo");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestRunResult r8TestRunResult = runTest(true);
+ if (parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V4_4_4)) {
+ r8TestRunResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ } else {
+ r8TestRunResult.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+ }
+
+ private R8TestRunResult runTest(boolean repackage) throws Exception {
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(
+ ClassWithSuperCall.class,
+ ClassWithoutSuperCall.class,
+ ClassWithoutDefinition.class,
+ Main.class)
+ .addKeepMainRule(Main.class)
+ .applyIf(repackage, this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(MissingSuperType.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/179889105): These should probably not be repackaged.
+ assertThat(ClassWithSuperCall.class, isRepackagedIf(inspector, repackage));
+ assertThat(ClassWithoutSuperCall.class, isRepackagedIf(inspector, repackage));
+ })
+ .addRunClasspathClasses(MissingSuperType.class)
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ static class MissingSuperType {
+
+ public void foo() {
+ System.out.println("MissingSuperType::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class ClassWithSuperCall extends MissingSuperType {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("ClassWithSuperCall::foo");
+ super.foo();
+ }
+ }
+
+ @NeverClassInline
+ public static class ClassWithoutSuperCall extends MissingSuperType {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("ClassWithoutSuperCall::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class ClassWithoutDefinition extends MissingSuperType {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new ClassWithSuperCall().foo();
+ new ClassWithoutSuperCall().foo();
+ new ClassWithoutDefinition().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageParameterSyntheticOutlineTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageParameterSyntheticOutlineTest.java
new file mode 100644
index 0000000..0636a50
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageParameterSyntheticOutlineTest.java
@@ -0,0 +1,139 @@
+// 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.repackage;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageParameterSyntheticOutlineTest extends RepackageTestBase {
+
+ private final String NEW_DESCRIPTOR = "Lfoo/ClassWithCodeToBeOutlined;";
+ private final String[] EXPECTED =
+ new String[] {"Param::testParam", "Return::print", "Param::testParam", "Return::print"};
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(REPACKAGE_CLASSES), // Repackage will use foo as the package name.
+ getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public RepackageParameterSyntheticOutlineTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Param.class, Return.class)
+ .addProgramClassFileData(
+ rewrittenPackageForClassWithCodeToBeOutlined(),
+ rewrittenMainWithMethodReferencesToCodeToBeOutlined())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Param.class, Return.class)
+ .addProgramClassFileData(
+ rewrittenPackageForClassWithCodeToBeOutlined(),
+ rewrittenMainWithMethodReferencesToCodeToBeOutlined())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRulesWithAllowObfuscation(Param.class, Return.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .addOptionsModification(
+ options -> {
+ options.outline.minSize = 2;
+ options.outline.threshold = 2;
+ })
+ .apply(this::configureRepackaging)
+ .addKeepPackageNamesRule("bar**")
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ // TODO(b/180092122): This should not fail.
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString("The synthetic method reference should have moved")));
+ });
+ }
+
+ private byte[] rewrittenPackageForClassWithCodeToBeOutlined() throws Exception {
+ return transformer(ClassWithCodeToBeOutlined.class)
+ .setClassDescriptor(NEW_DESCRIPTOR)
+ .transform();
+ }
+
+ private byte[] rewrittenMainWithMethodReferencesToCodeToBeOutlined() throws Exception {
+ return transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ DescriptorUtils.javaTypeToDescriptor(ClassWithCodeToBeOutlined.class.getTypeName()),
+ NEW_DESCRIPTOR)
+ .transform();
+ }
+
+ // Will be renamed by repackaging
+ public static class Param {
+
+ @NeverInline
+ public void testParam() {
+ System.out.println("Param::testParam");
+ }
+
+ @NeverInline
+ public Return getReturn() {
+ return new Return();
+ }
+ }
+
+ // Will be renamed by repackaging
+ public static class Return {
+
+ public void print() {
+ System.out.println("Return::print");
+ }
+ }
+
+ // Renamed to baz.ClassWithCodeToBeOutlined such that we can keep this package name.
+ public static class ClassWithCodeToBeOutlined {
+
+ @NeverInline
+ public static Return foo(Param param) {
+ param.testParam();
+ return param.getReturn();
+ }
+
+ @NeverInline
+ public static Return bar(Param param) {
+ param.testParam();
+ return param.getReturn();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Param param = new Param();
+ ClassWithCodeToBeOutlined.foo(param).print();
+ ClassWithCodeToBeOutlined.bar(param).print();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java
new file mode 100644
index 0000000..4df6622
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java
@@ -0,0 +1,104 @@
+// 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithSyntheticItemTest extends RepackageTestBase {
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public RepackageWithSyntheticItemTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(RepackageWithSyntheticItemTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(RepackageWithSyntheticItemTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(I.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(this::configureRepackaging)
+ .noClassInlining()
+ .addInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0")
+ .inspect(
+ inspector -> {
+ // Find the lambda class that starts with foo.
+ List<FoundClassSubject> classesStartingWithfoo =
+ inspector.allClasses().stream()
+ .filter(item -> item.getFinalName().startsWith("foo"))
+ .collect(Collectors.toList());
+ assertEquals(1, classesStartingWithfoo.size());
+ // TODO(b/172014416): We should not be able to look this up through the repackage name
+ String expectedOriginalNamePrefix =
+ isFlattenPackageHierarchy()
+ ? "foo.repackage.RepackageWithSyntheticItemTest$A"
+ : "foo.RepackageWithSyntheticItemTest$A";
+ assertThat(
+ classesStartingWithfoo.get(0).getOriginalName(),
+ containsString(expectedOriginalNamePrefix));
+ });
+ }
+
+ public static class A {
+
+ @NeverInline
+ public static void testLambda() {
+ Main.test(System.out::println);
+ }
+ }
+
+ public interface I {
+ void foo(int x);
+ }
+
+ public static class Main {
+
+ private static int argCount = 0;
+
+ @NeverInline
+ public static void test(I i) {
+ i.foo(argCount);
+ }
+
+ public static void main(String[] args) {
+ argCount = args.length;
+ A.testLambda();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index 07487ec..af6e166 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -171,10 +171,6 @@
.addProgramClassFileData(getTransformedClasses())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
- .applyIf(
- !parameters.canUseDefaultAndStaticInterfaceMethods()
- && !symbolicReferenceIsDefiningType,
- builder -> builder.addDontWarnCompanionClass(J.class))
.run(parameters.getRuntime(), Main.class)
.apply(result -> checkExpectedResult(result, true));
}
@@ -185,19 +181,11 @@
"TODO(b/144410139): Input does not verify. Should compilation throw an error?",
parameters.isCfRuntime() && !isR8);
result.assertFailureWithErrorThatMatches(containsString(VerifyError.class.getName()));
- } else if (isDesugaring()) {
- // TODO(b/145775365): Desugaring results in a reference to a non-existent companion class.
- result.assertFailureWithErrorThatMatches(
- containsString(NoClassDefFoundError.class.getName()));
} else {
result.assertFailureWithErrorThatMatches(containsString(NoSuchMethodError.class.getName()));
}
}
- private boolean isDesugaring() {
- return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N);
- }
-
interface I {
/* will be private */ default void bar() {
System.out.println("I::bar");
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 2479dce..b16dc35 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -12,9 +12,10 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -28,7 +29,6 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -42,7 +42,7 @@
private final TestParameters parameters;
- @Parameters(name = "{0}, {1}")
+ @Parameters(name = "{0}, target: {1}")
public static List<Object[]> data() {
// TODO(b/141817471): Extend with compilation modes.
return buildParameters(
@@ -69,7 +69,9 @@
}
@Test
- public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ public void testRuntime() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
testForRuntime(parameters)
.addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
.addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc)))
@@ -81,8 +83,9 @@
}
@Test
- public void testRetraceKotlinInlineStaticFunction()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRetraceKotlinInlineStaticFunction() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 945b732..ffc5515 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -13,9 +13,10 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -30,7 +31,6 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -84,7 +84,7 @@
}
@Test
- public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ public void testRuntime() throws Exception {
testForRuntime(parameters)
.addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
.addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc)))
@@ -94,8 +94,9 @@
}
@Test
- public void testRetraceKotlinInlineStaticFunction()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRetraceKotlinInlineStaticFunction() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainKt";
String mainFileName = "Main.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -125,8 +126,9 @@
}
@Test
- public void testRetraceKotlinInlineInstanceFunction()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRetraceKotlinInlineInstanceFunction() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainInstanceKt";
String mainFileName = "MainInstance.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -159,8 +161,9 @@
}
@Test
- public void testRetraceKotlinNestedInlineFunction()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRetraceKotlinNestedInlineFunction() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainNestedKt";
String mainFileName = "MainNested.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
@@ -192,8 +195,9 @@
}
@Test
- public void testRetraceKotlinNestedInlineFunctionOnFirstLine()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRetraceKotlinNestedInlineFunctionOnFirstLine() throws Exception {
+ // TODO(b/179666509): SMAP has changed.
+ assumeTrue(kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72));
String main = "retrace.MainNestedFirstLineKt";
String mainFileName = "MainNestedFirstLine.kt";
Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion);
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index 2cc7c96..0298ecd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -161,6 +161,7 @@
b.addRunClasspathFiles(kotlinStdlibLibraryForRuntime());
},
b -> b.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)))
+ .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc))
.addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
.addKeepMainRule(testClassKt)
.addKeepClassAndMembersRules(class1, class2)
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 7ba46d2..63c9f40 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -13,7 +13,9 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
import com.android.tools.r8.ToolHelper;
import java.io.BufferedReader;
import java.io.IOException;
@@ -23,8 +25,7 @@
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.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Rule;
@@ -36,17 +37,19 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class TreeShakingSpecificTest {
+public class TreeShakingSpecificTest extends TestBase {
private Backend backend;
- @Parameters(name = "Backend: {0}")
- public static Collection<Backend> data() {
- return Arrays.asList(Backend.values());
+ @Parameters(name = "Backend: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ TestParametersBuilder.builder().withNoneRuntime().build(), Backend.values());
}
- public TreeShakingSpecificTest(Backend backend) {
+ public TreeShakingSpecificTest(TestParameters parameters, Backend backend) {
this.backend = backend;
+ parameters.assertNoneRuntime();
}
private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
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 2f098bf..f828cfc 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -31,12 +31,14 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -330,6 +332,10 @@
}
public ClassFileTransformer setGenericSignature(String newGenericSignature) {
+ return setGenericSignature(signature -> newGenericSignature);
+ }
+
+ public ClassFileTransformer setGenericSignature(Function<String, String> newGenericSignature) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -340,7 +346,8 @@
String signature,
String superName,
String[] interfaces) {
- super.visit(version, access, name, newGenericSignature, superName, interfaces);
+ super.visit(
+ version, access, name, newGenericSignature.apply(signature), superName, interfaces);
}
});
}
@@ -586,6 +593,21 @@
});
}
+ public ClassFileTransformer setGenericSignature(
+ MethodPredicate predicate, Function<String, String> newSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ return predicate.test(access, name, descriptor, signature, exceptions)
+ ? super.visitMethod(
+ access, name, descriptor, newSignature.apply(signature), exceptions)
+ : super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+ });
+ }
+
public ClassFileTransformer removeFields(FieldPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {
@@ -649,6 +671,17 @@
});
}
+ /** Abstraction of the MethodVisitor.visitInvokeDynamicInsn method with its sub visitor. */
+ @FunctionalInterface
+ public interface InvokeDynamicInsnTransform {
+ void visitInvokeDynamicInsn(
+ String name,
+ String descriptor,
+ Handle bootstrapMethodHandle,
+ List<Object> bootstrapMethodArguments,
+ MethodVisitor visitor);
+ }
+
/** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
@FunctionalInterface
public interface MethodInsnTransform {
@@ -742,6 +775,55 @@
}
@FunctionalInterface
+ private interface VisitInvokeDynamicInsnCallback {
+ void visitInvokeDynamicInsn(
+ String name,
+ String descriptor,
+ Handle bootstrapMethodHandle,
+ Object... bootstrapMethodArguments);
+ }
+
+ private MethodVisitor redirectVisitInvokeDynamicInsn(
+ MethodVisitor visitor, VisitInvokeDynamicInsnCallback callback) {
+ return new MethodVisitor(ASM7, visitor) {
+ @Override
+ public void visitInvokeDynamicInsn(
+ String name,
+ String descriptor,
+ Handle bootstrapMethodHandle,
+ Object... bootstrapMethodArguments) {
+ callback.visitInvokeDynamicInsn(
+ name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
+ }
+ };
+ }
+
+ public ClassFileTransformer transformInvokeDynamicInsnInMethod(
+ String methodName, InvokeDynamicInsnTransform transform) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitInvokeDynamicInsn(
+ String name,
+ String descriptor,
+ Handle bootstrapMethodHandle,
+ Object... bootstrapMethodArguments) {
+ if (getContext().method.getMethodName().equals(methodName)) {
+ transform.visitInvokeDynamicInsn(
+ name,
+ descriptor,
+ bootstrapMethodHandle,
+ Arrays.asList(bootstrapMethodArguments),
+ redirectVisitInvokeDynamicInsn(this, super::visitInvokeDynamicInsn));
+ } else {
+ super.visitInvokeDynamicInsn(
+ name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
+ }
+ }
+ });
+ }
+
+ @FunctionalInterface
private interface VisitMethodInsnCallback {
void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface);
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 343a7f2..403212f 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -44,6 +44,9 @@
help='GitHub account to clone from.',
default="google",
type="string", action="store")
+ result.add_option('--build_only', '--build-only',
+ help='Build desugared library without archiving.',
+ type="string", action="store")
(options, args) = result.parse_args(argv)
return (options, args)
@@ -77,20 +80,44 @@
print('File available at: %s' %
destination.replace('gs://', 'https://storage.googleapis.com/', 1))
+def CloneDesugaredLibrary(github_account, checkout_dir):
+ git_utils.GitClone(
+ 'https://github.com/'
+ + github_account + '/' + LIBRARY_NAME, checkout_dir)
+
+def BuildDesugaredLibrary(checkout_dir):
+ with utils.ChangedWorkingDirectory(checkout_dir):
+ bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel')
+ cmd = [bazel, 'build', 'maven_release']
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
+ cmd = [bazel, 'shutdown']
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
+
+ # Locate the library jar and the maven zip with the jar from the
+ # bazel build.
+ library_jar = os.path.join(
+ checkout_dir, 'bazel-bin', 'src', 'share', 'classes', 'java', 'libjava.jar')
+ maven_zip = os.path.join(checkout_dir, 'bazel-bin', LIBRARY_NAME +'.zip')
+ return (library_jar, maven_zip)
+
+
+def MustBeExistingDirectory(path):
+ if (not os.path.exists(path) or not os.path.isdir(path)):
+ raise Exception(path + ' does not exist or is not a directory')
def Main(argv):
(options, args) = ParseOptions(argv)
if (len(args) > 0):
raise Exception('Unsupported arguments')
- if not utils.is_bot() and not options.dry_run:
+ if not utils.is_bot() and not (options.dry_run or options.build_only):
raise Exception('You are not a bot, don\'t archive builds. '
- + 'Use --dry-run to test locally')
- if (options.dry_run_output and
- (not os.path.exists(options.dry_run_output) or
- not os.path.isdir(options.dry_run_output))):
- raise Exception(options.dry_run_output
- + ' does not exist or is not a directory')
-
+ + 'Use --dry-run or --build-only to test locally')
+ if options.dry_run_output:
+ MustBeExistingDirectory(options.dry_run_output)
+ if options.build_only:
+ MustBeExistingDirectory(options.build_only)
if utils.is_bot():
archive.SetRLimitToMax()
@@ -98,57 +125,54 @@
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
+ if options.build_only:
+ with utils.TempDir() as checkout_dir:
+ CloneDesugaredLibrary(options.github_account, checkout_dir)
+ (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir)
+ shutil.copyfile(
+ library_jar,
+ os.path.join(options.build_only, os.path.basename(library_jar)))
+ shutil.copyfile(
+ maven_zip,
+ os.path.join(options.build_only, os.path.basename(maven_zip)))
+ return
+
# Only handling versioned desugar_jdk_libs.
is_master = False
with utils.TempDir() as checkout_dir:
- git_utils.GitClone(
- 'https://github.com/'
- + options.github_account + '/' + LIBRARY_NAME, checkout_dir)
- with utils.ChangedWorkingDirectory(checkout_dir):
- version = GetVersion(VERSION_FILE)
+ CloneDesugaredLibrary(options.github_account, checkout_dir)
+ version = GetVersion(os.path.join(checkout_dir, VERSION_FILE))
- destination = archive.GetVersionDestination(
- 'gs://', LIBRARY_NAME + '/' + version, is_master)
- if utils.cloud_storage_exists(destination) and not options.dry_run:
- raise Exception(
- 'Target archive directory %s already exists' % destination)
+ destination = archive.GetVersionDestination(
+ 'gs://', LIBRARY_NAME + '/' + version, is_master)
+ if utils.cloud_storage_exists(destination) and not options.dry_run:
+ raise Exception(
+ 'Target archive directory %s already exists' % destination)
- bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel')
- cmd = [bazel, 'build', 'maven_release']
- utils.PrintCmd(cmd)
- subprocess.check_call(cmd)
- cmd = [bazel, 'shutdown']
- utils.PrintCmd(cmd)
- subprocess.check_call(cmd)
+ (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir)
- # Locate the library jar and the maven zip with the jar from the
- # bazel build.
- library_jar = os.path.join(
- 'bazel-bin', 'src', 'share', 'classes', 'java', 'libjava.jar')
- maven_zip = os.path.join('bazel-bin', LIBRARY_NAME +'.zip')
+ storage_path = LIBRARY_NAME + '/' + version
+ # Upload the jar file with the library.
+ destination = archive.GetUploadDestination(
+ storage_path, LIBRARY_NAME + '.jar', is_master)
+ Upload(options, library_jar, storage_path, destination, is_master)
- storage_path = LIBRARY_NAME + '/' + version
- # Upload the jar file with the library.
- destination = archive.GetUploadDestination(
- storage_path, LIBRARY_NAME + '.jar', is_master)
- Upload(options, library_jar, storage_path, destination, is_master)
+ # Upload the maven zip file with the library.
+ destination = archive.GetUploadDestination(
+ storage_path, LIBRARY_NAME + '.zip', is_master)
+ Upload(options, maven_zip, storage_path, destination, is_master)
- # Upload the maven zip file with the library.
- destination = archive.GetUploadDestination(
- storage_path, LIBRARY_NAME + '.zip', is_master)
- Upload(options, maven_zip, storage_path, destination, is_master)
-
- # Upload the jar file for accessing GCS as a maven repro.
- maven_destination = archive.GetUploadDestination(
- utils.get_maven_path('desugar_jdk_libs', version),
- 'desugar_jdk_libs-%s.jar' % version,
- is_master)
- if options.dry_run:
- print('Dry run, not actually creating maven repo')
- else:
- utils.upload_file_to_cloud_storage(library_jar, maven_destination)
- print('Maven repo root available at: %s' % archive.GetMavenUrl(is_master))
+ # Upload the jar file for accessing GCS as a maven repro.
+ maven_destination = archive.GetUploadDestination(
+ utils.get_maven_path('desugar_jdk_libs', version),
+ 'desugar_jdk_libs-%s.jar' % version,
+ is_master)
+ if options.dry_run:
+ print('Dry run, not actually creating maven repo')
+ else:
+ utils.upload_file_to_cloud_storage(library_jar, maven_destination)
+ print('Maven repo root available at: %s' % archive.GetMavenUrl(is_master))
if __name__ == '__main__':
diff --git a/tools/test.py b/tools/test.py
index dffc4565..546a25f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -16,6 +16,7 @@
import time
import uuid
+import archive_desugar_jdk_libs
import gradle
import notify
import utils
@@ -161,6 +162,8 @@
help='Enable Java debug agent and suspend compilation (default disabled)',
default=False,
action='store_true')
+ result.add_option('--desugared-library', '--desugared_library',
+ help='Build and use desugared library from GitHub.')
return result.parse_args()
def archive_failures():
@@ -178,6 +181,23 @@
if utils.is_bot():
gradle.RunGradle(['--no-daemon', 'clean'])
+ desugar_jdk_libs = None
+ if options.desugared_library:
+ if options.desugared_library != 'HEAD':
+ print("Only value supported for --desugared-library is 'HEAD'")
+ exit(1)
+ desugar_jdk_libs_dir = 'build/desugar_jdk_libs'
+ shutil.rmtree(desugar_jdk_libs_dir, ignore_errors=True)
+ os.makedirs(desugar_jdk_libs_dir)
+ print('Building desugared library.')
+ with utils.TempDir() as checkout_dir:
+ archive_desugar_jdk_libs.CloneDesugaredLibrary('google', checkout_dir)
+ (library_jar, maven_zip) = archive_desugar_jdk_libs.BuildDesugaredLibrary(checkout_dir)
+ desugar_jdk_libs = os.path.join(desugar_jdk_libs_dir, os.path.basename(library_jar))
+ shutil.copyfile(library_jar, desugar_jdk_libs)
+ print('Desugared library for test in ' + desugar_jdk_libs)
+
+
gradle_args = ['--stacktrace']
if utils.is_bot():
# Bots don't like dangling processes.
@@ -261,6 +281,9 @@
gradle_args.append('--no-daemon')
if options.debug_agent:
gradle_args.append('--no-daemon')
+ if desugar_jdk_libs:
+ gradle_args.append('-Pdesugar_jdk_libs=' + desugar_jdk_libs)
+
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
gradle_args.append('r8WithDeps')