From 204ef7a708466d8739e5dcbfbc4794b57cabab0f Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sat, 19 Sep 2020 18:57:53 -0700 Subject: [PATCH] Fix variable argument functions in expressions Fixes #1531 --- .../sk89q/worldedit/command/UtilityCommands.java | 6 +++++- .../internal/expression/ExpressionHelper.java | 16 ++++++++++++++-- .../worldedit/internal/expression/Functions.java | 7 +++++++ .../src/main/resources/lang/strings.json | 1 + .../internal/expression/ExpressionTest.java | 3 +++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 2a88517f9..49a369167 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -527,7 +527,11 @@ public void calc(Actor actor, try { expression = Expression.compile(String.join(" ", input)); } catch (ExpressionException e) { - actor.printError(TranslatableComponent.of("worldedit.calc.invalid", TextComponent.of(String.join(" ", input)))); + actor.printError(TranslatableComponent.of( + "worldedit.calc.invalid.with-error", + TextComponent.of(String.join(" ", input)), + TextComponent.of(e.getMessage()) + )); return; } WorldEditAsyncCommandBuilder.createAndSendMessage(actor, () -> { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java index 03ea12bef..1fba749b4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionHelper.java @@ -65,9 +65,21 @@ public static MethodHandle resolveFunction(Functions functions, Set matchingFns = functions.getMap().get(fnName); check(!matchingFns.isEmpty(), ctx, "Unknown function '" + fnName + "'"); for (MethodHandle function : matchingFns) { + if (function.isVarargsCollector()) { + int nParams = function.type().parameterCount(); + // last param is the array, turn that varargs + int keptParams = nParams - 1; + function = function.asCollector( + // collect into the last array + function.type().parameterType(nParams - 1), + // collect the variable args (args over kept) + ctx.args.size() - keptParams + ); + // re-wrap it for the inner arguments + function = function.asType(function.type().wrap()); + } MethodType type = function.type(); - // Validate argc if not varargs - if (!function.isVarargsCollector() && type.parameterCount() != ctx.args.size()) { + if (type.parameterCount() != ctx.args.size()) { // skip non-matching function continue; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java index 0b8f651e0..bb1ac95b1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java @@ -63,6 +63,7 @@ static Functions create() { } private static MethodHandle clean(MethodHandle handle) { + boolean wasVarargs = handle.isVarargsCollector(); // box it all first handle = handle.asType(handle.type().wrap()); if (handle.type().returnType() != Double.class) { @@ -72,6 +73,12 @@ private static MethodHandle clean(MethodHandle handle) { handle = handle.asType(handle.type().changeReturnType(Number.class)); handle = filterReturnValue(handle, DOUBLE_VALUE); } + // return vararg-ity + if (wasVarargs) { + handle = handle.asVarargsCollector( + handle.type().parameterType(handle.type().parameterCount() - 1) + ); + } return handle; } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index cf1c256e5..ea2769985 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -174,6 +174,7 @@ "worldedit.remove.removed": "{0} entities have been marked for removal.", "worldedit.remove.explain-all": "Use -1 to remove all entities in loaded chunks", "worldedit.calc.invalid": "'{0}' could not be parsed as a valid expression", + "worldedit.calc.invalid.with-error": "'{0}' could not be parsed as a valid expression: '{1}'", "worldedit.paste.pasted": "The clipboard has been pasted at {0}", "worldedit.paste.selected": "Selected clipboard paste region.", diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java index 5b76f7c63..bde3cf658 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java @@ -51,6 +51,9 @@ public Stream testEvaluate() throws ExpressionException { // check functions testCase("sin(5)", sin(5)), testCase("atan2(3, 4)", atan2(3, 4)), + testCase("min(1, 2)", 1), + testCase("max(1, 2)", 2), + testCase("max(1, 2, 3, 4, 5)", 5), // check conditionals testCase("0 || 5", 5), testCase("2 || 5", 2),