Reproduce b/123575857: dependent rules with access modifier not honored.
Bug: 123575857
Change-Id: I6e0a8987c44bd9e0078e1bd91c0772c29ddb97b0
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
new file mode 100644
index 0000000..80a4ffb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.accessrelaxation;
+
+import static java.lang.System.exit;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@NeverMerge
+class MySerializable implements Serializable {
+ transient int value;
+
+ MySerializable(int value) {
+ this.value = value;
+ }
+
+ @NeverInline
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ System.out.println("Serializable::write");
+ out.writeInt(value);
+ }
+
+ @NeverInline
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ System.out.println("Serializable::read");
+ value = in.readInt();
+ }
+}
+
+class NoRelaxationForSerializableTestRunner {
+
+ public static void main(String[] args) {
+ MySerializable instance = new MySerializable(8);
+ byte[] bytes = {};
+ try(ByteArrayOutputStream bas = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bas)) {
+ oos.writeObject(instance);
+ oos.flush();
+ bytes = bas.toByteArray();
+ } catch(IOException e) {
+ e.printStackTrace(System.err);
+ exit(1);
+ }
+ try(ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bis)) {
+ MySerializable obj = (MySerializable) ois.readObject();
+ if (obj.value != 8) {
+ throw new AssertionError("Could not deserialize");
+ }
+ System.out.println(obj.value);
+ } catch(IOException | ClassNotFoundException e) {
+ e.printStackTrace(System.err);
+ exit(1);
+ }
+ }
+}
+
+@RunWith(Parameterized.class)
+public class NoRelaxationForSerializableTest extends AccessRelaxationTestBase {
+ private static final Class<?> MAIN = NoRelaxationForSerializableTestRunner.class;
+ private static final List<Class<?>> CLASSES = ImmutableList.of(
+ NeverMerge.class, NeverInline.class, MySerializable.class, MAIN);
+ private static final String KEEPMEMBER_RULES = StringUtils.lines(
+ "-keepclassmembers class * implements java.io.Serializable {",
+ " private void writeObject(java.io.ObjectOutputStream);",
+ " private void readObject(java.io.ObjectInputStream);",
+ "}"
+ );
+ private static final String EXPECTED_OUTPUT = StringUtils.lines(
+ "Serializable::write",
+ "Serializable::read",
+ "8"
+ );
+
+ private final boolean accessModification;
+ private Path configuration;
+
+ @Parameterized.Parameters(name = "Backend: {0}, access-modification: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(Backend.values(), BooleanUtils.values());
+ }
+
+ public NoRelaxationForSerializableTest(Backend backend, boolean accessModification) {
+ super(backend);
+ this.accessModification = accessModification;
+ }
+
+ @Before
+ public void setUpConfiguration() throws Exception {
+ configuration = temp.newFile("pg.conf").toPath().toAbsolutePath();
+ FileUtils.writeTextFile(configuration, StringUtils.lines(
+ keepMainProguardConfiguration(MAIN),
+ accessModification ? "-allowaccessmodification" : ""
+ ));
+ }
+
+ @Test
+ public void testProguard_withKeepRules() throws Exception {
+ assumeTrue(backend == Backend.CF);
+ testForProguard()
+ .addProgramClasses(CLASSES)
+ .addKeepRuleFiles(configuration)
+ .addKeepRules(KEEPMEMBER_RULES)
+ .compile()
+ .run(MAIN)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
+ .inspect(this::inspect);
+ }
+
+ @Test
+ public void testR8_withKeepRules() throws Exception {
+ // TODO(b/123575857): even with -allowaccessmodification, they should not be publicized.
+ assumeTrue(!accessModification);
+ R8TestCompileResult result = testForR8(backend)
+ .addProgramClasses(CLASSES)
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addKeepRuleFiles(configuration)
+ .addKeepRules(KEEPMEMBER_RULES)
+ .compile()
+ .inspect(this::inspect);
+ // TODO(b/117302947): Need to update ART binary.
+ if (backend == Backend.CF) {
+ result.run(MAIN).assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+ }
+
+ @Test
+ public void testProguard_withoutKeepRules() throws Exception {
+ assumeTrue(backend == Backend.CF);
+ testForProguard()
+ .addProgramClasses(CLASSES)
+ .addKeepRuleFiles(configuration)
+ .compile()
+ .run(MAIN)
+ .assertFailureWithErrorThatMatches(containsString("Could not deserialize"));
+ }
+
+ @Test
+ public void testR8_withoutKeepRules() throws Exception {
+ R8TestCompileResult result = testForR8(backend)
+ .addProgramClasses(CLASSES)
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addKeepRuleFiles(configuration)
+ .compile();
+ // TODO(b/117302947): Need to update ART binary.
+ if (backend == Backend.CF) {
+ result.run(MAIN).assertFailureWithErrorThatMatches(containsString("Could not deserialize"));
+ }
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertNotPublic(inspector, MySerializable.class,
+ new MethodSignature("writeObject", "void", ImmutableList.of("java.io.ObjectOutputStream")));
+ assertNotPublic(inspector, MySerializable.class,
+ new MethodSignature("readObject", "void", ImmutableList.of("java.io.ObjectInputStream")));
+ }
+
+}