diff options
19 files changed, 479 insertions, 38 deletions
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 0eb3e439ac..0d65bc7405 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -305,11 +305,31 @@ inline int CompilerDriver::IsFastInvoke( MethodReference* target_method, const MethodReference* devirt_target, uintptr_t* direct_code, uintptr_t* direct_method) { // Don't try to fast-path if we don't understand the caller's class. + // Referrer_class is the class that this invoke is contained in. if (UNLIKELY(referrer_class == nullptr)) { return 0; } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_class, resolved_method, + StackHandleScope<2> hs(soa.Self()); + // Methods_class is the class refered to by the class_idx field of the methodId the method_idx is + // pointing to. + // For example in + // .class LABC; + // .super LDEF; + // .method hi()V + // ... + // invoke-super {p0}, LDEF;->hi()V + // ... + // .end method + // the referrer_class is 'ABC' and the methods_class is DEF. Note that the methods class is 'DEF' + // even if 'DEF' inherits the method from it's superclass. + Handle<mirror::Class> methods_class(hs.NewHandle(mUnit->GetClassLinker()->ResolveType( + *target_method->dex_file, + target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_, + dex_cache, + class_loader))); + DCHECK(methods_class.Get() != nullptr); + mirror::Class* methods_declaring_class = resolved_method->GetDeclaringClass(); + if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_declaring_class, resolved_method, dex_cache.Get(), target_method->dex_method_index))) { return 0; @@ -318,18 +338,31 @@ inline int CompilerDriver::IsFastInvoke( // overridden (ie is final). const bool same_dex_file = target_method->dex_file == mUnit->GetDexFile(); bool can_sharpen_virtual_based_on_type = same_dex_file && - (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); + (*invoke_type == kVirtual) && (resolved_method->IsFinal() || + methods_declaring_class->IsFinal()); // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of // the super class. const size_t pointer_size = InstructionSetPointerSize(GetInstructionSet()); - bool can_sharpen_super_based_on_type = same_dex_file && (*invoke_type == kSuper) && - (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && - resolved_method->GetMethodIndex() < methods_class->GetVTableLength() && - (methods_class->GetVTableEntry( + // TODO We should be able to sharpen if we are going into the boot image as well. + bool can_sharpen_super_based_on_type = same_dex_file && + (*invoke_type == kSuper) && + !methods_class->IsInterface() && + (referrer_class != methods_declaring_class) && + referrer_class->IsSubClass(methods_declaring_class) && + resolved_method->GetMethodIndex() < methods_declaring_class->GetVTableLength() && + (methods_declaring_class->GetVTableEntry( resolved_method->GetMethodIndex(), pointer_size) == resolved_method) && resolved_method->IsInvokable(); + // TODO We should be able to sharpen if we are going into the boot image as well. + bool can_sharpen_interface_super_based_on_type = same_dex_file && + (*invoke_type == kSuper) && + methods_class->IsInterface() && + methods_class->IsAssignableFrom(referrer_class) && + resolved_method->IsInvokable(); - if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) { + if (can_sharpen_virtual_based_on_type || + can_sharpen_super_based_on_type || + can_sharpen_interface_super_based_on_type) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's // dex cache, check that this resolved method is where we expect it. CHECK_EQ(target_method->dex_file, mUnit->GetDexFile()); @@ -363,7 +396,6 @@ inline int CompilerDriver::IsFastInvoke( *devirt_target->dex_file, devirt_target->dex_method_index, dex_cache, class_loader, nullptr, kVirtual); } else { - StackHandleScope<1> hs(soa.Self()); auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile( *devirt_target->dex_file, class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get())))); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 37218139fb..c7430e7eb6 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -798,7 +798,7 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( @@ -833,31 +833,56 @@ ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_t } // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not. - // We need to look at the referrer's super class vtable. + // We need to look at the referrer's super class vtable. We need to do this to know if we need to + // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of + // which require runtime handling. if (invoke_type == kSuper) { if (compiling_class.Get() == nullptr) { - // Invoking a super method requires knowing the actual super class. If we did not resolve - // the compiling method's declaring class (which only happens for ahead of time compilation), - // bail out. + // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } - uint16_t vtable_index = resolved_method->GetMethodIndex(); - ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry( - vtable_index, class_linker->GetImagePointerSize()); - if (actual_method != resolved_method && - !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { - // TODO: The actual method could still be referenced in the current dex file, so we - // could try locating it. - // TODO: Remove the dex_file restriction. - return nullptr; - } - if (!actual_method->IsInvokable()) { - // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub - // could resolve the callee to the wrong method. + ArtMethod* current_method = graph_->GetArtMethod(); + DCHECK(current_method != nullptr); + Handle<mirror::Class> methods_class(hs.NewHandle( + dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(), + method_idx, + current_method))); + if (methods_class.Get() == nullptr) { + // Invoking a super method requires knowing the actual super class. If we did not resolve + // the compiling method's declaring class (which only happens for ahead of time + // compilation), bail out. + DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; + } else { + ArtMethod* actual_method; + if (methods_class->IsInterface()) { + actual_method = methods_class->FindVirtualMethodForInterfaceSuper( + resolved_method, class_linker->GetImagePointerSize()); + } else { + uint16_t vtable_index = resolved_method->GetMethodIndex(); + actual_method = compiling_class->GetSuperClass()->GetVTableEntry( + vtable_index, class_linker->GetImagePointerSize()); + } + if (actual_method != resolved_method && + !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) { + // The back-end code generator relies on this check in order to ensure that it will not + // attempt to read the dex_cache with a dex_method_index that is not from the correct + // dex_file. If we didn't do this check then the dex_method_index will not be updated in the + // builder, which means that the code-generator (and compiler driver during sharpening and + // inliner, maybe) might invoke an incorrect method. + // TODO: The actual method could still be referenced in the current dex file, so we + // could try locating it. + // TODO: Remove the dex_file restriction. + return nullptr; + } + if (!actual_method->IsInvokable()) { + // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub + // could resolve the callee to the wrong method. + return nullptr; + } + resolved_method = actual_method; } - resolved_method = actual_method; } // Check for incompatible class changes. The class linker has a fast path for @@ -923,7 +948,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type); - if (resolved_method == nullptr) { + if (UNLIKELY(resolved_method == nullptr)) { MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod); HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_, number_of_arguments, @@ -943,7 +968,6 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, // Potential class initialization check, in the case of a static method call. HClinitCheck* clinit_check = nullptr; HInvoke* invoke = nullptr; - if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) { // By default, consider that the called method implicitly requires // an initialization check of its declaring method. diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ea1afa8203..7e8a4a4fcd 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -116,6 +116,30 @@ inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* return resolved_method; } +inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self, + uint32_t method_idx, + ArtMethod* referrer) { + // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is + // because if we did so than an invoke-super could be incorrectly dispatched in cases where + // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass + // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete + // implementation of the method. If this class's implementation of the method is copied from an + // interface (either miranda, default or conflict) we would incorrectly assume that is what we + // want to invoke on, instead of the 'concrete' implementation that the direct superclass + // contains. + mirror::Class* declaring_class = referrer->GetDeclaringClass(); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + const DexFile* dex_file = h_dex_cache->GetDexFile(); + const DexFile::MethodId& method = dex_file->GetMethodId(method_idx); + mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_); + if (UNLIKELY(resolved_type == nullptr)) { + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader); + } + return resolved_type; +} + template <ClassLinker::ResolveMode kResolveMode> inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4975c29742..99dd073d56 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -284,6 +284,15 @@ class ClassLinker { ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) SHARED_REQUIRES(Locks::mutator_lock_); + + // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be + // different then the declaring class of the resolved method due to copied + // miranda/default/conflict methods. + mirror::Class* ResolveReferencedClassOfMethod(Thread* self, + uint32_t method_idx, + ArtMethod* referrer) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); template <ResolveMode kResolveMode> ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) SHARED_REQUIRES(Locks::mutator_lock_) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 638fdb4f46..24986253f7 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1036,8 +1036,15 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( - called->GetMethodIndex(), sizeof(void*)); + // TODO Maybe put this into a mirror::Class function. + mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod( + self, called_method.dex_method_index, caller); + if (ref_class->IsInterface()) { + called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*)); + } else { + called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry( + called->GetMethodIndex(), sizeof(void*)); + } } CHECK(called != nullptr) << PrettyMethod(orig_called) << " " diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 2890a9826e..5f401232b0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3948,11 +3948,27 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( // If we're using invoke-super(method), make sure that the executing method's class' superclass // has a vtable entry for the target method. Or the target is on a interface. if (method_type == METHOD_SUPER) { - if (res_method->GetDeclaringClass()->IsInterface()) { - // TODO Fill in this part. Verify what we can... - if (Runtime::Current()->IsAotCompiler()) { - Fail(VERIFY_ERROR_FORCE_INTERPRETER) << "Currently we only allow invoke-super in " - << "interpreter when using interface methods"; + uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_; + mirror::Class* reference_class = dex_cache_->GetResolvedType(class_idx); + if (reference_class == nullptr) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Unable to find referenced class from invoke-super"; + return nullptr; + } + if (reference_class->IsInterface()) { + // TODO Can we verify anything else. + if (class_idx == class_def_->class_idx_) { + Fail(VERIFY_ERROR_CLASS_CHANGE) << "Cannot invoke-super on self as interface"; + } + // TODO Revisit whether we want to allow invoke-super on direct interfaces only like the JLS + // does. + mirror::Class* this_class = GetDeclaringClass().GetClass(); + if (!reference_class->IsAssignableFrom(this_class)) { + Fail(VERIFY_ERROR_CLASS_CHANGE) + << "invoke-super in " << PrettyClass(this_class) << " in method " + << PrettyMethod(dex_method_idx_, *dex_file_) << " to method " + << PrettyMethod(method_idx, *dex_file_) << " references " + << "non-super-interface type " << PrettyClass(reference_class); + return nullptr; } } else { const RegType& super = GetDeclaringClass().GetSuperClass(®_types_); diff --git a/test/563-checker-invoke-super/build b/test/563-checker-invoke-super/build new file mode 100755 index 0000000000..e06193ba78 --- /dev/null +++ b/test/563-checker-invoke-super/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright 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. +# Make us exit on a failure. +# +set -e + +# Hard-wired use of experimental jack. +# TODO: fix this temporary work-around for lambdas, see b/19467889 +export USE_JACK=true +export JACK_SERVER=false +export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" + +# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA +export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" +./default-build "$@" --experimental default-methods diff --git a/test/563-checker-invoke-super/expected.txt b/test/563-checker-invoke-super/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/563-checker-invoke-super/expected.txt diff --git a/test/563-checker-invoke-super/info.txt b/test/563-checker-invoke-super/info.txt new file mode 100644 index 0000000000..23c0d2fa49 --- /dev/null +++ b/test/563-checker-invoke-super/info.txt @@ -0,0 +1,2 @@ +Tests that invoke-super's to interface methods are optimized to direct method +calls when in the same dex file. diff --git a/test/563-checker-invoke-super/src/Main.java b/test/563-checker-invoke-super/src/Main.java new file mode 100644 index 0000000000..8554dbd0ec --- /dev/null +++ b/test/563-checker-invoke-super/src/Main.java @@ -0,0 +1,39 @@ +/* + * 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. + */ + +interface IFace { + public default void $noinline$aMethod() { throw new RuntimeException("Should not be called"); } +} + +class ClassImplA implements IFace { + /// CHECK-START: void ClassImplA.testSuperInvoke() builder (after) + /// CHECK: InvokeStaticOrDirect + public void testSuperInvoke() { + IFace.super.$noinline$aMethod(); + } +} + +class ClassImplB extends ClassImplA { + /// CHECK-START: void ClassImplB.testSuperInvoke2() builder (after) + /// CHECK: InvokeStaticOrDirect + public void testSuperInvoke2() { + super.$noinline$aMethod(); + } +} + +public class Main { + public static void main(String[] args) { } +} diff --git a/test/972-iface-super-multidex/expected.txt b/test/972-iface-super-multidex/expected.txt new file mode 100644 index 0000000000..a9d31a5320 --- /dev/null +++ b/test/972-iface-super-multidex/expected.txt @@ -0,0 +1,2 @@ +SuperInterface default method called +Expected ICCE caught diff --git a/test/972-iface-super-multidex/info.txt b/test/972-iface-super-multidex/info.txt new file mode 100644 index 0000000000..f7948ad2a5 --- /dev/null +++ b/test/972-iface-super-multidex/info.txt @@ -0,0 +1,3 @@ +Smali-based tests for experimental interface default methods. + +Obviously needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali new file mode 100644 index 0000000000..2c76213015 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali @@ -0,0 +1,23 @@ +# +# 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 abstract interface LConflictInterface; +.super Ljava/lang/Object; +.implements LOneConflict; +.implements LTwoConflict; + +# public interface ConflictInterface extends OneConflict, TwoConflict { +# } diff --git a/test/972-iface-super-multidex/smali-multidex/oneconflict.smali b/test/972-iface-super-multidex/smali-multidex/oneconflict.smali new file mode 100644 index 0000000000..7001f02c0f --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/oneconflict.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 abstract interface LOneConflict; +.super Ljava/lang/Object; + +# public interface OneConflict { +# public String runDefault() { +# return "OneConflict default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "OneConflict default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali-multidex/superinterface.smali b/test/972-iface-super-multidex/smali-multidex/superinterface.smali new file mode 100644 index 0000000000..d45ecea045 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/superinterface.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 abstract interface LSuperInterface; +.super Ljava/lang/Object; + +# public interface SuperInterface { +# public String runDefault() { +# return "SuperInterface default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "SuperInterface default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali-multidex/twoconflict.smali b/test/972-iface-super-multidex/smali-multidex/twoconflict.smali new file mode 100644 index 0000000000..b971b74649 --- /dev/null +++ b/test/972-iface-super-multidex/smali-multidex/twoconflict.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 abstract interface LTwoConflict; +.super Ljava/lang/Object; + +# public interface TwoConflict { +# public String runDefault() { +# return "TwoConflict default method called"; +# } +# } + +.method public runDefault()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + const-string v0, "TwoConflict default method called" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/smali/concreteclass.smali b/test/972-iface-super-multidex/smali/concreteclass.smali new file mode 100644 index 0000000000..703da945cc --- /dev/null +++ b/test/972-iface-super-multidex/smali/concreteclass.smali @@ -0,0 +1,62 @@ +# +# 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 LConcreteClass; +.super Ljava/lang/Object; +.implements LSuperInterface; +.implements LConflictInterface; + +# public class ConcreteClass implements SuperInterface, ConflictInterface { +# public String runReal() { +# return SuperInterface.super.runDefault(); +# } +# public String runConflict() { +# return ConflictInterface.super.runDefault(); +# } +# public String runDefault() { +# return "This is the wrong class to invoke"; +# } +# } + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public runConflict()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + invoke-super {p0}, LConflictInterface;->runDefault()Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method + + + +.method public runReal()Ljava/lang/String; +.registers 2 + # Do an invoke super on this class, to confuse runtime/compiler. + invoke-super {p0}, LSuperInterface;->runDefault()Ljava/lang/String; + move-result-object v0 + return-object v0 +.end method + +.method public runDefault()Ljava/lang/String; +.registers 2 + const-string v0, "This is the wrong class to invoke!" + return-object v0 +.end method diff --git a/test/972-iface-super-multidex/src/Main.java b/test/972-iface-super-multidex/src/Main.java new file mode 100644 index 0000000000..3fb3f45428 --- /dev/null +++ b/test/972-iface-super-multidex/src/Main.java @@ -0,0 +1,55 @@ +/* + * Copyright 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.*; +public class Main { + public static void main(String[] args) { + Class<?> c = null; + try { + c = Class.forName("ConcreteClass"); + } catch (Exception e) { + System.out.println("Could not load class"); + e.printStackTrace(); + return; + } + try { + Method m = c.getMethod("runReal"); + System.out.println((String)m.invoke(c.newInstance(), new Object[0])); + } catch (Exception e) { + System.out.println("Unknown exception occurred"); + e.printStackTrace(); + } + try { + Method m = c.getMethod("runConflict"); + try { + System.out.println((String)m.invoke(c.newInstance(), new Object[0])); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME caught"); + e.printStackTrace(); + } catch (NoSuchMethodError e) { + System.out.println("Unexpected NSME caught"); + e.printStackTrace(); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE caught"); + } catch (Throwable e) { + System.out.println("Unknown exception caught!"); + e.printStackTrace(); + } + } +} diff --git a/test/etc/default-build b/test/etc/default-build index 7242428f1e..6e855ec30a 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -42,6 +42,12 @@ else HAS_SRC_MULTIDEX=false fi +if [ -d smali-multidex ]; then + HAS_SMALI_MULTIDEX=true +else + HAS_SMALI_MULTIDEX=false +fi + if [ -d src-ex ]; then HAS_SRC_EX=true else @@ -74,6 +80,9 @@ while true; do elif [ "x$1" = "x--no-src-multidex" ]; then HAS_SRC_MULTIDEX=false shift + elif [ "x$1" = "x--no-smali-multidex" ]; then + HAS_SMALI_MULTIDEX=false + shift elif [ "x$1" = "x--no-src-ex" ]; then HAS_SRC_EX=false shift @@ -171,6 +180,19 @@ if [ "${HAS_SMALI}" = "true" ]; then fi fi +if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then + # Compile Smali classes + ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'` + + # Don't bother with dexmerger if we provide our own main function in a smali file. + if [ ${HAS_SRC_MULTIDEX} = "true" ]; then + ${DXMERGER} classes2.dex classes2.dex smali_classes2.dex + else + mv smali_classes2.dex classes2.dex + fi +fi + + if [ ${HAS_SRC_EX} = "true" ]; then if [ ${USE_JACK} = "true" ]; then # Rename previous "classes.dex" so it is not overwritten. @@ -198,7 +220,7 @@ if [ ${HAS_SRC_EX} = "true" ]; then fi # Create a single jar with two dex files for multidex. -if [ ${HAS_SRC_MULTIDEX} = "true" ]; then +if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then zip $TEST_NAME.jar classes.dex classes2.dex elif [ ${NEED_DEX} = "true" ]; then zip $TEST_NAME.jar classes.dex |