diff options
author | 2024-12-09 15:49:25 +0000 | |
---|---|---|
committer | 2025-01-06 12:20:05 +0000 | |
commit | bdf31e2ff958067b228009d5166e23baab96bd99 (patch) | |
tree | e45c30c9bde53c6aeafd3e7a778ab72a61443e3f | |
parent | 26040bb0286ca351158c675dcedd32bc85dd6dd1 (diff) |
Handle invoke-virtual targeting invokeExact gracefully.
The interpreter handles such invoke-virtual as if they are native
method and executes MethodHandle_invokeExact, which throws UOE.
Bug: 383057088
Test: ./art/test/testrunner/testrunner.py --host --64 -b --optimizing
Test: ./art/test/testrunner/testrunner.py --host --64 -b --interpreter
Change-Id: I4dd4dea46eddd2b26e5866c80548b530a603d9c9
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 42 | ||||
-rw-r--r-- | test/2286-invokevirtual-invokeexact/expected-stderr.txt | 0 | ||||
-rw-r--r-- | test/2286-invokevirtual-invokeexact/expected-stdout.txt | 0 | ||||
-rw-r--r-- | test/2286-invokevirtual-invokeexact/info.txt | 2 | ||||
-rw-r--r-- | test/2286-invokevirtual-invokeexact/smali/Test.smali | 26 | ||||
-rw-r--r-- | test/2286-invokevirtual-invokeexact/src/Main.java | 45 | ||||
-rw-r--r-- | test/knownfailures.json | 3 |
7 files changed, 114 insertions, 4 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 8226987968..ba426b5694 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -32,6 +32,7 @@ #include "handle_cache-inl.h" #include "imtable-inl.h" #include "intrinsics.h" +#include "intrinsics_enum.h" #include "intrinsics_utils.h" #include "jit/jit.h" #include "jit/profiling_info.h" @@ -1051,6 +1052,23 @@ static ArtMethod* ResolveMethod(uint16_t method_idx, return resolved_method; } +static bool IsSignaturePolymorphic(ArtMethod* method) { + if (!method->IsIntrinsic()) { + return false; + } + Intrinsics intrinsic = method->GetIntrinsic(); + + switch (intrinsic) { +#define IS_POLYMOPHIC(Name, ...) \ + case Intrinsics::k ## Name: + ART_SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(IS_POLYMOPHIC) +#undef IS_POLYMOPHIC + return true; + default: + return false; + } +} + bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, @@ -1078,10 +1096,28 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, &is_string_constructor); MethodReference method_reference(&graph_->GetDexFile(), method_idx); - if (UNLIKELY(resolved_method == nullptr)) { + + // In the wild there are apps which have invoke-virtual targeting signature polymorphic methods + // like MethodHandle.invokeExact. It never worked in the first place: such calls were dispatched + // to the JNI implementation, which throws UOE. + // Now, when a signature-polymorphic method is implemented as an intrinsic, compiler's attempt to + // devirtualize such ill-formed virtual calls can lead to compiler crashes as an intrinsic + // (like MethodHandle.invokeExact) might expect arguments to be set up in a different manner than + // it's done for virtual calls. + // Create HInvokeUnresolved to make sure that such invoke-virtual calls are not devirtualized + // and are treated as native method calls. + if (kIsDebugBuild && resolved_method != nullptr) { + ScopedObjectAccess soa(Thread::Current()); + CHECK_EQ(IsSignaturePolymorphic(resolved_method), resolved_method->IsSignaturePolymorphic()); + } + + if (UNLIKELY(resolved_method == nullptr || + (invoke_type != kPolymorphic && IsSignaturePolymorphic(resolved_method)))) { DCHECK(!Thread::Current()->IsExceptionPending()); - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kUnresolvedMethod); + if (resolved_method == nullptr) { + MaybeRecordStat(compilation_stats_, + MethodCompilationStat::kUnresolvedMethod); + } HInvoke* invoke = new (allocator_) HInvokeUnresolved(allocator_, number_of_arguments, operands.GetNumberOfOperands(), diff --git a/test/2286-invokevirtual-invokeexact/expected-stderr.txt b/test/2286-invokevirtual-invokeexact/expected-stderr.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/2286-invokevirtual-invokeexact/expected-stderr.txt diff --git a/test/2286-invokevirtual-invokeexact/expected-stdout.txt b/test/2286-invokevirtual-invokeexact/expected-stdout.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/2286-invokevirtual-invokeexact/expected-stdout.txt diff --git a/test/2286-invokevirtual-invokeexact/info.txt b/test/2286-invokevirtual-invokeexact/info.txt new file mode 100644 index 0000000000..40c068f5a5 --- /dev/null +++ b/test/2286-invokevirtual-invokeexact/info.txt @@ -0,0 +1,2 @@ +Ensure that invoke-virtual targeting MethodHandle::invoke or MethodHandle::invokeExact +does not crash the runtime. diff --git a/test/2286-invokevirtual-invokeexact/smali/Test.smali b/test/2286-invokevirtual-invokeexact/smali/Test.smali new file mode 100644 index 0000000000..0b04aa453f --- /dev/null +++ b/test/2286-invokevirtual-invokeexact/smali/Test.smali @@ -0,0 +1,26 @@ +# Copyright (C) 2025 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 LTest; + +.super Ljava/lang/Object; + +.method public static test(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object; + .registers 2 + # invoke-polymorphic is the right way to execute invokeExact. + # invoke-polymorphic {p0, p1}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-virtual {p0, p1}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object; + move-result-object v0 + return-object v0 +.end method diff --git a/test/2286-invokevirtual-invokeexact/src/Main.java b/test/2286-invokevirtual-invokeexact/src/Main.java new file mode 100644 index 0000000000..68f8269292 --- /dev/null +++ b/test/2286-invokevirtual-invokeexact/src/Main.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 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 static java.lang.invoke.MethodType.methodType; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) throws Throwable { + MethodHandle mh = MethodHandles.lookup() + .findStatic(Main.class, "toString", methodType(Object.class, Object[].class)); + + Object[] objects = new Object[2]; + objects[0] = "111"; + objects[1] = 10; + + Class testClass = Class.forName("Test"); + + Method m = testClass.getDeclaredMethod("test", MethodHandle.class, Object[].class); + try { + Object o = m.invoke(null, mh, objects); + throw new AssertionError("unreachable"); + } catch (Exception expected) {} + } + + static Object toString(Object[] objects) { + return "objects"; + } +} diff --git a/test/knownfailures.json b/test/knownfailures.json index 1a3a44ee91..3471250665 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -1064,7 +1064,8 @@ "2264-throwing-systemcleaner", "2267-class-implements-itself", "2276-const-method-type-gc-cleanup", - "2281-method-handle-invoke-static-class-unload" + "2281-method-handle-invoke-static-class-unload", + "2286-invokevirtual-invokeexact" ], "variant": "jvm", "bug": "b/73888836", |