| // Copyright (c) 2018, 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.ir.optimize; |
| |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.D8; |
| import com.android.tools.r8.D8Command; |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.OutputMode; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.ProcessResult; |
| import com.android.tools.r8.jasmin.JasminBuilder; |
| import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder; |
| import com.android.tools.r8.jasmin.JasminTestBase; |
| import com.google.common.collect.ImmutableList; |
| import java.nio.file.Path; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class NullArrayAndNullObjectValueTest extends JasminTestBase { |
| |
| @Test |
| public void testNullIsArray() throws Exception { |
| JasminBuilder jasminBuilder = new JasminBuilder(); |
| ClassBuilder classBuilder = jasminBuilder.addClass("TestClass"); |
| |
| // Store directly to the null value. |
| classBuilder.addStaticMethod( |
| "nullArrayStore", |
| ImmutableList.of(), |
| "V", |
| ".limit stack 10", |
| ".limit locals 10", |
| ".catch java/lang/NullPointerException from L0 to L1 using L2", |
| "aconst_null", |
| "iconst_0", |
| "iconst_1", |
| "L0:", |
| "iastore", |
| "L1:", |
| "return", |
| "L2:", |
| "pop", |
| "getstatic java/lang/System/out Ljava/io/PrintStream;", |
| "ldc \"NPE\"", |
| "invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V", |
| "return"); |
| |
| // Load directly from the null value. |
| classBuilder.addStaticMethod( |
| "nullArrayLoad", |
| ImmutableList.of(), |
| "V", |
| ".limit stack 10", |
| ".limit locals 10", |
| ".catch java/lang/NullPointerException from L0 to L1 using L2", |
| "aconst_null", |
| "iconst_0", |
| "L0:", |
| "iaload", |
| "L1:", |
| "pop", |
| "return", |
| "L2:", |
| "pop", |
| "getstatic java/lang/System/out Ljava/io/PrintStream;", |
| "ldc \"NPE\"", |
| "invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V", |
| "return"); |
| |
| // Get the length of the null value. |
| classBuilder.addStaticMethod( |
| "nullArrayLength", |
| ImmutableList.of(), |
| "V", |
| ".limit stack 10", |
| ".limit locals 10", |
| ".catch java/lang/NullPointerException from L0 to L1 using L2", |
| "aconst_null", |
| "L0:", |
| "arraylength", |
| "L1:", |
| "pop", |
| "return", |
| "L2:", |
| "pop", |
| "getstatic java/lang/System/out Ljava/io/PrintStream;", |
| "ldc \"NPE\"", |
| "invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V", |
| "return"); |
| |
| // Use the same null value (local containing null) as both an object type and an array type. |
| classBuilder.addStaticMethod( |
| "nullToObjectAndArray", |
| ImmutableList.of(), |
| "V", |
| ".limit stack 10", |
| ".limit locals 10", |
| // Stored null in local 0 |
| "aconst_null", |
| "astore 0", |
| // Create single-cell array of object type and store the null in it. |
| "iconst_1", |
| "anewarray java/lang/String", |
| "iconst_0", |
| "aload 0", |
| "aastore", |
| // Create single-cell array of array type and store the null in it. |
| "iconst_1", |
| "anewarray [I", |
| "iconst_0", |
| "aload 0", |
| "aastore", |
| "return"); |
| |
| classBuilder.addMainMethod( |
| ".limit stack 5", |
| ".limit locals 5", |
| "invokestatic TestClass/nullToObjectAndArray()V", |
| "invokestatic TestClass/nullArrayStore()V", |
| "invokestatic TestClass/nullArrayLoad()V", |
| "invokestatic TestClass/nullArrayLength()V", |
| "return"); |
| |
| Path riJar = temp.getRoot().toPath().resolve("ri-out.jar"); |
| jasminBuilder.writeJar(riJar, new DiagnosticsHandler() {}); |
| ProcessResult riResult = ToolHelper.runJava(riJar, "TestClass"); |
| Assert.assertEquals(riResult.toString(), 0, riResult.exitCode); |
| |
| Path d8Jar = temp.getRoot().toPath().resolve("d8-out.jar"); |
| D8.run( |
| D8Command.builder().addProgramFiles(riJar).setOutput(d8Jar, OutputMode.DexIndexed).build()); |
| ProcessResult d8Result = ToolHelper.runArtRaw(d8Jar.toString(), "TestClass"); |
| Assert.assertEquals(d8Result.toString(), 0, riResult.exitCode); |
| |
| Assert.assertEquals(riResult.stdout, d8Result.stdout); |
| } |
| |
| @Test |
| public void testObjectNotArray() throws Exception { |
| JasminBuilder jasminBuilder = new JasminBuilder(); |
| ClassBuilder classBuilder = jasminBuilder.addClass("TestClass"); |
| |
| // Invalid attempt at using object as an array. |
| classBuilder.addStaticMethod( |
| "objectAsArray", |
| ImmutableList.of("Ljava/lang/Object;"), |
| "V", |
| ".limit stack 10", |
| ".limit locals 10", |
| ".catch java/lang/NullPointerException from L0 to L1 using L2", |
| "aload 0", |
| "iconst_0", |
| "iconst_1", |
| "L0:", |
| "iastore", |
| "L1:", |
| "return", |
| "L2:", |
| "pop", |
| "getstatic java/lang/System/out Ljava/io/PrintStream;", |
| "ldc \"NPE\"", |
| "invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V", |
| "return"); |
| classBuilder.addMainMethod( |
| ".limit stack 5", |
| ".limit locals 5", |
| "aconst_null", |
| "invokestatic TestClass/objectAsArray(Ljava/lang/Object;)V", |
| "return"); |
| |
| Path riJar = temp.getRoot().toPath().resolve("ri-out.jar"); |
| jasminBuilder.writeJar(riJar, new DiagnosticsHandler() {}); |
| ProcessResult riResult = ToolHelper.runJava(riJar, "TestClass"); |
| Assert.assertEquals(riResult.toString(), 1, riResult.exitCode); |
| assertTrue(riResult.stderr.contains("VerifyError")); |
| |
| Path d8Jar = temp.getRoot().toPath().resolve("d8-out.jar"); |
| try { |
| D8.run( |
| D8Command.builder().addProgramFiles(riJar).setOutput(d8Jar, OutputMode.DexIndexed) |
| .build()); |
| } catch (CompilationFailedException e) { |
| // Discard expected failure. |
| return; |
| } |
| Assert.fail("Expected D8 to fail compilation"); |
| } |
| } |