Handle ACONST_NULL multidimensional arrays.

Instead of inferring that an AALOAD following an ACONST_NULL means that
the null constant was of type Object[] and the result is of type Object,
infer that the result is also null and don't infer the type of the null
constant. Note that this is also incorrect but it doesn't matter since
the code will always throw a NullPointerException at runtime.

This removes dx's reliance on javac bug
https://bugs.openjdk.java.net/browse/JDK-7040592

Bug: 25075957

(cherry picked from commit 54576c56f05c659186dc2117ecb7ed2739c175df)

Change-Id: I021a6686cfc298ada50df04fd6b345f4ad838229
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
index 35e6228..55a9ac8 100644
--- a/dx/src/com/android/dx/cf/code/Simulator.java
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -141,14 +141,19 @@
      * actually present on the stack.</p>
      *
      * <p>In the case where there is a known-null on the stack where
-     * an array is expected, we just fall back to the implied type of
-     * the instruction. Due to the quirk described above, this means
-     * that source code that uses <code>boolean[]</code> might get
-     * translated surprisingly -- but correctly -- into an instruction
-     * that specifies a <code>byte[]</code>. It will be correct,
-     * because should the code actually execute, it will necessarily
-     * throw a <code>NullPointerException</code>, and it won't matter
-     * what opcode variant is used to achieve that result.</p>
+     * an array is expected, our behavior depends on the implied type
+     * of the instruction. When the implied type is a reference, we
+     * don't attempt to infer anything, as we don't know the dimension
+     * of the null constant and thus any explicit inferred type could
+     * be wrong. When the implied type is a primitive, we fall back to
+     * the implied type of the instruction. Due to the quirk described
+     * above, this means that source code that uses
+     * <code>boolean[]</code> might get translated surprisingly -- but
+     * correctly -- into an instruction that specifies a
+     * <code>byte[]</code>. It will be correct, because should the
+     * code actually execute, it will necessarily throw a
+     * <code>NullPointerException</code>, and it won't matter what
+     * opcode variant is used to achieve that result.</p>
      *
      * @param impliedType {@code non-null;} type implied by the
      * instruction; is <i>not</i> an array type
@@ -160,7 +165,9 @@
     private static Type requiredArrayTypeFor(Type impliedType,
             Type foundArrayType) {
         if (foundArrayType == Type.KNOWN_NULL) {
-            return impliedType.getArrayType();
+            return impliedType.isReference()
+                ? Type.KNOWN_NULL
+                : impliedType.getArrayType();
         }
 
         if ((impliedType == Type.OBJECT)
@@ -317,7 +324,9 @@
                         requiredArrayTypeFor(type, foundArrayType);
 
                     // Make type agree with the discovered requiredArrayType.
-                    type = requiredArrayType.getComponentType();
+                    type = (requiredArrayType == Type.KNOWN_NULL)
+                        ? Type.KNOWN_NULL
+                        : requiredArrayType.getComponentType();
 
                     machine.popArgs(frame, requiredArrayType, Type.INT);
                     break;
@@ -375,7 +384,9 @@
                      * if it has local info.
                      */
                     if (foundArrayLocal) {
-                        type = requiredArrayType.getComponentType();
+                        type = (requiredArrayType == Type.KNOWN_NULL)
+                            ? Type.KNOWN_NULL
+                            : requiredArrayType.getComponentType();
                     }
 
                     machine.popArgs(frame, requiredArrayType, Type.INT, type);
diff --git a/dx/tests/111-use-null-as-array/expected.txt b/dx/tests/111-use-null-as-array/expected.txt
index 7e2116b..cbb49ea 100644
--- a/dx/tests/111-use-null-as-array/expected.txt
+++ b/dx/tests/111-use-null-as-array/expected.txt
@@ -114,3 +114,137 @@
   0003: const/16 v2, #int 16 // #0010
   0005: aput v2, v0, v1
   0007: return-void
+multidimensional.test_getBooleanArray:()Z:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-byte v0, v0, v1
+  0006: return v0
+multidimensional.test_getByteArray:()B:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-byte v0, v0, v1
+  0006: return v0
+multidimensional.test_getCharArray:()C:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-char v0, v0, v1
+  0006: return v0
+multidimensional.test_getDoubleArray:()D:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-wide v0, v0, v1
+  0006: return-wide v0
+multidimensional.test_getFloatArray:()F:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget v0, v0, v1
+  0006: return v0
+multidimensional.test_getIntArray:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget v0, v0, v1
+  0006: return v0
+multidimensional.test_getLongArray:()J:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-wide v0, v0, v1
+  0006: return-wide v0
+multidimensional.test_getObjectArray:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-object v0, v0, v1
+  0006: return-object v0
+multidimensional.test_getShortArray:()S:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: aget-short v0, v0, v1
+  0006: return v0
+multidimensional.test_setBooleanArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #int 0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
+multidimensional.test_setByteArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #int 0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
+multidimensional.test_setCharArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #int 0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
+multidimensional.test_setDoubleArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: const-wide/16 v2, #double 0.0 // #0000
+  0006: aput-wide v2, v0, v1
+  0008: return-void
+multidimensional.test_setFloatArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #float 0.0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
+multidimensional.test_setIntArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #int 0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
+multidimensional.test_setLongArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v1
+  0004: const-wide/16 v2, #long 0 // #0000
+  0006: aput-wide v2, v0, v1
+  0008: return-void
+multidimensional.test_setObjectArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #null // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: aget-object v0, v2, v1
+  0004: aput-object v2, v0, v1
+  0006: return-void
+multidimensional.test_setShortArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v2, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aget-object v0, v0, v2
+  0004: const/4 v1, #int 0 // #0
+  0005: aput v1, v0, v2
+  0007: return-void
diff --git a/dx/tests/111-use-null-as-array/multidimensional.sh b/dx/tests/111-use-null-as-array/multidimensional.sh
new file mode 100644
index 0000000..d1afede
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/multidimensional.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo '
+.class multidimensional
+.super java/lang/Object
+'
+
+function onetype() {
+local typename=$1
+local stacksize=$2
+local defaultvalue=$3
+local descriptor=$4
+local defaultload=$5
+local loadstoreprefix=$6
+local returnprefix=${7:-$loadstoreprefix}
+echo "
+; Output from some versions of javac on:
+; public static $typename test_get${typename^}Array() {
+;     $typename[][] array = null;
+;     return array[1][1];
+; }
+.method public static test_get${typename^}Array()$descriptor
+    .limit locals 1
+    .limit stack 2
+
+    aconst_null
+    astore_0
+    aload_0
+    iconst_1
+    aaload
+    iconst_1
+    ${loadstoreprefix}aload
+    ${returnprefix}return
+.end method
+
+; Output from some versions of javac on:
+; public static void test_set${typename^}Array() {
+;     $typename[][] array = null;
+;     array[1][1] = $defaultvalue;
+; }
+.method public static test_set${typename^}Array()V
+    .limit locals 1
+    .limit stack $((stacksize+2))
+
+    aconst_null
+    astore_0
+    aload_0
+    iconst_1
+    aaload
+    iconst_1
+    $defaultload
+    ${loadstoreprefix}astore
+    return
+.end method
+"
+}
+
+onetype Object 1 null 'Ljava/lang/Object;' aconst_null a
+onetype boolean 1 false Z iconst_0 b i
+onetype byte 1 0 B iconst_0 b i
+onetype char 1 0 C iconst_0 c i
+onetype short 1 0 S iconst_0 s i
+onetype int 1 0 I iconst_0 i
+onetype long 2 0 J lconst_0 l
+onetype float 1 0 F fconst_0 f
+onetype double 2 0 D dconst_0 d
diff --git a/dx/tests/111-use-null-as-array/run b/dx/tests/111-use-null-as-array/run
index 7e4e1e8..ee89d3e 100644
--- a/dx/tests/111-use-null-as-array/run
+++ b/dx/tests/111-use-null-as-array/run
@@ -16,4 +16,9 @@
 
 $JAVAC -g -d . Blort.java
 dx --debug --dex --positions=none --no-locals \
-    --dump-to=- --dump-method="Blort.test*" *.class
+    --dump-to=- --dump-method="Blort.test*" Blort.class
+
+bash multidimensional.sh > multidimensional.j
+jasmin -d . multidimensional.j >/dev/null
+dx --debug --dex --positions=none --no-locals \
+    --dump-to=- --dump-method="multidimensional.*" multidimensional.class