blob: aac4a9ec617343457f32a57ed09d3ddace50213a [file] [log] [blame]
// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.twr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.io.IOException;
import java.util.List;
import java.util.jar.JarFile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TwrCloseResourceDuplicationTest extends TestBase {
static final int INPUT_CLASSES = 3;
static final String EXPECTED =
StringUtils.lines(
"foo opened 1",
"foo post close 1",
"foo opened 2",
"foo caught from 2: RuntimeException",
"foo post close 2",
"bar opened 1",
"bar post close 1",
"bar opened 2",
"bar caught from 2: RuntimeException",
"bar post close 2");
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public TwrCloseResourceDuplicationTest(TestParameters parameters) {
this.parameters = parameters;
}
private String getZipFile() throws IOException {
return ZipUtils.ZipBuilder.builder(temp.newFile("file.zip").toPath())
// DEX VMs from 4.4 up-to 9.0 including, will fail if no entry is added.
.addBytes("entry", new byte[1])
.build()
.toString();
}
@Test
public void testJvm() throws Exception {
assumeTrue(parameters.isCfRuntime());
testForJvm()
.addInnerClasses(getClass())
.run(parameters.getRuntime(), TestClass.class, getZipFile())
.assertSuccessWithOutput(EXPECTED);
}
@Test
public void testD8() throws Exception {
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class, getZipFile())
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
// There should be exactly one synthetic class besides the three program classes.
int expectedSynthetics =
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
? 1
: 0;
assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
});
}
@Test
public void testR8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRules(Foo.class, Bar.class)
.applyIf(
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
builder -> builder.addDontWarn("java.lang.AutoCloseable"))
.setMinApi(parameters.getApiLevel())
.noMinification()
.run(parameters.getRuntime(), TestClass.class, getZipFile())
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
// R8 will optimize the generated methods for the two cases below where the thrown
// exception is known or not, thus the synthetic methods will be 2.
int expectedSynthetics =
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
? 2
: 0;
List<FoundClassSubject> foundClassSubjects = inspector.allClasses();
assertEquals(INPUT_CLASSES + expectedSynthetics, foundClassSubjects.size());
});
}
static class Foo {
void foo(String name) {
try (JarFile f = new JarFile(name)) {
System.out.println("foo opened 1");
} catch (Exception e) {
System.out.println("foo caught from 1: " + e.getClass().getSimpleName());
} finally {
System.out.println("foo post close 1");
}
try (JarFile f = new JarFile(name)) {
System.out.println("foo opened 2");
throw new RuntimeException();
} catch (Exception e) {
System.out.println("foo caught from 2: " + e.getClass().getSimpleName());
} finally {
System.out.println("foo post close 2");
}
}
}
static class Bar {
void bar(String name) {
try (JarFile f = new JarFile(name)) {
System.out.println("bar opened 1");
} catch (Exception e) {
System.out.println("bar caught from 1: " + e.getClass().getSimpleName());
} finally {
System.out.println("bar post close 1");
}
try (JarFile f = new JarFile(name)) {
System.out.println("bar opened 2");
throw new RuntimeException();
} catch (Exception e) {
System.out.println("bar caught from 2: " + e.getClass().getSimpleName());
} finally {
System.out.println("bar post close 2");
}
}
}
static class TestClass {
public static void main(String[] args) {
new Foo().foo(args[0]);
new Bar().bar(args[0]);
}
}
}