diff --git a/src/main/java/com/alibaba/qlexpress4/runtime/MemberResolver.java b/src/main/java/com/alibaba/qlexpress4/runtime/MemberResolver.java index d89d684bc..ee9b848bc 100644 --- a/src/main/java/com/alibaba/qlexpress4/runtime/MemberResolver.java +++ b/src/main/java/com/alibaba/qlexpress4/runtime/MemberResolver.java @@ -61,6 +61,9 @@ public static Constructor resolveConstructor(Class cls, Class[] argType if (!constructor.isVarArgs()) { continue; } + if (argTypes.length < constructor.getParameterTypes().length - 1) { + continue; + } varArgsCandidates.add(adapt2VarArgTypes(constructor.getParameterTypes(), argTypes.length)); varArgsConstructorI.add(i); } @@ -156,6 +159,9 @@ public static IMethod resolveMethod(List methods, Class[] if (!declaredMethod.isVarArgs()) { continue; } + if (argTypes.length < declaredMethod.getParameterTypes().length - 1) { + continue; + } varArgsCandidates.add(adapt2VarArgTypes(declaredMethod.getParameterTypes(), argTypes.length)); varArgsMethodI.add(i); } diff --git a/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewFilledInstanceInstruction.java b/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewFilledInstanceInstruction.java index bf9f112a0..63edb585b 100644 --- a/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewFilledInstanceInstruction.java +++ b/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewFilledInstanceInstruction.java @@ -60,6 +60,11 @@ public QResult execute(QContext qContext, QLOptions qlOptions) { private Object newInstance(QContext qContext) { Constructor constructor = qContext.getReflectLoader().loadConstructor(newCls, new Class[0]); + if (constructor == null) { + throw errorReporter.reportFormat(QLErrorCodes.NO_SUITABLE_CONSTRUCTOR.name(), + QLErrorCodes.NO_SUITABLE_CONSTRUCTOR.getErrorMsg(), + "[]"); + } try { return constructor.newInstance(); } @@ -69,7 +74,7 @@ private Object newInstance(QContext qContext) { QLErrorCodes.INVOKE_CONSTRUCTOR_INNER_ERROR.getErrorMsg()); } catch (Exception e) { - throw errorReporter.report(QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.name(), + throw errorReporter.report(e, QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.name(), QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.getErrorMsg()); } } diff --git a/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewInstanceInstruction.java b/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewInstanceInstruction.java index 0efec4833..44d18613f 100644 --- a/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewInstanceInstruction.java +++ b/src/main/java/com/alibaba/qlexpress4/runtime/instruction/NewInstanceInstruction.java @@ -68,7 +68,7 @@ private Object newObject(Constructor constructor, Object[] params) { QLErrorCodes.INVOKE_CONSTRUCTOR_INNER_ERROR.getErrorMsg()); } catch (Exception e) { - throw errorReporter.report(QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.name(), + throw errorReporter.report(e, QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.name(), QLErrorCodes.INVOKE_CONSTRUCTOR_UNKNOWN_ERROR.getErrorMsg()); } } diff --git a/src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java b/src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java index 2d9ff2e64..7f68cd079 100644 --- a/src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java +++ b/src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java @@ -1968,4 +1968,62 @@ public Object call(QContext qContext, Parameters parameters) { QLResult result = express4Runner.execute("CURRENT_TIME()", context, QLOptions.DEFAULT_OPTIONS); assertTrue((Long)result.getResult() > 0); } + + @Test + public void testNewFilledInstanceWithNoDefaultConstructor() { + Express4Runner express4Runner = + new Express4Runner(InitOptions.builder().securityStrategy(QLSecurityStrategy.open()).build()); + + try { + express4Runner.execute( + "{'@class': 'com.alibaba.qlexpress4.inport.NoDefaultConstructor', 'name': 'test'}", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + fail("Expected QLRuntimeException for class without no-arg constructor"); + } + catch (QLRuntimeException e) { + assertTrue("Expected NO_SUITABLE_CONSTRUCTOR error but got: " + e.getMessage(), + e.getMessage().contains("no suitable constructor")); + } + } + + @Test + public void testVarArgsMethodWithTooFewArgs() { + Express4Runner express4Runner = + new Express4Runner(InitOptions.builder().securityStrategy(QLSecurityStrategy.open()).build()); + + // Calling a varargs method with fewer arguments than required parameters should + // produce a meaningful error instead of ArrayIndexOutOfBoundsException + try { + express4Runner.execute("com.alibaba.qlexpress4.inport.VarArgsHelper.sum()", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + fail("Expected QLRuntimeException for varargs method called with too few args"); + } + catch (QLRuntimeException e) { + // Should get a method-not-found or similar error, not ArrayIndexOutOfBoundsException + assertFalse("Should not expose ArrayIndexOutOfBoundsException", + e.getMessage().contains("ArrayIndexOutOfBounds")); + } + + // Verify normal varargs calls still work correctly + QLResult result1 = express4Runner.execute( + "com.alibaba.qlexpress4.inport.VarArgsHelper.sumAll()", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + assertEquals(0, result1.getResult()); + + QLResult result2 = express4Runner.execute( + "com.alibaba.qlexpress4.inport.VarArgsHelper.sumAll(1, 2, 3)", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + assertEquals(6, result2.getResult()); + + QLResult result3 = express4Runner.execute( + "com.alibaba.qlexpress4.inport.VarArgsHelper.sum(10, 1, 2, 3)", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + assertEquals(16, result3.getResult()); + + // Calling with exactly the minimum required args (no varargs items) should also work + QLResult result4 = express4Runner.execute( + "com.alibaba.qlexpress4.inport.VarArgsHelper.sum(10)", + new HashMap<>(), QLOptions.DEFAULT_OPTIONS); + assertEquals(10, result4.getResult()); + } } diff --git a/src/test/java/com/alibaba/qlexpress4/inport/NoDefaultConstructor.java b/src/test/java/com/alibaba/qlexpress4/inport/NoDefaultConstructor.java new file mode 100644 index 000000000..d7a81a46f --- /dev/null +++ b/src/test/java/com/alibaba/qlexpress4/inport/NoDefaultConstructor.java @@ -0,0 +1,19 @@ +package com.alibaba.qlexpress4.inport; + +/** + * A test helper class that intentionally has NO no-arg constructor, + * used to verify that NewFilledInstanceInstruction reports a proper + * error instead of NullPointerException. + */ +public class NoDefaultConstructor { + + private final String name; + + public NoDefaultConstructor(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/test/java/com/alibaba/qlexpress4/inport/VarArgsHelper.java b/src/test/java/com/alibaba/qlexpress4/inport/VarArgsHelper.java new file mode 100644 index 000000000..136e4c235 --- /dev/null +++ b/src/test/java/com/alibaba/qlexpress4/inport/VarArgsHelper.java @@ -0,0 +1,29 @@ +package com.alibaba.qlexpress4.inport; + +/** + * A test helper class with varargs methods, + * used to verify that MemberResolver handles varargs resolution correctly + * when fewer arguments than required parameters are provided. + */ +public class VarArgsHelper { + + public static String format(String template, Object... args) { + return String.format(template, args); + } + + public static int sum(int required, int... rest) { + int total = required; + for (int r : rest) { + total += r; + } + return total; + } + + public static int sumAll(int... values) { + int total = 0; + for (int v : values) { + total += v; + } + return total; + } +}