summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRohit Agrawal <rohitagr@google.com>2015-10-29 10:32:07 -0700
committerRohit Agrawal <rohitagr@google.com>2015-11-06 15:06:47 -0800
commitc2ea99d6fb4f9ac04b26ab0011fa6f1f85722e95 (patch)
treebefa998e4ff7771ecab11c87219425c73693e6db
parentbb4f0a2f4a333b10ca6b2dde1b721e78e27ad18d (diff)
downloaddalvik-c2ea99d6fb4f9ac04b26ab0011fa6f1f85722e95.tar.gz
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
-rw-r--r--dx/src/com/android/dx/cf/code/Simulator.java33
-rw-r--r--dx/tests/111-use-null-as-array/expected.txt134
-rw-r--r--dx/tests/111-use-null-as-array/multidimensional.sh80
-rw-r--r--dx/tests/111-use-null-as-array/run7
4 files changed, 242 insertions, 12 deletions
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
index 35e62281b..55a9ac8ea 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 @@ public class Simulator {
* 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 @@ public class Simulator {
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 @@ public class Simulator {
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 @@ public class Simulator {
* 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 7e2116b92..cbb49ead3 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 @@ regs: 0003; ins: 0000; outs: 0000
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 000000000..d1afede0c
--- /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 7e4e1e8a1..ee89d3eaa 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