Merge "Enhance visibility check when rebinding fields."
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index a5ac28c..d5ac39d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -202,7 +202,7 @@
}
return NEVER;
} else {
- /* package-private */
+ /* package-private */
return targetHolder.isSamePackage(contextHolder) ? PACKAGE : NEVER;
}
}
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 de171a3..3372a20 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -11,8 +11,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
+import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -124,7 +125,7 @@
// Rebind to the lowest library class or program class.
if (target != null && target.method != method) {
DexClass targetClass = appInfo.definitionFor(target.method.holder);
- // If the targetclass is not public but the targeted method is, we might run into
+ // If the target class is not public but the targeted method is, we might run into
// visibility problems when rebinding.
if (!targetClass.accessFlags.isPublic() && target.accessFlags.isPublic()) {
// If the original class is public and this method is public, it might have been called
@@ -178,27 +179,37 @@
return null;
}
- private void computeFieldRebinding(Set<DexField> fields,
+ private void computeFieldRebinding(Map<DexField, Set<DexEncodedMethod>> fields,
BiFunction<DexType, DexField, DexEncodedField> lookup,
BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
- for (DexField field : fields) {
+ for (Map.Entry<DexField, Set<DexEncodedMethod>> entry : fields.entrySet()) {
+ DexField field = entry.getKey();
field = lense.lookupField(field, null);
DexEncodedField target = lookup.apply(field.getHolder(), field);
// Rebind to the lowest library class or program class. Do not rebind accesses to fields that
- // are not public, as this might lead to access violation errors.
- if (target != null && target.field != field && isVisibleFromOtherClasses(target)) {
+ // are not visible from the access context.
+ Set<DexEncodedMethod> contexts = entry.getValue();
+ if (target != null && target.field != field && contexts.stream().allMatch(context ->
+ isVisibleFromOriginalContext(context.method.getHolder(), target))) {
builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
}
}
}
- private boolean isVisibleFromOtherClasses(DexEncodedField field) {
- // If the field is not public, the visibility on the class can not be a further constraint.
- if (!field.accessFlags.isPublic()) {
- return true;
+ private boolean isVisibleFromOriginalContext(DexType context, DexEncodedField field) {
+ DexType holderType = field.field.getHolder();
+ DexClass holder = appInfo.definitionFor(holderType);
+ if (holder == null) {
+ return false;
}
- // If the field is public, then a non-public holder class will further constrain visibility.
- return appInfo.definitionFor(field.field.getHolder()).accessFlags.isPublic();
+ Constraint classVisibility =
+ Constraint.deriveConstraint(context, holderType, holder.accessFlags, appInfo);
+ if (classVisibility == Constraint.NEVER) {
+ return false;
+ }
+ Constraint fieldVisibility =
+ Constraint.deriveConstraint(context, holderType, field.accessFlags, appInfo);
+ return fieldVisibility != Constraint.NEVER;
}
public GraphLense run() {
@@ -213,10 +224,15 @@
// Likewise static invokes.
computeMethodRebinding(appInfo.staticInvokes, this::anyLookup);
- computeFieldRebinding(Sets.union(appInfo.staticFieldReads, appInfo.staticFieldWrites),
+ computeFieldRebinding(appInfo.staticFieldReads,
appInfo::resolveFieldOn, DexClass::lookupField);
- computeFieldRebinding(Sets.union(appInfo.instanceFieldReads, appInfo.instanceFieldWrites),
+ computeFieldRebinding(appInfo.staticFieldWrites,
appInfo::resolveFieldOn, DexClass::lookupField);
+ computeFieldRebinding(appInfo.instanceFieldReads,
+ appInfo::resolveFieldOn, DexClass::lookupField);
+ computeFieldRebinding(appInfo.instanceFieldWrites,
+ appInfo::resolveFieldOn, DexClass::lookupField);
+
return builder.build(appInfo.dexItemFactory, lense);
}
}
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 a726391..2774709 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -99,10 +99,14 @@
Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
private final Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
- private final Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsWritten =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> instanceFieldsRead =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsRead =
+ Maps.newIdentityHashMap();
+ private final Map<DexType, Set<TargetWithContext<DexField>>> staticFieldsWritten =
+ Maps.newIdentityHashMap();
private final ProtoLiteExtension protoLiteExtension;
private final Set<DexField> protoLiteFields = Sets.newIdentityHashSet();
@@ -361,7 +365,7 @@
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- if (!registerItemWithTarget(instanceFieldsWritten, field)) {
+ if (!registerItemWithTargetAndContext(instanceFieldsWritten, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -374,7 +378,7 @@
@Override
public boolean registerInstanceFieldRead(DexField field) {
- if (!registerItemWithTarget(instanceFieldsRead, field)) {
+ if (!registerItemWithTargetAndContext(instanceFieldsRead, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -392,7 +396,7 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
- if (!registerItemWithTarget(staticFieldsRead, field)) {
+ if (!registerItemWithTargetAndContext(staticFieldsRead, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -404,7 +408,7 @@
@Override
public boolean registerStaticFieldWrite(DexField field) {
- if (!registerItemWithTarget(staticFieldsWritten, field)) {
+ if (!registerItemWithTargetAndContext(staticFieldsWritten, field, currentMethod)) {
return false;
}
if (Log.ENABLED) {
@@ -1208,34 +1212,41 @@
}
}
- private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
- return map.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toCollection(Sets::newIdentityHashSet));
+ private Map<DexField, Set<DexEncodedMethod>> collectFields(
+ Map<DexType, Set<TargetWithContext<DexField>>> map) {
+ Map<DexField, Set<DexEncodedMethod>> result = new IdentityHashMap<>();
+ for (Map.Entry<DexType, Set<TargetWithContext<DexField>>> entry : map.entrySet()) {
+ for (TargetWithContext<DexField> fieldWithContext : entry.getValue()) {
+ DexField field = fieldWithContext.getTarget();
+ DexEncodedMethod context = fieldWithContext.getContext();
+ result.computeIfAbsent(field, k -> Sets.newIdentityHashSet())
+ .add(context);
+ }
+ }
+ return result;
}
- SortedSet<DexField> collectInstanceFieldsRead() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsRead));
+ Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsRead() {
+ return Collections.unmodifiableMap(collectFields(instanceFieldsRead));
}
- SortedSet<DexField> collectInstanceFieldsWritten() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(instanceFieldsWritten));
+ Map<DexField, Set<DexEncodedMethod>> collectInstanceFieldsWritten() {
+ return Collections.unmodifiableMap(collectFields(instanceFieldsWritten));
}
- SortedSet<DexField> collectStaticFieldsRead() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsRead));
+ Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsRead() {
+ return Collections.unmodifiableMap(collectFields(staticFieldsRead));
}
- SortedSet<DexField> collectStaticFieldsWritten() {
- return ImmutableSortedSet.copyOf(
- PresortedComparable<DexField>::slowCompareTo, collectFields(staticFieldsWritten));
+ Map<DexField, Set<DexEncodedMethod>> collectStaticFieldsWritten() {
+ return Collections.unmodifiableMap(collectFields(staticFieldsWritten));
}
- private Set<DexField> collectReachedFields(Map<DexType, Set<DexField>> map,
- Function<DexField, DexField> lookup) {
- return map.values().stream().flatMap(set -> set.stream().map(lookup).filter(Objects::nonNull))
+ private Set<DexField> collectReachedFields(
+ Set<DexField> set, Function<DexField, DexField> lookup) {
+ return set.stream()
+ .map(lookup)
+ .filter(Objects::nonNull)
.collect(Collectors.toCollection(Sets::newIdentityHashSet));
}
@@ -1249,16 +1260,11 @@
return target == null ? null : target.field;
}
- SortedSet<DexField> collectFieldsRead() {
+ SortedSet<DexField> mergeFieldAccesses(Set<DexField> instanceFields, Set<DexField> staticFields) {
return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
- Sets.union(collectReachedFields(instanceFieldsRead, this::tryLookupInstanceField),
- collectReachedFields(staticFieldsRead, this::tryLookupStaticField)));
- }
-
- SortedSet<DexField> collectFieldsWritten() {
- return ImmutableSortedSet.copyOf(PresortedComparable<DexField>::slowCompareTo,
- Sets.union(collectReachedFields(instanceFieldsWritten, this::tryLookupInstanceField),
- collectReachedFields(staticFieldsWritten, this::tryLookupStaticField)));
+ Sets.union(
+ collectReachedFields(instanceFields, this::tryLookupInstanceField),
+ collectReachedFields(staticFields, this::tryLookupStaticField)));
}
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
@@ -1429,21 +1435,21 @@
*/
public final SortedSet<DexField> fieldsWritten;
/**
- * Set of all field ids used in instance field reads.
+ * Set of all field ids used in instance field reads, along with access context.
*/
- public final SortedSet<DexField> instanceFieldReads;
+ public final Map<DexField, Set<DexEncodedMethod>> instanceFieldReads;
/**
- * Set of all field ids used in instance field writes.
+ * Set of all field ids used in instance field writes, along with access context.
*/
- public final SortedSet<DexField> instanceFieldWrites;
+ public final Map<DexField, Set<DexEncodedMethod>> instanceFieldWrites;
/**
- * Set of all field ids used in static static field reads.
+ * Set of all field ids used in static static field reads, along with access context.
*/
- public final SortedSet<DexField> staticFieldReads;
+ public final Map<DexField, Set<DexEncodedMethod>> staticFieldReads;
/**
- * Set of all field ids used in static field writes.
+ * Set of all field ids used in static field writes, along with access context.
*/
- public final SortedSet<DexField> staticFieldWrites;
+ public final Map<DexField, Set<DexEncodedMethod>> staticFieldWrites;
/**
* Set of all methods referenced in virtual invokes;
*/
@@ -1515,8 +1521,10 @@
this.instanceFieldWrites = enqueuer.collectInstanceFieldsWritten();
this.staticFieldReads = enqueuer.collectStaticFieldsRead();
this.staticFieldWrites = enqueuer.collectStaticFieldsWritten();
- this.fieldsRead = enqueuer.collectFieldsRead();
- this.fieldsWritten = enqueuer.collectFieldsWritten();
+ this.fieldsRead = enqueuer.mergeFieldAccesses(
+ instanceFieldReads.keySet(), staticFieldReads.keySet());
+ this.fieldsWritten = enqueuer.mergeFieldAccesses(
+ instanceFieldWrites.keySet(), staticFieldWrites.keySet());
this.pinnedItems = rewritePinnedItemsToDescriptors(enqueuer.pinnedItems);
this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
this.interfaceInvokes = joinInvokedMethods(enqueuer.interfaceInvokes);
@@ -1532,8 +1540,8 @@
this.prunedTypes = Collections.emptySet();
this.switchMaps = Collections.emptyMap();
this.ordinalsMaps = Collections.emptyMap();
- assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application,
@@ -1566,8 +1574,8 @@
this.prunedTypes = mergeSets(previous.prunedTypes, removedClasses);
this.switchMaps = previous.switchMaps;
this.ordinalsMaps = previous.ordinalsMaps;
- assert Sets.intersection(instanceFieldReads, staticFieldReads).size() == 0;
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).size() == 0;
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1579,10 +1587,14 @@
this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
- this.instanceFieldReads = rewriteItems(previous.instanceFieldReads, lense::lookupField);
- this.instanceFieldWrites = rewriteItems(previous.instanceFieldWrites, lense::lookupField);
- this.staticFieldReads = rewriteItems(previous.staticFieldReads, lense::lookupField);
- this.staticFieldWrites = rewriteItems(previous.staticFieldWrites, lense::lookupField);
+ this.instanceFieldReads =
+ rewriteKeysWhileMergingValues(previous.instanceFieldReads, lense::lookupField);
+ this.instanceFieldWrites =
+ rewriteKeysWhileMergingValues(previous.instanceFieldWrites, lense::lookupField);
+ this.staticFieldReads =
+ rewriteKeysWhileMergingValues(previous.staticFieldReads, lense::lookupField);
+ this.staticFieldWrites =
+ rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
@@ -1608,8 +1620,8 @@
this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
this.protoLiteFields = previous.protoLiteFields;
// Sanity check sets after rewriting.
- assert Sets.intersection(instanceFieldReads, staticFieldReads).isEmpty();
- assert Sets.intersection(instanceFieldWrites, staticFieldWrites).isEmpty();
+ assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
+ assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
}
public AppInfoWithLiveness(AppInfoWithLiveness previous,
@@ -1744,6 +1756,18 @@
return builder.build();
}
+ private static <T extends PresortedComparable<T>, S> Map<T, Set<S>>
+ rewriteKeysWhileMergingValues(
+ Map<T, Set<S>> original, BiFunction<T, DexEncodedMethod, T> rewrite) {
+ Map<T, Set<S>> result = new IdentityHashMap<>();
+ for (T item : original.keySet()) {
+ T rewrittenKey = rewrite.apply(item, null);
+ result.computeIfAbsent(rewrittenKey, k -> Sets.newIdentityHashSet())
+ .addAll(original.get(item));
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
private static ImmutableSet<DexItem> rewriteMixedItems(
Set<DexItem> original, GraphLense lense) {
ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
@@ -2095,6 +2119,10 @@
return target;
}
+ public DexEncodedMethod getContext() {
+ return context;
+ }
+
@Override
public int hashCode() {
return target.hashCode() * 31 + context.hashCode();
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 4d7240e..923398d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -550,24 +550,6 @@
// Tests where the output of R8 fails when run with Art.
private static final Multimap<String, TestCondition> failingRunWithArt =
new ImmutableListMultimap.Builder<String, TestCondition>()
- // This test relies on specific field access patterns, which we rewrite.
- .put("064-field-access",
- TestCondition.match(
- TestCondition.R8DEX_NOT_AFTER_D8_COMPILER,
- TestCondition.runtimesUpTo(DexVm.Version.V4_4_4)))
- .put("064-field-access",
- TestCondition.match(
- TestCondition.R8DEX_COMPILER,
- TestCondition.runtimes(
- DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
- DexVm.Version.V5_1_1)))
- .put("064-field-access",
- TestCondition.match(
- TestCondition.tools(DexTool.NONE),
- TestCondition.D8_AFTER_R8CF_COMPILER,
- TestCondition.runtimes(
- DexVm.Version.DEFAULT, DexVm.Version.V7_0_0, DexVm.Version.V6_0_1,
- DexVm.Version.V5_1_1)))
// The growth limit test fails after processing by R8 because R8 will eliminate an
// "unneeded" const store. The following reflective call to the VM's GC will then see the
// large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 9fa694d..767420b 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -3,7 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
@@ -11,50 +13,113 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
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 org.junit.Ignore;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import regress_76025099.Main;
+import regress_76025099.impl.Impl;
@RunWith(VmTestRunner.class)
public class B76025099 extends TestBase {
-
private static final String PRG =
ToolHelper.EXAMPLES_BUILD_DIR + "regress_76025099" + FileUtils.JAR_EXTENSION;
- private AndroidApp runR8(AndroidApp app, Class main, Path out) throws Exception {
+ private AndroidApp runR8(AndroidApp app) throws Exception {
R8Command command =
ToolHelper.addProguardConfigurationConsumer(
ToolHelper.prepareR8CommandBuilder(app),
pgConfig -> {
pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ pgConfig.setPrintMappingFile(map);
})
- .addProguardConfiguration(
- ImmutableList.of(keepMainProguardConfiguration(main)),
- Origin.unknown())
- .setOutput(out, OutputMode.DexIndexed)
+ .addProguardConfigurationFiles(pgConfig)
+ .setOutput(tempRoot.toPath(), OutputMode.DexIndexed)
.build();
return ToolHelper.runR8(command, o -> {
o.enableMinification = false;
});
}
- @Ignore("b/76025099")
+ 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 = Main.class.getCanonicalName();
+ pgConfig = File.createTempFile("keep-rules", ".config", tempRoot).toPath();
+ String config = keepMainProguardConfiguration(Main.class);
+ config += System.lineSeparator() + "-dontobfuscate";
+ Files.write(pgConfig, config.getBytes());
+ map = File.createTempFile("proguard", ".map", tempRoot).toPath();
+ }
+
@Test
- public void test() throws Exception {
- Path out = temp.getRoot().toPath();
- Path jarPath = Paths.get(PRG);
- String mainName = Main.class.getCanonicalName();
+ public void testProguardAndD8() throws Exception {
+ if (!isRunProguard()) {
+ return;
+ }
+
ProcessResult jvmOutput = ToolHelper.runJava(ImmutableList.of(jarPath), mainName);
assertEquals(0, jvmOutput.exitCode);
- AndroidApp processedApp = runR8(readJar(jarPath), Main.class, out);
+
+ 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 void verifyFieldAccess(AndroidApp processedApp, ProcessResult jvmOutput)
+ throws Exception {
+ DexInspector inspector = new DexInspector(processedApp);
+ ClassSubject impl = inspector.clazz(Impl.class);
+ assertThat(impl, isPresent());
+ MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
+ assertThat(init, isPresent());
+ DexCode dexCode = init.getMethod().getCode().asDexCode();
+ checkInstructions(dexCode, ImmutableList.of(
+ InvokeDirect.class,
+ IputObject.class,
+ ReturnVoid.class
+ ));
+ IputObject iput = (IputObject) dexCode.instructions[1];
+ DexField fld = iput.getField();
+ assertEquals("name", fld.name.toString());
+ assertEquals("regress_76025099.impl.Impl", fld.getHolder().toSourceString());
+
ProcessResult artOutput = runOnArtRaw(processedApp, mainName);
assertEquals(0, artOutput.exitCode);
assertEquals(jvmOutput.stdout, artOutput.stdout);
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
index b13a08e..6642352 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.resolution;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
@@ -46,12 +45,11 @@
assertEquals(0, runInput.exitCode);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex));
- // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
- ProcessResult runDex = ToolHelper.runArtRaw(
+ ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
outDex.toString(), CLASS.getCanonicalName());
- assertNotEquals(runInput.stdout, runDex.stdout);
- assertNotEquals(runInput.exitCode, runDex.exitCode);
- assertNotEquals(
+ assertEquals(runInput.stdout, runDex.stdout);
+ assertEquals(runInput.exitCode, runDex.exitCode);
+ assertEquals(
-1,
runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
}