diff options
Diffstat (limited to 'test')
22 files changed, 736 insertions, 4 deletions
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java index e1d7db170d..b33f575dec 100644 --- a/test/616-cha-abstract/src/Main.java +++ b/test/616-cha-abstract/src/Main.java @@ -39,8 +39,8 @@ class Main2 extends Main1 { } public class Main { - static Main1 sMain1; - static Main1 sMain2; + static Base sMain1; + static Base sMain2; static boolean sIsOptimizing = true; static boolean sHasJIT = true; diff --git a/test/616-cha-interface-default/expected.txt b/test/616-cha-interface-default/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface-default/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface-default/info.txt b/test/616-cha-interface-default/info.txt new file mode 100644 index 0000000000..11baa1f0f2 --- /dev/null +++ b/test/616-cha-interface-default/info.txt @@ -0,0 +1,2 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. +Test it under multidex configuration to check cross-dex inlining. diff --git a/test/616-cha-interface-default/multidex.jpp b/test/616-cha-interface-default/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-interface-default/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface-default/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java new file mode 100644 index 0000000000..2cbcb500c4 --- /dev/null +++ b/test/616-cha-interface-default/src-multidex/Base.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Base { + default public int foo(int i) { + if (i != 1) { + return -2; + } + return i + 10; + } + + // Test default method that's not inlined. + default public int $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + return -1; + } + + default void printError(String msg) { + System.out.println(msg); + } +} diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java new file mode 100644 index 0000000000..951607d2cf --- /dev/null +++ b/test/616-cha-interface-default/src/Main.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 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. + */ + +class Main1 implements Base { +} + +class Main2 extends Main1 { + public void foobar() {} +} + +class Main3 implements Base { + public int foo(int i) { + if (i != 3) { + printError("error3"); + } + return -(i + 10); + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + static Base sMain3; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + static int getValue(Class<?> cls) { + if (cls == Main1.class || cls == Main2.class) { + return 1; + } + return 3; + } + + // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked. + // So sMain1.foo() can be devirtualized to Base.foo() and be inlined. + // After Dummy.createMain3() which links in Main3, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain3 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if (sMain1.$noinline$bar() != -1) { + System.out.println("-1 expected."); + } + if (sMain2.foo(getValue(sMain2.getClass())) != 11) { + System.out.println("11 expected."); + } + + if (createMain3) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain3 = Dummy.createMain3(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main3 is linked by + // calling Dummy.createMain3(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main3 is created. + assertIsInterpreted(); + } + + if (sMain3 != null) { + if (sMain3.foo(getValue(sMain3.getClass())) != -13) { + System.out.println("-13 expected."); + } + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that implements a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. + // sMain2 is an instance of Main2. + // Neither Main1 nor Main2 override default method Base.foo(). + // Main3 hasn't bee loaded yet. + sMain1 = new Main1(); + sMain2 = new Main2(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main3 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main3 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", true); + assertSingleImplementation(sMain3.getClass(), "foo", true); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain3() in another class to avoid class loading due to verifier. +class Dummy { + static Base createMain3() { + return new Main3(); + } +} diff --git a/test/616-cha-interface/expected.txt b/test/616-cha-interface/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface/info.txt b/test/616-cha-interface/info.txt new file mode 100644 index 0000000000..1fd330afd4 --- /dev/null +++ b/test/616-cha-interface/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java new file mode 100644 index 0000000000..3c9349663d --- /dev/null +++ b/test/616-cha-interface/src/Main.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Base { + void foo(int i); + void $noinline$bar(); +} + +class Main1 implements Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + // Test rewriting invoke-interface into invoke-virtual when inlining fails. + public void $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + } + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + sMain1.$noinline$bar(); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-miranda/expected.txt b/test/616-cha-miranda/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-miranda/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-miranda/info.txt b/test/616-cha-miranda/info.txt new file mode 100644 index 0000000000..c46f33f613 --- /dev/null +++ b/test/616-cha-miranda/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on miranda method. diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-miranda/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java new file mode 100644 index 0000000000..e548482eb3 --- /dev/null +++ b/test/616-cha-miranda/src/Main.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Iface { + public void foo(int i); +} + +abstract class Base implements Iface { + // Iface.foo(int) will be added as a miranda method. + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main1 extends Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testOverride() on stack + // should be deoptimized. + static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testOverride"); + testOverride(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testOverride(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testOverride(). + testOverride(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-proxy-method-inline/expected.txt b/test/616-cha-proxy-method-inline/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-proxy-method-inline/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-proxy-method-inline/info.txt b/test/616-cha-proxy-method-inline/info.txt new file mode 100644 index 0000000000..012685547c --- /dev/null +++ b/test/616-cha-proxy-method-inline/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on inlining a cross-dex proxy method. diff --git a/test/616-cha-proxy-method-inline/multidex.jpp b/test/616-cha-proxy-method-inline/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-proxy-method-inline/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-proxy-method-inline/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java new file mode 100644 index 0000000000..9deca3e646 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Foo { + public Object bar(Object obj); +} diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java new file mode 100644 index 0000000000..be7bc820b3 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src/Main.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +class DebugProxy implements java.lang.reflect.InvocationHandler { + private Object obj; + static Class<?>[] interfaces = {Foo.class}; + + public static Object newInstance(Object obj) { + return java.lang.reflect.Proxy.newProxyInstance( + Foo.class.getClassLoader(), + interfaces, + new DebugProxy(obj)); + } + + private DebugProxy(Object obj) { + this.obj = obj; + } + + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + Object result; + if (obj == null) { + return null; + } + try { + System.out.println("before invoking method " + m.getName()); + result = m.invoke(obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (Exception e) { + throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); + } finally { + System.out.println("after invoking method " + m.getName()); + } + return result; + } +} + +public class Main { + public static void call(Foo foo) { + if (foo == null) { + return; + } + foo.bar(null); + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + Foo foo = (Foo)DebugProxy.newInstance(null); + ensureJitCompiled(Main.class, "call"); + call(foo); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 2de34ca44f..abbdbb1f95 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -332,8 +332,11 @@ { "tests": ["912-classes", "616-cha", - "616-cha-abstract"], - "bug": "http://b/36344364 http://b36344221", + "616-cha-abstract", + "616-cha-interface", + "616-cha-miranda", + "616-cha-proxy-method-inline"], + "bug": "http://b/36344364 http://b/36344221", "variant": "no-dex2oat | relocate-npatchoat" }, { |