diff options
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 5 | ||||
| -rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 21 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 4 | ||||
| -rw-r--r-- | test/594-invoke-super/expected.txt | 7 | ||||
| -rw-r--r-- | test/594-invoke-super/info.txt | 1 | ||||
| -rw-r--r-- | test/594-invoke-super/smali/invoke-super.smali | 31 | ||||
| -rw-r--r-- | test/594-invoke-super/src/Main.java | 80 |
7 files changed, 142 insertions, 7 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index a834216d0c..aaddc01f1f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -721,6 +721,11 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } + if (!methods_class->IsAssignableFrom(compiling_class.Get())) { + // We cannot statically determine the target method. The runtime will throw a + // NoSuchMethodError on this one. + return nullptr; + } ArtMethod* actual_method; if (methods_class->IsInterface()) { actual_method = methods_class->FindVirtualMethodForInterfaceSuper( diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 16fbfaad32..fc6257302a 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -514,12 +514,18 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_ CHECK(self->IsExceptionPending()); return nullptr; } else if (!method_reference_class->IsInterface()) { - // It is not an interface. - mirror::Class* super_class = referring_class->GetSuperClass(); + // It is not an interface. If the referring class is in the class hierarchy of the + // referenced class in the bytecode, we use its super class. Otherwise, we throw + // a NoSuchMethodError. + mirror::Class* super_class = nullptr; + if (method_reference_class->IsAssignableFrom(referring_class)) { + super_class = referring_class->GetSuperClass(); + } uint16_t vtable_index = resolved_method->GetMethodIndex(); if (access_check) { // Check existence of super class. - if (super_class == nullptr || !super_class->HasVTable() || + if (super_class == nullptr || + !super_class->HasVTable() || vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) { // Behavior to agree with that of the verifier. ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), @@ -693,8 +699,13 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, mirror::Object* this_objec // Need to do full type resolution... return nullptr; } else if (!method_reference_class->IsInterface()) { - // It is not an interface. - mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass(); + // It is not an interface. If the referring class is in the class hierarchy of the + // referenced class in the bytecode, we use its super class. Otherwise, we cannot + // resolve the method. + if (!method_reference_class->IsAssignableFrom(referring_class)) { + return nullptr; + } + mirror::Class* super_class = referring_class->GetSuperClass(); if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) { // The super class does not have the method. return nullptr; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 8802e62435..b629118436 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4096,8 +4096,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( << " to super " << PrettyMethod(res_method); return nullptr; } - mirror::Class* super_klass = super.GetClass(); - if (res_method->GetMethodIndex() >= super_klass->GetVTableLength()) { + if (!reference_class->IsAssignableFrom(GetDeclaringClass().GetClass()) || + (res_method->GetMethodIndex() >= super.GetClass()->GetVTableLength())) { Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(dex_method_idx_, *dex_file_) << " to super " << super diff --git a/test/594-invoke-super/expected.txt b/test/594-invoke-super/expected.txt new file mode 100644 index 0000000000..de26026c99 --- /dev/null +++ b/test/594-invoke-super/expected.txt @@ -0,0 +1,7 @@ +new A +I am A's foo +new B +I am B's foo +new A +new B +passed diff --git a/test/594-invoke-super/info.txt b/test/594-invoke-super/info.txt new file mode 100644 index 0000000000..440d8b8c66 --- /dev/null +++ b/test/594-invoke-super/info.txt @@ -0,0 +1 @@ +Invoke-super on various references. diff --git a/test/594-invoke-super/smali/invoke-super.smali b/test/594-invoke-super/smali/invoke-super.smali new file mode 100644 index 0000000000..6f787dd170 --- /dev/null +++ b/test/594-invoke-super/smali/invoke-super.smali @@ -0,0 +1,31 @@ +# +# Copyright (C) 2016 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 public LZ; +.super LA; + +.method public constructor <init>()V +.registers 1 + invoke-direct {v0}, LA;-><init>()V + return-void +.end method + +.method public foo()V +.registers 3 + new-instance v0, LY; + invoke-direct {v0}, LY;-><init>()V + invoke-super {v0}, LY;->foo()V + return-void +.end method diff --git a/test/594-invoke-super/src/Main.java b/test/594-invoke-super/src/Main.java new file mode 100644 index 0000000000..53f2bbf67a --- /dev/null +++ b/test/594-invoke-super/src/Main.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 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.InvocationTargetException; +import java.lang.reflect.Method; + +// +// Two classes A and B with method foo(). +// + +class A { + A() { System.out.println("new A"); } + + public void foo() { System.out.println("I am A's foo"); } + + // We previously used to invoke this method with a Y instance, due + // to invoke-super underspecified behavior. + public void bar() { System.out.println("I am A's bar"); } +} + +class B { + B() { System.out.println("new B"); } + + public void foo() { System.out.println("I am B's foo"); } +} + +// +// Two subclasses X and Y that call foo() on super. +// + +class X extends A { + public void foo() { super.foo(); } +} + +class Y extends B { + public void foo() { super.foo(); } +} + +// +// Driver class. +// + +public class Main { + + public static void main(String[] args) throws Exception { + // The normal stuff, X's super goes to A, Y's super goes to B. + new X().foo(); + new Y().foo(); + + // And now it gets interesting. + + // In bytecode, we define a class Z that is a subclass of A, and we call + // invoke-super on an instance of Y. + Class<?> z = Class.forName("Z"); + Method m = z.getMethod("foo"); + try { + m.invoke(z.newInstance()); + throw new Error("Expected InvocationTargetException"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NoSuchMethodError)) { + throw new Error("Expected NoSuchMethodError"); + } + } + + System.out.println("passed"); + } +} |