This is an automated email from the ASF dual-hosted git repository.
lincoln pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/master by this push:
new 36302ed6e61 [FLINK-39273][table-common] Fix argument name conflict
when UDF method contains lambda expression
36302ed6e61 is described below
commit 36302ed6e612e0f75136546f8026c16e72cea6ed
Author: dylanhz <[email protected]>
AuthorDate: Thu Mar 26 21:51:26 2026 +0800
[FLINK-39273][table-common] Fix argument name conflict when UDF method
contains lambda expression
This closes #27787.
---
.../table/types/extraction/ExtractionUtils.java | 11 ++++++++++-
.../types/extraction/ExtractionUtilsTest.java | 23 ++++++++++++++++++++++
.../extraction/TypeInferenceExtractorTest.java | 18 +++++++++++++++++
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/extraction/ExtractionUtils.java
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/extraction/ExtractionUtils.java
index c788a71905e..4db958749ec 100644
---
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/extraction/ExtractionUtils.java
+++
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/extraction/ExtractionUtils.java
@@ -956,22 +956,31 @@ public final class ExtractionUtils {
* <p>NOTE: the first parameter may be "this" if the function is not
static. See more at <a
*
href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-3.html">3.6.
Receiving
* Arguments</a>
+ *
+ * <p>The visitor matches methods by both name and descriptor to avoid
visiting synthetic
+ * methods generated by the compiler for lambda expressions (e.g. {@code
lambda$eval$0}). These
+ * synthetic methods may share the same descriptor as the target method
and would otherwise
+ * corrupt the extracted parameter names.
*/
private static class ParameterExtractor extends ClassVisitor {
private static final int OPCODE = Opcodes.ASM9;
+ private final String methodName;
+
private final String methodDescriptor;
private final Map<Integer, String> parameterNamesWithIndex = new
TreeMap<>();
ParameterExtractor(Constructor<?> constructor) {
super(OPCODE);
+ methodName = "<init>";
methodDescriptor = getConstructorDescriptor(constructor);
}
ParameterExtractor(Method method) {
super(OPCODE);
+ methodName = method.getName();
methodDescriptor = getMethodDescriptor(method);
}
@@ -986,7 +995,7 @@ public final class ExtractionUtils {
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature,
String[] exceptions) {
- if (descriptor.equals(methodDescriptor)) {
+ if (name.equals(methodName) &&
descriptor.equals(methodDescriptor)) {
return new MethodVisitor(OPCODE) {
@Override
public void visitLocalVariable(
diff --git
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/ExtractionUtilsTest.java
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/ExtractionUtilsTest.java
index 431f0c23d91..5229729a7d2 100644
---
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/ExtractionUtilsTest.java
+++
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/ExtractionUtilsTest.java
@@ -30,6 +30,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -241,4 +242,26 @@ public class ExtractionUtilsTest {
List<CompletableFuture<Long>> listOfGenericFuture,
Long[] array) {}
}
+
+ /**
+ * Verifies that {@link ExtractionUtils#extractExecutableNames} returns
correct parameter names
+ * when a method contains a lambda that captures its own parameters,
producing a synthetic
+ * method with the same bytecode descriptor.
+ */
+ @Test
+ void testExtractExecutableNamesWithLambdaCapture() {
+ Method method =
ExtractionUtils.collectMethods(LambdaCaptureClass.class, "eval").get(0);
+ assertThat(ExtractionUtils.extractExecutableNames(method))
+ .isEqualTo(ImmutableList.of("id", "field"));
+ }
+
+ /** A single-method class where the lambda captures all parameters of the
enclosing method. */
+ public static class LambdaCaptureClass {
+
+ @SuppressWarnings("unused")
+ public String eval(Long id, String field) {
+ Supplier<String> supplier = () -> String.valueOf(id) + field;
+ return supplier.get();
+ }
+ }
}
diff --git
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/TypeInferenceExtractorTest.java
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/TypeInferenceExtractorTest.java
index 44dfb281133..f253819ce2c 100644
---
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/TypeInferenceExtractorTest.java
+++
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/extraction/TypeInferenceExtractorTest.java
@@ -231,6 +231,17 @@ class TypeInferenceExtractorTest {
TypeStrategies.explicit(
DataTypes.BIGINT().notNull().bridgedTo(long.class))),
// ---
+ // test eval method with lambda capture whose synthetic method
shares the
+ // same bytecode descriptor, previously causing "Argument name
conflict"
+ TestSpec.forScalarFunction(
+ "ScalarFunctionWithLambdaCapture",
+ ScalarFunctionWithLambdaCapture.class)
+ .expectStaticArgument(
+ StaticArgument.scalar("id",
DataTypes.BIGINT(), false))
+ .expectStaticArgument(
+ StaticArgument.scalar("field",
DataTypes.STRING(), false))
+
.expectOutput(TypeStrategies.explicit(DataTypes.STRING())),
+ // ---
// test overloaded arguments extraction async
TestSpec.forAsyncScalarFunction(OverloadedFunctionAsync.class)
.expectOutputMapping(
@@ -1590,6 +1601,13 @@ class TypeInferenceExtractorTest {
}
}
+ private static class ScalarFunctionWithLambdaCapture extends
ScalarFunction {
+ public String eval(Long id, String field) {
+ Supplier<String> supplier = () -> String.valueOf(id) + field;
+ return supplier.get();
+ }
+ }
+
private static class VarArgFunction extends ScalarFunction {
public String eval(int i, int... more) {
return null;