| // Copyright (c) 2020, 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.rewrite.assertions; |
| |
| import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static org.hamcrest.CoreMatchers.equalTo; |
| import static org.hamcrest.CoreMatchers.not; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.AssertionsConfiguration; |
| import com.android.tools.r8.D8TestBuilder; |
| import com.android.tools.r8.KotlinTestBase; |
| import com.android.tools.r8.R8FullTestBuilder; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.ThrowableConsumer; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.KotlinTargetVersion; |
| import com.android.tools.r8.utils.BooleanUtils; |
| import com.android.tools.r8.utils.ThrowingConsumer; |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.InstructionSubject; |
| import com.android.tools.r8.utils.codeinspector.MethodSubject; |
| import com.google.common.collect.ImmutableList; |
| import java.nio.file.Path; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import org.junit.Assume; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.FieldVisitor; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| |
| @RunWith(Parameterized.class) |
| public class AssertionConfigurationKotlinTest extends KotlinTestBase implements Opcodes { |
| |
| private static class KotlinCompilationKey { |
| KotlinTargetVersion targetVersion; |
| boolean useJvmAssertions; |
| |
| private KotlinCompilationKey(KotlinTargetVersion targetVersion, boolean useJvmAssertions) { |
| this.targetVersion = targetVersion; |
| this.useJvmAssertions = useJvmAssertions; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(targetVersion, useJvmAssertions); |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (other == null) { |
| return false; |
| } |
| if (getClass() != other.getClass()) { |
| return false; |
| } |
| KotlinCompilationKey kotlinCompilationKey = (KotlinCompilationKey) other; |
| return targetVersion == kotlinCompilationKey.targetVersion |
| && useJvmAssertions == kotlinCompilationKey.useJvmAssertions; |
| } |
| } |
| |
| private static final Package pkg = AssertionConfigurationKotlinTest.class.getPackage(); |
| private static final String kotlintestclasesPackage = pkg.getName() + ".kotlintestclasses"; |
| private static final String testClassKt = kotlintestclasesPackage + ".TestClassKt"; |
| private static final String class1 = kotlintestclasesPackage + ".Class1"; |
| private static final String class2 = kotlintestclasesPackage + ".Class2"; |
| |
| private static final Map<KotlinCompilationKey, Path> kotlinClasses = new HashMap<>(); |
| private final TestParameters parameters; |
| private final boolean kotlinStdlibAsLibrary; |
| private final boolean useJvmAssertions; |
| private final KotlinCompilationKey kotlinCompilationKey; |
| |
| @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}") |
| public static Collection<Object[]> data() { |
| return buildParameters( |
| getTestParameters().withAllRuntimesAndApiLevels().build(), |
| KotlinTargetVersion.values(), |
| BooleanUtils.values(), |
| BooleanUtils.values()); |
| } |
| |
| public AssertionConfigurationKotlinTest( |
| TestParameters parameters, |
| KotlinTargetVersion targetVersion, |
| boolean kotlinStdlibAsClasspath, |
| boolean useJvmAssertions) { |
| super(targetVersion); |
| this.parameters = parameters; |
| this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath; |
| this.useJvmAssertions = useJvmAssertions; |
| this.kotlinCompilationKey = new KotlinCompilationKey(targetVersion, useJvmAssertions); |
| } |
| |
| @BeforeClass |
| public static void compileKotlin() throws Exception { |
| for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { |
| for (boolean useJvmAssertions : BooleanUtils.values()) { |
| Path ktClasses = |
| kotlinc(KOTLINC, targetVersion) |
| .addSourceFiles(getKotlinFilesInTestPackage(pkg)) |
| .setUseJvmAssertions(useJvmAssertions) |
| .compile(); |
| kotlinClasses.put(new KotlinCompilationKey(targetVersion, useJvmAssertions), ktClasses); |
| } |
| } |
| } |
| |
| private Path kotlinStdlibLibraryForRuntime() throws Exception { |
| Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar(); |
| if (parameters.getRuntime().isCf()) { |
| return kotlinStdlibCf; |
| } |
| |
| Path kotlinStdlibDex = temp.newFolder().toPath().resolve("kotlin-stdlib-dex.jar"); |
| testForD8() |
| .addProgramFiles(kotlinStdlibCf) |
| .setMinApi(parameters.getApiLevel()) |
| .compile() |
| .writeToZip(kotlinStdlibDex); |
| return kotlinStdlibDex; |
| } |
| |
| private void runD8Test( |
| ThrowableConsumer<D8TestBuilder> builderConsumer, |
| ThrowingConsumer<CodeInspector, RuntimeException> inspector, |
| List<String> outputLines) |
| throws Exception { |
| if (kotlinStdlibAsLibrary) { |
| testForD8() |
| .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .setMinApi(parameters.getApiLevel()) |
| .apply(builderConsumer) |
| .addRunClasspathFiles(kotlinStdlibLibraryForRuntime()) |
| .run( |
| parameters.getRuntime(), |
| getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt") |
| .inspect(inspector) |
| .assertSuccessWithOutputLines(outputLines); |
| } else { |
| testForD8() |
| .addProgramFiles(ToolHelper.getKotlinStdlibJar()) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .setMinApi(parameters.getApiLevel()) |
| .apply(builderConsumer) |
| .run( |
| parameters.getRuntime(), |
| getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt") |
| .inspect(inspector) |
| .assertSuccessWithOutputLines(outputLines); |
| } |
| } |
| |
| public void runR8Test( |
| ThrowableConsumer<R8FullTestBuilder> builderConsumer, |
| ThrowingConsumer<CodeInspector, RuntimeException> inspector, |
| List<String> outputLines) |
| throws Exception { |
| runR8Test(builderConsumer, inspector, outputLines, false); |
| } |
| |
| public void runR8Test( |
| ThrowableConsumer<R8FullTestBuilder> builderConsumer, |
| ThrowingConsumer<CodeInspector, RuntimeException> inspector, |
| List<String> outputLines, |
| boolean enableJvmAssertions) |
| throws Exception { |
| |
| testForR8(parameters.getBackend()) |
| .applyIf( |
| kotlinStdlibAsLibrary, |
| b -> { |
| b.addClasspathFiles(ToolHelper.getKotlinStdlibJar()); |
| b.addRunClasspathFiles(kotlinStdlibLibraryForRuntime()); |
| }, |
| b -> b.addProgramFiles(ToolHelper.getKotlinStdlibJar())) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .addKeepMainRule(testClassKt) |
| .addKeepClassAndMembersRules(class1, class2) |
| .setMinApi(parameters.getApiLevel()) |
| .apply(builderConsumer) |
| .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary) |
| .addRunClasspathFiles(kotlinStdlibLibraryForRuntime()) |
| .compile() |
| .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")) |
| .enableRuntimeAssertions(enableJvmAssertions) |
| .run(parameters.getRuntime(), testClassKt) |
| .inspect(inspector) |
| .assertSuccessWithOutputLines(outputLines); |
| } |
| |
| private List<String> allAssertionsExpectedLines() { |
| return ImmutableList.of("AssertionError in Class1", "AssertionError in Class2", "DONE"); |
| } |
| |
| private List<String> noAllAssertionsExpectedLines() { |
| return ImmutableList.of("DONE"); |
| } |
| |
| private void checkAssertionCodeRemoved(ClassSubject subject, boolean isR8) { |
| assertThat(subject, isPresent()); |
| if (subject.getOriginalName().equals("kotlin._Assertions")) { |
| assert !kotlinStdlibAsLibrary; |
| // With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as well, |
| // as is not used. |
| assertEquals( |
| (isR8 ? 0 : 1), |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .filter(InstructionSubject::isStaticPut) |
| .count()); |
| assertFalse( |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .anyMatch(InstructionSubject::isConstNumber)); |
| } else { |
| // The value of kotlin._Assertions.ENABLED is propagated from the assertions configuration |
| // for the class kotlin._Assertions. |
| assertFalse( |
| subject |
| .uniqueMethodWithName("m") |
| .streamInstructions() |
| .anyMatch(InstructionSubject::isThrow)); |
| } |
| } |
| |
| private void checkAssertionCodeRemoved(CodeInspector inspector, String clazz, boolean isR8) { |
| checkAssertionCodeRemoved(inspector.clazz(clazz), isR8); |
| } |
| |
| private void checkAssertionCodeEnabled(ClassSubject subject, boolean isR8) { |
| assertThat(subject, isPresent()); |
| if (subject.getOriginalName().equals("kotlin._Assertions")) { |
| assert !kotlinStdlibAsLibrary; |
| // With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used. |
| assertEquals( |
| isR8 ? 1 : 2, |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .filter(InstructionSubject::isStaticPut) |
| .count()); |
| assertTrue( |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .anyMatch(instruction -> instruction.isConstNumber(1))); |
| } else { |
| assertTrue( |
| subject |
| .uniqueMethodWithName("m") |
| .streamInstructions() |
| .anyMatch(InstructionSubject::isThrow)); |
| } |
| } |
| |
| private void checkAssertionCodeEnabled(CodeInspector inspector, String clazz, boolean isR8) { |
| |
| checkAssertionCodeEnabled(inspector.clazz(clazz), isR8); |
| } |
| |
| private void checkAssertionCodeLeft(CodeInspector inspector, String clazz, boolean isR8) { |
| ClassSubject subject = inspector.clazz(clazz); |
| if (clazz.equals("kotlin._Assertions")) { |
| assert !kotlinStdlibAsLibrary; |
| if (isR8 && useJvmAssertions) { |
| // When JVM assertions are used the class kotlin._Assertions is unused. |
| assertThat(subject, not(isPresent())); |
| return; |
| } |
| assertThat(subject, isPresent()); |
| // With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used. |
| assertEquals( |
| isR8 ? 1 : 2, |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .filter(InstructionSubject::isStaticPut) |
| .count()); |
| assertFalse( |
| subject |
| .uniqueMethodWithName("<clinit>") |
| .streamInstructions() |
| .anyMatch(InstructionSubject::isConstNumber)); |
| } else { |
| assertThat(subject, isPresent()); |
| MethodSubject clinit = subject.uniqueMethodWithName("<clinit>"); |
| if (useJvmAssertions) { |
| assertTrue(clinit.streamInstructions().anyMatch(InstructionSubject::isStaticPut)); |
| } else { |
| assertThat(clinit, not(isPresent())); |
| } |
| assertTrue( |
| subject |
| .uniqueMethodWithName("m") |
| .streamInstructions() |
| .anyMatch(InstructionSubject::isThrow)); |
| } |
| } |
| |
| private void checkAssertionCodeRemoved(CodeInspector inspector, boolean isR8) { |
| if (isR8) { |
| assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent())); |
| } else if (!kotlinStdlibAsLibrary) { |
| checkAssertionCodeRemoved(inspector, "kotlin._Assertions", isR8); |
| } |
| checkAssertionCodeRemoved(inspector, class1, isR8); |
| checkAssertionCodeRemoved(inspector, class2, isR8); |
| } |
| |
| private void checkAssertionCodeEnabled(CodeInspector inspector, boolean isR8) { |
| if (isR8) { |
| assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent())); |
| } else if (!kotlinStdlibAsLibrary) { |
| checkAssertionCodeEnabled(inspector, "kotlin._Assertions", isR8); |
| } |
| checkAssertionCodeEnabled(inspector, class1, isR8); |
| checkAssertionCodeEnabled(inspector, class2, isR8); |
| } |
| |
| private void checkAssertionCodeLeft(CodeInspector inspector, boolean isR8) { |
| if (!kotlinStdlibAsLibrary) { |
| checkAssertionCodeLeft(inspector, "kotlin._Assertions", isR8); |
| } |
| checkAssertionCodeLeft(inspector, class1, isR8); |
| checkAssertionCodeLeft(inspector, class2, isR8); |
| } |
| |
| @Test |
| public void testAssertionsForDex() throws Exception { |
| Assume.assumeTrue(parameters.isDexRuntime()); |
| // Leaving assertions in or disabling them on Dalvik/Art means no assertions. |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::passthroughAllAssertions), |
| inspector -> checkAssertionCodeLeft(inspector, false), |
| noAllAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::passthroughAllAssertions), |
| inspector -> checkAssertionCodeLeft(inspector, true), |
| noAllAssertionsExpectedLines()); |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::disableAllAssertions), |
| inspector -> checkAssertionCodeRemoved(inspector, false), |
| noAllAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::disableAllAssertions), |
| inspector -> checkAssertionCodeRemoved(inspector, true), |
| noAllAssertionsExpectedLines()); |
| // Compile time enabling assertions gives assertions on Dalvik/Art. |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::enableAllAssertions), |
| inspector -> checkAssertionCodeEnabled(inspector, false), |
| allAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::enableAllAssertions), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| if (useJvmAssertions) { |
| // Enabling for the kotlin generated Java classes should enable all. |
| runD8Test( |
| builder -> |
| builder |
| .addAssertionsConfiguration(b -> b.setEnable().setScopeClass(class1).build()) |
| .addAssertionsConfiguration(b -> b.setEnable().setScopeClass(class2).build()), |
| inspector -> { |
| // The default is applied to kotlin._Assertions (which for DEX is remove). |
| if (!kotlinStdlibAsLibrary) { |
| checkAssertionCodeRemoved(inspector, "kotlin._Assertions", false); |
| } |
| checkAssertionCodeEnabled(inspector, class1, false); |
| checkAssertionCodeEnabled(inspector, class2, false); |
| }, |
| allAssertionsExpectedLines()); |
| } else { |
| // Enabling for the class kotlin._Assertions should enable all. |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopeClass("kotlin._Assertions").build()), |
| inspector -> checkAssertionCodeEnabled(inspector, false), |
| allAssertionsExpectedLines()); |
| } |
| if (useJvmAssertions) { |
| // Enabling for the kotlin generated Java classes should enable all. |
| runR8Test( |
| builder -> |
| builder |
| .addAssertionsConfiguration(b -> b.setEnable().setScopeClass(class1).build()) |
| .addAssertionsConfiguration(b -> b.setEnable().setScopeClass(class2).build()), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| } else { |
| // Enabling for the class kotlin._Assertions should enable all. |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopeClass("kotlin._Assertions").build()), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| } |
| if (useJvmAssertions) { |
| // Enabling for the Java package for the kotlin test classes package should enable all. |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopePackage(kotlintestclasesPackage).build()), |
| inspector -> { |
| // The default is applied to kotlin._Assertions (which for DEX is remove). |
| if (!kotlinStdlibAsLibrary) { |
| checkAssertionCodeRemoved(inspector, "kotlin._Assertions", false); |
| } |
| checkAssertionCodeEnabled(inspector, class1, false); |
| checkAssertionCodeEnabled(inspector, class2, false); |
| }, |
| allAssertionsExpectedLines()); |
| } else { |
| // Enabling for the kotlin package (containing kotlin._Assertions) should enable all. |
| runD8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopePackage("kotlin").build()), |
| inspector -> checkAssertionCodeEnabled(inspector, false), |
| allAssertionsExpectedLines()); |
| } |
| if (useJvmAssertions) { |
| // Enabling for the Java package for the kotlin test classes package should enable all. |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopePackage(kotlintestclasesPackage).build()), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| } else { |
| // Enabling for the kotlin package (containing kotlin._Assertions) should enable all. |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| b -> b.setEnable().setScopePackage("kotlin").build()), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| } |
| } |
| |
| @Test |
| public void testAssertionsForCfEnableWithStackMap() throws Exception { |
| Assume.assumeTrue(parameters.isCfRuntime()); |
| Assume.assumeTrue(useJvmAssertions); |
| Assume.assumeTrue(kotlinCompilationKey.targetVersion == KotlinTargetVersion.JAVA_8); |
| // Compile time enabling or disabling assertions means the -ea flag has no effect. |
| runR8Test( |
| builder -> { |
| builder.addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions); |
| }, |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| runR8Test( |
| builder -> { |
| builder.addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions); |
| }, |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines(), |
| true); |
| } |
| |
| @Test |
| public void testAssertionsForCfPassThrough() throws Exception { |
| Assume.assumeTrue(parameters.isCfRuntime()); |
| // Leaving assertion code means assertions are controlled by the -ea flag. |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::passthroughAllAssertions), |
| inspector -> checkAssertionCodeLeft(inspector, true), |
| noAllAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::passthroughAllAssertions), |
| inspector -> checkAssertionCodeLeft(inspector, true), |
| allAssertionsExpectedLines(), |
| true); |
| } |
| |
| @Test |
| public void testAssertionsForCfEnable() throws Exception { |
| Assume.assumeTrue(parameters.isCfRuntime()); |
| // Compile time enabling or disabling assertions means the -ea flag has no effect. |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::enableAllAssertions), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::enableAllAssertions), |
| inspector -> checkAssertionCodeEnabled(inspector, true), |
| allAssertionsExpectedLines(), |
| true); |
| } |
| |
| @Test |
| public void testAssertionsForCfDisable() throws Exception { |
| Assume.assumeTrue(parameters.isCfRuntime()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::disableAllAssertions), |
| inspector -> checkAssertionCodeRemoved(inspector, true), |
| noAllAssertionsExpectedLines()); |
| runR8Test( |
| builder -> |
| builder.addAssertionsConfiguration( |
| AssertionsConfiguration.Builder::disableAllAssertions), |
| inspector -> checkAssertionCodeRemoved(inspector, true), |
| noAllAssertionsExpectedLines(), |
| true); |
| } |
| |
| @Test |
| public void TestWithModifiedKotlinAssertions() throws Exception { |
| Assume.assumeTrue(parameters.isDexRuntime()); |
| testForD8() |
| .addProgramClassFileData(dumpModifiedKotlinAssertions()) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .setMinApi(parameters.getApiLevel()) |
| .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions) |
| .run( |
| parameters.getRuntime(), |
| getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt") |
| .assertSuccessWithOutputLines(noAllAssertionsExpectedLines()); |
| testForD8() |
| .addProgramClassFileData(dumpModifiedKotlinAssertions()) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .setMinApi(parameters.getApiLevel()) |
| .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions) |
| .run( |
| parameters.getRuntime(), |
| getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt") |
| .assertSuccessWithOutputLines(allAssertionsExpectedLines()); |
| testForD8() |
| .addProgramClassFileData(dumpModifiedKotlinAssertions()) |
| .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) |
| .setMinApi(parameters.getApiLevel()) |
| .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions) |
| .run( |
| parameters.getRuntime(), |
| getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt") |
| .assertSuccessWithOutputLines(noAllAssertionsExpectedLines()); |
| } |
| |
| // Slightly modified version of kotlin._Assertions to hit all code paths in the assertion |
| // rewriter. See "Code added" below. |
| public static byte[] dumpModifiedKotlinAssertions() { |
| |
| ClassWriter classWriter = new ClassWriter(0); |
| FieldVisitor fieldVisitor; |
| MethodVisitor methodVisitor; |
| |
| classWriter.visit( |
| V1_6, |
| ACC_PUBLIC | ACC_FINAL | ACC_SUPER, |
| "kotlin/_Assertions", |
| null, |
| "java/lang/Object", |
| null); |
| |
| { |
| fieldVisitor = |
| classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "ENABLED", "Z", null, null); |
| fieldVisitor.visitEnd(); |
| } |
| { |
| fieldVisitor = |
| classWriter.visitField( |
| ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "INSTANCE", "Lkotlin/_Assertions;", null, null); |
| fieldVisitor.visitEnd(); |
| } |
| { |
| methodVisitor = |
| classWriter.visitMethod( |
| ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC | ACC_DEPRECATED, |
| "ENABLED$annotations", |
| "()V", |
| null, |
| null); |
| methodVisitor.visitCode(); |
| methodVisitor.visitInsn(RETURN); |
| methodVisitor.visitMaxs(0, 0); |
| methodVisitor.visitEnd(); |
| } |
| { |
| methodVisitor = classWriter.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null); |
| methodVisitor.visitCode(); |
| methodVisitor.visitVarInsn(ALOAD, 0); |
| methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); |
| methodVisitor.visitInsn(RETURN); |
| methodVisitor.visitMaxs(1, 1); |
| methodVisitor.visitEnd(); |
| } |
| { |
| methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); |
| methodVisitor.visitCode(); |
| methodVisitor.visitTypeInsn(NEW, "kotlin/_Assertions"); |
| methodVisitor.visitInsn(DUP); |
| methodVisitor.visitMethodInsn(INVOKESPECIAL, "kotlin/_Assertions", "<init>", "()V", false); |
| methodVisitor.visitVarInsn(ASTORE, 0); |
| methodVisitor.visitVarInsn(ALOAD, 0); |
| methodVisitor.visitFieldInsn( |
| PUTSTATIC, "kotlin/_Assertions", "INSTANCE", "Lkotlin/_Assertions;"); |
| |
| // Code added (added an additional call to getClass().desiredAssertionStatus() which |
| // result is not assigned to ENABLED). |
| methodVisitor.visitVarInsn(ALOAD, 0); |
| methodVisitor.visitMethodInsn( |
| INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); |
| methodVisitor.visitMethodInsn( |
| INVOKEVIRTUAL, "java/lang/Class", "desiredAssertionStatus", "()Z", false); |
| methodVisitor.visitInsn(POP); |
| // End code added. |
| |
| methodVisitor.visitVarInsn(ALOAD, 0); |
| methodVisitor.visitMethodInsn( |
| INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); |
| methodVisitor.visitMethodInsn( |
| INVOKEVIRTUAL, "java/lang/Class", "desiredAssertionStatus", "()Z", false); |
| methodVisitor.visitFieldInsn(PUTSTATIC, "kotlin/_Assertions", "ENABLED", "Z"); |
| methodVisitor.visitInsn(RETURN); |
| methodVisitor.visitMaxs(2, 1); |
| methodVisitor.visitEnd(); |
| } |
| classWriter.visitEnd(); |
| |
| return classWriter.toByteArray(); |
| } |
| } |