Rewrite non-rebound field references during class merging
Change-Id: I2577c6c5e600e75964eb6f974057efd35290752f
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0f43472..5e8e3a6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -484,7 +484,7 @@
}
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run());
+ appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run(executorService));
appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView);
if (options.shouldDesugarNests()) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index a7bd6c1..877f2ba 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
+import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -598,17 +599,26 @@
assert application.verifyWithLens(lens);
// The application has already been rewritten with the given applied lens. Therefore, we
- // temporarily replace that lens with the identity lens to avoid the overhead of traversing
- // the entire lens chain upon each lookup during the rewriting.
- NonIdentityGraphLens temporaryRootLens = lens;
- while (temporaryRootLens.getPrevious() != appliedLens) {
- GraphLens previousLens = temporaryRootLens.getPrevious();
+ // temporarily replace that lens with a lens that does not have any rewritings to avoid the
+ // overhead of traversing the entire lens chain upon each lookup during the rewriting.
+ NonIdentityGraphLens firstUnappliedLens = lens;
+ while (firstUnappliedLens.getPrevious() != appliedLens) {
+ GraphLens previousLens = firstUnappliedLens.getPrevious();
assert previousLens.isNonIdentityLens();
- temporaryRootLens = previousLens.asNonIdentityLens();
+ firstUnappliedLens = previousLens.asNonIdentityLens();
}
- temporaryRootLens.withAlternativeParentLens(
- GraphLens.getIdentityLens(),
+ // Insert a member rebinding lens above the first unapplied lens.
+ MemberRebindingLens appliedMemberRebindingLens =
+ firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingLens);
+ GraphLens newMemberRebindingLens =
+ appliedMemberRebindingLens != null
+ ? appliedMemberRebindingLens.toRewrittenFieldRebindingLens(
+ appView.dexItemFactory(), appliedLens)
+ : GraphLens.getIdentityLens();
+
+ firstUnappliedLens.withAlternativeParentLens(
+ newMemberRebindingLens,
() -> {
appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index ecb09ca..977d06d 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -21,6 +21,10 @@
return null;
}
+ public DexField getResolvedFieldReference() {
+ return null;
+ }
+
public boolean isSuccessfulResolution() {
return false;
}
@@ -78,6 +82,11 @@
}
@Override
+ public DexField getResolvedFieldReference() {
+ return resolvedField.toReference();
+ }
+
+ @Override
public DexEncodedField getResolvedMember() {
return resolvedField;
}
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 f2de6db..b3f5d14 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
/**
* A GraphLens implements a virtual view on top of the graph, used to delay global rewrites until
@@ -348,15 +349,21 @@
public DexField lookupField(DexField field) {
// Lookup the field using the graph lens and return the (non-rebound) reference from the lookup
// result.
- return internalLookupField(field, FieldLookupResult::getReference);
+ return lookupFieldResult(field).getReference();
}
- protected abstract DexField internalLookupField(
+ /** Lookup a rebound or non-rebound field reference using the current graph lens. */
+ public FieldLookupResult lookupFieldResult(DexField field) {
+ // Lookup the field using the graph lens and return the lookup result.
+ return internalLookupField(field, x -> x);
+ }
+
+ protected abstract FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation);
interface LookupFieldContinuation {
- DexField lookupField(FieldLookupResult previous);
+ FieldLookupResult lookupField(FieldLookupResult previous);
}
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
@@ -408,6 +415,10 @@
public abstract boolean isIdentityLens();
+ public boolean isMemberRebindingLens() {
+ return false;
+ }
+
public abstract boolean isNonIdentityLens();
public NonIdentityGraphLens asNonIdentityLens() {
@@ -616,6 +627,19 @@
return previousLens;
}
+ @SuppressWarnings("unchecked")
+ public final <T extends GraphLens> T findPrevious(Predicate<NonIdentityGraphLens> predicate) {
+ GraphLens current = getPrevious();
+ while (current.isNonIdentityLens()) {
+ NonIdentityGraphLens nonIdentityGraphLens = current.asNonIdentityLens();
+ if (predicate.test(nonIdentityGraphLens)) {
+ return (T) nonIdentityGraphLens;
+ }
+ current = nonIdentityGraphLens.getPrevious();
+ }
+ return null;
+ }
+
public final void withAlternativeParentLens(GraphLens lens, Action action) {
GraphLens oldParent = getPrevious();
previousLens = lens;
@@ -663,7 +687,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
return previousLens.internalLookupField(
reference, previous -> continuation.lookupField(internalDescribeLookupField(previous)));
@@ -772,7 +796,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
// Passes the field reference back to the next graph lens. The identity lens intentionally
// does not set the rebound field reference, since it does not know what that is.
@@ -847,7 +871,7 @@
}
@Override
- protected DexField internalLookupField(
+ protected FieldLookupResult internalLookupField(
DexField reference, LookupFieldContinuation continuation) {
return getIdentityLens().internalLookupField(reference, continuation);
}
@@ -981,12 +1005,15 @@
if (previous.hasReboundReference()) {
// Rewrite the rebound reference and then "fixup" the non-rebound reference.
DexField rewrittenReboundReference = previous.getRewrittenReboundReference(fieldMap);
+ DexField rewrittenNonReboundReference =
+ previous.getReference() == previous.getReboundReference()
+ ? rewrittenReboundReference
+ : rewrittenReboundReference.withHolder(
+ internalDescribeLookupClassType(previous.getReference().getHolderType()),
+ dexItemFactory);
return FieldLookupResult.builder(this)
.setReboundReference(rewrittenReboundReference)
- .setReference(
- rewrittenReboundReference.withHolder(
- internalDescribeLookupClassType(previous.getReference().getHolderType()),
- dexItemFactory))
+ .setReference(rewrittenNonReboundReference)
.build();
} else {
// TODO(b/168282032): We should always have the rebound reference, so this should become
@@ -1011,8 +1038,8 @@
internalDescribeLookupClassType(previous.getReference().getHolderType()),
dexItemFactory);
return MethodLookupResult.builder(this)
- .setReboundReference(rewrittenReboundReference)
.setReference(rewrittenReference)
+ .setReboundReference(rewrittenReboundReference)
.setPrototypeChanges(
internalDescribePrototypeChanges(
previous.getPrototypeChanges(), rewrittenReboundReference))
@@ -1090,18 +1117,18 @@
*
* <p>Handle methods moved from interface to class or class to interface.
*/
- protected final Type mapVirtualInterfaceInvocationTypes(
+ public static Type mapVirtualInterfaceInvocationTypes(
DexDefinitionSupplier definitions,
DexMethod newMethod,
DexMethod originalMethod,
Type type) {
if (type == Type.VIRTUAL || type == Type.INTERFACE) {
// Get the invoke type of the actual definition.
- DexClass newTargetClass = definitions.definitionFor(newMethod.holder);
+ DexClass newTargetClass = definitions.definitionFor(newMethod.getHolderType());
if (newTargetClass == null) {
return type;
}
- DexClass originalTargetClass = definitions.definitionFor(originalMethod.holder);
+ DexClass originalTargetClass = definitions.definitionFor(originalMethod.getHolderType());
if (originalTargetClass != null
&& (originalTargetClass.isInterface() ^ (type == Type.INTERFACE))) {
// The invoke was wrong to start with, so we keep it wrong. This is to ensure we get
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 8b827d3..0512e69 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -32,16 +32,20 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessControl;
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.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexEncodedField;
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.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
@@ -374,7 +378,7 @@
{
InstanceGet instanceGet = current.asInstanceGet();
DexField field = instanceGet.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupGetFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -399,7 +403,7 @@
{
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -421,7 +425,7 @@
{
StaticGet staticGet = current.asStaticGet();
DexField field = staticGet.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupGetFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -445,7 +449,7 @@
{
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = rewriteFieldReference(field, method, graphLens);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
@@ -581,6 +585,22 @@
assert code.hasNoVerticallyMergedClasses(appView);
}
+ private DexField rewriteFieldReference(
+ DexField reference, ProgramMethod context, GraphLens graphLens) {
+ FieldLookupResult lookup = graphLens.lookupFieldResult(reference);
+ if (lookup.hasReboundReference()) {
+ DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
+ DexEncodedField definition = lookup.getReboundReference().lookupOnClass(holder);
+ if (definition != null) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ if (AccessControl.isMemberAccessible(field, holder, context, appView).isTrue()) {
+ return lookup.getReboundReference();
+ }
+ }
+ }
+ return lookup.getReference();
+ }
+
// If the initialValue is a default value and its type is rewritten from a reference type to a
// primitive type, then the default value type lattice needs to be changed.
private Value rewriteValueIfDefault(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
index 7bc5e6b..7f99055 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -22,7 +22,6 @@
private final DexType nestConstructorType;
private final Map<DexField, DexMethod> getFieldMap;
private final Map<DexField, DexMethod> putFieldMap;
- private final AppView<?> appView;
NestedPrivateMethodLens(
AppView<?> appView,
@@ -43,7 +42,6 @@
assert methodMap instanceof IdentityHashMap;
assert getFieldMap instanceof IdentityHashMap;
assert putFieldMap instanceof IdentityHashMap;
- this.appView = appView;
this.nestConstructorType = nestConstructorType;
this.getFieldMap = getFieldMap;
this.putFieldMap = putFieldMap;
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
new file mode 100644
index 0000000..7e66078
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize;
+
+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.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This lens is used to populate the rebound field reference during lookup, such that both the
+ * non-rebound and rebound field references are available to all descendants of this lens.
+ *
+ * <p>TODO(b/157616970): All uses of this should be replaced by {@link MemberRebindingIdentityLens}.
+ */
+public class FieldRebindingIdentityLens extends NonIdentityGraphLens {
+
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
+
+ private FieldRebindingIdentityLens(
+ Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap,
+ DexItemFactory dexItemFactory,
+ GraphLens previousLens) {
+ super(dexItemFactory, previousLens);
+ this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public boolean hasCodeRewritings() {
+ return false;
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ assert previous.getReboundReference() == null;
+ return FieldLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundFieldReference(previous.getReference()))
+ .build();
+ }
+
+ private DexField getReboundFieldReference(DexField field) {
+ return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ }
+
+ @Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(method);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ return getPrevious().getRenamedMethodSignature(originalMethod, applied);
+ }
+
+ @Override
+ public final DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+
+ @Override
+ public MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ return previous;
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ public static class Builder {
+
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+
+ private Builder() {}
+
+ void recordDefinitionForNonReboundFieldReference(
+ DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
+ }
+
+ FieldRebindingIdentityLens build(DexItemFactory dexItemFactory) {
+ // This intentionally does not return null when the map is empty. In this case there are no
+ // non-rebound field references, but the member rebinding lens is still needed to populate the
+ // rebound reference during field lookup.
+ return new FieldRebindingIdentityLens(
+ nonReboundFieldReferenceToDefinitionMap, dexItemFactory, GraphLens.getIdentityLens());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index cf6f791..9d4121c 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -3,28 +3,31 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
-import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -34,14 +37,14 @@
private final GraphLens lens;
private final InternalOptions options;
- private final MemberRebindingLens.Builder builder;
+ private final MemberRebindingLens.Builder lensBuilder;
public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) {
assert appView.graphLens().isContextFreeForMethods();
this.appView = appView;
this.lens = appView.graphLens();
this.options = appView.options();
- this.builder = MemberRebindingLens.builder(appView);
+ this.lensBuilder = MemberRebindingLens.builder(appView);
}
private DexMethod validTargetFor(DexMethod target, DexMethod original) {
@@ -192,7 +195,8 @@
method, target, originalClass, targetClass, lookupTarget);
}
}
- builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
+ lensBuilder.map(
+ method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
});
}
@@ -310,59 +314,132 @@
return null;
}
- private void computeFieldRebinding() {
+ private void recordNonReboundFieldAccesses(ExecutorService executorService)
+ throws ExecutionException {
+ assert verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(executorService);
FieldAccessInfoCollection<?> fieldAccessInfoCollection =
appView.appInfo().getFieldAccessInfoCollection();
- fieldAccessInfoCollection.forEach(this::computeFieldRebindingForIndirectAccesses);
+ fieldAccessInfoCollection.forEach(lensBuilder::recordNonReboundFieldAccesses);
}
- private void computeFieldRebindingForIndirectAccesses(FieldAccessInfo fieldAccessInfo) {
- fieldAccessInfo.forEachIndirectAccessWithContexts(
- this::computeFieldRebindingForIndirectAccessWithContexts);
- }
-
- private void computeFieldRebindingForIndirectAccessWithContexts(
- DexField field, ProgramMethodSet contexts) {
- SuccessfulFieldResolutionResult resolutionResult =
- appView.appInfo().resolveField(field).asSuccessfulResolution();
- if (resolutionResult == null) {
- return;
- }
-
- DexClassAndField resolvedField = resolutionResult.getResolutionPair();
- if (resolvedField.getReference() == field) {
- assert false;
- return;
- }
-
- // Rebind to the lowest library class or program class. Do not rebind accesses to fields that
- // are not visible from the access context.
- boolean accessibleInAllContexts = true;
- for (ProgramMethod context : contexts) {
- boolean inaccessibleInContext =
- AccessControl.isMemberAccessible(
- resolvedField, resolutionResult.getResolvedHolder(), context, appView)
- .isPossiblyFalse();
- if (inaccessibleInContext) {
- accessibleInAllContexts = false;
- break;
- }
- }
-
- if (accessibleInAllContexts) {
- builder.map(
- field,
- lens.lookupField(
- validTargetFor(resolvedField.getReference(), field, DexClass::lookupField)));
- }
- }
-
- public GraphLens run() {
+ public MemberRebindingLens run(ExecutorService executorService) throws ExecutionException {
AppInfoWithLiveness appInfo = appView.appInfo();
computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
- computeFieldRebinding();
- GraphLens lens = builder.build(this.lens);
+ recordNonReboundFieldAccesses(executorService);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
- return lens;
+ return lensBuilder.build();
+ }
+
+ private boolean verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(
+ ExecutorService executorService) throws ExecutionException {
+ Set<DexField> nonReboundFieldReferences = computeNonReboundFieldReferences(executorService);
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection =
+ appView.appInfo().getFieldAccessInfoCollection();
+ fieldAccessInfoCollection.forEach(
+ info -> {
+ DexField reboundFieldReference = info.getField();
+ info.forEachIndirectAccess(
+ nonReboundFieldReference -> {
+ assert reboundFieldReference != nonReboundFieldReference;
+ assert reboundFieldReference
+ == appView
+ .appInfo()
+ .resolveField(nonReboundFieldReference)
+ .getResolvedFieldReference();
+ nonReboundFieldReferences.remove(nonReboundFieldReference);
+ });
+ });
+ assert nonReboundFieldReferences.isEmpty();
+ return true;
+ }
+
+ private Set<DexField> computeNonReboundFieldReferences(ExecutorService executorService)
+ throws ExecutionException {
+ Set<DexField> nonReboundFieldReferences = Sets.newConcurrentHashSet();
+ ThreadUtils.processItems(
+ appView.appInfo()::forEachMethod,
+ method -> {
+ if (method.getDefinition().hasCode()) {
+ method.registerCodeReferences(
+ new UseRegistry(appView.dexItemFactory()) {
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerFieldReference(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerFieldReference(field);
+ }
+
+ private void registerFieldReference(DexField field) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().resolveField(field).asSuccessfulResolution();
+ if (resolutionResult != null
+ && resolutionResult.getResolvedField().toReference() != field) {
+ nonReboundFieldReferences.add(field);
+ }
+ }
+
+ @Override
+ public void registerInitClass(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Intentionally empty.
+ }
+ });
+ }
+ },
+ executorService);
+ return nonReboundFieldReferences;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 944af37..8e3f4c1 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -165,12 +165,14 @@
synchronized (fieldAccessInfo) {
// Record the fact that there is a non-rebound access to the given field. We don't
// distinguish between non-rebound reads and writes, so we just record it as a read.
- ConcreteAccessContexts accessContexts =
- fieldAccessInfo.getReadsWithContexts().isConcrete()
- ? fieldAccessInfo.getReadsWithContexts().asConcrete()
- : new ConcreteAccessContexts();
+ if (fieldAccessInfo.getReadsWithContexts().isBottom()) {
+ fieldAccessInfo.setReadsWithContexts(new ConcreteAccessContexts());
+ } else {
+ assert fieldAccessInfo.getReadsWithContexts().isConcrete();
+ }
// For the purpose of member rebinding, we don't care about the access contexts, so we
// simply use the empty set.
+ ConcreteAccessContexts accessContexts = fieldAccessInfo.getReadsWithContexts().asConcrete();
accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index a6a6021..9e23dd0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -4,88 +4,104 @@
package com.android.tools.r8.optimize;
+import static com.android.tools.r8.graph.GraphLens.NestedGraphLens.mapVirtualInterfaceInvocationTypes;
+
import com.android.tools.r8.graph.AppView;
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.DexType;
+import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.google.common.collect.ImmutableMap;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
-public class MemberRebindingLens extends NestedGraphLens {
+public class MemberRebindingLens extends NonIdentityGraphLens {
- public static class Builder {
-
- private final AppView<?> appView;
-
- private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
- private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
-
- protected Builder(AppView<?> appView) {
- this.appView = appView;
- }
-
- public void map(DexField from, DexField to) {
- if (from == to) {
- assert !fieldMap.containsKey(from);
- return;
- }
- fieldMap.put(from, to);
- }
-
- public void map(DexMethod from, DexMethod to, Invoke.Type type) {
- if (from == to) {
- assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to;
- return;
- }
- Map<DexMethod, DexMethod> methodMap =
- methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
- assert methodMap.getOrDefault(from, to) == to;
- methodMap.put(from, to);
- }
-
- public GraphLens build(GraphLens previousLens) {
- if (fieldMap.isEmpty() && methodMaps.isEmpty()) {
- return previousLens;
- }
- return new MemberRebindingLens(appView, methodMaps, fieldMap, previousLens);
- }
- }
-
- private final AppView<?> appView;
+ private final AppView<AppInfoWithLiveness> appView;
private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps;
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
public MemberRebindingLens(
- AppView<?> appView,
+ AppView<AppInfoWithLiveness> appView,
Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps,
- Map<DexField, DexField> fieldMap,
- GraphLens previousLens) {
- super(
- ImmutableMap.of(),
- ImmutableMap.of(),
- fieldMap,
- null,
- null,
- previousLens,
- appView.dexItemFactory());
+ Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap) {
+ super(appView.dexItemFactory(), appView.graphLens());
this.appView = appView;
this.methodMaps = methodMaps;
+ this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
}
- public static Builder builder(AppView<?> appView) {
+ public static Builder builder(AppView<AppInfoWithLiveness> appView) {
return new Builder(appView);
}
@Override
- public boolean isLegitimateToHaveEmptyMappings() {
+ public boolean isMemberRebindingLens() {
return true;
}
@Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(method);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ return this != applied
+ ? getPrevious().getRenamedMethodSignature(originalMethod, applied)
+ : originalMethod;
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ @Override
+ protected DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ assert previous.getReboundReference() == null;
+ return FieldLookupResult.builder(this)
+ .setReference(previous.getReference())
+ .setReboundReference(getReboundFieldReference(previous.getReference()))
+ .build();
+ }
+
+ private DexField getReboundFieldReference(DexField field) {
+ return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
Map<DexMethod, DexMethod> methodMap =
@@ -97,12 +113,68 @@
return MethodLookupResult.builder(this)
.setReference(newMethod)
.setPrototypeChanges(previous.getPrototypeChanges())
- .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ .setType(
+ mapVirtualInterfaceInvocationTypes(
+ appView, newMethod, previous.getReference(), previous.getType()))
.build();
}
@Override
- protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
+ DexItemFactory dexItemFactory, GraphLens lens) {
+ FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
+ nonReboundFieldReferenceToDefinitionMap.forEach(
+ (nonReboundFieldReference, reboundFieldReference) -> {
+ DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
+ DexField rewrittenNonReboundFieldReference =
+ rewrittenReboundFieldReference.withHolder(
+ lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
+ builder.recordDefinitionForNonReboundFieldReference(
+ rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
+ });
+ return builder.build(dexItemFactory);
+ }
+
+ public static class Builder {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final Map<Invoke.Type, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
+ private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
+ new IdentityHashMap<>();
+
+ private Builder(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public void map(DexMethod from, DexMethod to, Invoke.Type type) {
+ if (from == to) {
+ assert !methodMaps.containsKey(type) || methodMaps.get(type).getOrDefault(from, to) == to;
+ return;
+ }
+ Map<DexMethod, DexMethod> methodMap =
+ methodMaps.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
+ assert methodMap.getOrDefault(from, to) == to;
+ methodMap.put(from, to);
+ }
+
+ void recordNonReboundFieldAccesses(FieldAccessInfo info) {
+ DexField reboundFieldReference = info.getField();
+ info.forEachIndirectAccess(
+ nonReboundFieldReference ->
+ recordNonReboundFieldAccess(nonReboundFieldReference, reboundFieldReference));
+ }
+
+ private void recordNonReboundFieldAccess(
+ DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
+ }
+
+ public MemberRebindingLens build() {
+ return new MemberRebindingLens(appView, methodMaps, nonReboundFieldReferenceToDefinitionMap);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
index 5d2fef0..52e7b3d 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -40,6 +40,11 @@
}
@Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ return previous;
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context) {
if (previous.getType() == Type.DIRECT && publicizedMethods.contains(previous.getReference())) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 2848d3f..a95cfb4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -50,7 +50,7 @@
interface GraphLensLookupResultProvider {
- abstract MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
+ MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
}
private final AppView<?> appView;
@@ -133,7 +133,7 @@
@Override
protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
- return super.mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
+ return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 3a3355e..070d6c8 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -72,13 +72,10 @@
@BeforeClass
public static void beforeAll() throws Exception {
- assertThrowsWithHorizontalClassMerging(
- () -> {
- if (data().stream().count() > 0) {
- r8R8Debug = compileR8(CompilationMode.DEBUG);
- r8R8Release = compileR8(CompilationMode.RELEASE);
- }
- });
+ if (data().stream().count() > 0) {
+ r8R8Debug = compileR8(CompilationMode.DEBUG);
+ r8R8Release = compileR8(CompilationMode.RELEASE);
+ }
}
@Parameters(name = "{0}")
@@ -214,7 +211,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
ProcessResult runResult = ToolHelper.runJava(helloJar, "hello.Hello");
assertEquals(0, runResult.exitCode);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
index 91dc3b1..e357250 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -21,19 +21,16 @@
@Test
public void test() throws Exception {
- if (enableHorizontalClassMerging) {
- thrown.expect(Throwable.class);
- }
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
.addKeepMainRule(Main.class)
.addOptionsModification(
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
- .addHorizontallyMergedLambdaClassesInspector(
+ .addHorizontallyMergedClassesInspector(
inspector -> {
if (enableHorizontalClassMerging) {
- inspector.assertMerged(C.class, D.class);
+ inspector.assertMerged(C.class, D.class).assertMergedIntoDifferentType(D.class);
}
})
.enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index 3581801..3afcfb2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -22,9 +22,6 @@
@Test
public void test() throws Exception {
- if (enableHorizontalClassMerging) {
- thrown.expect(Throwable.class);
- }
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
index ecf49fc..0f28809 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.classmerging.vertical.NonReboundFieldAccessWithMergedTypeTest.GreetingBase;
import com.android.tools.r8.classmerging.vertical.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,13 +30,12 @@
@Test
public void test() throws Exception {
- thrown.expect(Throwable.class);
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
.addKeepMainRule(Main.class)
.addVerticallyMergedClassesInspector(
- inspector -> inspector.assertMergedIntoSubtype(GreetingBase.class))
+ inspector -> inspector.assertMergedIntoSubtype(C.class))
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
index 5a3f831..6d8cef1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
@@ -30,7 +30,6 @@
@Test
public void test() throws Exception {
- thrown.expect(Throwable.class);
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
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 cbd5710..a04b0c1 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
@@ -97,7 +97,6 @@
@Test
public void testHello() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Path prevGeneratedJar = null;
String prevRunResult = null;
@@ -131,7 +130,6 @@
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Assume.assumeTrue(!ToolHelper.isWindows());
Assume.assumeTrue(parameters.isCfRuntime());
Assume.assumeTrue(CfVm.JDK11 == parameters.getRuntime().asCf().getVm());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index a684ea3..9ebb411 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -41,7 +41,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(SingleTargetAfterInliningTest.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
deleted file mode 100644
index 59cfbe8..0000000
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.regress;
-
-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.Assert.assertTrue;
-
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class B76025099 extends TestBase {
-
- private Backend backend;
-
- @Parameterized.Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public B76025099(Backend backend) {
- this.backend = backend;
- }
-
- private static final String PRG =
- ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
-
- private AndroidApp runR8(AndroidApp app) throws Exception {
- R8Command command =
- ToolHelper.addProguardConfigurationConsumer(
- ToolHelper.prepareR8CommandBuilder(app, emptyConsumer(backend)),
- pgConfig -> {
- pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(map);
- })
- .addLibraryFiles(runtimeJar(backend))
- .addProguardConfigurationFiles(pgConfig)
- .setDisableMinification(true)
- .setOutput(tempRoot.toPath(), outputMode(backend))
- .build();
- return ToolHelper.runR8(command);
- }
-
- private File tempRoot;
- private Path jarPath;
- private AndroidApp originalApp;
- private String mainName;
- private Path pgConfig;
- private Path map;
-
- @Before
- public void setUp() throws Exception {
- tempRoot = temp.getRoot();
- jarPath = Paths.get(PRG);
- originalApp = readJar(jarPath);
- mainName = "regress_76025099.Main";
- pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
- String config = keepMainProguardConfiguration(mainName);
- config += System.lineSeparator() + "-dontobfuscate";
- Files.write(pgConfig, config.getBytes());
- map = File.createTempFile("proguard", ".map", tempRoot).toPath();
- }
-
- @Test
- public void testProguardAndD8() throws Exception {
- Assume.assumeTrue(backend == Backend.DEX);
- if (!isRunProguard()) {
- return;
- }
-
- ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
- assertEquals(0, jvmOutput.exitCode);
-
- Path proguarded =
- File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, tempRoot).toPath();
- ProcessResult proguardResult = ToolHelper.runProguardRaw(jarPath, proguarded, pgConfig, map);
- assertEquals(0, proguardResult.exitCode);
-
- AndroidApp processedApp = ToolHelper.runD8(readJar(proguarded));
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- @Test
- public void testR8() throws Exception {
- ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
- assertEquals(0, jvmOutput.exitCode);
-
- AndroidApp processedApp = runR8(originalApp);
- verifyFieldAccess(processedApp, jvmOutput);
- }
-
- private static InstructionSubject findInstructionOrNull(
- Iterator<InstructionSubject> iterator, Function<InstructionSubject, Boolean> predicate) {
- while (iterator.hasNext()) {
- InstructionSubject instruction = iterator.next();
- if (predicate.apply(instruction)) {
- return instruction;
- }
- }
- return null;
- }
-
- private void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
- throws Exception {
- CodeInspector inspector = new CodeInspector(processedApp);
- ClassSubject impl = inspector.clazz("regress_76025099.impl.Impl");
- assertThat(impl, isPresent());
- MethodSubject init = impl.init("java.lang.String");
- assertThat(init, isPresent());
- Iterator<InstructionSubject> iterator = init.iterateInstructions();
-
- assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isInvoke));
-
- InstructionSubject instruction =
- findInstructionOrNull(iterator, InstructionSubject::isInstancePut);
- assertNotNull(instruction);
- FieldAccessInstructionSubject fieldAccessInstruction =
- (FieldAccessInstructionSubject) instruction;
- assertEquals("name", fieldAccessInstruction.name());
- assertTrue(fieldAccessInstruction.holder().is(impl.getDexProgramClass().type.toString()));
-
- assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isReturnVoid));
-
- ProcessResult output;
- if (backend == Backend.DEX) {
- output = runOnArtRaw(processedApp, mainName);
- } else {
- assert backend == Backend.CF;
- output = runOnJavaRaw(processedApp, mainName, Collections.emptyList());
- }
- assertEquals(0, output.exitCode);
- assertEquals(jvmOutput.stdout, output.stdout);
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
new file mode 100644
index 0000000..e179440
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress.b76025099;
+
+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.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardTestCompileResult;
+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.regress.b76025099.testclasses.Main;
+import com.android.tools.r8.regress.b76025099.testclasses.impl.Impl;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Iterator;
+import java.util.function.Function;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B76025099 extends TestBase {
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines(Main.class.getTypeName());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public B76025099(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testProguardAndD8() throws Exception {
+ assumeTrue(isRunProguard());
+
+ ProguardTestCompileResult proguardCompileResult =
+ testForProguard()
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
+ .addKeepMainRule(Main.class)
+ .noMinification()
+ .compile();
+
+ if (parameters.isDexRuntime()) {
+ testForD8()
+ .addProgramFiles(proguardCompileResult.outputJar())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::verifyFieldAccess)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ proguardCompileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::verifyFieldAccess)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private static InstructionSubject findInstructionOrNull(
+ Iterator<InstructionSubject> iterator, Function<InstructionSubject, Boolean> predicate) {
+ while (iterator.hasNext()) {
+ InstructionSubject instruction = iterator.next();
+ if (predicate.apply(instruction)) {
+ return instruction;
+ }
+ }
+ return null;
+ }
+
+ private void verifyFieldAccess(CodeInspector inspector) {
+ ClassSubject impl = inspector.clazz(Impl.class);
+ assertThat(impl, isPresent());
+ MethodSubject init = impl.init("java.lang.String");
+ assertThat(init, isPresent());
+ Iterator<InstructionSubject> iterator = init.iterateInstructions();
+
+ assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isInvoke));
+
+ InstructionSubject instruction =
+ findInstructionOrNull(iterator, InstructionSubject::isInstancePut);
+ assertNotNull(instruction);
+ FieldAccessInstructionSubject fieldAccessInstruction =
+ (FieldAccessInstructionSubject) instruction;
+ assertEquals("name", fieldAccessInstruction.name());
+ assertTrue(fieldAccessInstruction.holder().is(impl.getDexProgramClass().type.toString()));
+
+ assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isReturnVoid));
+ }
+}
diff --git a/src/test/examples/regress_76025099/Logger.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
similarity index 81%
rename from src/test/examples/regress_76025099/Logger.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
index d6e9f95..b0178e0 100644
--- a/src/test/examples/regress_76025099/Logger.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Logger.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099;
+package com.android.tools.r8.regress.b76025099.testclasses;
public interface Logger {
String getName();
diff --git a/src/test/examples/regress_76025099/Main.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
similarity index 74%
rename from src/test/examples/regress_76025099/Main.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
index 659bdbd..3d49201 100644
--- a/src/test/examples/regress_76025099/Main.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/Main.java
@@ -1,9 +1,9 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099;
+package com.android.tools.r8.regress.b76025099.testclasses;
-import regress_76025099.impl.Factory;
+import com.android.tools.r8.regress.b76025099.testclasses.impl.Factory;
public class Main {
public static void main(String[] args) {
diff --git a/src/test/examples/regress_76025099/helper/AbstractBase.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
similarity index 62%
rename from src/test/examples/regress_76025099/helper/AbstractBase.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
index 046e12f..9d5694c 100644
--- a/src/test/examples/regress_76025099/helper/AbstractBase.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractBase.java
@@ -1,10 +1,12 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099.helper;
+package com.android.tools.r8.regress.b76025099.testclasses.helper;
-import regress_76025099.Logger;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.regress.b76025099.testclasses.Logger;
+@NoVerticalClassMerging
abstract class AbstractBase implements Logger {
protected String name;
diff --git a/src/test/examples/regress_76025099/helper/AbstractSub.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
similarity index 63%
rename from src/test/examples/regress_76025099/helper/AbstractSub.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
index 9240e4a..0dd1af7 100644
--- a/src/test/examples/regress_76025099/helper/AbstractSub.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/helper/AbstractSub.java
@@ -1,7 +1,6 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099.helper;
+package com.android.tools.r8.regress.b76025099.testclasses.helper;
-public abstract class AbstractSub extends AbstractBase {
-}
+public abstract class AbstractSub extends AbstractBase {}
diff --git a/src/test/examples/regress_76025099/impl/Factory.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
similarity index 82%
rename from src/test/examples/regress_76025099/impl/Factory.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
index 0cc1cc9..fd98f42 100644
--- a/src/test/examples/regress_76025099/impl/Factory.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Factory.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099.impl;
+package com.android.tools.r8.regress.b76025099.testclasses.impl;
public class Factory {
public static Impl getImpl(String name) {
diff --git a/src/test/examples/regress_76025099/impl/Impl.java b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
similarity index 67%
rename from src/test/examples/regress_76025099/impl/Impl.java
rename to src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
index 74eefcf..b24bde9 100644
--- a/src/test/examples/regress_76025099/impl/Impl.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/testclasses/impl/Impl.java
@@ -1,12 +1,12 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_76025099.impl;
+package com.android.tools.r8.regress.b76025099.testclasses.impl;
-import regress_76025099.helper.AbstractSub;
+import com.android.tools.r8.regress.b76025099.testclasses.helper.AbstractSub;
public class Impl extends AbstractSub {
Impl(String name) {
this.name = name;
}
-}
\ No newline at end of file
+}