diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-20 14:03:55 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-20 14:03:55 -0800 |
| commit | 4c1a2915e40eceeb68dbc323d28b8bf8763af83b (patch) | |
| tree | b6df7a910dfb981a44919eb33fc29aaaca02b7b2 | |
| parent | 066bed5fb19102e4f560f242b0cee645f1ed8b31 (diff) | |
| download | android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.tar.gz android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.tar.bz2 android_dalvik-4c1a2915e40eceeb68dbc323d28b8bf8763af83b.zip | |
auto import from //branches/cupcake/...@127101
96 files changed, 1284 insertions, 295 deletions
diff --git a/docs/verifier.html b/docs/verifier.html index 42abc9202..656b83273 100644 --- a/docs/verifier.html +++ b/docs/verifier.html @@ -139,6 +139,12 @@ some amount of additional memory or spending additional cycles on non-DEX-optimized instructions. We don't want to throw an IllegalAccessError at verification time, since that would indicate that access to the class being verified was illegal. +<p> +One approach that might be worth pursuing: for situations like illegal +accesses, the verifier makes an in-RAM private copy of the method, and +alters the instructions there. The class object is altered to point at +the new copy of the instructions. This requires minimal memory overhead +and provides a better experience for developers. <p> The VerifyError is accompanied by detailed, if somewhat cryptic, diff --git a/dx/tests/042-dex-ignore-result/expected.txt b/dx/tests/042-dex-ignore-result/expected.txt index 8ba83ea8e..a1db6a475 100644 --- a/dx/tests/042-dex-ignore-result/expected.txt +++ b/dx/tests/042-dex-ignore-result/expected.txt @@ -1,2 +1 @@ -javac 1.7.0-internal_bootstrap Good! diff --git a/dx/tests/043-dex-two-classes/expected.txt b/dx/tests/043-dex-two-classes/expected.txt index 82ec25d38..e88eb13a0 100644 --- a/dx/tests/043-dex-two-classes/expected.txt +++ b/dx/tests/043-dex-two-classes/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap processing Blort.class... processing Zorch.class... Good! diff --git a/dx/tests/044-dex-math-ops/expected.txt b/dx/tests/044-dex-math-ops/expected.txt index 7fe180571..f6f8b7996 100644 --- a/dx/tests/044-dex-math-ops/expected.txt +++ b/dx/tests/044-dex-math-ops/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.blort:(DD)V: regs: 000f; ins: 0005; outs: 0000 0000: move-object v0, v10 diff --git a/dx/tests/045-dex-switch-ops/expected.txt b/dx/tests/045-dex-switch-ops/expected.txt index 72c571bab..46476ea83 100644 --- a/dx/tests/045-dex-switch-ops/expected.txt +++ b/dx/tests/045-dex-switch-ops/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.switchTest1:(I)I: regs: 0005; ins: 0002; outs: 0000 0000: move-object v0, v3 diff --git a/dx/tests/046-dex-exceptions/expected.txt b/dx/tests/046-dex-exceptions/expected.txt index 5eddfcb7e..933a54764 100644 --- a/dx/tests/046-dex-exceptions/expected.txt +++ b/dx/tests/046-dex-exceptions/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.exTest1:(I)I: regs: 0004; ins: 0001; outs: 0001 0000: move v0, v3 diff --git a/dx/tests/047-dex-wide-args/expected.txt b/dx/tests/047-dex-wide-args/expected.txt index a6badfde8..cc353fc51 100644 --- a/dx/tests/047-dex-wide-args/expected.txt +++ b/dx/tests/047-dex-wide-args/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(IJIJ)J: regs: 0010; ins: 0006; outs: 0000 0000: move v0, v10 diff --git a/dx/tests/048-dex-new-array/expected.txt b/dx/tests/048-dex-new-array/expected.txt index 6866d1f61..15332ca5b 100644 --- a/dx/tests/048-dex-new-array/expected.txt +++ b/dx/tests/048-dex-new-array/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()V: regs: 0002; ins: 0000; outs: 0001 0000: const/4 v1, #int 0 // #0 diff --git a/dx/tests/049-dex-instanceof/expected.txt b/dx/tests/049-dex-instanceof/expected.txt index c8fa6052d..77f903c03 100644 --- a/dx/tests/049-dex-instanceof/expected.txt +++ b/dx/tests/049-dex-instanceof/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(Ljava/lang/Object;)Z: regs: 0003; ins: 0001; outs: 0000 0000: move-object v0, v2 diff --git a/dx/tests/050-dex-checkcast/expected.txt b/dx/tests/050-dex-checkcast/expected.txt index 270b03a8a..96f7f201b 100644 --- a/dx/tests/050-dex-checkcast/expected.txt +++ b/dx/tests/050-dex-checkcast/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(Ljava/lang/Object;)LBlort;: regs: 0003; ins: 0001; outs: 0000 0000: move-object v0, v2 diff --git a/dx/tests/051-dex-explicit-null/expected.txt b/dx/tests/051-dex-explicit-null/expected.txt index 8fb700cc2..18ce09259 100644 --- a/dx/tests/051-dex-explicit-null/expected.txt +++ b/dx/tests/051-dex-explicit-null/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()Ljava/lang/Object;: regs: 0001; ins: 0000; outs: 0000 0000: const/4 v0, #null // #0 diff --git a/dx/tests/052-dex-static-var-access/expected.txt b/dx/tests/052-dex-static-var-access/expected.txt index bfa91965e..239f98492 100644 --- a/dx/tests/052-dex-static-var-access/expected.txt +++ b/dx/tests/052-dex-static-var-access/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()Ljava/lang/Object;: regs: 0004; ins: 0000; outs: 0000 0000: sget-byte v1, Blort.staticByte:B diff --git a/dx/tests/053-dex-instance-var-access/expected.txt b/dx/tests/053-dex-instance-var-access/expected.txt index a0c0acaff..b73f617be 100644 --- a/dx/tests/053-dex-instance-var-access/expected.txt +++ b/dx/tests/053-dex-instance-var-access/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()Ljava/lang/Object;: regs: 0006; ins: 0001; outs: 0000 0000: move-object v0, v5 diff --git a/dx/tests/054-dex-high16/expected.txt b/dx/tests/054-dex-high16/expected.txt index 42902bc47..b9d6cf304 100644 --- a/dx/tests/054-dex-high16/expected.txt +++ b/dx/tests/054-dex-high16/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.testDouble:()V: regs: 0002; ins: 0000; outs: 0002 0000: const-wide/high16 v0, #double -Infinity // #fff0000000000000 diff --git a/dx/tests/055-dex-explicit-throw/expected.txt b/dx/tests/055-dex-explicit-throw/expected.txt index 1c6243176..b7e4c334a 100644 --- a/dx/tests/055-dex-explicit-throw/expected.txt +++ b/dx/tests/055-dex-explicit-throw/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()V: regs: 0001; ins: 0000; outs: 0000 0000: sget-object v0, Blort.theException:Ljava/lang/RuntimeException; diff --git a/dx/tests/056-dex-call-interface/expected.txt b/dx/tests/056-dex-call-interface/expected.txt index d3e7451d5..faf18c42e 100644 --- a/dx/tests/056-dex-call-interface/expected.txt +++ b/dx/tests/056-dex-call-interface/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(LZorch;D)I: regs: 000b; ins: 0003; outs: 0003 0000: move-object v0, v8 diff --git a/dx/tests/057-dex-call-virtual/expected.txt b/dx/tests/057-dex-call-virtual/expected.txt index d4932ccd5..ea50d354d 100644 --- a/dx/tests/057-dex-call-virtual/expected.txt +++ b/dx/tests/057-dex-call-virtual/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(LZorch;)I: regs: 0004; ins: 0001; outs: 0002 0000: move-object v0, v3 diff --git a/dx/tests/058-dex-call-direct/expected.txt b/dx/tests/058-dex-call-direct/expected.txt index cac99b10e..11820a3d4 100644 --- a/dx/tests/058-dex-call-direct/expected.txt +++ b/dx/tests/058-dex-call-direct/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(LBlort;)I: regs: 0004; ins: 0001; outs: 0002 0000: move-object v0, v3 diff --git a/dx/tests/059-dex-call-super/expected.txt b/dx/tests/059-dex-call-super/expected.txt index e18c9df06..8bb3bdebe 100644 --- a/dx/tests/059-dex-call-super/expected.txt +++ b/dx/tests/059-dex-call-super/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()I: regs: 0004; ins: 0001; outs: 0002 0000: move-object v0, v3 diff --git a/dx/tests/060-dex-call-static/expected.txt b/dx/tests/060-dex-call-static/expected.txt index f451938b2..94329898a 100644 --- a/dx/tests/060-dex-call-static/expected.txt +++ b/dx/tests/060-dex-call-static/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()I: regs: 0001; ins: 0000; outs: 0001 0000: invoke-static {}, Zorch.zorch1:()V diff --git a/dx/tests/061-dex-try-catch/expected.txt b/dx/tests/061-dex-try-catch/expected.txt index dc8e6a7f5..d66f40468 100644 --- a/dx/tests/061-dex-try-catch/expected.txt +++ b/dx/tests/061-dex-try-catch/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(I)V: regs: 0004; ins: 0001; outs: 0000 0000: move v0, v3 diff --git a/dx/tests/062-dex-synch-method/expected.txt b/dx/tests/062-dex-synch-method/expected.txt index 9492aa1e8..68bdebe76 100644 --- a/dx/tests/062-dex-synch-method/expected.txt +++ b/dx/tests/062-dex-synch-method/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.testInstance1:()V: regs: 0003; ins: 0001; outs: 0000 0000: move-object v0, v2 diff --git a/dx/tests/063-dex-empty-switch/expected.txt b/dx/tests/063-dex-empty-switch/expected.txt index 4e0f6ba85..e4d1a469e 100644 --- a/dx/tests/063-dex-empty-switch/expected.txt +++ b/dx/tests/063-dex-empty-switch/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(I)I: regs: 0005; ins: 0002; outs: 0000 0000: move-object v0, v3 diff --git a/dx/tests/064-dex-array-access/expected.txt b/dx/tests/064-dex-array-access/expected.txt index d1912f81d..ae251e780 100644 --- a/dx/tests/064-dex-array-access/expected.txt +++ b/dx/tests/064-dex-array-access/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test01:([Z)Z: regs: 0007; ins: 0002; outs: 0000 0000: move-object v0, v5 diff --git a/dx/tests/065-dex-new-array/expected.txt b/dx/tests/065-dex-new-array/expected.txt index a16ebe979..0b26182be 100644 --- a/dx/tests/065-dex-new-array/expected.txt +++ b/dx/tests/065-dex-new-array/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()[Z: regs: 0003; ins: 0001; outs: 0000 0000: move-object v0, v2 diff --git a/dx/tests/066-dex-try-catch-rethrow/expected.txt b/dx/tests/066-dex-try-catch-rethrow/expected.txt index 371146313..13af56c09 100644 --- a/dx/tests/066-dex-try-catch-rethrow/expected.txt +++ b/dx/tests/066-dex-try-catch-rethrow/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()V: regs: 0005; ins: 0000; outs: 0002 0000: const-string v1, "x" diff --git a/dx/tests/067-dex-switch-and-try/expected.txt b/dx/tests/067-dex-switch-and-try/expected.txt index 43817109d..5e55bf41b 100644 --- a/dx/tests/067-dex-switch-and-try/expected.txt +++ b/dx/tests/067-dex-switch-and-try/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(I)I: regs: 0006; ins: 0002; outs: 0000 0000: move-object v0, v4 diff --git a/dx/tests/068-dex-infinite-loop/expected.txt b/dx/tests/068-dex-infinite-loop/expected.txt index 6f3064da3..1a84bd9be 100644 --- a/dx/tests/068-dex-infinite-loop/expected.txt +++ b/dx/tests/068-dex-infinite-loop/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()V: regs: 0000; ins: 0000; outs: 0000 0000: goto/32 0000 // +0000 diff --git a/dx/tests/069-dex-source-position/expected.txt b/dx/tests/069-dex-source-position/expected.txt index b769055dd..853ee6581 100644 --- a/dx/tests/069-dex-source-position/expected.txt +++ b/dx/tests/069-dex-source-position/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:(I)I: regs: 0006; ins: 0001; outs: 0001 0000: move v0, v5 diff --git a/dx/tests/070-dex-multianewarray/expected.txt b/dx/tests/070-dex-multianewarray/expected.txt index e9fac8ff9..9ddbabe9b 100644 --- a/dx/tests/070-dex-multianewarray/expected.txt +++ b/dx/tests/070-dex-multianewarray/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test01:()Ljava/lang/Object;: regs: 0003; ins: 0000; outs: 0002 0000: const/4 v1, #int 2 // #2 diff --git a/dx/tests/072-dex-switch-edge-cases/expected.txt b/dx/tests/072-dex-switch-edge-cases/expected.txt index 8be71dab2..665928422 100644 --- a/dx/tests/072-dex-switch-edge-cases/expected.txt +++ b/dx/tests/072-dex-switch-edge-cases/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(I)I: regs: 0005; ins: 0002; outs: 0000 0000: move-object v0, v3 diff --git a/dx/tests/073-dex-null-array-refs/expected.txt b/dx/tests/073-dex-null-array-refs/expected.txt index 3acb6b265..7f3ee21b0 100644 --- a/dx/tests/073-dex-null-array-refs/expected.txt +++ b/dx/tests/073-dex-null-array-refs/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()Ljava/lang/Object;: regs: 0002; ins: 0000; outs: 0000 0000: const/4 v0, #null // #0 diff --git a/dx/tests/074-dex-form35c-edge-case/expected.txt b/dx/tests/074-dex-form35c-edge-case/expected.txt index bc4f2da29..6afdcd773 100644 --- a/dx/tests/074-dex-form35c-edge-case/expected.txt +++ b/dx/tests/074-dex-form35c-edge-case/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()V: regs: 0012; ins: 0001; outs: 0003 0000: move-object/from16 v0, v17 diff --git a/dx/tests/075-dex-cat2-value-merge/expected.txt b/dx/tests/075-dex-cat2-value-merge/expected.txt index 9bb8e3324..e1b9b1b3b 100644 --- a/dx/tests/075-dex-cat2-value-merge/expected.txt +++ b/dx/tests/075-dex-cat2-value-merge/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:([J)V: regs: 0008; ins: 0001; outs: 0000 0000: move-object v0, v7 diff --git a/dx/tests/076-dex-synch-and-stack/expected.txt b/dx/tests/076-dex-synch-and-stack/expected.txt index c8c017887..eba839ed2 100644 --- a/dx/tests/076-dex-synch-and-stack/expected.txt +++ b/dx/tests/076-dex-synch-and-stack/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()V: regs: 0006; ins: 0001; outs: 0001 0000: move-object v0, v5 diff --git a/dx/tests/077-dex-code-alignment/expected.txt b/dx/tests/077-dex-code-alignment/expected.txt index 8f6db7de2..e69de29bb 100644 --- a/dx/tests/077-dex-code-alignment/expected.txt +++ b/dx/tests/077-dex-code-alignment/expected.txt @@ -1 +0,0 @@ -javac 1.7.0-internal_bootstrap diff --git a/dx/tests/078-dex-local-variable-table/expected.txt b/dx/tests/078-dex-local-variable-table/expected.txt index d55333b98..3e5cd69d0 100644 --- a/dx/tests/078-dex-local-variable-table/expected.txt +++ b/dx/tests/078-dex-local-variable-table/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test01:(Ljava/lang/Object;)V: regs: 0003; ins: 0001; outs: 0001 0000: move-object v0, v2 diff --git a/dx/tests/079-dex-local-variable-renumbering/expected.txt b/dx/tests/079-dex-local-variable-renumbering/expected.txt index f8f15c86c..94ba113e3 100644 --- a/dx/tests/079-dex-local-variable-renumbering/expected.txt +++ b/dx/tests/079-dex-local-variable-renumbering/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(I)I: regs: 0015; ins: 0001; outs: 0000 0000: move/from16 v1, v20 diff --git a/dx/tests/080-dex-exception-tables/expected.txt b/dx/tests/080-dex-exception-tables/expected.txt index 9337e7307..4cf43f186 100644 --- a/dx/tests/080-dex-exception-tables/expected.txt +++ b/dx/tests/080-dex-exception-tables/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()I: regs: 0002; ins: 0000; outs: 0000 0000: invoke-static {}, Blort.call1:()V diff --git a/dx/tests/081-dex-throws-list/expected.txt b/dx/tests/081-dex-throws-list/expected.txt index 502f724b8..1350edfab 100644 --- a/dx/tests/081-dex-throws-list/expected.txt +++ b/dx/tests/081-dex-throws-list/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:()I: system-annotation dalvik.annotation.Throws {value: {java.lang.RuntimeException}} Blort.test2:()I: diff --git a/dx/tests/082-dex-throws-list-sharing/expected.txt b/dx/tests/082-dex-throws-list-sharing/expected.txt index 016b41f66..0f3392494 100644 --- a/dx/tests/082-dex-throws-list-sharing/expected.txt +++ b/dx/tests/082-dex-throws-list-sharing/expected.txt @@ -1,3 +1,2 @@ -javac 1.7.0-internal_bootstrap java.lang.RuntimeException java.lang.Error, java.lang.UnsupportedOperationException diff --git a/dx/tests/083-ssa-phi-placement/expected.txt b/dx/tests/083-ssa-phi-placement/expected.txt index c02e4393e..9a0cb0b65 100644 --- a/dx/tests/083-ssa-phi-placement/expected.txt +++ b/dx/tests/083-ssa-phi-placement/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/084-dex-high-register-moves/expected.txt b/dx/tests/084-dex-high-register-moves/expected.txt index e24cde0e6..33466c4e2 100644 --- a/dx/tests/084-dex-high-register-moves/expected.txt +++ b/dx/tests/084-dex-high-register-moves/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()V: regs: 0018; ins: 0000; outs: 0000 0000: const/16 v22, #int 0 // #0000 diff --git a/dx/tests/086-ssa-edge-split/expected.txt b/dx/tests/086-ssa-edge-split/expected.txt index bc29b2076..e59af312c 100644 --- a/dx/tests/086-ssa-edge-split/expected.txt +++ b/dx/tests/086-ssa-edge-split/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/087-ssa-local-vars/expected.txt b/dx/tests/087-ssa-local-vars/expected.txt index 9d8f81f02..6c0a6f78b 100644 --- a/dx/tests/087-ssa-local-vars/expected.txt +++ b/dx/tests/087-ssa-local-vars/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/088-ssa-combine-blocks/expected.txt b/dx/tests/088-ssa-combine-blocks/expected.txt index fef77e3c4..7bf445dfe 100644 --- a/dx/tests/088-ssa-combine-blocks/expected.txt +++ b/dx/tests/088-ssa-combine-blocks/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/089-dex-define-object/expected.txt b/dx/tests/089-dex-define-object/expected.txt index 8ba83ea8e..a1db6a475 100644 --- a/dx/tests/089-dex-define-object/expected.txt +++ b/dx/tests/089-dex-define-object/expected.txt @@ -1,2 +1 @@ -javac 1.7.0-internal_bootstrap Good! diff --git a/dx/tests/090-dex-unify-arrays/expected.txt b/dx/tests/090-dex-unify-arrays/expected.txt index 818e985e5..7a4125f95 100644 --- a/dx/tests/090-dex-unify-arrays/expected.txt +++ b/dx/tests/090-dex-unify-arrays/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test1:(Z)Ljava/lang/Object;: regs: 0004; ins: 0001; outs: 0000 0000: move v0, v3 diff --git a/dx/tests/091-ssa-const-collector/expected.txt b/dx/tests/091-ssa-const-collector/expected.txt index ef82e8ddf..0edb6e8e8 100644 --- a/dx/tests/091-ssa-const-collector/expected.txt +++ b/dx/tests/091-ssa-const-collector/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/092-ssa-cfg-edge-cases/expected.txt b/dx/tests/092-ssa-cfg-edge-cases/expected.txt index 394550a37..41fefc48a 100644 --- a/dx/tests/092-ssa-cfg-edge-cases/expected.txt +++ b/dx/tests/092-ssa-cfg-edge-cases/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/093-ssa-invoke-range/expected.txt b/dx/tests/093-ssa-invoke-range/expected.txt index 2fad8d6b1..bb383c0de 100644 --- a/dx/tests/093-ssa-invoke-range/expected.txt +++ b/dx/tests/093-ssa-invoke-range/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap reading Blort.class... method <init> ()V first 000c diff --git a/dx/tests/095-dex-const-string-jumbo/expected.txt b/dx/tests/095-dex-const-string-jumbo/expected.txt index 1dbcc35b8..a4014d914 100644 --- a/dx/tests/095-dex-const-string-jumbo/expected.txt +++ b/dx/tests/095-dex-const-string-jumbo/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap Blort.test:()V: regs: 0003; ins: 0001; outs: 0001 0000: move-object v0, v2 diff --git a/dx/tests/096-dex-giant-catch/expected.txt b/dx/tests/096-dex-giant-catch/expected.txt index 5c1b6d465..e71992eef 100644 --- a/dx/tests/096-dex-giant-catch/expected.txt +++ b/dx/tests/096-dex-giant-catch/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap catches try 0024..00010017 catch java.lang.RuntimeException -> 00011260 diff --git a/dx/tests/097-dex-branch-offset-zero/expected.txt b/dx/tests/097-dex-branch-offset-zero/expected.txt index a57ea8838..2b021a566 100644 --- a/dx/tests/097-dex-branch-offset-zero/expected.txt +++ b/dx/tests/097-dex-branch-offset-zero/expected.txt @@ -1,2 +1 @@ -javac 1.7.0-internal_bootstrap No bad branches found. diff --git a/dx/tests/099-dex-core-library-error/expected.txt b/dx/tests/099-dex-core-library-error/expected.txt index 022871a82..d9c405bc2 100644 --- a/dx/tests/099-dex-core-library-error/expected.txt +++ b/dx/tests/099-dex-core-library-error/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap exit code: 1 exit code: 1 exit code: 0 diff --git a/dx/tests/108-string-annotation/expected.txt b/dx/tests/108-string-annotation/expected.txt index befa6908d..57da807fe 100644 --- a/dx/tests/108-string-annotation/expected.txt +++ b/dx/tests/108-string-annotation/expected.txt @@ -1,4 +1,3 @@ -javac 1.7.0-internal_bootstrap elements[0]: name diff --git a/dx/tests/run-test b/dx/tests/run-test index 84eadb962..a9221de1d 100755 --- a/dx/tests/run-test +++ b/dx/tests/run-test @@ -33,7 +33,10 @@ cd "${progdir}" progdir=`pwd` prog="${progdir}"/`basename "${prog}"` -export JAVAC="${progdir}/../../../prebuilt/common/openjdk/bin/javac -version" +export JAVAC="${progdir}/../../../prebuilt/common/openjdk/bin/javac" +if [ "!" -e "$JAVAC" ]; then + JAVAC="javac" +fi info="info.txt" run="run" diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Retention.java b/libcore/annotation/src/main/java/java/lang/annotation/Retention.java index 1c440ae92..198fccc15 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Retention.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Retention.java @@ -17,14 +17,10 @@ package java.lang.annotation; -// BEGIN android-note -// Un-linked RetentionPolicy#RUNTIME due to clearjavadoc problem (it doesn't -// yet deal with links to enums). -// END android-note - /** * Defines a meta-annotation for determining the scope of retention for an - * annotation. The default value is {@code RetentionPolicy.RUNTIME}. + * annotation. If the retention annotation is not set {@code + * RetentionPolicy.CLASS} is used as default retention. * * @since Android 1.0 */ @@ -32,5 +28,8 @@ package java.lang.annotation; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { - RetentionPolicy value() default RetentionPolicy.CLASS; + // BEGIN android-changed + // copied from newer version of harmony + RetentionPolicy value(); + // END android-changed } diff --git a/libcore/luni/src/main/java/java/util/HashMap.java b/libcore/luni/src/main/java/java/util/HashMap.java index 056c113d7..594e8a801 100644 --- a/libcore/luni/src/main/java/java/util/HashMap.java +++ b/libcore/luni/src/main/java/java/util/HashMap.java @@ -299,6 +299,7 @@ public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, putAllImpl(map); } + // BEGIN android-changed /** * Removes all mappings from this hash map, leaving it empty. * @@ -308,12 +309,17 @@ public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, */ @Override public void clear() { + internalClear(); + } + + void internalClear() { if (elementCount > 0) { elementCount = 0; Arrays.fill(elementData, null); modCount++; } } + // END android-changed /** * Returns a shallow copy of this map. @@ -326,9 +332,10 @@ public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, public Object clone() { try { // BEGIN android-changed + // copied from newer version of harmony HashMap<K, V> map = (HashMap<K, V>) super.clone(); - map.elementCount = 0; map.elementData = newElementArray(elementData.length); + map.internalClear(); Entry<K, V> entry; for (int i = 0; i < elementData.length; i++) { if ((entry = elementData[i]) != null){ @@ -448,20 +455,26 @@ public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, final Entry<K,V> findNonNullKeyEntry(Object key, int index, int keyHash) { Entry<K,V> m = elementData[index]; // BEGIN android-changed - while (m != null) { - if (m.origKeyHash == keyHash) { - if (key instanceof String) { - // The VM can optimize String.equals but not Object.equals - if (((String) key).equals(m.key)) { + // The VM can optimize String.equals but not Object.equals + if (key instanceof String) { + String keyString = (String) key; + while (m != null) { + if (m.origKeyHash == keyHash) { + if (keyString.equals(m.key)) { return m; } - } else { + } + m = m.next; + } + } else { + while (m != null) { + if (m.origKeyHash == keyHash) { if (key.equals(m.key)) { return m; } } + m = m.next; } - m = m.next; } return null; // END android-changed diff --git a/libcore/luni/src/main/java/java/util/LinkedHashMap.java b/libcore/luni/src/main/java/java/util/LinkedHashMap.java index 1a613458f..a32a28b12 100644 --- a/libcore/luni/src/main/java/java/util/LinkedHashMap.java +++ b/libcore/luni/src/main/java/java/util/LinkedHashMap.java @@ -19,9 +19,10 @@ package java.util; /** - * LinkedHashMap is a variant on HashMap. Its entries are kept in a + * LinkedHashMap is a variant of HashMap. Its entries are kept in a * doubly-linked list. The iteration order is, by default, the order in which - * keys were inserted. + * keys were inserted. Reinserting an already existing key doesn't change the + * order. A key is existing if a call to {@code containsKey} would return true. * <p> * If the three argument constructor is used, and {@code order} is specified as * {@code true}, the iteration will be in the order that entries were accessed. @@ -30,6 +31,21 @@ package java.util; * <p> * Null elements are allowed, and all the optional map operations are supported. * <p> + * <b>Note:</b> The implementation of {@code LinkedHashMap} is not synchronized. + * If one thread of several threads accessing an instance modifies the map + * structurally, access to the map needs to be synchronized. For + * insertion-ordered instances a structural modification is an operation that + * removes or adds an entry. Access-ordered instances also are structurally + * modified by put(), get() and putAll() since these methods change the order of + * the entries. Changes in the value of an entry are not structural changes. + * <p> + * The Iterator that can be created by calling the {@code iterator} method + * throws a {@code ConcurrentModificationException} if the map is structurally + * changed while an iterator is used to iterate over the elements. Only the + * {@code remove} method that is provided by the iterator allows for removal of + * elements during iteration. It is not possible to guarantee that this + * mechanism works in all cases of unsynchronized concurrent modification. It + * should only be used for debugging purposes. * * @since Android 1.0 */ @@ -115,7 +131,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> { /** * Constructs a new {@code LinkedHashMap} instance containing the mappings - * from the specified map. + * from the specified map. The order of the elements is preserved. * * @param m * the mappings to add. @@ -314,6 +330,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> { } // BEGIN android-changed + // copied from newer version of harmony /** * Maps the specified key to the specified value. * @@ -352,7 +369,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> { if (++elementCount > threshold) { rehash(); } - m = (LinkedHashMapEntry<K, V>) createHashedEntry(key, 0, 0); + m = (LinkedHashMapEntry<K, V>) createHashedEntry(null, 0, 0); } else { linkEntry(m); } @@ -595,6 +612,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> { return false; } + // BEGIN android-changed /** * Removes all elements from this map, leaving it empty. * @@ -604,11 +622,18 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> { */ @Override public void clear() { - super.clear(); + internalClear(); + } + + @Override + void internalClear() { + super.internalClear(); head = tail = null; } + // END android-changed // BEGIN android-removed + // copied from newer version of harmony // /** // * Answers a new HashMap with the same mappings and size as this HashMap. // * diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index 7afb54e52..de0129588 100755 --- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -1469,18 +1469,48 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, return 0; } - while (sent < count) { - result = send(handle, (jbyte *) message + sent, (int) count - sent, SOCKET_NOFLAGS); - if (result == -1) { - int err = convertError(errno); - log_socket_close(handle, err); - throwSocketException(env, err); + result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + + if (SOCKERR_WOULDBLOCK == err){ + jclass socketExClass,errorCodeExClass; + jmethodID errorCodeExConstructor, socketExConstructor,socketExCauseMethod; + jobject errorCodeEx, socketEx; + const char* errorMessage = netLookupErrorString(err); + jstring errorMessageString = env->NewStringUTF(errorMessage); + + errorCodeExClass = env->FindClass("org/apache/harmony/luni/util/ErrorCodeException"); + if (!errorCodeExClass){ + return 0; + } + errorCodeExConstructor = env->GetMethodID(errorCodeExClass,"<init>","(I)V"); + if (!errorCodeExConstructor){ + return 0; + } + errorCodeEx = env->NewObject(errorCodeExClass,errorCodeExConstructor,err); + + socketExClass = env->FindClass("java/net/SocketException"); + if (!socketExClass) { + return 0; + } + socketExConstructor = env->GetMethodID(socketExClass,"<init>","(Ljava/lang/String;)V"); + if (!socketExConstructor) { + return 0; + } + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); + env->Throw((jthrowable)socketEx); return 0; } - sent += result; + throwSocketException(env, err); + return 0; } - add_send_stats(handle, sent); - return sent; + + add_send_stats(handle, result); + return result; } static jint osNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java index 3c6a3b8df..6882aa139 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java @@ -36,6 +36,7 @@ */ package org.apache.harmony.xnet.provider.jsse; +import java.nio.ByteBuffer; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; @@ -54,25 +55,28 @@ public class SSLSessionContextImpl implements SSLSessionContext { private int cacheSize = 20; private long timeout = 0; - private final LinkedHashMap<byte[], SSLSession> sessions = - new LinkedHashMap<byte[],SSLSession>(cacheSize, 0.75f, true) { - public boolean removeEldestEntry(Map.Entry eldest) { + private final LinkedHashMap<ByteBuffer, SSLSession> sessions = + new LinkedHashMap<ByteBuffer, SSLSession>(cacheSize, 0.75f, true) { + @Override + public boolean removeEldestEntry( + Map.Entry<ByteBuffer, SSLSession> eldest) { return cacheSize > 0 && this.size() > cacheSize; } }; - private volatile LinkedHashMap<byte[], SSLSession> clone = new LinkedHashMap<byte[], SSLSession>(); + private volatile LinkedHashMap<ByteBuffer, SSLSession> clone = + new LinkedHashMap<ByteBuffer, SSLSession>(); /** * @see javax.net.ssl.SSLSessionContext#getIds() */ public Enumeration<byte[]> getIds() { return new Enumeration<byte[]>() { - Iterator<byte[]> iterator = clone.keySet().iterator(); + Iterator<ByteBuffer> iterator = clone.keySet().iterator(); public boolean hasMoreElements() { return iterator.hasNext(); } public byte[] nextElement() { - return iterator.next(); + return iterator.next().array(); } }; } @@ -82,7 +86,7 @@ public class SSLSessionContextImpl implements SSLSessionContext { */ public SSLSession getSession(byte[] sessionId) { synchronized (sessions) { - return (SSLSession) sessions.get(sessionId); + return sessions.get(ByteBuffer.wrap(sessionId)); } } @@ -90,14 +94,18 @@ public class SSLSessionContextImpl implements SSLSessionContext { * @see javax.net.ssl.SSLSessionContext#getSessionCacheSize() */ public int getSessionCacheSize() { - return cacheSize; + synchronized (sessions) { + return cacheSize; + } } /** * @see javax.net.ssl.SSLSessionContext#getSessionTimeout() */ public int getSessionTimeout() { - return (int) (timeout/1000); + synchronized (sessions) { + return (int) (timeout/1000); + } } /** @@ -109,18 +117,16 @@ public class SSLSessionContextImpl implements SSLSessionContext { } synchronized (sessions) { cacheSize = size; - Set<byte[]> set = sessions.keySet(); - if (cacheSize > 0 && cacheSize < set.size()) { - // Resize the cache to the maximum - Iterator<byte[]> iterator = set.iterator(); - for (int i = 0; iterator.hasNext(); i++) { + if (cacheSize > 0 && cacheSize < sessions.size()) { + int removals = sessions.size() - cacheSize; + Iterator<ByteBuffer> iterator = sessions.keySet().iterator(); + while (removals-- > 0) { iterator.next(); - if (i >= cacheSize) { - iterator.remove(); - } + iterator.remove(); } + clone = (LinkedHashMap<ByteBuffer, SSLSession>) + sessions.clone(); } - clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone(); } } @@ -131,18 +137,21 @@ public class SSLSessionContextImpl implements SSLSessionContext { if (seconds < 0) { throw new IllegalArgumentException("seconds < 0"); } - synchronized (sessions) { timeout = seconds*1000; // Check timeouts and remove expired sessions SSLSession ses; - for (Iterator<byte[]> iterator = sessions.keySet().iterator(); iterator.hasNext();) { - ses = (SSLSession)(sessions.get(iterator.next())); - if (!ses.isValid()) { + Iterator<Map.Entry<ByteBuffer, SSLSession>> iterator = + sessions.entrySet().iterator(); + while (iterator.hasNext()) { + SSLSession session = iterator.next().getValue(); + if (!session.isValid()) { + // safe to remove with this special method since it doesn't + // make the iterator throw a ConcurrentModificationException iterator.remove(); } } - clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone(); + clone = (LinkedHashMap<ByteBuffer, SSLSession>) sessions.clone(); } } @@ -152,8 +161,8 @@ public class SSLSessionContextImpl implements SSLSessionContext { */ void putSession(SSLSession ses) { synchronized (sessions) { - sessions.put(ses.getId(), ses); - clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone(); + sessions.put(ByteBuffer.wrap(ses.getId()), ses); + clone = (LinkedHashMap<ByteBuffer, SSLSession>) sessions.clone(); } } } diff --git a/libdex/DexSwapVerify.c b/libdex/DexSwapVerify.c index 86c0d402f..6ccd82001 100644 --- a/libdex/DexSwapVerify.c +++ b/libdex/DexSwapVerify.c @@ -2726,6 +2726,7 @@ int dexFixByteOrdering(u1* addr, int len) CheckState state; bool okay = true; + memset(&state, 0, sizeof(state)); LOGV("+++ swapping and verifying\n"); /* diff --git a/tests/024-illegal-access/expected.txt b/tests/024-illegal-access/expected.txt index 453812ddc..5f951f493 100644 --- a/tests/024-illegal-access/expected.txt +++ b/tests/024-illegal-access/expected.txt @@ -1,3 +1,2 @@ -java.lang.VerifyError: PublicAccess - at Main.main(Main.java:3) - at dalvik.system.NativeStart.main(Native Method) +Got expected failure 1 +Got expected failure 2 diff --git a/tests/024-illegal-access/info.txt b/tests/024-illegal-access/info.txt index 6c17ca726..16a284dac 100644 --- a/tests/024-illegal-access/info.txt +++ b/tests/024-illegal-access/info.txt @@ -1,2 +1,3 @@ Test that an attempt to access a private field results in a verification -error. +error. Also try to access a non-public class in a different package with +"instanceof". diff --git a/tests/024-illegal-access/src/CheckInstanceof.java b/tests/024-illegal-access/src/CheckInstanceof.java new file mode 100644 index 000000000..de48cd2d2 --- /dev/null +++ b/tests/024-illegal-access/src/CheckInstanceof.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009 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. + */ + +/** + * Make sure we're performing access checks on classes used in "instanceof". + */ +public class CheckInstanceof { + public static void main(Object obj) { + if (obj instanceof otherpkg.Package) + System.out.println("yes!"); + else + System.out.println("no!"); + } +} diff --git a/tests/024-illegal-access/src/Main.java b/tests/024-illegal-access/src/Main.java index 97fc55f5a..bde73e945 100644 --- a/tests/024-illegal-access/src/Main.java +++ b/tests/024-illegal-access/src/Main.java @@ -1,5 +1,41 @@ +/* + * Copyright (C) 2009 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. + */ + public class Main { static public void main(String[] args) { - PublicAccess.main(); + try { + PublicAccess.main(); + System.err.println("ERROR: call 1 not expected to succeed"); + } catch (VerifyError ve) { + // dalvik + System.out.println("Got expected failure 1"); + } catch (IllegalAccessError iae) { + // reference + System.out.println("Got expected failure 1"); + } + + try { + CheckInstanceof.main(new Object()); + System.err.println("ERROR: call 2 not expected to succeed"); + } catch (VerifyError ve) { + // dalvik + System.out.println("Got expected failure 2"); + } catch (IllegalAccessError iae) { + // reference + System.out.println("Got expected failure 2"); + } } } diff --git a/tests/024-illegal-access/src/otherpkg/Package.java b/tests/024-illegal-access/src/otherpkg/Package.java new file mode 100644 index 000000000..a82b32149 --- /dev/null +++ b/tests/024-illegal-access/src/otherpkg/Package.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2008 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. + */ + +package otherpkg; + +/* + * Package-scope class (public here). + */ +public class Package { +} + diff --git a/tests/024-illegal-access/src2/otherpkg/Package.java b/tests/024-illegal-access/src2/otherpkg/Package.java new file mode 100644 index 000000000..7f740d4c5 --- /dev/null +++ b/tests/024-illegal-access/src2/otherpkg/Package.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2008 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. + */ + +package otherpkg; + +/* + * Package-scope class. + */ +class Package { +} + diff --git a/tests/etc/local-run-test-jar b/tests/etc/local-run-test-jar index 66ca06233..0c802ba37 100755 --- a/tests/etc/local-run-test-jar +++ b/tests/etc/local-run-test-jar @@ -24,6 +24,7 @@ GDB="n" VERIFY="y" OPTIMIZE="y" VALGRIND="n" +DEV_MODE="n" QUIET="n" while true; do @@ -47,6 +48,9 @@ while true; do elif [ "x$1" = "x--valgrind" ]; then VALGRIND="y" shift + elif [ "x$1" = "x--dev" ]; then + DEV_MODE="y" + shift elif [ "x$1" = "x--no-verify" ]; then VERIFY="n" shift @@ -104,7 +108,11 @@ DATA_DIR=/tmp DEBUG_OPTS="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n" export ANDROID_PRINTF_LOG=brief -export ANDROID_LOG_TAGS='*:s' +if [ "$DEV_MODE" = "y" ]; then + export ANDROID_LOG_TAGS='*:d' +else + export ANDROID_LOG_TAGS='*:s' +fi export ANDROID_DATA="$DATA_DIR" export ANDROID_ROOT="${BASE}/system" export LD_LIBRARY_PATH="${BASE}/system/lib" diff --git a/tests/run-test b/tests/run-test index 82f9721d6..b50390561 100755 --- a/tests/run-test +++ b/tests/run-test @@ -83,6 +83,7 @@ while true; do run_args="${run_args} --valgrind" shift elif [ "x$1" = "x--dev" ]; then + run_args="${run_args} --dev" dev_mode="yes" shift elif [ "x$1" = "x--update" ]; then diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c index 0b8a94d98..2c5e74945 100644 --- a/vm/analysis/CodeVerify.c +++ b/vm/analysis/CodeVerify.c @@ -327,32 +327,6 @@ static bool isTypeWidthEqual1nr(RegType type1, RegType type2) } /* - * Given a 32-bit constant, return the most-restricted RegType that can hold - * the value. - */ -static RegType determineCat1Const(s4 value) -{ - if (value < -32768) - return kRegTypeInteger; - else if (value < -128) - return kRegTypeShort; - else if (value < 0) - return kRegTypeByte; - else if (value == 0) - return kRegTypeZero; - else if (value == 1) - return kRegTypeOne; - else if (value < 128) - return kRegTypePosByte; - else if (value < 32768) - return kRegTypePosShort; - else if (value < 65536) - return kRegTypeChar; - else - return kRegTypeInteger; -} - -/* * Convert a VM PrimitiveType enum value to the equivalent RegType value. */ static RegType primitiveTypeToRegType(PrimitiveType primType) @@ -923,7 +897,6 @@ bad_sig: static RegType getMethodReturnType(const Method* meth) { RegType type; - bool okay = true; const char* descriptor = dexProtoGetReturnType(&meth->prototype); switch (*descriptor) { @@ -957,6 +930,7 @@ static RegType getMethodReturnType(const Method* meth) case 'L': case '[': { + bool okay = true; ClassObject* clazz = lookupClassByDescriptor(meth, descriptor, &okay); assert(okay); @@ -2455,7 +2429,7 @@ static void checkFinalFieldAccess(const Method* meth, const Field* field, /* make sure we're in the same class */ if (meth->clazz != field->clazz) { - LOG_VFY_METH(meth, "VFY: can't modify final field %s.%\n", + LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s\n", field->clazz->descriptor, field->name); *pOkay = false; return; @@ -2716,6 +2690,54 @@ static bool initRegisterTable(const Method* meth, const InsnFlags* insnFlags, /* + * Verify that the arguments in a filled-new-array instruction are valid. + * + * "resClass" is the class refered to by pDecInsn->vB. + */ +static void verifyFilledNewArrayRegs(const Method* meth, + const RegType* insnRegs, const int insnRegCount, + const DecodedInstruction* pDecInsn, ClassObject* resClass, bool isRange, + bool* pOkay) +{ + u4 argCount = pDecInsn->vA; + RegType expectedType; + PrimitiveType elemType; + unsigned int ui; + + assert(dvmIsArrayClass(resClass)); + elemType = resClass->elementClass->primitiveType; + if (elemType == PRIM_NOT) { + LOG_VFY("VFY: filled-new-array not yet supported on reference types\n"); + *pOkay = false; + return; + } + + expectedType = primitiveTypeToRegType(elemType); + //LOGI("filled-new-array: %s -> %d\n", resClass->descriptor, expectedType); + + /* + * Verify each register. If "argCount" is bad, verifyRegisterType() + * will run off the end of the list and fail. It's legal, if silly, + * for argCount to be zero. + */ + for (ui = 0; ui < argCount; ui++) { + u4 getReg; + + if (isRange) + getReg = pDecInsn->vC + ui; + else + getReg = pDecInsn->arg[ui]; + + verifyRegisterType(insnRegs, insnRegCount, getReg, expectedType, pOkay); + if (!*pOkay) { + LOG_VFY("VFY: filled-new-array arg %u(%u) not valid\n", ui, getReg); + return; + } + } +} + + +/* * =========================================================================== * Entry point and driver loop * =========================================================================== @@ -3309,12 +3331,12 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_CONST: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - determineCat1Const((s4)decInsn.vB), &okay); + dvmDetermineCat1Const((s4)decInsn.vB), &okay); break; case OP_CONST_HIGH16: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - determineCat1Const((s4) decInsn.vB << 16), &okay); + dvmDetermineCat1Const((s4) decInsn.vB << 16), &okay); break; case OP_CONST_WIDE_16: case OP_CONST_WIDE_32: @@ -3332,8 +3354,18 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, break; case OP_CONST_CLASS: assert(gDvm.classJavaLangClass != NULL); - setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(gDvm.classJavaLangClass), &okay); + /* make sure we can resolve the class; access check is important */ + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + if (resClass == NULL) { + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); + dvmLogUnableToResolveClass(badClassDesc, meth); + LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s\n", + decInsn.vB, badClassDesc, meth->clazz->descriptor); + okay = false; + } else { + setRegisterType(workRegs, insnRegCount, decInsn.vA, + regTypeFromClass(gDvm.classJavaLangClass), &okay); + } break; case OP_MONITOR_ENTER: @@ -3378,17 +3410,29 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, } break; case OP_INSTANCE_OF: + /* make sure we're checking a reference type */ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &okay); if (!okay) break; if (!regTypeIsReference(tmpType)) { - LOG_VFY("VFY: vB not a reference\n"); + LOG_VFY("VFY: vB not a reference (%d)\n", tmpType); okay = false; break; } - /* result is boolean */ - setRegisterType(workRegs, insnRegCount, decInsn.vA, - kRegTypeBoolean, &okay); + + /* make sure we can resolve the class; access check is important */ + resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); + if (resClass == NULL) { + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); + dvmLogUnableToResolveClass(badClassDesc, meth); + LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s\n", + decInsn.vC, badClassDesc, meth->clazz->descriptor); + okay = false; + } else { + /* result is boolean */ + setRegisterType(workRegs, insnRegCount, decInsn.vA, + kRegTypeBoolean, &okay); + } break; case OP_ARRAY_LENGTH: @@ -3442,7 +3486,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_NEW_ARRAY: resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); if (resClass == NULL) { - const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); + const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s\n", decInsn.vC, badClassDesc, meth->clazz->descriptor); @@ -3451,6 +3495,9 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, LOG_VFY("VFY: new-array on non-array class\n"); okay = false; } else { + /* make sure "size" register is valid type */ + verifyRegisterType(workRegs, insnRegCount, decInsn.vB, + kRegTypeInteger, &okay); /* set register type to array class */ setRegisterType(workRegs, insnRegCount, decInsn.vA, regTypeFromClass(resClass), &okay); @@ -3458,7 +3505,6 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, break; case OP_FILLED_NEW_ARRAY: case OP_FILLED_NEW_ARRAY_RANGE: - /* (decInsn.vA == 0) is silly, but not illegal */ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); @@ -3470,13 +3516,11 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, LOG_VFY("VFY: filled-new-array on non-array class\n"); okay = false; } else { - /* - * TODO: verify decInsn.vA range - * TODO: if resClass is array of references, verify the registers - * in the argument list against the array type. - * TODO: if resClass is array of primitives, verify that the - * contents of the registers are appropriate. - */ + bool isRange = (decInsn.opCode == OP_FILLED_NEW_ARRAY_RANGE); + + /* check the arguments to the instruction */ + verifyFilledNewArrayRegs(meth, workRegs, insnRegCount, &decInsn, + resClass, isRange, &okay); /* filled-array result goes into "result" register */ setResultRegisterType(workRegs, insnRegCount, regTypeFromClass(resClass), &okay); @@ -3802,6 +3846,7 @@ aget_1nr_common: if (!okay) break; + /* get the class of the array we're pulling an object from */ resClass = getClassFromRegister(workRegs, insnRegCount, decInsn.vB, &okay); if (!okay) @@ -3811,7 +3856,7 @@ aget_1nr_common: assert(resClass != NULL); if (!dvmIsArrayClass(resClass)) { - LOG_VFY("VFY: aget-object on non-ref array class\n"); + LOG_VFY("VFY: aget-object on non-array class\n"); okay = false; break; } @@ -3826,9 +3871,14 @@ aget_1nr_common: assert(resClass->arrayDim > 1); elementClass = dvmFindArrayClass(&resClass->descriptor[1], resClass->classLoader); - } else { + } else if (resClass->descriptor[1] == 'L') { assert(resClass->arrayDim == 1); elementClass = resClass->elementClass; + } else { + LOG_VFY("VFY: aget-object on non-ref array class (%s)\n", + resClass->descriptor); + okay = false; + break; } dstType = regTypeFromClass(elementClass); @@ -4655,7 +4705,7 @@ sput_1nr_common: case OP_INVOKE_INTERFACE: case OP_INVOKE_INTERFACE_RANGE: { - RegType thisType, returnType; + RegType /*thisType,*/ returnType; Method* absMethod; bool isRange; @@ -4666,6 +4716,7 @@ sput_1nr_common: if (!okay) break; +#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */ /* * Get the type of the "this" arg, which should always be an * interface class. Because we don't do a full merge on @@ -4676,7 +4727,6 @@ sput_1nr_common: if (!okay) break; -#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */ if (thisType == kRegTypeZero) { /* null pointer always passes (and always fails at runtime) */ } else { @@ -4934,10 +4984,40 @@ sput_1nr_common: break; + /* + * Verifying "quickened" instructions is tricky, because we have + * discarded the original field/method information. The byte offsets + * and vtable indices only have meaning in the context of an object + * instance. + * + * If a piece of code declares a local reference variable, assigns + * null to it, and then issues a virtual method call on it, we + * cannot evaluate the method call during verification. This situation + * isn't hard to handle, since we know the call will always result in an + * NPE, and the arguments and return value don't matter. Any code that + * depends on the result of the method call is inaccessible, so the + * fact that we can't fully verify anything that comes after the bad + * call is not a problem. + * + * We must also consider the case of multiple code paths, only some of + * which involve a null reference. We can completely verify the method + * if we sidestep the results of executing with a null reference. + * For example, if on the first pass through the code we try to do a + * virtual method invocation through a null ref, we have to skip the + * method checks and have the method return a "wildcard" type (which + * merges with anything to become that other thing). The move-result + * will tell us if it's a reference, single-word numeric, or double-word + * value. We continue to perform the verification, and at the end of + * the function any invocations that were never fully exercised are + * marked as null-only. + * + * We would do something similar for the field accesses. The field's + * type, once known, can be used to recover the width of short integers. + * If the object reference was null, the field-get returns the "wildcard" + * type, which is acceptable for any operation. + */ case OP_EXECUTE_INLINE: case OP_INVOKE_DIRECT_EMPTY: - okay = false; // TODO - implement optimized opcodes - break; case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: @@ -4948,7 +5028,7 @@ sput_1nr_common: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: - okay = false; // TODO - implement optimized opcodes + okay = false; break; /* these should never appear */ diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c index 90e4c6feb..09199dbab 100644 --- a/vm/analysis/DexOptimize.c +++ b/vm/analysis/DexOptimize.c @@ -1657,7 +1657,7 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) bool allowed = dvmCheckClassAccess(referrer, resClass); untweakLoader(referrer, resClass); if (!allowed) { - LOGI("DexOpt: resolve class illegal access: %s -> %s\n", + LOGW("DexOpt: resolve class illegal access: %s -> %s\n", referrer->descriptor, resClass->descriptor); return NULL; } diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c index f78133bb3..5a3e8bdad 100644 --- a/vm/analysis/DexVerify.c +++ b/vm/analysis/DexVerify.c @@ -331,13 +331,20 @@ static void decodeInstruction(const Method* meth, int insnIdx, */ static bool checkNewInstance(const Method* meth, int insnIdx) { - DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + DvmDex* pDvmDex = meth->clazz->pDvmDex; DecodedInstruction decInsn; const char* classDescriptor; + u4 idx; decodeInstruction(meth, insnIdx, &decInsn); - classDescriptor = dexStringByTypeIdx(pDexFile, decInsn.vB); // 2nd item + idx = decInsn.vB; // 2nd item + if (idx >= pDvmDex->pHeader->typeIdsSize) { + LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", + idx, pDvmDex->pHeader->typeIdsSize); + return false; + } + classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); if (classDescriptor[0] != 'L') { LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n", classDescriptor); @@ -354,12 +361,20 @@ static bool checkNewInstance(const Method* meth, int insnIdx) */ static bool checkNewArray(const Method* meth, int insnIdx) { - DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + DvmDex* pDvmDex = meth->clazz->pDvmDex; DecodedInstruction decInsn; const char* classDescriptor; + u4 idx; decodeInstruction(meth, insnIdx, &decInsn); - classDescriptor = dexStringByTypeIdx(pDexFile, decInsn.vC); // 3rd item + idx = decInsn.vC; // 3rd item + if (idx >= pDvmDex->pHeader->typeIdsSize) { + LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", + idx, pDvmDex->pHeader->typeIdsSize); + return false; + } + + classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); int bracketCount = 0; const char* cp = classDescriptor; @@ -589,7 +604,7 @@ static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, break; case OP_FILLED_NEW_ARRAY: - if (!checkTypeIndex(meth, i, false)) + if (!checkTypeIndex(meth, i, true)) return false; break; case OP_FILLED_NEW_ARRAY_RANGE: diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c index 8e0054ebf..eb243af2a 100644 --- a/vm/analysis/RegisterMap.c +++ b/vm/analysis/RegisterMap.c @@ -14,11 +14,13 @@ * limitations under the License. */ +// ** UNDER CONSTRUCTION ** + /* * This code generate "register maps" for Dalvik bytecode. In a stack-based - * VM we would call these "stack maps". They are used to increase the - * precision in the garbage collector when finding references in the - * interpreter call stack. + * VM we might call these "stack maps". They are used to increase the + * precision in the garbage collector when scanning references in the + * interpreter thread stacks. */ #include "Dalvik.h" #include "analysis/CodeVerify.h" @@ -36,6 +38,11 @@ results in the optimized DEX file, which increases their size by about 25% unless we use compression, and for performance reasons we don't want to just re-run the verifier. +On the plus side, we know that verification has completed successfully -- +or at least are allowed to assume that it would -- so we skip a lot of +the checks (like verifying that the register indices in instructions +are reasonable). + Both type-precise and live-precise information can be generated knowing only whether or not a register holds a reference. We don't need to know what kind of reference or whether the object has been initialized. @@ -64,7 +71,7 @@ typedef u1 SRegType; * can be category 1 or 2, so we need two slots. */ #define kExtraRegs 2 -#define RESULT_REGISTER(_insnRegCount) (_insnRegCount) +#define RESULT_REGISTER(_insnRegCountPlus) (_insnRegCountPlus - kExtraRegs) /* * Working state. @@ -84,7 +91,7 @@ typedef struct WorkState { * Number of registers we track for each instruction. This is equal * to the method's declared "registersSize" plus kExtraRegs. */ - int insnRegCount; + int insnRegCountPlus; /* * Instruction widths and flags, one entry per code unit. @@ -197,7 +204,7 @@ RegisterMap* dvmGenerateRegisterMap(const Method* meth) pState->method = meth; pState->insnsSize = dvmGetMethodInsnsSize(meth); - pState->insnRegCount = meth->registersSize + kExtraRegs; + pState->insnRegCountPlus = meth->registersSize + kExtraRegs; pState->insnFlags = calloc(sizeof(InsnFlags), pState->insnsSize); pState->addrRegs = calloc(sizeof(SRegType*), pState->insnsSize); @@ -228,7 +235,7 @@ RegisterMap* dvmGenerateRegisterMap(const Method* meth) for (offset = 0; offset < pState->insnsSize; offset++) { if (dvmInsnIsGcPoint(pState->insnFlags, offset)) { pState->addrRegs[offset] = regPtr; - regPtr += pState->insnRegCount; + regPtr += pState->insnRegCountPlus; } } assert(regPtr - pState->regAlloc == pState->insnsSize * gcPointCount); @@ -313,7 +320,7 @@ static bool setTypesFromSignature(WorkState* pState) while (*ccp != 0) { switch (*ccp) { case 'L': - case '[': + //case '[': *pCurReg++ = kRegTypeReference; break; case 'Z': @@ -386,7 +393,7 @@ static inline int compareRegisters(const SRegType* src1, const SRegType* src2, static bool analyzeMethod(WorkState* pState) { const Method* meth = pState->method; - SRegType workRegs[pState->insnRegCount]; + SRegType workRegs[pState->insnRegCountPlus]; InsnFlags* insnFlags = pState->insnFlags; int insnsSize = pState->insnsSize; int insnIdx, startGuess; @@ -454,7 +461,7 @@ static bool analyzeMethod(WorkState* pState) if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) { SRegType* insnRegs = getRegisterLine(pState, insnIdx); assert(insnRegs != NULL); - copyRegisters(workRegs, insnRegs, pState->insnRegCount); + copyRegisters(workRegs, insnRegs, pState->insnRegCountPlus); } else { #ifndef NDEBUG @@ -464,7 +471,8 @@ static bool analyzeMethod(WorkState* pState) */ SRegType* insnRegs = getRegisterLine(pState, insnIdx); if (insnRegs != NULL && - compareRegisters(workRegs, insnRegs, pState->insnRegCount) != 0) + compareRegisters(workRegs, insnRegs, + pState->insnRegCountPlus) != 0) { char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); LOG_VFY("HUH? workRegs diverged in %s.%s %s\n", @@ -494,6 +502,129 @@ bail: } /* + * Get a pointer to the method being invoked. + * + * Returns NULL on failure. + */ +static Method* getInvokedMethod(const Method* meth, + const DecodedInstruction* pDecInsn, MethodType methodType) +{ + Method* resMethod; + char* sigOriginal = NULL; + + /* + * Resolve the method. This could be an abstract or concrete method + * depending on what sort of call we're making. + */ + if (methodType == METHOD_INTERFACE) { + resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB); + } else { + resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType); + } + if (resMethod == NULL) { + /* failed; print a meaningful failure message */ + DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; + const DexMethodId* pMethodId; + const char* methodName; + char* methodDesc; + const char* classDescriptor; + + pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB); + methodName = dexStringById(pDexFile, pMethodId->nameIdx); + methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId); + classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); + + LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n", + dvmMethodTypeStr(methodType), pDecInsn->vB, + classDescriptor, methodName, methodDesc); + free(methodDesc); + return NULL; + } + + return resMethod; +} + +/* + * Return the register type for the method. Since we don't care about + * the actual type, we can just look at the "shorty" signature. + * + * Returns kRegTypeUnknown for "void". + */ +static SRegType getMethodReturnType(const Method* meth) +{ + SRegType type; + + switch (meth->shorty[0]) { + case 'I': + type = kRegTypeInteger; + break; + case 'C': + type = kRegTypeChar; + break; + case 'S': + type = kRegTypeShort; + break; + case 'B': + type = kRegTypeByte; + break; + case 'Z': + type = kRegTypeBoolean; + break; + case 'V': + type = kRegTypeUnknown; + break; + case 'F': + type = kRegTypeFloat; + break; + case 'D': + type = kRegTypeDoubleLo; + break; + case 'J': + type = kRegTypeLongLo; + break; + case 'L': + //case '[': + type = kRegTypeReference; + break; + default: + /* we verified signature return type earlier, so this is impossible */ + assert(false); + type = kRegTypeConflict; + break; + } + + return type; +} + +/* + * Copy a category 1 register. + */ +static inline void copyRegister1(SRegType* insnRegs, u4 vdst, u4 vsrc) +{ + insnRegs[vdst] = insnRegs[vsrc]; +} + +/* + * Copy a category 2 register. Note the source and destination may overlap. + */ +static inline void copyRegister2(SRegType* insnRegs, u4 vdst, u4 vsrc) +{ + //memmove(&insnRegs[vdst], &insnRegs[vsrc], sizeof(SRegType) * 2); + SRegType r1 = insnRegs[vsrc]; + SRegType r2 = insnRegs[vsrc+1]; + insnRegs[vdst] = r1; + insnRegs[vdst+1] = r2; +} + +/* + * Set the type of a category 1 register. + */ +static inline void setRegisterType(SRegType* insnRegs, u4 vdst, SRegType type) +{ + insnRegs[vdst] = type; +} + +/* * Decode the specified instruction and update the register info. */ static bool handleInstruction(WorkState* pState, SRegType* workRegs, @@ -524,14 +655,15 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, * The behavior can be determined from the InstrFlags. */ DecodedInstruction decInsn; - SRegType entryRegs[pState->insnRegCount]; + SRegType entryRegs[pState->insnRegCountPlus]; + const int insnRegCountPlus = pState->insnRegCountPlus; bool justSetResult = false; int branchTarget = 0; + SRegType tmpType; dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn); const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode); - /* * Make a copy of the previous register state. If the instruction * throws an exception, we merge *this* into the destination rather @@ -541,14 +673,537 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, */ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx)) { - copyRegisters(entryRegs, workRegs, pState->insnRegCount); + copyRegisters(entryRegs, workRegs, insnRegCountPlus); } switch (decInsn.opCode) { case OP_NOP: break; - default: break; // TODO: fill this in + case OP_MOVE: + case OP_MOVE_FROM16: + case OP_MOVE_16: + case OP_MOVE_OBJECT: + case OP_MOVE_OBJECT_FROM16: + case OP_MOVE_OBJECT_16: + copyRegister1(workRegs, decInsn.vA, decInsn.vB); + break; + case OP_MOVE_WIDE: + case OP_MOVE_WIDE_FROM16: + case OP_MOVE_WIDE_16: + copyRegister2(workRegs, decInsn.vA, decInsn.vB); + break; + + /* + * The move-result instructions copy data out of a "pseudo-register" + * with the results from the last method invocation. In practice we + * might want to hold the result in an actual CPU register, so the + * Dalvik spec requires that these only appear immediately after an + * invoke or filled-new-array. + * + * These calls invalidate the "result" register. (This is now + * redundant with the reset done below, but it can make the debug info + * easier to read in some cases.) + */ + case OP_MOVE_RESULT: + case OP_MOVE_RESULT_OBJECT: + copyRegister1(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus)); + break; + case OP_MOVE_RESULT_WIDE: + copyRegister2(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus)); + break; + + case OP_MOVE_EXCEPTION: + /* + * This statement can only appear as the first instruction in an + * exception handler (though not all exception handlers need to + * have one of these). We verify that as part of extracting the + * exception type from the catch block list. + */ + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + break; + + case OP_CONST_4: + case OP_CONST_16: + case OP_CONST: + /* could be boolean, int, float, or a null reference */ + setRegisterType(workRegs, decInsn.vA, + dvmDetermineCat1Const((s4)decInsn.vB)); + break; + case OP_CONST_HIGH16: + /* could be boolean, int, float, or a null reference */ + setRegisterType(workRegs, decInsn.vA, + dvmDetermineCat1Const((s4) decInsn.vB << 16)); + break; + case OP_CONST_WIDE_16: + case OP_CONST_WIDE_32: + case OP_CONST_WIDE: + case OP_CONST_WIDE_HIGH16: + /* could be long or double; default to long and allow conversion */ + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_CONST_STRING: + case OP_CONST_STRING_JUMBO: + case OP_CONST_CLASS: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_MONITOR_ENTER: + case OP_MONITOR_EXIT: + break; + + case OP_CHECK_CAST: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + case OP_INSTANCE_OF: + /* result is boolean */ + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + + case OP_ARRAY_LENGTH: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + + case OP_NEW_INSTANCE: + case OP_NEW_ARRAY: + /* add the new uninitialized reference to the register ste */ + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + case OP_FILLED_NEW_ARRAY: + case OP_FILLED_NEW_ARRAY_RANGE: + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + kRegTypeReference); + justSetResult = true; + break; + + case OP_CMPL_FLOAT: + case OP_CMPG_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + case OP_CMPL_DOUBLE: + case OP_CMPG_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + case OP_CMP_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean); + break; + + case OP_THROW: + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + case OP_PACKED_SWITCH: + case OP_SPARSE_SWITCH: + break; + + case OP_FILL_ARRAY_DATA: + break; + + case OP_IF_EQ: + case OP_IF_NE: + case OP_IF_LT: + case OP_IF_GE: + case OP_IF_GT: + case OP_IF_LE: + case OP_IF_EQZ: + case OP_IF_NEZ: + case OP_IF_LTZ: + case OP_IF_GEZ: + case OP_IF_GTZ: + case OP_IF_LEZ: + break; + + case OP_AGET: + tmpType = kRegTypeInteger; + goto aget_1nr_common; + case OP_AGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto aget_1nr_common; + case OP_AGET_BYTE: + tmpType = kRegTypeByte; + goto aget_1nr_common; + case OP_AGET_CHAR: + tmpType = kRegTypeChar; + goto aget_1nr_common; + case OP_AGET_SHORT: + tmpType = kRegTypeShort; + goto aget_1nr_common; +aget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_AGET_WIDE: + /* + * We know this is either long or double, and we don't really + * discriminate between those during verification, so we + * call it a long. + */ + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_AGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_APUT: + case OP_APUT_BOOLEAN: + case OP_APUT_BYTE: + case OP_APUT_CHAR: + case OP_APUT_SHORT: + case OP_APUT_WIDE: + case OP_APUT_OBJECT: + break; + + case OP_IGET: + tmpType = kRegTypeInteger; + goto iget_1nr_common; + case OP_IGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto iget_1nr_common; + case OP_IGET_BYTE: + tmpType = kRegTypeByte; + goto iget_1nr_common; + case OP_IGET_CHAR: + tmpType = kRegTypeChar; + goto iget_1nr_common; + case OP_IGET_SHORT: + tmpType = kRegTypeShort; + goto iget_1nr_common; +iget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_IGET_WIDE: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_IGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_IPUT: + case OP_IPUT_BOOLEAN: + case OP_IPUT_BYTE: + case OP_IPUT_CHAR: + case OP_IPUT_SHORT: + case OP_IPUT_WIDE: + case OP_IPUT_OBJECT: + break; + + case OP_SGET: + tmpType = kRegTypeInteger; + goto sget_1nr_common; + case OP_SGET_BOOLEAN: + tmpType = kRegTypeBoolean; + goto sget_1nr_common; + case OP_SGET_BYTE: + tmpType = kRegTypeByte; + goto sget_1nr_common; + case OP_SGET_CHAR: + tmpType = kRegTypeChar; + goto sget_1nr_common; + case OP_SGET_SHORT: + tmpType = kRegTypeShort; + goto sget_1nr_common; +sget_1nr_common: + setRegisterType(workRegs, decInsn.vA, tmpType); + break; + + case OP_SGET_WIDE: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + + case OP_SGET_OBJECT: + setRegisterType(workRegs, decInsn.vA, kRegTypeReference); + break; + + case OP_SPUT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_BYTE: + case OP_SPUT_CHAR: + case OP_SPUT_SHORT: + case OP_SPUT_WIDE: + case OP_SPUT_OBJECT: + break; + + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_VIRTUAL); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_DIRECT); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: + { + Method* calledMethod; + + calledMethod = getInvokedMethod(meth, &decInsn, METHOD_STATIC); + if (calledMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(calledMethod)); + justSetResult = true; + } + break; + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + { + Method* absMethod; + + absMethod = getInvokedMethod(meth, &decInsn, METHOD_INTERFACE); + if (absMethod == NULL) + goto bail; + setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus), + getMethodReturnType(absMethod)); + justSetResult = true; + } + break; + + case OP_NEG_INT: + case OP_NOT_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_NEG_LONG: + case OP_NOT_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_NEG_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_NEG_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_INT_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_INT_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_INT_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_LONG_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_LONG_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_LONG_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_FLOAT_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_FLOAT_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_FLOAT_TO_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_DOUBLE_TO_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_DOUBLE_TO_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_DOUBLE_TO_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_INT_TO_BYTE: + setRegisterType(workRegs, decInsn.vA, kRegTypeByte); + break; + case OP_INT_TO_CHAR: + setRegisterType(workRegs, decInsn.vA, kRegTypeChar); + break; + case OP_INT_TO_SHORT: + setRegisterType(workRegs, decInsn.vA, kRegTypeShort); + break; + + case OP_ADD_INT: + case OP_SUB_INT: + case OP_MUL_INT: + case OP_REM_INT: + case OP_DIV_INT: + case OP_SHL_INT: + case OP_SHR_INT: + case OP_USHR_INT: + case OP_AND_INT: + case OP_OR_INT: + case OP_XOR_INT: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_ADD_LONG: + case OP_SUB_LONG: + case OP_MUL_LONG: + case OP_DIV_LONG: + case OP_REM_LONG: + case OP_AND_LONG: + case OP_OR_LONG: + case OP_XOR_LONG: + case OP_SHL_LONG: + case OP_SHR_LONG: + case OP_USHR_LONG: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_ADD_FLOAT: + case OP_SUB_FLOAT: + case OP_MUL_FLOAT: + case OP_DIV_FLOAT: + case OP_REM_FLOAT: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_ADD_DOUBLE: + case OP_SUB_DOUBLE: + case OP_MUL_DOUBLE: + case OP_DIV_DOUBLE: + case OP_REM_DOUBLE: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_ADD_INT_2ADDR: + case OP_SUB_INT_2ADDR: + case OP_MUL_INT_2ADDR: + case OP_REM_INT_2ADDR: + case OP_SHL_INT_2ADDR: + case OP_SHR_INT_2ADDR: + case OP_USHR_INT_2ADDR: + case OP_AND_INT_2ADDR: + case OP_OR_INT_2ADDR: + case OP_XOR_INT_2ADDR: + case OP_DIV_INT_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + case OP_ADD_LONG_2ADDR: + case OP_SUB_LONG_2ADDR: + case OP_MUL_LONG_2ADDR: + case OP_DIV_LONG_2ADDR: + case OP_REM_LONG_2ADDR: + case OP_AND_LONG_2ADDR: + case OP_OR_LONG_2ADDR: + case OP_XOR_LONG_2ADDR: + case OP_SHL_LONG_2ADDR: + case OP_SHR_LONG_2ADDR: + case OP_USHR_LONG_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo); + break; + case OP_ADD_FLOAT_2ADDR: + case OP_SUB_FLOAT_2ADDR: + case OP_MUL_FLOAT_2ADDR: + case OP_DIV_FLOAT_2ADDR: + case OP_REM_FLOAT_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeFloat); + break; + case OP_ADD_DOUBLE_2ADDR: + case OP_SUB_DOUBLE_2ADDR: + case OP_MUL_DOUBLE_2ADDR: + case OP_DIV_DOUBLE_2ADDR: + case OP_REM_DOUBLE_2ADDR: + setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo); + break; + case OP_ADD_INT_LIT16: + case OP_RSUB_INT: + case OP_MUL_INT_LIT16: + case OP_DIV_INT_LIT16: + case OP_REM_INT_LIT16: + case OP_AND_INT_LIT16: + case OP_OR_INT_LIT16: + case OP_XOR_INT_LIT16: + case OP_ADD_INT_LIT8: + case OP_RSUB_INT_LIT8: + case OP_MUL_INT_LIT8: + case OP_DIV_INT_LIT8: + case OP_REM_INT_LIT8: + case OP_SHL_INT_LIT8: + case OP_SHR_INT_LIT8: + case OP_USHR_INT_LIT8: + case OP_AND_INT_LIT8: + case OP_OR_INT_LIT8: + case OP_XOR_INT_LIT8: + setRegisterType(workRegs, decInsn.vA, kRegTypeInteger); + break; + + + /* + * See comments in analysis/CodeVerify.c re: why some of these are + * annoying to deal with. In here, "annoying" turns into "impossible", + * since we make no effort to keep reference type info. + * + * Handling most of these would require retaining the field/method + * reference info that we discarded when the instructions were + * quickened. + */ + case OP_EXECUTE_INLINE: + case OP_INVOKE_DIRECT_EMPTY: + case OP_IGET_QUICK: + case OP_IGET_WIDE_QUICK: + case OP_IGET_OBJECT_QUICK: + case OP_IPUT_QUICK: + case OP_IPUT_WIDE_QUICK: + case OP_IPUT_OBJECT_QUICK: + case OP_INVOKE_VIRTUAL_QUICK: + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: + dvmAbort(); // can't work + break; + + + /* these should never appear */ + case OP_UNUSED_3E: + case OP_UNUSED_3F: + case OP_UNUSED_40: + case OP_UNUSED_41: + case OP_UNUSED_42: + case OP_UNUSED_43: + case OP_UNUSED_73: + case OP_UNUSED_79: + case OP_UNUSED_7A: + case OP_UNUSED_E3: + case OP_UNUSED_E4: + case OP_UNUSED_E5: + case OP_UNUSED_E6: + case OP_UNUSED_E7: + case OP_UNUSED_E8: + case OP_UNUSED_E9: + case OP_UNUSED_EA: + case OP_UNUSED_EB: + case OP_UNUSED_EC: + case OP_UNUSED_ED: + case OP_UNUSED_EF: + case OP_UNUSED_F1: + case OP_UNUSED_FC: + case OP_UNUSED_FD: + case OP_UNUSED_FE: + case OP_UNUSED_FF: + dvmAbort(); + break; /* * DO NOT add a "default" clause here. Without it the compiler will @@ -563,7 +1218,7 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, * the verifier. */ if (!justSetResult) { - int reg = RESULT_REGISTER(pState->insnRegCount); + int reg = RESULT_REGISTER(pState->insnRegCountPlus); workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown; } @@ -585,7 +1240,7 @@ static bool handleInstruction(WorkState* pState, SRegType* workRegs, /* if not yet visited, or regs were updated, set "changed" */ if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) || compareRegisters(workRegs, entryRegs, - pState->insnRegCount) != 0) + pState->insnRegCountPlus) != 0) { dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true); } @@ -747,7 +1402,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, { const Method* meth = pState->method; InsnFlags* insnFlags = pState->insnFlags; - const int insnRegCount = pState->insnRegCount; + const int insnRegCountPlus = pState->insnRegCountPlus; SRegType* targetRegs = getRegisterLine(pState, nextInsn); if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) { @@ -759,7 +1414,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, * just an optimization.) */ LOGVV("COPY into 0x%04x\n", nextInsn); - copyRegisters(targetRegs, workRegs, insnRegCount); + copyRegisters(targetRegs, workRegs, insnRegCountPlus); dvmInsnSetChanged(insnFlags, nextInsn, true); } else { /* merge registers, set Changed only if different */ @@ -767,7 +1422,7 @@ static void updateRegisters(WorkState* pState, int nextInsn, bool changed = false; int i; - for (i = 0; i < insnRegCount; i++) { + for (i = 0; i < insnRegCountPlus; i++) { targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed); } diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h index d431aa7ce..07071d2d4 100644 --- a/vm/analysis/RegisterMap.h +++ b/vm/analysis/RegisterMap.h @@ -15,17 +15,21 @@ */ /* - * Register map declaration. + * Declaration of register map data structure and related functions. */ #ifndef _DALVIK_REGISTERMAP #define _DALVIK_REGISTERMAP +/* + * This is a single variable-size structure. It may be allocated on the + * heap or mapped out of a DEX file. + */ typedef struct RegisterMap { /* header */ char addrWidth; /* bytes per address, 1 or 2 */ char regWidth; /* bytes per register line, 1+ */ - char pad0; - char pad1; + + /* char pad0, pad1; */ /* entries start here; 32-bit align guaranteed */ u4 entries[1]; diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c index 77134a733..8dcc6f8dc 100644 --- a/vm/analysis/VerifySubs.c +++ b/vm/analysis/VerifySubs.c @@ -195,6 +195,7 @@ bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags, const int insnCount = dvmGetMethodInsnsSize(meth); const u2* insns = meth->insns + curOffset; const u2* switchInsns; + u2 expectedSignature; int switchCount, tableSize; int offsetToSwitch, offsetToKeys, offsetToTargets, targ; int offset, absOffset; @@ -229,13 +230,22 @@ bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags, /* 0=sig, 1=count, 2/3=firstKey */ offsetToTargets = 4; offsetToKeys = -1; + expectedSignature = kPackedSwitchSignature; } else { /* 0=sig, 1=count, 2..count*2 = keys */ offsetToKeys = 2; offsetToTargets = 2 + 2*switchCount; + expectedSignature = kSparseSwitchSignature; } tableSize = offsetToTargets + switchCount*2; + if (switchInsns[0] != expectedSignature) { + LOG_VFY_METH(meth, + "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n", + switchInsns[0], expectedSignature); + return false; + } + /* make sure the end of the switch is in range */ if (curOffset + offsetToSwitch + tableSize > insnCount) { LOG_VFY_METH(meth, @@ -368,6 +378,9 @@ void dvmLogVerifyFailure(const Method* meth, const char* format, ...) /* * Show a relatively human-readable message describing the failure to * resolve a class. + * + * TODO: this is somewhat misleading when resolution fails because of + * illegal access rather than nonexistent class. */ void dvmLogUnableToResolveClass(const char* missingClassDescr, const Method* meth) @@ -434,4 +447,29 @@ bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags, return true; } +/* + * Given a 32-bit constant, return the most-restricted RegType enum entry + * that can hold the value. + */ +char dvmDetermineCat1Const(s4 value) +{ + if (value < -32768) + return kRegTypeInteger; + else if (value < -128) + return kRegTypeShort; + else if (value < 0) + return kRegTypeByte; + else if (value == 0) + return kRegTypeZero; + else if (value == 1) + return kRegTypeOne; + else if (value < 128) + return kRegTypePosByte; + else if (value < 32768) + return kRegTypePosShort; + else if (value < 65536) + return kRegTypeChar; + else + return kRegTypeInteger; +} diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h index 4a317d4ee..4d5b57ce8 100644 --- a/vm/analysis/VerifySubs.h +++ b/vm/analysis/VerifySubs.h @@ -67,4 +67,7 @@ void dvmLogUnableToResolveClass(const char* missingClassDescr, bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags, int curOffset, int* pOffset, bool* pConditional); +/* return a RegType enumeration value that "value" just fits into */ +char dvmDetermineCat1Const(s4 value); + #endif /*_DALVIK_VERIFYSUBS*/ diff --git a/vm/mterp/NOTES.txt b/vm/mterp/NOTES.txt index b7b9bc0ed..e348934e8 100644 --- a/vm/mterp/NOTES.txt +++ b/vm/mterp/NOTES.txt @@ -35,3 +35,38 @@ result into the specified destination register. Using a virtual register to hold a partial or temporary result is not allowed. Virtual registers must not be modified if we abort the instruction with an exception. + +==== Method results and GC ==== + +The return value from a method is held in local storage (on the native +stack for the portable interpreter, and in glue->retval for asm). It's not +accessible to a GC. In practice this isn't a problem, because if the +following instruction is not a "move-result" then the result is ignored, +and none of the move-result* instructions are GC points. + +(This is potentially an issue when debugging, since we can theoretically +single-step by individual bytecodes, but in practice we always step by +source lines and move-result is grouped with the method invoke.) + +This suggests a rule: + + * Don't do anything that can cause a GC in the invoke-* handler after + a method returns successfully. + +Unsuccessful returns, such as a native method call that returns with an +exception pending, are not interesting because the return value is ignored. + +If it's not possible to obey this rule, then we need to track the value +used in a return-object instruction for a brief period. The easiest way +to accomplish this is to store it in the interpreted stack where the GC +can find it, and use a live-precise GC to ignore the value. + +The "trackref" functions can also be used, but they add overhead to method +calls returning objects, and ensuring that we stop tracking the reference +when it's no longer needed can be awkward. + +Any solution must work correctly when returning into or returning from native +code. JNI handles returns from interp->native by adding the value to the +local references table, but returns from native->interp are simply stored +in the usual "retval". + diff --git a/vm/mterp/c/OP_NOT_LONG.c b/vm/mterp/c/OP_NOT_LONG.c index 4aafe8c58..4fb393ae4 100644 --- a/vm/mterp/c/OP_NOT_LONG.c +++ b/vm/mterp/c/OP_NOT_LONG.c @@ -1,2 +1,2 @@ -HANDLE_UNOP(OP_NOT_LONG, "not-long", , & 0xffffffffffffffffULL, _WIDE) +HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE) OP_END diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S index e75fac4a8..857168239 100644 --- a/vm/mterp/out/InterpAsm-x86.S +++ b/vm/mterp/out/InterpAsm-x86.S @@ -3146,9 +3146,9 @@ dvmAsmInstructionStart = .L_OP_NOP andb $0xf,%cl # ecx<- A FETCH_INST_WORD(1) ADVANCE_PC(1) - testl %eax,%eax - sete %al - movzbl %al,%eax + + + notl %eax SET_VREG(%eax,%ecx) GOTO_NEXT @@ -3184,11 +3184,8 @@ dvmAsmInstructionStart = .L_OP_NOP andb $0xf,rINST_LO # rINST_FULL<- A GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0] GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1] - orl %ecx,%eax - testl %eax,%eax # any bits set? - sete %cl - movzbl %cl,%eax - movl $0,%ecx + notl %eax + notl %ecx SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx FETCH_INST_WORD(1) @@ -4306,7 +4303,7 @@ dvmAsmInstructionStart = .L_OP_NOP movzbl rINST_HI,%ecx # ecx<- AA FETCH_INST_WORD(2) 1: - fprem1 + fprem fstsw %ax sahf jp 1b @@ -4419,7 +4416,7 @@ dvmAsmInstructionStart = .L_OP_NOP movzbl rINST_HI,%ecx # ecx<- AA FETCH_INST_WORD(2) 1: - fprem1 + fprem fstsw %ax sahf jp 1b @@ -5108,7 +5105,7 @@ dvmAsmInstructionStart = .L_OP_NOP flds (rFP,%ecx,4) # vAA to fp stack FETCH_INST_WORD(1) 1: - fprem1 + fprem fstsw %ax sahf jp 1b @@ -5221,7 +5218,7 @@ dvmAsmInstructionStart = .L_OP_NOP fldl (rFP,%ecx,4) # vAA to fp stack FETCH_INST_WORD(1) 1: - fprem1 + fprem fstsw %ax sahf jp 1b @@ -5251,7 +5248,7 @@ dvmAsmInstructionStart = .L_OP_NOP movswl 2(rPC),%ecx # ecx<- ssssCCCC movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA andb $0xf,rINST_LO # rINST_FULL<- A - addl %ecx,%eax # ex: addl %ecx, %eax + addl %ecx,%eax # for example: addl %ecx, %eax SET_VREG(%eax,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) @@ -5262,21 +5259,27 @@ dvmAsmInstructionStart = .L_OP_NOP .balign 64 .L_OP_RSUB_INT: /* 0xd1 */ /* File: x86/OP_RSUB_INT.S */ -/* File: x86/binop1.S */ +/* File: x86/binopLit16.S */ /* - * Generic 32-bit binary operation in which both operands loaded to - * registers (op0 in eax, op1 in ecx). + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 */ - /* binop vAA, vBB, vCC */ - movzbl 2(rPC),%eax # eax<- BB - movzbl 3(rPC),%ecx # ecx<- CC - GET_VREG(%eax,%eax) # eax<- vBB - GET_VREG(%ecx,%ecx) # eax<- vBB - subl %eax,%ecx # ex: addl %ecx,%eax - movzbl rINST_HI,%eax # tmp<- AA + /* binop/lit16 vA, vB, #+CCCC */ + movzbl rINST_HI,%eax # eax<- 000000BA + sarl $4,%eax # eax<- B + GET_VREG(%eax,%eax) # eax<- vB + movswl 2(rPC),%ecx # ecx<- ssssCCCC + movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA + andb $0xf,rINST_LO # rINST_FULL<- A + subl %eax,%ecx # for example: addl %ecx, %eax + SET_VREG(%ecx,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) - SET_VREG(%ecx,%eax) GOTO_NEXT @@ -5379,7 +5382,7 @@ dvmAsmInstructionStart = .L_OP_NOP movswl 2(rPC),%ecx # ecx<- ssssCCCC movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA andb $0xf,rINST_LO # rINST_FULL<- A - andl %ecx,%eax # ex: addl %ecx, %eax + andl %ecx,%eax # for example: addl %ecx, %eax SET_VREG(%eax,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) @@ -5407,7 +5410,7 @@ dvmAsmInstructionStart = .L_OP_NOP movswl 2(rPC),%ecx # ecx<- ssssCCCC movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA andb $0xf,rINST_LO # rINST_FULL<- A - orl %ecx,%eax # ex: addl %ecx, %eax + orl %ecx,%eax # for example: addl %ecx, %eax SET_VREG(%eax,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) @@ -5436,7 +5439,7 @@ dvmAsmInstructionStart = .L_OP_NOP movswl 2(rPC),%ecx # ecx<- ssssCCCC movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA andb $0xf,rINST_LO # rINST_FULL<- A - xor %ecx,%eax # ex: addl %ecx, %eax + xor %ecx,%eax # for example: addl %ecx, %eax SET_VREG(%eax,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) @@ -6461,8 +6464,8 @@ dvmAsmSisterStart: */ .LOP_NEW_INSTANCE_abstract: movl offClassObject_descriptor(%ecx),%eax - movl $.LstrInstantiationError,OUT_ARG1(%esp) - movl %eax,OUT_ARG0(%esp) + movl $.LstrInstantiationError,OUT_ARG0(%esp) + movl %eax,OUT_ARG1(%esp) call dvmThrowExceptionWithClassMessage UNSPILL(rPC) jmp common_exceptionThrown @@ -6582,9 +6585,9 @@ dvmAsmSisterStart: testl rINST_FULL,rINST_FULL je 4f UNSPILL_TMP(rPC) - andl $0xf0,rPC # rPC<- 000000B0 - sall $12,rPC # rPC<- 000B0000 - orl %ecx,rPC # rpc<- 000BFEDC + andl $0x0f,rPC # rPC<- 0000000A + sall $16,rPC # rPC<- 000A0000 + orl %ecx,rPC # rpc<- 000AFEDC 3: movl $0xf,%ecx andl rPC,%ecx # ecx<- next reg to load @@ -6682,9 +6685,9 @@ dvmAsmSisterStart: testl rINST_FULL,rINST_FULL je 4f UNSPILL_TMP(rPC) - andl $0xf0,rPC # rPC<- 000000B0 - sall $12,rPC # rPC<- 000B0000 - orl %ecx,rPC # rpc<- 000BFEDC + andl $0x0f,rPC # rPC<- 0000000A + sall $16,rPC # rPC<- 000A0000 + orl %ecx,rPC # rpc<- 000AFEDC 3: movl $0xf,%ecx andl rPC,%ecx # ecx<- next reg to load diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c index 65b7e1ea2..02592bdff 100644 --- a/vm/mterp/out/InterpC-allstubs.c +++ b/vm/mterp/out/InterpC-allstubs.c @@ -2285,7 +2285,7 @@ HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE) OP_END /* File: c/OP_NOT_LONG.c */ -HANDLE_UNOP(OP_NOT_LONG, "not-long", , & 0xffffffffffffffffULL, _WIDE) +HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE) OP_END /* File: c/OP_NEG_FLOAT.c */ diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c index 450490398..cc23d33f3 100644 --- a/vm/mterp/out/InterpC-portdbg.c +++ b/vm/mterp/out/InterpC-portdbg.c @@ -2629,7 +2629,7 @@ HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE) OP_END /* File: c/OP_NOT_LONG.c */ -HANDLE_UNOP(OP_NOT_LONG, "not-long", , & 0xffffffffffffffffULL, _WIDE) +HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE) OP_END /* File: c/OP_NEG_FLOAT.c */ diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c index 02a81d87c..78722361b 100644 --- a/vm/mterp/out/InterpC-portstd.c +++ b/vm/mterp/out/InterpC-portstd.c @@ -2349,7 +2349,7 @@ HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE) OP_END /* File: c/OP_NOT_LONG.c */ -HANDLE_UNOP(OP_NOT_LONG, "not-long", , & 0xffffffffffffffffULL, _WIDE) +HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE) OP_END /* File: c/OP_NEG_FLOAT.c */ diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S index 107717ee2..85b468d78 100644 --- a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S +++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S @@ -89,9 +89,9 @@ testl rINST_FULL,rINST_FULL je 4f UNSPILL_TMP(rPC) - andl $$0xf0,rPC # rPC<- 000000B0 - sall $$12,rPC # rPC<- 000B0000 - orl %ecx,rPC # rpc<- 000BFEDC + andl $$0x0f,rPC # rPC<- 0000000A + sall $$16,rPC # rPC<- 000A0000 + orl %ecx,rPC # rpc<- 000AFEDC 3: movl $$0xf,%ecx andl rPC,%ecx # ecx<- next reg to load diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S index 299e249f3..da951f746 100644 --- a/vm/mterp/x86/OP_NEW_INSTANCE.S +++ b/vm/mterp/x86/OP_NEW_INSTANCE.S @@ -84,8 +84,8 @@ */ .L${opcode}_abstract: movl offClassObject_descriptor(%ecx),%eax - movl $$.LstrInstantiationError,OUT_ARG1(%esp) - movl %eax,OUT_ARG0(%esp) + movl $$.LstrInstantiationError,OUT_ARG0(%esp) + movl %eax,OUT_ARG1(%esp) call dvmThrowExceptionWithClassMessage UNSPILL(rPC) jmp common_exceptionThrown diff --git a/vm/mterp/x86/OP_NOT_INT.S b/vm/mterp/x86/OP_NOT_INT.S index ba37ca426..c910716d9 100644 --- a/vm/mterp/x86/OP_NOT_INT.S +++ b/vm/mterp/x86/OP_NOT_INT.S @@ -1,2 +1,2 @@ %verify "executed" -%include "x86/unop.S" {"pre0":"testl %eax,%eax","pre1":"sete %al","instr":"movzbl %al,%eax"} +%include "x86/unop.S" {"instr":"notl %eax"} diff --git a/vm/mterp/x86/OP_NOT_LONG.S b/vm/mterp/x86/OP_NOT_LONG.S index b080d23cc..3eca12046 100644 --- a/vm/mterp/x86/OP_NOT_LONG.S +++ b/vm/mterp/x86/OP_NOT_LONG.S @@ -6,11 +6,8 @@ andb $$0xf,rINST_LO # rINST_FULL<- A GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0] GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1] - orl %ecx,%eax - testl %eax,%eax # any bits set? - sete %cl - movzbl %cl,%eax - movl $$0,%ecx + notl %eax + notl %ecx SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx FETCH_INST_WORD(1) diff --git a/vm/mterp/x86/OP_REM_DOUBLE.S b/vm/mterp/x86/OP_REM_DOUBLE.S index f28a22432..809ac0a83 100644 --- a/vm/mterp/x86/OP_REM_DOUBLE.S +++ b/vm/mterp/x86/OP_REM_DOUBLE.S @@ -7,7 +7,7 @@ movzbl rINST_HI,%ecx # ecx<- AA FETCH_INST_WORD(2) 1: - fprem1 + fprem fstsw %ax sahf jp 1b diff --git a/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S index f1131b024..b199e6e52 100644 --- a/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S +++ b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S @@ -7,7 +7,7 @@ fldl (rFP,%ecx,4) # vAA to fp stack FETCH_INST_WORD(1) 1: - fprem1 + fprem fstsw %ax sahf jp 1b diff --git a/vm/mterp/x86/OP_REM_FLOAT.S b/vm/mterp/x86/OP_REM_FLOAT.S index 1e013684c..d78bc9a2c 100644 --- a/vm/mterp/x86/OP_REM_FLOAT.S +++ b/vm/mterp/x86/OP_REM_FLOAT.S @@ -7,7 +7,7 @@ movzbl rINST_HI,%ecx # ecx<- AA FETCH_INST_WORD(2) 1: - fprem1 + fprem fstsw %ax sahf jp 1b diff --git a/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S index 17d3e4eb3..fd1742bfd 100644 --- a/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S +++ b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S @@ -7,7 +7,7 @@ flds (rFP,%ecx,4) # vAA to fp stack FETCH_INST_WORD(1) 1: - fprem1 + fprem fstsw %ax sahf jp 1b diff --git a/vm/mterp/x86/OP_RSUB_INT.S b/vm/mterp/x86/OP_RSUB_INT.S index 442ab326d..fa9b410ac 100644 --- a/vm/mterp/x86/OP_RSUB_INT.S +++ b/vm/mterp/x86/OP_RSUB_INT.S @@ -1,2 +1,2 @@ %verify "executed" -%include "x86/binop1.S" {"instr":"subl %eax,%ecx","result":"%ecx","tmp":"%eax"} +%include "x86/binopLit16.S" {"instr":"subl %eax,%ecx","result":"%ecx"} diff --git a/vm/mterp/x86/binopLit16.S b/vm/mterp/x86/binopLit16.S index 9ac8146ca..762068f9e 100644 --- a/vm/mterp/x86/binopLit16.S +++ b/vm/mterp/x86/binopLit16.S @@ -1,4 +1,4 @@ -%default {"result":"eax"} +%default {"result":"%eax"} /* * Generic 32-bit "lit16" binary operation. Provide an "instr" line * that specifies an instruction that performs "result = eax op ecx". @@ -15,8 +15,8 @@ movswl 2(rPC),%ecx # ecx<- ssssCCCC movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA andb $$0xf,rINST_LO # rINST_FULL<- A - $instr # ex: addl %ecx, %eax - SET_VREG(%eax,rINST_FULL) + $instr # for example: addl %ecx, %eax + SET_VREG($result,rINST_FULL) FETCH_INST_WORD(2) ADVANCE_PC(2) GOTO_NEXT diff --git a/vm/oo/Array.c b/vm/oo/Array.c index cfe2456db..ebddca1ee 100644 --- a/vm/oo/Array.c +++ b/vm/oo/Array.c @@ -559,6 +559,8 @@ static ClassObject* createArrayClass(const char* descriptor, Object* loader) * class in arrays of primitives. * * "type" should be 'I', 'J', 'Z', etc. + * + * Returns NULL if the type doesn't correspond to a known primitive type. */ ClassObject* dvmFindPrimitiveClass(char type) { @@ -593,8 +595,7 @@ ClassObject* dvmFindPrimitiveClass(char type) idx = PRIM_VOID; break; default: - LOGE("Unknown primitive type '%c'\n", type); - assert(false); + LOGW("Unknown primitive type '%c'\n", type); return NULL; } diff --git a/vm/oo/Class.c b/vm/oo/Class.c index 7773066cb..ca2870a32 100644 --- a/vm/oo/Class.c +++ b/vm/oo/Class.c @@ -2799,48 +2799,48 @@ static bool createIftable(ClassObject* clazz) assert(idx == ifCount); - /* - * Remove anything redundant from our recent additions. Note we have - * to traverse the recent adds when looking for duplicates, because - * it's possible the recent additions are self-redundant. This - * reduces the memory footprint of classes with lots of inherited - * interfaces. - * - * (I don't know if this will cause problems later on when we're trying - * to find a static field. It looks like the proper search order is - * (1) current class, (2) interfaces implemented by current class, - * (3) repeat with superclass. A field implemented by an interface - * and by a superclass might come out wrong if the superclass also - * implements the interface. The javac compiler will reject the - * situation as ambiguous, so the concern is somewhat artificial.) - * - * UPDATE: this makes ReferenceType.Interfaces difficult to implement, - * because it wants to return just the interfaces declared to be - * implemented directly by the class. I'm excluding this code for now. - */ if (false) { - for (i = superIfCount; i < ifCount; i++) { - int j; + /* + * Remove anything redundant from our recent additions. Note we have + * to traverse the recent adds when looking for duplicates, because + * it's possible the recent additions are self-redundant. This + * reduces the memory footprint of classes with lots of inherited + * interfaces. + * + * (I don't know if this will cause problems later on when we're trying + * to find a static field. It looks like the proper search order is + * (1) current class, (2) interfaces implemented by current class, + * (3) repeat with superclass. A field implemented by an interface + * and by a superclass might come out wrong if the superclass also + * implements the interface. The javac compiler will reject the + * situation as ambiguous, so the concern is somewhat artificial.) + * + * UPDATE: this makes ReferenceType.Interfaces difficult to implement, + * because it wants to return just the interfaces declared to be + * implemented directly by the class. I'm excluding this code for now. + */ + for (i = superIfCount; i < ifCount; i++) { + int j; - for (j = 0; j < ifCount; j++) { - if (i == j) - continue; - if (clazz->iftable[i].clazz == clazz->iftable[j].clazz) { - LOGVV("INTF: redundant interface %s in %s\n", - clazz->iftable[i].clazz->descriptor, - clazz->descriptor); - - if (i != ifCount-1) - memmove(&clazz->iftable[i], &clazz->iftable[i+1], - (ifCount - i -1) * sizeof(InterfaceEntry)); - ifCount--; - i--; // adjust for i++ above - break; + for (j = 0; j < ifCount; j++) { + if (i == j) + continue; + if (clazz->iftable[i].clazz == clazz->iftable[j].clazz) { + LOGVV("INTF: redundant interface %s in %s\n", + clazz->iftable[i].clazz->descriptor, + clazz->descriptor); + + if (i != ifCount-1) + memmove(&clazz->iftable[i], &clazz->iftable[i+1], + (ifCount - i -1) * sizeof(InterfaceEntry)); + ifCount--; + i--; // adjust for i++ above + break; + } } } - } - LOGVV("INTF: class '%s' nodupes=%d\n", clazz->descriptor, ifCount); - } // if (false) + LOGVV("INTF: class '%s' nodupes=%d\n", clazz->descriptor, ifCount); + } // if (false) clazz->iftableCount = ifCount; @@ -2956,6 +2956,13 @@ static bool createIftable(ClassObject* clazz) == 0) { LOGVV("INTF: matched at %d\n", j); + if (!dvmIsPublicMethod(clazz->vtable[j])) { + LOGW("Implementation of %s.%s is not public\n", + clazz->descriptor, clazz->vtable[j]->name); + dvmThrowException("Ljava/lang/IllegalAccessError;", + "interface implementation not public"); + goto bail; + } clazz->iftable[i].methodIndexArray[methIdx] = j; break; } |
