diff options
author | Rohit Agrawal <rohitagr@google.com> | 2015-10-29 10:32:07 -0700 |
---|---|---|
committer | Rohit Agrawal <rohitagr@google.com> | 2015-11-06 15:06:47 -0800 |
commit | c2ea99d6fb4f9ac04b26ab0011fa6f1f85722e95 (patch) | |
tree | befa998e4ff7771ecab11c87219425c73693e6db | |
parent | bb4f0a2f4a333b10ca6b2dde1b721e78e27ad18d (diff) | |
download | dalvik-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.java | 33 | ||||
-rw-r--r-- | dx/tests/111-use-null-as-array/expected.txt | 134 | ||||
-rw-r--r-- | dx/tests/111-use-null-as-array/multidimensional.sh | 80 | ||||
-rw-r--r-- | dx/tests/111-use-null-as-array/run | 7 |
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 |