blob: b9883efe9e0c7c092bc0376ac89be409e95f3add [file] [log] [blame]
// 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.TestParameters;
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(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 = "{0}, access-modification: {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
public NoRelaxationForSerializableTest(TestParameters parameters, boolean accessModification) {
super(parameters);
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(parameters.isCfRuntime());
testForProguard()
.addProgramClasses(CLASSES)
.addTestingAnnotationsAsProgramClasses()
.addKeepRuleFiles(configuration)
.addKeepRules(KEEPMEMBER_RULES)
.compile()
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT)
.inspect(this::inspect);
}
@Test
public void testR8_withKeepRules() throws Exception {
R8TestCompileResult result =
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.enableInliningAnnotations()
.addKeepRuleFiles(configuration)
.addKeepRules(KEEPMEMBER_RULES)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect);
// TODO(b/117302947): Need to update ART binary.
if (parameters.isCfRuntime()) {
result
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
}
@Test
public void testProguard_withoutKeepRules() throws Exception {
assumeTrue(parameters.isCfRuntime());
testForProguard()
.addProgramClasses(CLASSES)
.addTestingAnnotationsAsProgramClasses()
.addKeepRuleFiles(configuration)
.compile()
.run(parameters.getRuntime(), MAIN)
.assertFailureWithErrorThatMatches(containsString("Could not deserialize"));
}
@Test
public void testR8_withoutKeepRules() throws Exception {
R8TestCompileResult result =
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.enableInliningAnnotations()
.addKeepRuleFiles(configuration)
.setMinApi(parameters.getApiLevel())
.compile();
// TODO(b/117302947): Need to update ART binary.
if (parameters.isCfRuntime()) {
result
.run(parameters.getRuntime(), 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")));
}
}