From 5d283e1a1749eb47504483a9ccb8702a2f321511 Mon Sep 17 00:00:00 2001 From: N0tMyFaultOG Date: Fri, 13 Nov 2020 22:35:53 +0100 Subject: [PATCH] Fix variable argument functions in expressions 204ef7a708466d8739e5dcbfbc4794b57cabab0f Co-Authored-By: Octavia Togami <2093023+octylFractal@users.noreply.github.com> --- .../sk89q/worldedit/command/UtilityCommands.java | 6 +++++- .../internal/expression/ExpressionHelper.java | 15 ++++++++++++++- .../worldedit/internal/expression/Functions.java | 7 +++++++ .../src/main/resources/lang/strings.json | 1 + .../internal/expression/ExpressionTest.java | 3 +++ 5 files changed, 30 insertions(+), 2 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 47acd5d54..c6d94f35c 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 @@ -678,7 +678,11 @@ public class UtilityCommands { 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 916460ceb..922657976 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,22 @@ public class ExpressionHelper { 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..b40c91a94 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 @@ public final class Functions { } 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 @@ public final class Functions { 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 e8487f2c2..362de4445 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -342,6 +342,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 27ca1e513..6d39e20f7 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 @@ class ExpressionTest extends BaseExpressionTest { // 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),