| // 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; |
| |
| import static com.android.tools.r8.DiagnosticsMatcher.diagnosticException; |
| import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; |
| import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; |
| import static org.hamcrest.CoreMatchers.allOf; |
| import static org.hamcrest.core.StringContains.containsString; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import com.android.tools.r8.cf.CfVersion; |
| import com.android.tools.r8.utils.ExceptionDiagnostic; |
| import com.android.tools.r8.utils.InternalOptions; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.objectweb.asm.ClassWriter; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| |
| @RunWith(Parameterized.class) |
| public class FailCompilationOnFutureVersionsTest extends TestBase { |
| |
| static final int UNSUPPORTED_CF_VERSION = |
| new InternalOptions().getSupportedCfVersion().major() + 1; |
| static final int UNSUPPORTED_DEX_VERSION = InternalOptions.SUPPORTED_DEX_VERSION + 1; |
| |
| private final TestParameters parameters; |
| |
| @Parameterized.Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withDexRuntimes().withAllApiLevels().build(); |
| } |
| |
| public FailCompilationOnFutureVersionsTest(TestParameters parameters) { |
| this.parameters = parameters; |
| assertTrue("Test assumes the first DEX version char is '0'.", UNSUPPORTED_DEX_VERSION < 100); |
| } |
| |
| @Test |
| public void testDex() throws CompilationFailedException, IOException { |
| // Generate a DEX file with a version higher than the supported one. |
| Path out = |
| testForD8() |
| .addProgramClasses(TestClass.class) |
| .setMinApi(parameters) |
| .addOptionsModification( |
| options -> |
| options.testing.forceDexVersionBytes = |
| ("0" + UNSUPPORTED_DEX_VERSION).getBytes()) |
| .compile() |
| .writeToZip(); |
| try { |
| testForD8() |
| .addProgramFiles(out) |
| .setMinApi(parameters) |
| .compileWithExpectedDiagnostics( |
| diagnotics -> { |
| diagnotics.assertOnlyErrors(); |
| diagnotics.assertAllErrorsMatch( |
| diagnosticMessage( |
| containsString( |
| "Unsupported DEX file version: 0" + UNSUPPORTED_DEX_VERSION))); |
| }); |
| } catch (CompilationFailedException e) { |
| return; |
| } |
| fail("Expected compilation error"); |
| } |
| |
| @Test |
| public void testCfVersionUnsupportedByAsm() { |
| try { |
| testForD8() |
| .addProgramClassFileData(CfDump.dump(UNSUPPORTED_CF_VERSION)) |
| .setMinApi(parameters) |
| .compileWithExpectedDiagnostics( |
| diagnotics -> { |
| diagnotics.assertOnlyErrors(); |
| diagnotics.assertAllErrorsMatch( |
| allOf( |
| diagnosticType(ExceptionDiagnostic.class), |
| diagnosticException(IllegalArgumentException.class), |
| diagnosticMessage( |
| containsString( |
| "Unsupported class file major version " |
| + UNSUPPORTED_CF_VERSION)))); |
| }); |
| } catch (CompilationFailedException e) { |
| return; |
| } |
| fail("Expected compilation error"); |
| } |
| |
| @Test |
| public void testCfUnsupportedByD8() { |
| try { |
| testForD8() |
| .addProgramClassFileData(CfDump.dump(CfVersion.V26.major())) |
| .setMinApi(parameters) |
| .addOptionsModification( |
| options -> options.getTestingOptions().supportedCfVersionForTesting = CfVersion.V25) |
| .compileWithExpectedDiagnostics( |
| diagnotics -> { |
| diagnotics.assertOnlyErrors(); |
| diagnotics.assertAllErrorsMatch( |
| diagnosticMessage( |
| containsString( |
| "Unsupported class file version: " + CfVersion.V26.major()))); |
| }); |
| } catch (CompilationFailedException e) { |
| return; |
| } |
| fail("Expected compilation error"); |
| } |
| |
| @Test |
| public void testOverrideCfUnsupportedByD8() throws Exception { |
| testForD8() |
| .addProgramClassFileData(CfDump.dump(CfVersion.V26.major())) |
| .setMinApi(parameters) |
| .addOptionsModification( |
| options -> { |
| options.getTestingOptions().supportedCfVersionForTesting = CfVersion.V25; |
| options.getTestingOptions().allowAnyClassFileVersion = true; |
| }) |
| .compile(); |
| } |
| |
| public static class CfDump implements Opcodes { |
| |
| public static byte[] dump(int version) { |
| // Generate a class file with a version higher than the supported one. |
| ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); |
| MethodVisitor mv; |
| cw.visit(version, ACC_PUBLIC + ACC_SUPER, "Test", null, "java/lang/Object", null); |
| { |
| mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); |
| mv.visitCode(); |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(1, 1); |
| mv.visitEnd(); |
| } |
| cw.visitEnd(); |
| return cw.toByteArray(); |
| } |
| } |
| |
| public static class TestClass { |
| // Intentionally empty stub class for the DEX generation. |
| } |
| } |