summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Almaz Mingaleev <mingaleev@google.com> 2024-12-09 15:49:25 +0000
committer Almaz Mingaleev <mingaleev@google.com> 2025-01-06 12:20:05 +0000
commitbdf31e2ff958067b228009d5166e23baab96bd99 (patch)
treee45c30c9bde53c6aeafd3e7a778ab72a61443e3f
parent26040bb0286ca351158c675dcedd32bc85dd6dd1 (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.cc42
-rw-r--r--test/2286-invokevirtual-invokeexact/expected-stderr.txt0
-rw-r--r--test/2286-invokevirtual-invokeexact/expected-stdout.txt0
-rw-r--r--test/2286-invokevirtual-invokeexact/info.txt2
-rw-r--r--test/2286-invokevirtual-invokeexact/smali/Test.smali26
-rw-r--r--test/2286-invokevirtual-invokeexact/src/Main.java45
-rw-r--r--test/knownfailures.json3
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",