You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ch...@apache.org on 2026/10/26 10:20:30 UTC
(kyuubi) branch master updated: [KYUUBI #7731][UTIL] Stop re-labeling invocation failures as missing members in ReflectUtils
This is an automated email from the ASF dual-hosted git repository.
pan3793 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new d7ca98566a [KYUUBI #7731][UTIL] Stop re-labeling invocation failures as missing members in ReflectUtils
d7ca98566a is described below
commit d7ca98566a6eac3de2b53da898f401b2c8a4a84f
Author: YangJie <ya...@baidu.com>
AuthorDate: Mon Oct 26 18:20:20 2026 +0800
[KYUUBI #7731][UTIL] Stop re-labeling invocation failures as missing members in ReflectUtils
### Why are the changes needed?
Closes #7731.
`ReflectUtils.getField` and `invokeAs` wrapped the whole body in one `try` and rewrote any `Exception` as `RuntimeException(s"$clz does not have ...", e)`. Because `bind`/`get`/`invoke` ran inside that same `try`, a failure thrown by the found field or the invoked method itself was relabeled as a missing member, and the real exception survived only as the cause.
This moves the `try`/`catch` to wrap only the lookup (`DynFields`/`DynMethods` `build`/`buildChecked`). The "does not have" message now applies only when the lookup fails; a failure while binding, reading, or invoking the found member keeps its own type and message.
### How was this patch tested?
Added two `ReflectUtilsSuite` cases: an invoked method that throws `IllegalArgumentException("bad input")` must propagate that exception rather than a "does not have" message, and binding a `Class` target to an instance field must surface the field wrapper's "Cannot bind" `IllegalArgumentException`. Both fail if the catch is widened back over the invocation.
```
build/mvn test -pl kyuubi-util-scala -am
build/mvn scalastyle:check spotless:check -pl kyuubi-util-scala -am
```
Module green on Zulu 17.0.18 (23/23), scalastyle 0 errors, spotless clean.
### Was this patch assisted by generative AI tooling?
Assisted-by: Claude Opus 4.8
Closes #7732 from LuciferYang/kyuubi-util-scala-reflectutils-errormessage.
Closes #7731
35281db5e [yangjie01] [KYUUBI #7731][UTIL] Stop re-labeling invocation failures as missing members in ReflectUtils
Authored-by: YangJie <ya...@baidu.com>
Signed-off-by: Cheng Pan <ch...@apache.org>
---
.../apache/kyuubi/util/reflect/ReflectUtils.scala | 58 ++++++++++++----------
.../kyuubi/util/reflect/ReflectUtilsSuite.scala | 17 +++++++
2 files changed, 49 insertions(+), 26 deletions(-)
diff --git a/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/reflect/ReflectUtils.scala b/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/reflect/ReflectUtils.scala
index 950db8a1ed..6b634c07d8 100644
--- a/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/reflect/ReflectUtils.scala
+++ b/kyuubi-util-scala/src/main/scala/org/apache/kyuubi/util/reflect/ReflectUtils.scala
@@ -54,19 +54,22 @@ object ReflectUtils {
*/
def getField[T](target: AnyRef, fieldName: String): T = {
val (clz, obj) = getTargetClass(target)
- try {
- val field = DynFields.builder
- .hiddenImpl(clz, fieldName)
- .impl(clz, fieldName)
- .build[T]
- if (field.isStatic) {
- field.asStatic.get
- } else {
- field.bind(obj).get
+ // only the lookup is re-labeled as a missing field; a failure thrown while
+ // binding or reading the found field keeps its own message
+ val field =
+ try {
+ DynFields.builder
+ .hiddenImpl(clz, fieldName)
+ .impl(clz, fieldName)
+ .build[T]
+ } catch {
+ case e: Exception =>
+ throw new RuntimeException(s"$clz does not have $fieldName field", e)
}
- } catch {
- case e: Exception =>
- throw new RuntimeException(s"$clz does not have $fieldName field", e)
+ if (field.isStatic) {
+ field.asStatic.get
+ } else {
+ field.bind(obj).get
}
}
@@ -82,21 +85,24 @@ object ReflectUtils {
def invokeAs[T](target: AnyRef, methodName: String, args: (Class[_], AnyRef)*): T = {
val (clz, obj) = getTargetClass(target)
val argClasses = args.map(_._1)
- try {
- val method = DynMethods.builder(methodName)
- .hiddenImpl(clz, argClasses: _*)
- .impl(clz, argClasses: _*)
- .buildChecked
- if (method.isStatic) {
- method.asStatic.invoke[T](args.map(_._2): _*)
- } else {
- method.bind(obj).invoke[T](args.map(_._2): _*)
+ // only the lookup is re-labeled as a missing method; a failure thrown by the
+ // invoked method itself keeps its own type and message
+ val method =
+ try {
+ DynMethods.builder(methodName)
+ .hiddenImpl(clz, argClasses: _*)
+ .impl(clz, argClasses: _*)
+ .buildChecked
+ } catch {
+ case e: Exception =>
+ throw new RuntimeException(
+ s"$clz does not have $methodName${argClasses.map(_.getName).mkString("(", ", ", ")")}",
+ e)
}
- } catch {
- case e: Exception =>
- throw new RuntimeException(
- s"$clz does not have $methodName${argClasses.map(_.getName).mkString("(", ", ", ")")}",
- e)
+ if (method.isStatic) {
+ method.asStatic.invoke[T](args.map(_._2): _*)
+ } else {
+ method.bind(obj).invoke[T](args.map(_._2): _*)
}
}
diff --git a/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/reflect/ReflectUtilsSuite.scala b/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/reflect/ReflectUtilsSuite.scala
index 0cd25f177c..d5afb987f3 100644
--- a/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/reflect/ReflectUtilsSuite.scala
+++ b/kyuubi-util-scala/src/test/scala/org/apache/kyuubi/util/reflect/ReflectUtilsSuite.scala
@@ -87,6 +87,21 @@ class ReflectUtilsSuite extends AnyFunSuite {
}("class org.apache.kyuubi.util.reflect.ObjectA$ does not have methodNotExists(" +
"java.lang.String, java.lang.String)")
}
+
+ test("invokeAs propagates the failure thrown by the invoked method") {
+ // the lookup succeeded and the method ran; re-labeling its failure as
+ // "does not have" points readers at a lookup problem that does not exist
+ interceptContains[IllegalArgumentException](invokeAs[String](obj2, "methodThrows"))(
+ "bad input")
+ }
+
+ test("getField reports the bind failure for an instance field on a class target") {
+ // binding a class target to an instance field fails with the truthful
+ // IllegalArgumentException from the field wrapper, not a missing-field message
+ interceptContains[IllegalArgumentException](getField[String](
+ classOf[ClassA],
+ "field0"))("Cannot bind")
+ }
}
class ClassA(val field0: String = "field0") {
@@ -103,6 +118,8 @@ class ClassB extends ClassA {
def method3(): String = "method3"
private def method4(): String = "method4"
+
+ def methodThrows(): String = throw new IllegalArgumentException("bad input")
}
object ObjectA {