Merge "Handle ACONST_NULL multidimensional arrays."
am: 0a0ee368e9

* commit '0a0ee368e98d7a919a33b158de99f6c642a1a31b':
  Handle ACONST_NULL multidimensional arrays.
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