From 019341d2de17ba5b2f3efefb995b06d74f7c650f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:27:05 +0100 Subject: [PATCH 01/19] init commit --- src/main/java/ch/njol/skript/Skript.java | 4 +- .../njol/skript/expressions/ExprFunction.java | 4 + .../function/DynamicFunctionReference.java | 4 +- .../lang/function/FunctionReference.java | 32 +- .../lang/function/FunctionRegistry.java | 397 ++++++++++++++++++ .../njol/skript/lang/function/Functions.java | 74 ++-- .../skript/tests/misc/function overloading.sk | 24 ++ 7 files changed, 497 insertions(+), 42 deletions(-) create mode 100644 src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java create mode 100644 src/test/skript/tests/misc/function overloading.sk diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 27a75d44e4d..1457b79f3de 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -1747,8 +1747,8 @@ public static boolean log(final Verbosity minVerb) { } public static void debug(final String info) { - if (!debug()) - return; +// if (!debug()) +// return; SkriptLogger.log(SkriptLogger.DEBUG, info); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprFunction.java b/src/main/java/ch/njol/skript/expressions/ExprFunction.java index 01cc92fb86f..7b443f41115 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprFunction.java +++ b/src/main/java/ch/njol/skript/expressions/ExprFunction.java @@ -35,6 +35,10 @@ public class ExprFunction extends SimpleExpression { Skript.registerExpression(ExprFunction.class, DynamicFunctionReference.class, ExpressionType.COMBINED, "[the|a] function [named] %string% [(in|from) %-script%]", "[the] functions [named] %strings% [(in|from) %-script%]", + "[all [[of] the]|the] functions (in|from) %script%", + + "[the|a] function [named] %string% with param[eter]s %*classinfos% [(in|from) %-script%]", + "[the] functions [named] %strings% with param[eter]s %*classinfos% [(in|from) %-script%]", "[all [[of] the]|the] functions (in|from) %script%" ); } diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index 5256556deda..db973c023c8 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -57,10 +57,10 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { Function function; if (source != null) { //noinspection unchecked - function = (Function) Functions.getFunction(name, source.getConfig().getFileName()); + function = (Function) FunctionRegistry.function(source.getConfig().getFileName(), name); } else { //noinspection unchecked - function = (Function) Functions.getFunction(name, null); + function = (Function) FunctionRegistry.function(null, name); } this.resolved = function != null; this.function = new WeakReference<>(function); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 98b453cbe50..44059610d1f 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -14,9 +14,9 @@ import ch.njol.skript.util.LiteralUtils; import ch.njol.util.StringUtils; import org.bukkit.event.Event; -import org.skriptlang.skript.util.Executable; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.converter.Converters; +import org.skriptlang.skript.util.Executable; import java.util.ArrayList; import java.util.Arrays; @@ -106,6 +106,8 @@ public boolean validateParameterArity(boolean first) { return parameters.length >= sign.getMinParameters(); } + private Class[] parameterTypes; + /** * Validates this function reference. Prints errors if needed. * @param first True if this is called while loading a script. False when @@ -119,7 +121,25 @@ public boolean validateFunction(boolean first) { function = null; SkriptLogger.setNode(node); Skript.debug("Validating function " + functionName); - Signature sign = Functions.getSignature(functionName, script); + + // attempt to get the types of the parameters for this function reference + boolean allSameType = true; + parameterTypes = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + parameterTypes[i] = parameters[i].getReturnType(); + + if (parameterTypes[0] != Object.class && parameterTypes[i] != Object.class) { + allSameType &= parameterTypes[i] == parameterTypes[0]; + } + } + + Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); + + // functions may have non-single arguments + // if all arguments are of the same type (or object) will try to find a function with an array type + if (parameterTypes.length > 0 && allSameType && sign == null) { + sign = FunctionRegistry.signature(script, functionName, Object[].class); + } // Check if the requested function exists if (sign == null) { @@ -208,12 +228,14 @@ public boolean validateFunction(boolean first) { Parameter p = sign.parameters[singleListParam ? 0 : i]; RetainingLogHandler log = SkriptLogger.startRetainingLog(); try { + System.out.println(parameters[i].getReturnType().getSimpleName()); + System.out.println(p.type.getC().getSimpleName()); //noinspection unchecked Expression e = parameters[i].getConvertedExpression(p.type.getC()); if (e == null) { if (first) { if (LiteralUtils.hasUnparsedLiteral(parameters[i])) { - Skript.error("Can't understand this expression: " + parameters[i].toString()); + Skript.error("(Function) Can't understand this expression: " + parameters[i].toString()); } else { Skript.error("The " + StringUtils.fancyOrderNumber(i + 1) + " argument given to the function '" + functionName + "' is not of the required type " + p.type + "." + " Check the correct order of the arguments and put lists into parentheses if appropriate (e.g. 'give(player, (iron ore and gold ore))')." @@ -267,7 +289,7 @@ public boolean resetReturnValue() { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) Functions.getFunction(functionName, script); + function = (Function) FunctionRegistry.function(script, functionName, parameterTypes); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); @@ -348,7 +370,7 @@ public T[] execute(Event event, Object... arguments) { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) Functions.getFunction(functionName, script); + function = (Function) FunctionRegistry.function(script, functionName, parameterTypes); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java new file mode 100644 index 00000000000..0f25e1fad70 --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -0,0 +1,397 @@ +package ch.njol.skript.lang.function; + +import ch.njol.skript.SkriptAPIException; +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +class FunctionRegistry { + + private FunctionRegistry() { + throw new UnsupportedOperationException("Cannot instantiate utility class"); + } + + /** + * The pattern for a valid function name. + * Functions must start with a letter and can only contain letters, numbers, and underscores. + */ + public final static String FUNCTION_NAME_PATTERN = "\\p{IsAlphabetic}[\\p{IsAlphabetic}\\d_]*"; + + /** + * The namespace for functions registered using Java. + */ + private static final Namespace GLOBAL_NAMESPACE = new Namespace(Scope.GLOBAL, null); + + /** + * Map for all function names to their identifiers, allowing for quicker lookup. + */ + private static final Map>> identifiers = new HashMap<>(); + + /** + * Map for all identifier to function combinations, belonging to a namespace. + */ + private static final Map>> functions = new HashMap<>(); + private static final Map>> signatures = new HashMap<>(); + + public static void registerSignature(@NotNull Signature signature) { + registerSignature(null, signature); + } + + public static void registerSignature(@Nullable String script, @NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature is null"); + + Namespace namespace = GLOBAL_NAMESPACE; + if (script != null) { + namespace = new Namespace(signature.isLocal() ? Scope.LOCAL : Scope.GLOBAL, script); + } + + FunctionIdentifier identifier = FunctionIdentifier.of(signature); + + Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); + Set identifiersWithName = javaIdentifiers.getOrDefault(identifier.name, new HashSet<>()); + boolean exists = identifiersWithName.add(identifier); + if (!exists) { + alreadyRegisteredError(identifier.name, identifier); + } + javaIdentifiers.put(identifier.name, identifiersWithName); + identifiers.put(namespace, javaIdentifiers); + + Map> orDefault = signatures.getOrDefault(namespace, new HashMap<>()); + orDefault.put(identifier, signature); + signatures.put(namespace, orDefault); + } + + /** + * Registers a function. + * + * @param function The function to register. + * @throws SkriptAPIException if the function name is invalid or if + * a function with the same name and parameters is already registered + * in this namespace. + */ + public static void registerFunction(@NotNull Function function) { + registerFunction(null, function); + } + + /** + * Registers a function. + * + * @param script The script to register the function in. + * @param function The function to register. + * @throws SkriptAPIException if the function name is invalid or if + * a function with the same name and parameters is already registered + * in this namespace. + */ + public static void registerFunction(@Nullable String script, @NotNull Function function) { + Preconditions.checkNotNull(function, "function is null"); + + String name = function.getName(); + if (!name.matches(FUNCTION_NAME_PATTERN)) { + throw new SkriptAPIException("Invalid function name '" + name + "'"); + } + Namespace namespace = GLOBAL_NAMESPACE; + if (script != null) { + namespace = new Namespace(function.getSignature().isLocal() ? Scope.LOCAL : Scope.GLOBAL, script); + } + + FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); + if (!signatureExists(namespace, identifier)) { + registerSignature(script, function.getSignature()); + } + + Map> identifierToFunction = functions.getOrDefault(namespace, new HashMap<>()); + Function existing = identifierToFunction.put(identifier, function); + if (existing != null) { + alreadyRegisteredError(name, identifier); + } + functions.put(namespace, identifierToFunction); + } + + private static void alreadyRegisteredError(String name, FunctionIdentifier identifier) { + throw new SkriptAPIException("Function '%s' with parameters %s is already registered" + .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()))); + } + + /** + * Checks if a function with the given name and arguments exists in the given script. + * If no local function is found, checks for global functions. + * + * @param script The script to check in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return True if a function with the given name and argument types exists in the script or globally, false otherwise. + */ + public static boolean signatureExists(@Nullable String script, @NotNull String name, Class... args) { + if (script == null) { + return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + + boolean local = signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, args)); + if (!local) { + return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + return true; + } + + /** + * Checks if a function with the given name and arguments exists in the namespace. + * + * @param namespace The namespace to check in. + * @param identifier The identifier of the function. + * @return True if a function with the given name and arguments exists in the namespace, false otherwise. + */ + private static boolean signatureExists(@NotNull Namespace namespace, @NotNull FunctionIdentifier identifier) { + Preconditions.checkNotNull(namespace, "namespace is null"); + Preconditions.checkNotNull(identifier, "identifier is null"); + + Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); + + if (!javaIdentifiers.containsKey(identifier.name)) { + return false; + } + + for (FunctionIdentifier other : javaIdentifiers.get(identifier.name)) { + if (Arrays.equals(identifier.args, other.args)) { + return true; + } + } + + return false; + } + + /** + * Gets a function from a script. If no local function is found, checks for global functions. + * + * @param script The script to get the function from. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The function with the given name and argument types, or null if no such function exists. + */ + public static Function function(@Nullable String script, @NotNull String name, Class... args) { + if (script == null) { + return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + + Function function = function(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, args)); + if (function == null) { + return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + return function; + } + + /** + * Gets a function from a namespace. + *

+ * To match the arguments, we check if a converter between the candidate type and the provided type exists + * for each argument in the function signature. + *

+ * + * @param namespace The namespace to get the function from. + * @param provided The provided of the function. + * @return The function with the given name and argument types, or null if no such function exists. + */ + private static Function function(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { + Preconditions.checkNotNull(namespace, "namespace is null"); + Preconditions.checkNotNull(provided, "provided is null"); + + Map> identifierFunctionMap = functions.get(namespace); + Map> namespaceIdentifiers = identifiers.get(namespace); + if (identifierFunctionMap == null || namespaceIdentifiers == null) { + System.out.println(1); + return null; + } + + System.out.println(provided.name); + Set existing = namespaceIdentifiers.get(provided.name); + if (existing == null) { + System.out.println(2); + return null; + } + + if (existing.size() == 1) { + FunctionIdentifier identifier = existing.stream().findAny().orElse(null); + System.out.println(identifier); + System.out.println("3 " + identifierFunctionMap.get(identifier)); + return identifierFunctionMap.get(identifier); + } + + List candidates = candidates(provided, existing); + if (candidates.isEmpty()) { + System.out.println(4); + return null; + } else if (candidates.size() == 1) { + System.out.println("5 " + identifierFunctionMap.get(candidates.get(0))); + return identifierFunctionMap.get(candidates.get(0)); + } else { + throw new SkriptAPIException("Ambiguous function call: " + provided.name); + } + } + + /** + * Returns a list of candidates for the provided function. + * + * @param provided The provided function. + * @param existing The existing functions with the same name. + * @return A list of candidates for the provided function. + */ + private static @NotNull List candidates(@NotNull FunctionIdentifier provided, + Set existing) { + List candidates = new ArrayList<>(); + for (FunctionIdentifier candidate : existing) { + if (!candidate.name.equals(provided.name)) { + continue; + } + + if (provided.args.length > candidate.args.length + || provided.args.length < candidate.minArgCount) { + continue; + } + +// When runtime types are added, uncomment this for function overloading between types :) +// for (int i = 0; i < provided.args.length; i++) { +// if (!Converters.converterExists(provided.args[i], candidate.args[i])) { +// continue candidates; +// } +// } + + candidates.add(candidate); + } + return candidates; + } + + public static Signature signature(@Nullable String script, @NotNull String name, Class... args) { + if (script == null) { + return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + + Signature signature = signature(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, args)); + if (signature == null) { + return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + } + return signature; + } + + private static Signature signature(@NotNull Namespace namespace, @NotNull FunctionIdentifier identifier) { + Preconditions.checkNotNull(namespace, "namespace is null"); + Preconditions.checkNotNull(identifier, "identifier is null"); + + Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); + + if (!javaIdentifiers.containsKey(identifier.name)) { +// System.out.println(9); + return null; + } + + List candidates = candidates(identifier, javaIdentifiers.get(identifier.name)); + if (candidates.isEmpty()) { +// System.out.println(10); + return null; + } else if (candidates.size() == 1) { +// System.out.println("11 " + signatures.get(namespace).get(candidates.get(0))); + return signatures.get(namespace).get(candidates.get(0)); + } else { + throw new SkriptAPIException("Ambiguous signature call: " + identifier.name); + } + } + + /** + * Scope of functions in namespace. + */ + private enum Scope { + GLOBAL, + LOCAL + } + + /** + * A namespace for a function. + * + * @param scope + * @param name + */ + private record Namespace(@NotNull Scope scope, @Nullable String name) { + + } + + /** + * An identifier for a function. + *

Used to differentiate between functions with the same name but different parameters.

+ * + * @param name The name of the function. + * @param args The arguments of the function. + */ + private record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, Class... args) { + + /** + * Returns the identifier for the given arguments. + * + * @param name The name of the function. + * @param args The types of the arguments. + * @return The identifier for the signature. + */ + private static FunctionIdentifier of(@NotNull String name, Class... args) { + Preconditions.checkNotNull(name, "name is null"); + + return new FunctionIdentifier(name, false, args.length, args); + } + + /** + * Returns the identifier for the given signature. + * + * @param signature The signature to get the identifier for. + * @return The identifier for the signature. + */ + private static FunctionIdentifier of(@NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature is null"); + + Parameter[] signatureParams = signature.parameters; + Class[] parameters = new Class[signatureParams.length]; + + int optionalArgs = 0; + for (int i = 0; i < signatureParams.length; i++) { + Parameter param = signatureParams[i]; + if (param.def != null) { + optionalArgs++; + } + + if (param.isSingleValue()) { + parameters[i] = param.getType().getC(); + } else { + parameters[i] = param.getType().getC().arrayType(); // for functions with Object[] arguments + } + } + + return new FunctionIdentifier(signature.getName(), signature.isLocal(), + parameters.length - optionalArgs, parameters); + } + + @Override + public int hashCode() { + return Objects.hash(name, Arrays.hashCode(args)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FunctionIdentifier other)) { + return false; + } + + if (!name.equals(other.name)) { + return false; + } + + if (args.length != other.args.length) { + return false; + } + + for (int i = 0; i < args.length; i++) { + if (args[i] != other.args[i]) + return false; + } + + return true; + } + } + +} diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index bac1537d899..dcd11307d0e 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -12,12 +12,7 @@ import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.script.Script; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Static methods to work with functions. @@ -66,9 +61,11 @@ public static JavaFunction registerFunction(JavaFunction function) { String name = function.getName(); if (!name.matches(functionNamePattern)) throw new SkriptAPIException("Invalid function name '" + name + "'"); - javaNamespace.addSignature(function.getSignature()); - javaNamespace.addFunction(function); - globalFunctions.put(function.getName(), javaNamespace); +// javaNamespace.addSignature(function.getSignature()); +// javaNamespace.addFunction(function); +// globalFunctions.put(function.getName(), javaNamespace); + + FunctionRegistry.registerFunction(function); return function; } @@ -85,12 +82,13 @@ public static JavaFunction registerFunction(JavaFunction function) { */ public static @Nullable Function loadFunction(Script script, SectionNode node, Signature signature) { String name = signature.name; - Namespace namespace = getScriptNamespace(script.getConfig().getFileName()); - if (namespace == null) { - namespace = globalFunctions.get(name); - if (namespace == null) - return null; // Probably duplicate signature; reported before - } +// Namespace namespace = getScriptNamespace(script.getConfig().getFileName()); +// if (namespace == null) { +// namespace = globalFunctions.get(name); +// if (namespace == null) +// return null; // Probably duplicate signature; reported before +// } + Parameter[] params = signature.parameters; ClassInfo c = signature.returnType; @@ -98,12 +96,14 @@ public static JavaFunction registerFunction(JavaFunction function) { Skript.debug((signature.local ? "local " : "") + "function " + name + "(" + StringUtils.join(params, ", ") + ")" + (c != null ? " :: " + (signature.isSingle() ? c.getName().getSingular() : c.getName().getPlural()) : "") + ":"); - Function f = new ScriptFunction<>(signature, node); + Function function = new ScriptFunction<>(signature, node); // Register the function for signature - namespace.addFunction(f); +// namespace.addFunction(function); + + FunctionRegistry.registerFunction(script.getConfig().getFileName(), function); - return f; + return function; } @@ -148,30 +148,38 @@ public static JavaFunction registerFunction(JavaFunction function) { * @see Functions#parseSignature(String, String, String, String, boolean) */ public static @Nullable Signature registerSignature(Signature signature) { - // Ensure there are no duplicate functions - if (signature.local) { - Namespace namespace = getScriptNamespace(signature.script); - if (namespace != null && namespace.getSignature(signature.name, true) != null) - return signError("A local function named '" + signature.name + "' already exists in the script"); + boolean exists; + Parameter[] parameters = signature.parameters; + + if (parameters.length == 1 && !parameters[0].isSingleValue()) { + exists = FunctionRegistry.signatureExists(signature.script, signature.getName(), parameters[0].type.getC().arrayType()); } else { - if (globalFunctions.containsKey(signature.name)) { - Namespace namespace = globalFunctions.get(signature.name); - if (namespace == javaNamespace) { // Special messages for built-in functions - return signError("Function name '" + signature.name + "' is reserved by Skript"); - } else { - Signature sign = namespace.getSignature(signature.name, false); - assert sign != null : "globalFunctions points to a wrong namespace"; - return signError("A global function named '" + signature.name + "' already exists in script '" + sign.script + "'"); - } + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + types[i] = parameters[i].type.getC(); } + + exists = FunctionRegistry.signatureExists(signature.script, signature.getName(), types); + } + + if (exists) { + return signError("Function '%s' with the same argument types already exists.".formatted(signature.getName())); } Namespace.Key namespaceKey = new Namespace.Key(Namespace.Origin.SCRIPT, signature.script); Namespace namespace = namespaces.computeIfAbsent(namespaceKey, k -> new Namespace()); - namespace.addSignature(signature); + if (namespace.getSignature(signature.name) != null) { + namespace.addSignature(signature); + } if (!signature.local) globalFunctions.put(signature.name, namespace); + if (signature.local) { + FunctionRegistry.registerSignature(signature.script, signature); + } else { + FunctionRegistry.registerSignature(null, signature); + } + Skript.debug("Registered function signature: " + signature.name); return signature; diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk new file mode 100644 index 00000000000..2e7f78fdb9d --- /dev/null +++ b/src/test/skript/tests/misc/function overloading.sk @@ -0,0 +1,24 @@ +function overloaded(x: int, y: int) :: int: + return 1 + +function overloaded(x: int, y: int, z: int) :: int: + return 2 + +function overloaded(x: text, y: text) :: int: + return 3 + +function overloaded(x: text, y: int, z: int) :: int: + return 4 + +test "function overloading": + broadcast last parse logs + + broadcast "%overloaded(1, 2)%" + broadcast "%overloaded(1, 2, 3)%" + broadcast "%overloaded('a', 'b')%" + broadcast "%overloaded('a', 2, 3)%" + + assert overloaded(1, 2) = 1 + assert overloaded(1, 2, 3) = 2 + assert overloaded('a', 'b') = 3 + assert overloaded('a', 2, 3) = 4 From 395ad195b94f0051bd9e3054d54c0cd80534d56f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 25 Mar 2025 23:28:18 +0100 Subject: [PATCH 02/19] fix some errors --- src/main/java/ch/njol/skript/Skript.java | 4 +- .../lang/function/FunctionReference.java | 56 +++++++++++-------- .../lang/function/FunctionRegistry.java | 33 ++++++----- .../njol/skript/lang/function/Functions.java | 23 ++++---- .../skript/tests/misc/function overloading.sk | 5 -- 5 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 1457b79f3de..27a75d44e4d 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -1747,8 +1747,8 @@ public static boolean log(final Verbosity minVerb) { } public static void debug(final String info) { -// if (!debug()) -// return; + if (!debug()) + return; SkriptLogger.log(SkriptLogger.DEBUG, info); } diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 44059610d1f..ab577d073f1 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -7,6 +7,7 @@ import ch.njol.skript.config.Node; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.UnparsedLiteral; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.skript.registrations.Classes; @@ -99,9 +100,12 @@ public FunctionReference( public boolean validateParameterArity(boolean first) { if (!first && script == null) return false; + Signature sign = Functions.getSignature(functionName, script); + if (sign == null) return false; + // Not enough parameters return parameters.length >= sign.getMinParameters(); } @@ -121,25 +125,7 @@ public boolean validateFunction(boolean first) { function = null; SkriptLogger.setNode(node); Skript.debug("Validating function " + functionName); - - // attempt to get the types of the parameters for this function reference - boolean allSameType = true; - parameterTypes = new Class[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - parameterTypes[i] = parameters[i].getReturnType(); - - if (parameterTypes[0] != Object.class && parameterTypes[i] != Object.class) { - allSameType &= parameterTypes[i] == parameterTypes[0]; - } - } - - Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); - - // functions may have non-single arguments - // if all arguments are of the same type (or object) will try to find a function with an array type - if (parameterTypes.length > 0 && allSameType && sign == null) { - sign = FunctionRegistry.signature(script, functionName, Object[].class); - } + Signature sign = Functions.getSignature(functionName, script); // Check if the requested function exists if (sign == null) { @@ -228,8 +214,6 @@ public boolean validateFunction(boolean first) { Parameter p = sign.parameters[singleListParam ? 0 : i]; RetainingLogHandler log = SkriptLogger.startRetainingLog(); try { - System.out.println(parameters[i].getReturnType().getSimpleName()); - System.out.println(p.type.getC().getSimpleName()); //noinspection unchecked Expression e = parameters[i].getConvertedExpression(p.type.getC()); if (e == null) { @@ -275,6 +259,34 @@ public boolean validateFunction(boolean first) { return true; } + /** + * Attempts to get this function's signature. + */ + private Signature getSignature() { + // attempt to get the types of the parameters for this function reference + boolean allSameType = true; + parameterTypes = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + System.out.println(parameters[i].getClass().getSimpleName()); + parameterTypes[i] = parameters[i].getReturnType(); + + if (parameterTypes[0] != Object.class && parameterTypes[i] != Object.class) { + allSameType &= parameterTypes[i] == parameterTypes[0]; + } + } + System.out.println("s = " + Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + + Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); + + // functions may have non-single arguments + // if all arguments are of the same type (or object) will try to find a function with an array type + if (parameterTypes.length > 0 && allSameType && sign == null) { + sign = FunctionRegistry.signature(script, functionName, Object[].class); + } + + return sign; + } + public @Nullable Function getFunction() { return function; } @@ -289,7 +301,7 @@ public boolean resetReturnValue() { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) FunctionRegistry.function(script, functionName, parameterTypes); + function = (Function) Functions.getFunction(functionName, script); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 0f25e1fad70..a63b85dcafb 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.stream.Collectors; class FunctionRegistry { @@ -17,7 +18,7 @@ private FunctionRegistry() { * The pattern for a valid function name. * Functions must start with a letter and can only contain letters, numbers, and underscores. */ - public final static String FUNCTION_NAME_PATTERN = "\\p{IsAlphabetic}[\\p{IsAlphabetic}\\d_]*"; + final static String FUNCTION_NAME_PATTERN = "\\p{IsAlphabetic}[\\p{IsAlphabetic}\\d_]*"; /** * The namespace for functions registered using Java. @@ -33,11 +34,11 @@ private FunctionRegistry() { * Map for all identifier to function combinations, belonging to a namespace. */ private static final Map>> functions = new HashMap<>(); - private static final Map>> signatures = new HashMap<>(); - public static void registerSignature(@NotNull Signature signature) { - registerSignature(null, signature); - } + /** + * Map for all identifier to signature combinations, belonging to a namespace. + */ + private static final Map>> signatures = new HashMap<>(); public static void registerSignature(@Nullable String script, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); @@ -199,30 +200,23 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct Map> identifierFunctionMap = functions.get(namespace); Map> namespaceIdentifiers = identifiers.get(namespace); if (identifierFunctionMap == null || namespaceIdentifiers == null) { - System.out.println(1); return null; } - System.out.println(provided.name); Set existing = namespaceIdentifiers.get(provided.name); if (existing == null) { - System.out.println(2); return null; } if (existing.size() == 1) { FunctionIdentifier identifier = existing.stream().findAny().orElse(null); - System.out.println(identifier); - System.out.println("3 " + identifierFunctionMap.get(identifier)); return identifierFunctionMap.get(identifier); } List candidates = candidates(provided, existing); if (candidates.isEmpty()) { - System.out.println(4); return null; } else if (candidates.size() == 1) { - System.out.println("5 " + identifierFunctionMap.get(candidates.get(0))); return identifierFunctionMap.get(candidates.get(0)); } else { throw new SkriptAPIException("Ambiguous function call: " + provided.name); @@ -292,7 +286,10 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun // System.out.println("11 " + signatures.get(namespace).get(candidates.get(0))); return signatures.get(namespace).get(candidates.get(0)); } else { - throw new SkriptAPIException("Ambiguous signature call: " + identifier.name); + String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); + System.out.println(identifier); + System.out.println(options); + throw new SkriptAPIException("Ambiguous signature call for '%s'".formatted(identifier.name)); } } @@ -333,6 +330,9 @@ private record FunctionIdentifier(@NotNull String name, boolean local, int minAr private static FunctionIdentifier of(@NotNull String name, Class... args) { Preconditions.checkNotNull(name, "name is null"); + if (args == null) { + return new FunctionIdentifier(name, false, 0); + } return new FunctionIdentifier(name, false, args.length, args); } @@ -392,6 +392,13 @@ public boolean equals(Object obj) { return true; } + + @Override + public String toString() { + return "FunctionIdentifier{name='%s', local=%s, minArgCount=%d, args=[%s]}".formatted( + name, local, minArgCount, + Arrays.stream(args).map(Class::getSimpleName).collect(Collectors.joining(", "))); + } } } diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index dcd11307d0e..870cec51881 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -61,9 +61,9 @@ public static JavaFunction registerFunction(JavaFunction function) { String name = function.getName(); if (!name.matches(functionNamePattern)) throw new SkriptAPIException("Invalid function name '" + name + "'"); -// javaNamespace.addSignature(function.getSignature()); -// javaNamespace.addFunction(function); -// globalFunctions.put(function.getName(), javaNamespace); + javaNamespace.addSignature(function.getSignature()); + javaNamespace.addFunction(function); + globalFunctions.put(function.getName(), javaNamespace); FunctionRegistry.registerFunction(function); @@ -82,12 +82,12 @@ public static JavaFunction registerFunction(JavaFunction function) { */ public static @Nullable Function loadFunction(Script script, SectionNode node, Signature signature) { String name = signature.name; -// Namespace namespace = getScriptNamespace(script.getConfig().getFileName()); -// if (namespace == null) { -// namespace = globalFunctions.get(name); -// if (namespace == null) -// return null; // Probably duplicate signature; reported before -// } + Namespace namespace = getScriptNamespace(script.getConfig().getFileName()); + if (namespace == null) { + namespace = globalFunctions.get(name); + if (namespace == null) + return null; // Probably duplicate signature; reported before + } Parameter[] params = signature.parameters; ClassInfo c = signature.returnType; @@ -98,8 +98,9 @@ public static JavaFunction registerFunction(JavaFunction function) { Function function = new ScriptFunction<>(signature, node); - // Register the function for signature -// namespace.addFunction(function); + if (namespace.getFunction(signature.name) != null) { + namespace.addFunction(function); + } FunctionRegistry.registerFunction(script.getConfig().getFileName(), function); diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index 2e7f78fdb9d..cddec39ce47 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -13,11 +13,6 @@ function overloaded(x: text, y: int, z: int) :: int: test "function overloading": broadcast last parse logs - broadcast "%overloaded(1, 2)%" - broadcast "%overloaded(1, 2, 3)%" - broadcast "%overloaded('a', 'b')%" - broadcast "%overloaded('a', 2, 3)%" - assert overloaded(1, 2) = 1 assert overloaded(1, 2, 3) = 2 assert overloaded('a', 'b') = 3 From 4b253f319fdbc428056627dd078bb748fd58461d Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:12:04 +0200 Subject: [PATCH 03/19] liftoff --- .../lang/function/FunctionReference.java | 66 ++++-- .../lang/function/FunctionRegistry.java | 221 ++++++++++++------ .../njol/skript/lang/function/Functions.java | 33 +-- .../skript/tests/misc/function overloading.sk | 17 +- 4 files changed, 222 insertions(+), 115 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index ab577d073f1..b3157712dd5 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -7,7 +7,6 @@ import ch.njol.skript.config.Node; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser; -import ch.njol.skript.lang.UnparsedLiteral; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.skript.registrations.Classes; @@ -125,7 +124,7 @@ public boolean validateFunction(boolean first) { function = null; SkriptLogger.setNode(node); Skript.debug("Validating function " + functionName); - Signature sign = Functions.getSignature(functionName, script); + Signature sign = getRegisteredSignature(); // Check if the requested function exists if (sign == null) { @@ -219,7 +218,7 @@ public boolean validateFunction(boolean first) { if (e == null) { if (first) { if (LiteralUtils.hasUnparsedLiteral(parameters[i])) { - Skript.error("(Function) Can't understand this expression: " + parameters[i].toString()); + Skript.error("Can't understand this expression: " + parameters[i].toString()); } else { Skript.error("The " + StringUtils.fancyOrderNumber(i + 1) + " argument given to the function '" + functionName + "' is not of the required type " + p.type + "." + " Check the correct order of the arguments and put lists into parentheses if appropriate (e.g. 'give(player, (iron ore and gold ore))')." @@ -259,34 +258,57 @@ public boolean validateFunction(boolean first) { return true; } - /** - * Attempts to get this function's signature. - */ - private Signature getSignature() { - // attempt to get the types of the parameters for this function reference - boolean allSameType = true; + // attempt to get the types of the parameters for this function reference + private void parseParameters() { + if (parameterTypes != null) { + return; + } + parameterTypes = new Class[parameters.length]; for (int i = 0; i < parameters.length; i++) { - System.out.println(parameters[i].getClass().getSimpleName()); - parameterTypes[i] = parameters[i].getReturnType(); - - if (parameterTypes[0] != Object.class && parameterTypes[i] != Object.class) { - allSameType &= parameterTypes[i] == parameterTypes[0]; - } + Expression parsed = LiteralUtils.defendExpression(parameters[i]); + parameterTypes[i] = parsed.getReturnType(); } - System.out.println("s = " + Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + } + + /** + * Attempts to get this function's signature. + */ + private Signature getRegisteredSignature() { + parseParameters(); + Skript.debug("Getting signature for '%s' with types %s", + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getName).toArray())); Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); - // functions may have non-single arguments - // if all arguments are of the same type (or object) will try to find a function with an array type - if (parameterTypes.length > 0 && allSameType && sign == null) { - sign = FunctionRegistry.signature(script, functionName, Object[].class); + // if we can't find a signature based on param types, just use the function name + // and find whatever matches first + if (sign == null) { + sign = FunctionRegistry.signature(script, functionName); } return sign; } + /** + * Attempts to get this function's signature. + */ + private Function getRegisteredFunction() { + parseParameters(); + + Skript.debug("Getting function '%s' with types %s", + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getName).toArray())); + Function function = FunctionRegistry.function(script, functionName, parameterTypes); + + // if we can't find a function based on param types, just use the function name + // and find whatever matches first + if (function == null) { + function = FunctionRegistry.function(script, functionName); + } + + return function; + } + public @Nullable Function getFunction() { return function; } @@ -301,7 +323,7 @@ public boolean resetReturnValue() { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) Functions.getFunction(functionName, script); + function = (Function) getRegisteredFunction(); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); @@ -382,7 +404,7 @@ public T[] execute(Event event, Object... arguments) { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) FunctionRegistry.function(script, functionName, parameterTypes); + function = (Function) Functions.getFunction(script, functionName); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index a63b85dcafb..6df61a1bb81 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -1,13 +1,20 @@ package ch.njol.skript.lang.function; +import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import com.google.common.base.Preconditions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.converter.Converters; import java.util.*; import java.util.stream.Collectors; +/** + * A registry for functions. + * + * @author Efnilite + */ class FunctionRegistry { private FunctionRegistry() { @@ -40,21 +47,30 @@ private FunctionRegistry() { */ private static final Map>> signatures = new HashMap<>(); + /** + * Registers a signature. + * + * @param signature The signature to register. + * @throws SkriptAPIException if a function with the same name and parameters is already registered + * in this namespace. + */ public static void registerSignature(@Nullable String script, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); + // namespace Namespace namespace = GLOBAL_NAMESPACE; - if (script != null) { - namespace = new Namespace(signature.isLocal() ? Scope.LOCAL : Scope.GLOBAL, script); + if (script != null && signature.isLocal()) { + namespace = new Namespace(Scope.LOCAL, script); } FunctionIdentifier identifier = FunctionIdentifier.of(signature); + // register Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); Set identifiersWithName = javaIdentifiers.getOrDefault(identifier.name, new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - alreadyRegisteredError(identifier.name, identifier); + alreadyRegisteredError(identifier.name, identifier, namespace); } javaIdentifiers.put(identifier.name, identifiersWithName); identifiers.put(namespace, javaIdentifiers); @@ -92,9 +108,11 @@ public static void registerFunction(@Nullable String script, @NotNull Function> identifierToFunction = functions.getOrDefault(namespace, new HashMap<>()); Function existing = identifierToFunction.put(identifier, function); if (existing != null) { - alreadyRegisteredError(name, identifier); + alreadyRegisteredError(name, identifier, namespace); } functions.put(namespace, identifierToFunction); } - private static void alreadyRegisteredError(String name, FunctionIdentifier identifier) { - throw new SkriptAPIException("Function '%s' with parameters %s is already registered" - .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()))); + private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, Namespace namespace) { + throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" + .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), + namespace)); } /** - * Checks if a function with the given name and arguments exists in the given script. - * If no local function is found, checks for global functions. + * Checks if a signature with the given name and arguments exists in the given script. * * @param script The script to check in. * @param name The name of the function. * @param args The types of the arguments of the function. - * @return True if a function with the given name and argument types exists in the script or globally, false otherwise. + * @return True if a signature with the given name and argument types exists in the script, false otherwise. */ public static boolean signatureExists(@Nullable String script, @NotNull String name, Class... args) { if (script == null) { return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); } - boolean local = signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, args)); - if (!local) { - return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); - } - return true; + return signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, args)); } /** @@ -184,13 +199,9 @@ public static Function function(@Nullable String script, @NotNull String name /** * Gets a function from a namespace. - *

- * To match the arguments, we check if a converter between the candidate type and the provided type exists - * for each argument in the function signature. - *

* * @param namespace The namespace to get the function from. - * @param provided The provided of the function. + * @param provided The provided identifier of the function. * @return The function with the given name and argument types, or null if no such function exists. */ private static Function function(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { @@ -200,61 +211,55 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct Map> identifierFunctionMap = functions.get(namespace); Map> namespaceIdentifiers = identifiers.get(namespace); if (identifierFunctionMap == null || namespaceIdentifiers == null) { + Skript.debug("No functions found in namespace %s".formatted(namespace)); return null; } Set existing = namespaceIdentifiers.get(provided.name); if (existing == null) { + Skript.debug("No functions named '%s' exist in the '%s' namespace".formatted(provided.name, namespace.name)); return null; } if (existing.size() == 1) { FunctionIdentifier identifier = existing.stream().findAny().orElse(null); + Skript.debug("Exact match for '%s': %s".formatted(provided.name, identifier)); return identifierFunctionMap.get(identifier); } - List candidates = candidates(provided, existing); + Set candidates = candidates(provided, existing); if (candidates.isEmpty()) { + Skript.debug("Failed to find a function for '%s'".formatted(provided.name)); return null; } else if (candidates.size() == 1) { - return identifierFunctionMap.get(candidates.get(0)); + Skript.debug("Matched function for '%s': %s".formatted(provided.name, candidates.stream().findAny().orElse(null))); + return identifierFunctionMap.get(candidates.stream().findAny().orElse(null)); } else { - throw new SkriptAPIException("Ambiguous function call: " + provided.name); + String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); + Skript.debug("Failed to match an exact function for '%s'".formatted(provided.name)); + Skript.debug("Identifier: %s".formatted(provided)); + Skript.debug("Options: %s".formatted(options)); + Skript.error(("Skript cannot determine which function named '%s' to call. " + + "Try clarifying the type of the arguments.").formatted(provided.name)); + return null; } } /** - * Returns a list of candidates for the provided function. + * Gets the signature for a function with the given name and arguments. If no local function is found, + * checks for global functions. * - * @param provided The provided function. - * @param existing The existing functions with the same name. - * @return A list of candidates for the provided function. + *
    + *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code args} is null or empty, + * the first function with the same name as the {@code name} param will be returned.
  • + *
+ * + * @param script The script to get the function from. If null, only global functions will be checked. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - private static @NotNull List candidates(@NotNull FunctionIdentifier provided, - Set existing) { - List candidates = new ArrayList<>(); - for (FunctionIdentifier candidate : existing) { - if (!candidate.name.equals(provided.name)) { - continue; - } - - if (provided.args.length > candidate.args.length - || provided.args.length < candidate.minArgCount) { - continue; - } - -// When runtime types are added, uncomment this for function overloading between types :) -// for (int i = 0; i < provided.args.length; i++) { -// if (!Converters.converterExists(provided.args[i], candidate.args[i])) { -// continue candidates; -// } -// } - - candidates.add(candidate); - } - return candidates; - } - public static Signature signature(@Nullable String script, @NotNull String name, Class... args) { if (script == null) { return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); @@ -267,32 +272,118 @@ public static Signature signature(@Nullable String script, @NotNull String na return signature; } - private static Signature signature(@NotNull Namespace namespace, @NotNull FunctionIdentifier identifier) { + /** + * Gets the signature for a function with the given name and arguments. + * + * @param namespace The namespace to get the function from. + * @param provided The provided identifier of the function. + * @return The signature for the function with the given name and argument types, or null if no such signature exists + * in the specified namespace. + */ + private static Signature signature(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { Preconditions.checkNotNull(namespace, "namespace is null"); - Preconditions.checkNotNull(identifier, "identifier is null"); + Preconditions.checkNotNull(provided, "provided is null"); Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); - if (!javaIdentifiers.containsKey(identifier.name)) { -// System.out.println(9); + if (!javaIdentifiers.containsKey(provided.name)) { + Skript.debug("No functions named '%s' exist in the '%s' namespace", namespace.name, provided.name); return null; } - List candidates = candidates(identifier, javaIdentifiers.get(identifier.name)); + Set candidates = candidates(provided, javaIdentifiers.get(provided.name)); if (candidates.isEmpty()) { -// System.out.println(10); + Skript.debug("Failed to find a signature for '%s'", provided.name); return null; } else if (candidates.size() == 1) { -// System.out.println("11 " + signatures.get(namespace).get(candidates.get(0))); - return signatures.get(namespace).get(candidates.get(0)); + Skript.debug("Matched signature for '%s': %s", + provided.name, signatures.get(namespace).get(candidates.stream().findAny().orElse(null))); + return signatures.get(namespace).get(candidates.stream().findAny().orElse(null)); } else { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); - System.out.println(identifier); - System.out.println(options); - throw new SkriptAPIException("Ambiguous signature call for '%s'".formatted(identifier.name)); + + Skript.debug("Failed to match an exact signature for '%s'", provided.name); + Skript.debug("Identifier: %s", provided); + Skript.debug("Options: %s", options); + Skript.error(("Skript cannot determine which function named '%s' to call. " + + "Try clarifying the type of the arguments.").formatted(provided.name)); + return null; } } + /** + * Returns a list of candidates for the provided function. + * + * @param provided The provided function. + * @param existing The existing functions with the same name. + * @return A list of candidates for the provided function. + */ + private static @NotNull Set candidates(@NotNull FunctionIdentifier provided, + Set existing) { + Set candidates = new HashSet<>(); + + candidates: + for (FunctionIdentifier candidate : existing) { + // if the provided name does not match the candidate name, skip + if (!candidate.name.equals(provided.name)) { + continue; + } + + // if the provided arguments are null, that means the function + // 1. has no arguments. return the first name match. + // 2. has an unknown amount of arguments. return the first name match. + if (provided.args == null || provided.args.length == 0) { + return Set.of(candidate); + } + + // if argument counts are not possible, skip + if (provided.args.length > candidate.args.length + || provided.args.length < candidate.minArgCount) { + continue; + } + + // if the types of the provided arguments do not match the candidate arguments, skip + for (int i = 0; i < provided.args.length; i++) { + if (!Converters.converterExists(provided.args[i], candidate.args[i])) { + continue candidates; + } + } + + candidates.add(candidate); + } + + if (candidates.isEmpty()) { + return Set.of(); + } else if (candidates.size() == 1) { + return candidates; + } + + // let overloaded(Long, Long) and overloaded(String, String) be two functions. + // allow overloaded(1, {_x}) to match Long, Long and avoid String, String, + // and allow overloaded({_x}, 1) to match Long, Long and avoid String, String + // despite not being an exact match in all arguments, + // since variables can be any type. + for (FunctionIdentifier candidate : new HashSet<>(candidates)) { + int argIndex = 0; + + while (argIndex < provided.args.length) { + if (provided.args[argIndex] == Object.class) { + argIndex++; + continue; + } + + if (provided.args[argIndex] != candidate.args[argIndex]) { + candidates.remove(candidate); + break; + } + + argIndex++; + } + } + + return candidates; + } + /** * Scope of functions in namespace. */ diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index 870cec51881..1071f6e1ec0 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -98,7 +98,7 @@ public static JavaFunction registerFunction(JavaFunction function) { Function function = new ScriptFunction<>(signature, node); - if (namespace.getFunction(signature.name) != null) { + if (namespace.getFunction(signature.name) == null) { namespace.addFunction(function); } @@ -135,8 +135,10 @@ public static JavaFunction registerFunction(JavaFunction function) { singleReturn = !p.getSecond(); if (returnClass == null) returnClass = Classes.getClassInfoFromUserInput(p.getFirst()); - if (returnClass == null) - return signError("Cannot recognise the type '" + returnType + "'"); + if (returnClass == null) { + Skript.error("Cannot recognise the type '" + returnType + "'"); + return null; + } } //noinspection unchecked return new Signature<>(script, name, parameters.toArray(new Parameter[0]), local, (ClassInfo) returnClass, singleReturn, null); @@ -164,12 +166,13 @@ public static JavaFunction registerFunction(JavaFunction function) { } if (exists) { - return signError("Function '%s' with the same argument types already exists.".formatted(signature.getName())); + Skript.error("Function '%s' with the same argument types already exists.".formatted(signature.getName())); + return null; } Namespace.Key namespaceKey = new Namespace.Key(Namespace.Origin.SCRIPT, signature.script); Namespace namespace = namespaces.computeIfAbsent(namespaceKey, k -> new Namespace()); - if (namespace.getSignature(signature.name) != null) { + if (namespace.getSignature(signature.name) == null) { namespace.addSignature(signature); } if (!signature.local) @@ -186,26 +189,6 @@ public static JavaFunction registerFunction(JavaFunction function) { return signature; } - /** - * Creates an error and returns Function null. - * @param error Error message. - * @return Null. - */ - private static @Nullable Function error(String error) { - Skript.error(error); - return null; - } - - /** - * Creates an error and returns Signature null. - * @param error Error message. - * @return Null. - */ - private static @Nullable Signature signError(String error) { - Skript.error(error); - return null; - } - /** * Gets a function, if it exists. Note that even if function exists in scripts, * it might not have been parsed yet. If you want to check for existence, diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index cddec39ce47..775657d3fef 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -11,9 +11,20 @@ function overloaded(x: text, y: int, z: int) :: int: return 4 test "function overloading": - broadcast last parse logs + # literal overloading assert overloaded(1, 2) = 1 assert overloaded(1, 2, 3) = 2 - assert overloaded('a', 'b') = 3 - assert overloaded('a', 2, 3) = 4 + assert overloaded("a", "b") = 3 + assert overloaded("a", 2, 3) = 4 + + # overloading with unknown type + assert overloaded(1, {_x}) = 1 + assert overloaded({_x}, 1) = 1 + + # can't determine the type, so error + parse: + overloaded({_y}, {_x}) + + broadcast "%last parse logs%" + assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments." \ No newline at end of file From 54e2270a7f705c97653db9411d00b08645b5dcf9 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:47:39 +0200 Subject: [PATCH 04/19] cleanup --- src/main/java/ch/njol/skript/expressions/ExprFunction.java | 4 ---- src/test/skript/tests/misc/function overloading.sk | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprFunction.java b/src/main/java/ch/njol/skript/expressions/ExprFunction.java index 7b443f41115..01cc92fb86f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprFunction.java +++ b/src/main/java/ch/njol/skript/expressions/ExprFunction.java @@ -35,10 +35,6 @@ public class ExprFunction extends SimpleExpression { Skript.registerExpression(ExprFunction.class, DynamicFunctionReference.class, ExpressionType.COMBINED, "[the|a] function [named] %string% [(in|from) %-script%]", "[the] functions [named] %strings% [(in|from) %-script%]", - "[all [[of] the]|the] functions (in|from) %script%", - - "[the|a] function [named] %string% with param[eter]s %*classinfos% [(in|from) %-script%]", - "[the] functions [named] %strings% with param[eter]s %*classinfos% [(in|from) %-script%]", "[all [[of] the]|the] functions (in|from) %script%" ); } diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index 775657d3fef..1370b5f8a1d 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -26,5 +26,4 @@ test "function overloading": parse: overloaded({_y}, {_x}) - broadcast "%last parse logs%" - assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments." \ No newline at end of file + assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments." From fd7e6a541b40b070b33ac18b19ea1c5bcc42f05b Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:50:14 +0200 Subject: [PATCH 05/19] yes thank you ai good point --- .../java/ch/njol/skript/lang/function/FunctionReference.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index b3157712dd5..55bd4e929e9 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -100,7 +100,7 @@ public boolean validateParameterArity(boolean first) { if (!first && script == null) return false; - Signature sign = Functions.getSignature(functionName, script); + Signature sign = getRegisteredSignature(); if (sign == null) return false; @@ -404,7 +404,7 @@ public T[] execute(Event event, Object... arguments) { // If needed, acquire the function reference if (function == null) //noinspection unchecked - function = (Function) Functions.getFunction(script, functionName); + function = (Function) getRegisteredFunction(); if (function == null) { // It might be impossible to resolve functions in some cases! Skript.error("Couldn't resolve call for '" + functionName + "'."); From 02f32335573b518b722cf43f06f63e0708da3560 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:17:55 +0200 Subject: [PATCH 06/19] improve docs --- .../skript/lang/function/FunctionRegistry.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 6df61a1bb81..4f669b2dcf4 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -15,7 +15,7 @@ * * @author Efnilite */ -class FunctionRegistry { +final class FunctionRegistry { private FunctionRegistry() { throw new UnsupportedOperationException("Cannot instantiate utility class"); @@ -50,6 +50,7 @@ private FunctionRegistry() { /** * Registers a signature. * + * @param script The script to register the signature in. If script is null, will register this signature globally. * @param signature The signature to register. * @throws SkriptAPIException if a function with the same name and parameters is already registered * in this namespace. @@ -95,7 +96,7 @@ public static void registerFunction(@NotNull Function function) { /** * Registers a function. * - * @param script The script to register the function in. + * @param script The script to register the function in. If script is null, will register this function globally. * @param function The function to register. * @throws SkriptAPIException if the function name is invalid or if * a function with the same name and parameters is already registered @@ -138,6 +139,12 @@ private static void alreadyRegisteredError(String name, FunctionIdentifier ident /** * Checks if a signature with the given name and arguments exists in the given script. * + *
    + *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code args} is null or empty, + * the first function with the same name as the {@code name} param will be returned.
  • + *
+ * * @param script The script to check in. * @param name The name of the function. * @param args The types of the arguments of the function. @@ -180,6 +187,12 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu /** * Gets a function from a script. If no local function is found, checks for global functions. * + *
    + *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code args} is null or empty, + * the first function with the same name as the {@code name} param will be returned.
  • + *
+ * * @param script The script to get the function from. * @param name The name of the function. * @param args The types of the arguments of the function. From ec8325c8d14c80c3c614cc1b9379119bf79510c9 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:35:09 +0200 Subject: [PATCH 07/19] add function unloading --- .../lang/function/FunctionRegistry.java | 67 ++++++++++++++----- .../njol/skript/lang/function/Functions.java | 10 +-- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 4f669b2dcf4..169f26c82be 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -50,12 +50,12 @@ private FunctionRegistry() { /** * Registers a signature. * - * @param script The script to register the signature in. If script is null, will register this signature globally. + * @param script The script to register the signature in. If script is null, will register this signature globally. * @param signature The signature to register. * @throws SkriptAPIException if a function with the same name and parameters is already registered * in this namespace. */ - public static void registerSignature(@Nullable String script, @NotNull Signature signature) { + public static void register(@Nullable String script, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); // namespace @@ -89,20 +89,20 @@ public static void registerSignature(@Nullable String script, @NotNull Signature * a function with the same name and parameters is already registered * in this namespace. */ - public static void registerFunction(@NotNull Function function) { - registerFunction(null, function); + public static void register(@NotNull Function function) { + register(null, function); } /** * Registers a function. * - * @param script The script to register the function in. If script is null, will register this function globally. + * @param script The script to register the function in. If script is null, will register this function globally. * @param function The function to register. * @throws SkriptAPIException if the function name is invalid or if * a function with the same name and parameters is already registered * in this namespace. */ - public static void registerFunction(@Nullable String script, @NotNull Function function) { + public static void register(@Nullable String script, @NotNull Function function) { Preconditions.checkNotNull(function, "function is null"); String name = function.getName(); @@ -118,7 +118,7 @@ public static void registerFunction(@Nullable String script, @NotNull Function * * @param script The script to check in. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return True if a signature with the given name and argument types exists in the script, false otherwise. */ public static boolean signatureExists(@Nullable String script, @NotNull String name, Class... args) { @@ -161,7 +161,7 @@ public static boolean signatureExists(@Nullable String script, @NotNull String n /** * Checks if a function with the given name and arguments exists in the namespace. * - * @param namespace The namespace to check in. + * @param namespace The namespace to check in. * @param identifier The identifier of the function. * @return True if a function with the given name and arguments exists in the namespace, false otherwise. */ @@ -194,8 +194,8 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu * * * @param script The script to get the function from. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return The function with the given name and argument types, or null if no such function exists. */ public static Function function(@Nullable String script, @NotNull String name, Class... args) { @@ -214,7 +214,7 @@ public static Function function(@Nullable String script, @NotNull String name * Gets a function from a namespace. * * @param namespace The namespace to get the function from. - * @param provided The provided identifier of the function. + * @param provided The provided identifier of the function. * @return The function with the given name and argument types, or null if no such function exists. */ private static Function function(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { @@ -269,8 +269,8 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct * * * @param script The script to get the function from. If null, only global functions will be checked. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ public static Signature signature(@Nullable String script, @NotNull String name, Class... args) { @@ -289,7 +289,7 @@ public static Signature signature(@Nullable String script, @NotNull String na * Gets the signature for a function with the given name and arguments. * * @param namespace The namespace to get the function from. - * @param provided The provided identifier of the function. + * @param provided The provided identifier of the function. * @return The signature for the function with the given name and argument types, or null if no such signature exists * in the specified namespace. */ @@ -332,7 +332,7 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun * @return A list of candidates for the provided function. */ private static @NotNull Set candidates(@NotNull FunctionIdentifier provided, - Set existing) { + Set existing) { Set candidates = new HashSet<>(); candidates: @@ -397,6 +397,39 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun return candidates; } + /** + * Removes a function's signature from the registry. + * + * @param signature The signature to remove. + */ + public static void remove(Signature signature) { + String name = signature.getName(); + FunctionIdentifier identifier = FunctionIdentifier.of(signature); + + for (Namespace namespace : identifiers.keySet()) { + if (!identifiers.containsKey(namespace)) { + continue; + } + + Map> nameToIdentifiers = identifiers.get(namespace); + + if (!nameToIdentifiers.containsKey(name)) { + continue; + } + + Set identifiers = nameToIdentifiers.get(name); + for (FunctionIdentifier other : new HashSet<>(identifiers)) { + if (Arrays.equals(identifier.args, other.args) && other.local == signature.isLocal()) { + identifiers.remove(other); + nameToIdentifiers.put(name, identifiers); + + FunctionRegistry.identifiers.put(namespace, nameToIdentifiers); + return; + } + } + } + } + /** * Scope of functions in namespace. */ diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index 1071f6e1ec0..72eb0c858b3 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -65,7 +65,7 @@ public static JavaFunction registerFunction(JavaFunction function) { javaNamespace.addFunction(function); globalFunctions.put(function.getName(), javaNamespace); - FunctionRegistry.registerFunction(function); + FunctionRegistry.register(function); return function; } @@ -102,7 +102,7 @@ public static JavaFunction registerFunction(JavaFunction function) { namespace.addFunction(function); } - FunctionRegistry.registerFunction(script.getConfig().getFileName(), function); + FunctionRegistry.register(script.getConfig().getFileName(), function); return function; } @@ -179,9 +179,9 @@ public static JavaFunction registerFunction(JavaFunction function) { globalFunctions.put(signature.name, namespace); if (signature.local) { - FunctionRegistry.registerSignature(signature.script, signature); + FunctionRegistry.register(signature.script, signature); } else { - FunctionRegistry.registerSignature(null, signature); + FunctionRegistry.register(null, signature); } Skript.debug("Registered function signature: " + signature.name); @@ -348,6 +348,8 @@ public static int clearFunctions(String script) { } public static void unregisterFunction(Signature signature) { + FunctionRegistry.remove(signature); + Iterator namespaceIterator = namespaces.values().iterator(); while (namespaceIterator.hasNext()) { Namespace namespace = namespaceIterator.next(); From aae534150c4865f52e74e537063d543c65ceb4a1 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:04:03 +0200 Subject: [PATCH 08/19] fix error placement --- .../njol/skript/lang/function/DynamicFunctionReference.java | 4 ++++ .../java/ch/njol/skript/lang/function/FunctionReference.java | 5 +++++ .../java/ch/njol/skript/lang/function/FunctionRegistry.java | 2 -- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index db973c023c8..5d032dd4234 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -1,6 +1,7 @@ package ch.njol.skript.lang.function; import ch.njol.skript.ScriptLoader; +import ch.njol.skript.Skript; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionList; import ch.njol.skript.lang.util.common.AnyNamed; @@ -62,6 +63,9 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { //noinspection unchecked function = (Function) FunctionRegistry.function(null, name); } + if (function == null) { + Skript.error("Skript cannot determine which dynamic function named '%s' to call.".formatted(name)); + } this.resolved = function != null; this.function = new WeakReference<>(function); if (resolved) { diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 55bd4e929e9..d4c53eb076e 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -306,6 +306,11 @@ private Function getRegisteredFunction() { function = FunctionRegistry.function(script, functionName); } + if (function == null) { + Skript.error("Skript cannot determine which function named '%s' to call.".formatted(functionName)); + return null; + } + return function; } diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 169f26c82be..48260f15677 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -252,8 +252,6 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct Skript.debug("Failed to match an exact function for '%s'".formatted(provided.name)); Skript.debug("Identifier: %s".formatted(provided)); Skript.debug("Options: %s".formatted(options)); - Skript.error(("Skript cannot determine which function named '%s' to call. " + - "Try clarifying the type of the arguments.").formatted(provided.name)); return null; } } From 3229c094180c29daa4745f375da5891f6aa1f719 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:22:01 +0200 Subject: [PATCH 09/19] improve error & fix reloading --- .../function/DynamicFunctionReference.java | 3 +- .../lang/function/FunctionReference.java | 7 +-- .../lang/function/FunctionRegistry.java | 43 +++++++++++++------ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index 5d032dd4234..3106efb7fb3 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -64,7 +64,8 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { function = (Function) FunctionRegistry.function(null, name); } if (function == null) { - Skript.error("Skript cannot determine which dynamic function named '%s' to call.".formatted(name)); + Skript.error(("Skript cannot determine which function named '%s' to call. " + + "Try clarifying the type of the arguments using the value within expression.").formatted(name)); } this.resolved = function != null; this.function = new WeakReference<>(function); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index d4c53eb076e..e2bd0afbf40 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -278,7 +278,7 @@ private Signature getRegisteredSignature() { parseParameters(); Skript.debug("Getting signature for '%s' with types %s", - functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getName).toArray())); + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); // if we can't find a signature based on param types, just use the function name @@ -297,7 +297,7 @@ private Function getRegisteredFunction() { parseParameters(); Skript.debug("Getting function '%s' with types %s", - functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getName).toArray())); + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Function function = FunctionRegistry.function(script, functionName, parameterTypes); // if we can't find a function based on param types, just use the function name @@ -307,7 +307,8 @@ private Function getRegisteredFunction() { } if (function == null) { - Skript.error("Skript cannot determine which function named '%s' to call.".formatted(functionName)); + Skript.error(("Skript cannot determine which function named '%s' to call. " + + "Try clarifying the type of the arguments using the value within expression.").formatted(functionName)); return null; } diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 48260f15677..20a8b115bbe 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -298,7 +298,7 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); if (!javaIdentifiers.containsKey(provided.name)) { - Skript.debug("No functions named '%s' exist in the '%s' namespace", namespace.name, provided.name); + Skript.debug("No signatures named '%s' exist in the '%s' namespace", provided.name, namespace.name); return null; } @@ -316,8 +316,6 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun Skript.debug("Failed to match an exact signature for '%s'", provided.name); Skript.debug("Identifier: %s", provided); Skript.debug("Options: %s", options); - Skript.error(("Skript cannot determine which function named '%s' to call. " + - "Try clarifying the type of the arguments.").formatted(provided.name)); return null; } } @@ -329,8 +327,10 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun * @param existing The existing functions with the same name. * @return A list of candidates for the provided function. */ - private static @NotNull Set candidates(@NotNull FunctionIdentifier provided, - Set existing) { + private static @NotNull Set candidates( + @NotNull FunctionIdentifier provided, + Set existing + ) { Set candidates = new HashSet<>(); candidates: @@ -415,15 +415,32 @@ public static void remove(Signature signature) { continue; } - Set identifiers = nameToIdentifiers.get(name); - for (FunctionIdentifier other : new HashSet<>(identifiers)) { - if (Arrays.equals(identifier.args, other.args) && other.local == signature.isLocal()) { - identifiers.remove(other); - nameToIdentifiers.put(name, identifiers); - - FunctionRegistry.identifiers.put(namespace, nameToIdentifiers); - return; + Set ids = nameToIdentifiers.get(name); + for (FunctionIdentifier other : new HashSet<>(ids)) { + if (!Arrays.equals(identifier.args, other.args) || other.local != signature.isLocal()) { + continue; } + + nameToIdentifiers.computeIfPresent(name, (k, set) -> { + set.remove(other); + Skript.debug("Removed identifier '%s' from %s", other, namespace); + return set.isEmpty() ? null : set; + }); + identifiers.put(namespace, nameToIdentifiers); + + functions.computeIfPresent(namespace, (ns, map) -> { + map.remove(other); + Skript.debug("Removed function '%s' from %s", other, namespace); + return map.isEmpty() ? null : map; + }); + + signatures.computeIfPresent(namespace, (ns, map) -> { + map.remove(other); + Skript.debug("Removed signature '%s' from %s", other, namespace); + return map.isEmpty() ? null : map; + }); + + return; } } } From 8b4978418f8ecd79774bd17febc2c9eae62c1bc3 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 3 Apr 2025 01:14:49 +0200 Subject: [PATCH 10/19] add all the testing --- .../function/DynamicFunctionReference.java | 2 + .../lang/function/FunctionReference.java | 23 +- .../lang/function/FunctionRegistryTest.java | 269 ++++++++++++++++++ .../skript/tests/misc/function overloading.sk | 20 +- 4 files changed, 300 insertions(+), 14 deletions(-) create mode 100644 src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index 3106efb7fb3..60215ebf739 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -57,6 +57,8 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { this.name = name; Function function; if (source != null) { + // will return the first function found that matches name. + // TODO: add a way to specify param types //noinspection unchecked function = (Function) FunctionRegistry.function(source.getConfig().getFileName(), name); } else { diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index e2bd0afbf40..fd569b40524 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -27,6 +27,9 @@ */ public class FunctionReference implements Contract, Executable { + private static final String AMBIGUOUS_ERROR = "Skript cannot determine which function named '%s' to call. " + + "Try clarifying the type of the arguments using the 'value within' expression."; + /** * Name of function that is called, for logging purposes. */ @@ -281,10 +284,11 @@ private Signature getRegisteredSignature() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); - // if we can't find a signature based on param types, just use the function name - // and find whatever matches first - if (sign == null) { - sign = FunctionRegistry.signature(script, functionName); + // if we can't find a signature based on param types, + // to avoid weird behaviour, just error + if (function == null) { + Skript.error(AMBIGUOUS_ERROR.formatted(functionName)); + return null; } return sign; @@ -300,15 +304,10 @@ private Function getRegisteredFunction() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Function function = FunctionRegistry.function(script, functionName, parameterTypes); - // if we can't find a function based on param types, just use the function name - // and find whatever matches first - if (function == null) { - function = FunctionRegistry.function(script, functionName); - } - + // if we can't find a function based on param types, + // to avoid weird behaviour, just error if (function == null) { - Skript.error(("Skript cannot determine which function named '%s' to call. " + - "Try clarifying the type of the arguments using the value within expression.").formatted(functionName)); + Skript.error(AMBIGUOUS_ERROR.formatted(functionName)); return null; } diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java new file mode 100644 index 00000000000..0e4c375cfa6 --- /dev/null +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -0,0 +1,269 @@ +package ch.njol.skript.lang.function; + +import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.registrations.DefaultClasses; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class FunctionRegistryTest { + + private static final String FUNCTION_NAME = "testFunctionRegistry"; + private static final String TEST_SCRIPT = "test"; + + private static final Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, null, DefaultClasses.BOOLEAN, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + @Test + public void testSimpleMultipleRegistrationsFunction() { + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.register(TEST_FUNCTION); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + + assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION)); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + } + + @Test + public void testSimpleRegisterRemoveRegisterGlobal() { + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.register(TEST_FUNCTION); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.register(TEST_FUNCTION); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + } + + @Test + public void testSimpleRegisterRemoveRegisterLocal() { + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION); + + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION); + FunctionRegistry.register(TEST_FUNCTION); + + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + } + + private static final Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.BOOLEAN, false, null) + }, DefaultClasses.BOOLEAN, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + private static final Function TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.NUMBER, false, null) + }, DefaultClasses.BOOLEAN, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + @Test + public void testMultipleRegistrations() { + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_FUNCTION_B); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_FUNCTION_N); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION_B)); + assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION_N)); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); + } + + @Test + public void testRegisterRemoveRegisterGlobal() { + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_FUNCTION_B); + + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_FUNCTION_N); + + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + } + + @Test + public void testRegisterRemoveRegisterLocal() { + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_B); + + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_N); + + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); + + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_N); + FunctionRegistry.register(null, TEST_FUNCTION_B); + + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + + FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + } + + // todo add test for two same functions in diff namespaces + +} diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index 1370b5f8a1d..fbf5d2dd4a8 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -1,3 +1,19 @@ +local function overloaded(x: int) :: int: + return 1 + +local function overloaded(x: text) :: int: + return 2 + +test "function overloading with 1 parameter": + set {_x} to 1 + + parse: + overloaded({_x}) + + assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments using the 'value within' expression." + + assert overloaded(number within {_x}) = 1 + function overloaded(x: int, y: int) :: int: return 1 @@ -10,7 +26,7 @@ function overloaded(x: text, y: text) :: int: function overloaded(x: text, y: int, z: int) :: int: return 4 -test "function overloading": +test "function overloading with 2 parameters": # literal overloading assert overloaded(1, 2) = 1 @@ -26,4 +42,4 @@ test "function overloading": parse: overloaded({_y}, {_x}) - assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments." + assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments using the 'value within' expression." \ No newline at end of file From 7bf3846ac76b23350316b11b14238084cebc13d0 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:26:10 +0200 Subject: [PATCH 11/19] add way to add local javafunctions --- .../ch/njol/skript/lang/function/JavaFunction.java | 10 +++++++++- .../njol/skript/lang/function/SimpleJavaFunction.java | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/lang/function/JavaFunction.java b/src/main/java/ch/njol/skript/lang/function/JavaFunction.java index 74c19cb8e20..fdc334644e7 100644 --- a/src/main/java/ch/njol/skript/lang/function/JavaFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/JavaFunction.java @@ -17,8 +17,16 @@ public JavaFunction(String name, Parameter[] parameters, ClassInfo returnT this(name, parameters, returnType, single, null); } + public JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { + this(name, parameters, returnType, single, local, null); + } + public JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, @Nullable Contract contract) { - this(new Signature<>("none", name, parameters, false, returnType, single, Thread.currentThread().getStackTrace()[3].getClassName(), contract)); + this(name, parameters, returnType, single, false, contract); + } + + public JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local, @Nullable Contract contract) { + this(new Signature<>("none", name, parameters, local, returnType, single, Thread.currentThread().getStackTrace()[3].getClassName(), contract)); } @Override diff --git a/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java b/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java index 32d264ac361..f0c89da61c9 100644 --- a/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java @@ -19,6 +19,10 @@ public SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo r super(name, parameters, returnType, single); } + public SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { + super(name, parameters, returnType, single, local); + } + public SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, Contract contract) { super(name, parameters, returnType, single, contract); } From 78cf7d8d6b0630aef7311e19111164a4e3e90e84 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:47:16 +0200 Subject: [PATCH 12/19] improved reliability with java functions --- .../lang/function/FunctionReference.java | 17 ++++-- .../lang/function/FunctionRegistry.java | 41 ++++++++----- .../lang/function/FunctionRegistryTest.java | 60 +++++++++++++------ .../skript/tests/misc/function overloading.sk | 4 +- 4 files changed, 84 insertions(+), 38 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index fd569b40524..ad0cb1eafd6 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -284,9 +284,13 @@ private Signature getRegisteredSignature() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); - // if we can't find a signature based on param types, - // to avoid weird behaviour, just error - if (function == null) { + + // if we can't find a signature based on param types, try to match any function + if (sign == null) { + sign = FunctionRegistry.signature(script, functionName); + } + + if (sign == null) { Skript.error(AMBIGUOUS_ERROR.formatted(functionName)); return null; } @@ -304,8 +308,11 @@ private Function getRegisteredFunction() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); Function function = FunctionRegistry.function(script, functionName, parameterTypes); - // if we can't find a function based on param types, - // to avoid weird behaviour, just error + // if we can't find a signature based on param types, try to match any function + if (function == null) { + function = FunctionRegistry.function(script, functionName); + } + if (function == null) { Skript.error(AMBIGUOUS_ERROR.formatted(functionName)); return null; diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 20a8b115bbe..008c91923cc 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -57,6 +57,7 @@ private FunctionRegistry() { */ public static void register(@Nullable String script, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); + Skript.debug("Registering signature '" + signature.getName() + "'"); // namespace Namespace namespace = GLOBAL_NAMESPACE; @@ -104,6 +105,7 @@ public static void register(@NotNull Function function) { */ public static void register(@Nullable String script, @NotNull Function function) { Preconditions.checkNotNull(function, "function is null"); + Skript.debug("Registering function '" + function.getName() + "'"); String name = function.getName(); if (!name.matches(FUNCTION_NAME_PATTERN)) { @@ -127,6 +129,7 @@ public static void register(@Nullable String script, @NotNull Function functi if (existing != null) { alreadyRegisteredError(name, identifier, namespace); } + functions.put(namespace, identifierToFunction); } @@ -312,7 +315,6 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun return signatures.get(namespace).get(candidates.stream().findAny().orElse(null)); } else { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); - Skript.debug("Failed to match an exact signature for '%s'", provided.name); Skript.debug("Identifier: %s", provided); Skript.debug("Options: %s", options); @@ -340,11 +342,11 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun continue; } - // if the provided arguments are null, that means the function - // 1. has no arguments. return the first name match. - // 2. has an unknown amount of arguments. return the first name match. + // if we have no provided arguments, we can match any function + // we can skip the rest of the checks if (provided.args == null || provided.args.length == 0) { - return Set.of(candidate); + candidates.add(candidate); + continue; } // if argument counts are not possible, skip @@ -365,7 +367,10 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun if (candidates.isEmpty()) { return Set.of(); - } else if (candidates.size() == 1) { + } else if (candidates.size() == 1 || provided.args == null || provided.args.length == 0) { + // if there is only one candidate, + // or we should match any function (provided.args == null || provided.args.length == 0), + // then return without trying to convert return candidates; } @@ -417,26 +422,29 @@ public static void remove(Signature signature) { Set ids = nameToIdentifiers.get(name); for (FunctionIdentifier other : new HashSet<>(ids)) { - if (!Arrays.equals(identifier.args, other.args) || other.local != signature.isLocal()) { + if (!Arrays.equals(identifier.args, other.args)) { continue; } nameToIdentifiers.computeIfPresent(name, (k, set) -> { - set.remove(other); - Skript.debug("Removed identifier '%s' from %s", other, namespace); + if (set.remove(other)) { + Skript.debug("Removed identifier '%s' from %s", other, namespace); + } return set.isEmpty() ? null : set; }); identifiers.put(namespace, nameToIdentifiers); functions.computeIfPresent(namespace, (ns, map) -> { - map.remove(other); - Skript.debug("Removed function '%s' from %s", other, namespace); + if (map.remove(other) != null) { + Skript.debug("Removed function '%s' from %s", other, namespace); + } return map.isEmpty() ? null : map; }); signatures.computeIfPresent(namespace, (ns, map) -> { - map.remove(other); - Skript.debug("Removed signature '%s' from %s", other, namespace); + if (map.remove(other) != null) { + Skript.debug("Removed signature '%s' from %s", other, namespace); + } return map.isEmpty() ? null : map; }); @@ -537,9 +545,14 @@ public boolean equals(Object obj) { return false; } + if (local != other.local) { + return false; + } + for (int i = 0; i < args.length; i++) { - if (args[i] != other.args[i]) + if (args[i] != other.args[i]) { return false; + } } return true; diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index 0e4c375cfa6..5374f5c5c96 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -12,7 +12,8 @@ public class FunctionRegistryTest { private static final String FUNCTION_NAME = "testFunctionRegistry"; private static final String TEST_SCRIPT = "test"; - private static final Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, null, DefaultClasses.BOOLEAN, true) { + private static final Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[0], + DefaultClasses.BOOLEAN, true, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { return new Boolean[]{true}; @@ -91,7 +92,7 @@ public void testSimpleRegisterRemoveRegisterLocal() { FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION); FunctionRegistry.register(TEST_FUNCTION); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); @@ -206,8 +207,31 @@ public void testRegisterRemoveRegisterGlobal() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); } + private static final Function LOCAL_TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.BOOLEAN, false, null) + }, DefaultClasses.BOOLEAN, true, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + private static final Function LOCAL_TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.NUMBER, false, null) + }, DefaultClasses.BOOLEAN, true, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + @Test public void testRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); @@ -217,15 +241,15 @@ public void testRegisterRemoveRegisterLocal() { assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_B); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); @@ -233,15 +257,15 @@ public void testRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_N); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); @@ -250,18 +274,18 @@ public void testRegisterRemoveRegisterLocal() { assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION_N); - FunctionRegistry.register(null, TEST_FUNCTION_B); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); + FunctionRegistry.register(null, LOCAL_TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); - FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); - FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); } // todo add test for two same functions in diff namespaces diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index fbf5d2dd4a8..f1674b38bd4 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -5,13 +5,15 @@ local function overloaded(x: text) :: int: return 2 test "function overloading with 1 parameter": - set {_x} to 1 + set {_x} to true parse: overloaded({_x}) assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments using the 'value within' expression." + set {_x} to 1 + assert overloaded(number within {_x}) = 1 function overloaded(x: int, y: int) :: int: From f11dc403627dc6f8db652ba5ddd5748e1b5a42e5 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 7 Apr 2025 18:08:33 +0200 Subject: [PATCH 13/19] improve global/local conflict resolution --- .../lang/function/FunctionRegistry.java | 28 ++++++++-------- .../lang/function/FunctionRegistryTest.java | 32 ++++++++++++------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 008c91923cc..bc16f05ea1f 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -72,7 +72,7 @@ public static void register(@Nullable String script, @NotNull Signature signa Set identifiersWithName = javaIdentifiers.getOrDefault(identifier.name, new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - alreadyRegisteredError(identifier.name, identifier, namespace); + return; } javaIdentifiers.put(identifier.name, identifiersWithName); identifiers.put(namespace, javaIdentifiers); @@ -155,10 +155,10 @@ private static void alreadyRegisteredError(String name, FunctionIdentifier ident */ public static boolean signatureExists(@Nullable String script, @NotNull String name, Class... args) { if (script == null) { - return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - return signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, args)); + return signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, true, args)); } /** @@ -179,7 +179,7 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu } for (FunctionIdentifier other : javaIdentifiers.get(identifier.name)) { - if (Arrays.equals(identifier.args, other.args)) { + if (identifier == other) { return true; } } @@ -203,12 +203,12 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu */ public static Function function(@Nullable String script, @NotNull String name, Class... args) { if (script == null) { - return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Function function = function(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, args)); + Function function = function(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, true, args)); if (function == null) { - return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } return function; } @@ -276,12 +276,12 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct */ public static Signature signature(@Nullable String script, @NotNull String name, Class... args) { if (script == null) { - return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Signature signature = signature(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, args)); + Signature signature = signature(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, true, args)); if (signature == null) { - return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, args)); + return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } return signature; } @@ -487,13 +487,13 @@ private record FunctionIdentifier(@NotNull String name, boolean local, int minAr * @param args The types of the arguments. * @return The identifier for the signature. */ - private static FunctionIdentifier of(@NotNull String name, Class... args) { + private static FunctionIdentifier of(@NotNull String name, boolean local, Class... args) { Preconditions.checkNotNull(name, "name is null"); if (args == null) { - return new FunctionIdentifier(name, false, 0); + return new FunctionIdentifier(name, local, 0); } - return new FunctionIdentifier(name, false, args.length, args); + return new FunctionIdentifier(name, local, args.length, args); } /** @@ -528,7 +528,7 @@ private static FunctionIdentifier of(@NotNull Signature signature) { @Override public int hashCode() { - return Objects.hash(name, Arrays.hashCode(args)); + return Objects.hash(name, local, Arrays.hashCode(args)); } @Override diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index 5374f5c5c96..5a14607726f 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -13,7 +13,7 @@ public class FunctionRegistryTest { private static final String TEST_SCRIPT = "test"; private static final Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[0], - DefaultClasses.BOOLEAN, true, true) { + DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { return new Boolean[]{true}; @@ -68,6 +68,14 @@ public void testSimpleRegisterRemoveRegisterGlobal() { FunctionRegistry.remove(TEST_FUNCTION.getSignature()); } + private static final Function LOCAL_TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[0], + DefaultClasses.BOOLEAN, true, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + @Test public void testSimpleRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); @@ -75,31 +83,31 @@ public void testSimpleRegisterRemoveRegisterLocal() { assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); - FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); - FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); - FunctionRegistry.register(TEST_SCRIPT, TEST_FUNCTION); - FunctionRegistry.register(TEST_FUNCTION); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); + FunctionRegistry.register(LOCAL_TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); - FunctionRegistry.remove(TEST_FUNCTION.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); } private static final Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, From f303ff7216df757c09f054666a51d54fba8e1be8 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 8 Apr 2025 00:45:56 +0200 Subject: [PATCH 14/19] fix tests --- .../lang/function/FunctionRegistry.java | 9 ++++--- .../lang/function/FunctionRegistryTest.java | 25 ++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index bc16f05ea1f..c889e6f8372 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -3,6 +3,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import com.google.common.base.Preconditions; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.lang.converter.Converters; @@ -15,6 +16,7 @@ * * @author Efnilite */ +@Internal // for now final class FunctionRegistry { private FunctionRegistry() { @@ -179,7 +181,7 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu } for (FunctionIdentifier other : javaIdentifiers.get(identifier.name)) { - if (identifier == other) { + if (identifier.equals(other)) { return true; } } @@ -515,10 +517,11 @@ private static FunctionIdentifier of(@NotNull Signature signature) { optionalArgs++; } + Class type = param.getType().getC(); if (param.isSingleValue()) { - parameters[i] = param.getType().getC(); + parameters[i] = type; } else { - parameters[i] = param.getType().getC().arrayType(); // for functions with Object[] arguments + parameters[i] = type.arrayType(); } } diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index 5a14607726f..f9e65698c4f 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -103,16 +103,17 @@ public void testSimpleRegisterRemoveRegisterLocal() { assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); - assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); +// assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); +// assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); +// assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); } private static final Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, false, null) + new Parameter<>("a", DefaultClasses.BOOLEAN, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -122,7 +123,7 @@ public void testSimpleRegisterRemoveRegisterLocal() { private static final Function TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[]{ - new Parameter<>("a", DefaultClasses.NUMBER, false, null) + new Parameter<>("a", DefaultClasses.NUMBER, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -146,7 +147,7 @@ public void testMultipleRegistrations() { assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); +// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); @@ -187,7 +188,7 @@ public void testRegisterRemoveRegisterGlobal() { assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); +// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); @@ -196,13 +197,13 @@ public void testRegisterRemoveRegisterGlobal() { assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); +// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); +// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); @@ -222,7 +223,7 @@ public void testRegisterRemoveRegisterGlobal() { private static final Function LOCAL_TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, false, null) + new Parameter<>("a", DefaultClasses.BOOLEAN, true, null) }, DefaultClasses.BOOLEAN, true, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -232,7 +233,7 @@ public void testRegisterRemoveRegisterGlobal() { private static final Function LOCAL_TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[]{ - new Parameter<>("a", DefaultClasses.NUMBER, false, null) + new Parameter<>("a", DefaultClasses.NUMBER, true, null) }, DefaultClasses.BOOLEAN, true, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -288,7 +289,7 @@ public void testRegisterRemoveRegisterLocal() { assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); +// assertTrue(FunctionRegistry.signatureExists(FUNCTION_NAME, Boolean.class)); assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); From dd5e6c769efe79e95cc66a179c8721d39b8faf25 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 8 Apr 2025 10:40:56 +0200 Subject: [PATCH 15/19] woo tests run --- .../lang/function/FunctionRegistry.java | 17 +- .../lang/function/FunctionRegistryTest.java | 148 +++++++++++++----- 2 files changed, 111 insertions(+), 54 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index c889e6f8372..b19a892be64 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -239,12 +239,6 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct return null; } - if (existing.size() == 1) { - FunctionIdentifier identifier = existing.stream().findAny().orElse(null); - Skript.debug("Exact match for '%s': %s".formatted(provided.name, identifier)); - return identifierFunctionMap.get(identifier); - } - Set candidates = candidates(provided, existing); if (candidates.isEmpty()) { Skript.debug("Failed to find a function for '%s'".formatted(provided.name)); @@ -422,9 +416,8 @@ public static void remove(Signature signature) { continue; } - Set ids = nameToIdentifiers.get(name); - for (FunctionIdentifier other : new HashSet<>(ids)) { - if (!Arrays.equals(identifier.args, other.args)) { + for (FunctionIdentifier other : nameToIdentifiers.get(name)) { + if (!identifier.equals(other)) { continue; } @@ -480,7 +473,7 @@ private record Namespace(@NotNull Scope scope, @Nullable String name) { * @param name The name of the function. * @param args The arguments of the function. */ - private record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, Class... args) { + record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, Class... args) { /** * Returns the identifier for the given arguments. @@ -489,7 +482,7 @@ private record FunctionIdentifier(@NotNull String name, boolean local, int minAr * @param args The types of the arguments. * @return The identifier for the signature. */ - private static FunctionIdentifier of(@NotNull String name, boolean local, Class... args) { + static FunctionIdentifier of(@NotNull String name, boolean local, Class... args) { Preconditions.checkNotNull(name, "name is null"); if (args == null) { @@ -504,7 +497,7 @@ private static FunctionIdentifier of(@NotNull String name, boolean local, Class< * @param signature The signature to get the identifier for. * @return The identifier for the signature. */ - private static FunctionIdentifier of(@NotNull Signature signature) { + static FunctionIdentifier of(@NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); Parameter[] signatureParams = signature.parameters; diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index f9e65698c4f..fc9f01b4f11 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -1,9 +1,14 @@ package ch.njol.skript.lang.function; +import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.lang.function.FunctionRegistry.FunctionIdentifier; +import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.registrations.DefaultClasses; import org.jetbrains.annotations.Nullable; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; import static org.junit.Assert.*; @@ -98,17 +103,17 @@ public void testSimpleRegisterRemoveRegisterLocal() { assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); - FunctionRegistry.register(LOCAL_TEST_FUNCTION); + FunctionRegistry.register(TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); -// assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); -// assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); -// assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); - FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); + FunctionRegistry.remove(TEST_FUNCTION.getSignature()); } private static final Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, @@ -147,7 +152,7 @@ public void testMultipleRegistrations() { assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); -// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); @@ -188,7 +193,7 @@ public void testRegisterRemoveRegisterGlobal() { assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); -// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); @@ -197,13 +202,13 @@ public void testRegisterRemoveRegisterGlobal() { assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); -// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); -// assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); @@ -243,60 +248,119 @@ public void testRegisterRemoveRegisterGlobal() { @Test public void testRegisterRemoveRegisterLocal() { - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - - FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); - - assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); + + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); + FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); + FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); - FunctionRegistry.register(null, LOCAL_TEST_FUNCTION_B); + FunctionRegistry.register(null, TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); -// assertTrue(FunctionRegistry.signatureExists(FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); - FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); + FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); } // todo add test for two same functions in diff namespaces + @Test + public void testIdentifierEmptyOf() { + FunctionIdentifier identifier = FunctionIdentifier.of(FUNCTION_NAME, true); + + assertEquals(FUNCTION_NAME, identifier.name()); + assertTrue(identifier.local()); + assertEquals(0, identifier.minArgCount()); + assertArrayEquals(new Class[0], identifier.args()); + + assertEquals(FunctionIdentifier.of(FUNCTION_NAME, true), identifier); + } + + @Test + public void testIdentifierOf() { + FunctionIdentifier identifier = FunctionIdentifier.of(FUNCTION_NAME, true, Boolean.class, Number.class); + + assertEquals(FUNCTION_NAME, identifier.name()); + assertTrue(identifier.local()); + assertEquals(2, identifier.minArgCount()); + assertArrayEquals(new Class[]{Boolean.class, Number.class}, identifier.args()); + + assertEquals(FunctionIdentifier.of(FUNCTION_NAME, true, Boolean.class, Number.class), identifier); + } + + + @Test + public void testIdentifierSignatureOf() { + SimpleJavaFunction function = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new Parameter<>("b", DefaultClasses.NUMBER, false, new SimpleLiteral(1, true)) + }, DefaultClasses.BOOLEAN, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); + + assertEquals(FUNCTION_NAME, identifier.name()); + assertFalse(identifier.local()); + assertEquals(1, identifier.minArgCount()); + assertArrayEquals(new Class[]{Boolean.class, Number[].class}, identifier.args()); + + SimpleJavaFunction function2 = new SimpleJavaFunction<>(FUNCTION_NAME, + new Parameter[]{ + new Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new Parameter<>("b", DefaultClasses.NUMBER, false, null) + }, DefaultClasses.BOOLEAN, true) { + @Override + public Boolean @Nullable [] executeSimple(Object[][] params) { + return new Boolean[]{true}; + } + }; + + assertEquals(FunctionIdentifier.of(function2.getSignature()), identifier); + } + } From 7f34b519c5c36543652d7e9f5dab9dcb4b69c309 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 8 Apr 2025 10:42:20 +0200 Subject: [PATCH 16/19] cleanup --- .../ch/njol/skript/lang/function/FunctionRegistryTest.java | 3 --- src/test/skript/tests/misc/function overloading.sk | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index fc9f01b4f11..e263816d961 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -302,8 +302,6 @@ public void testRegisterRemoveRegisterLocal() { FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); } - // todo add test for two same functions in diff namespaces - @Test public void testIdentifierEmptyOf() { FunctionIdentifier identifier = FunctionIdentifier.of(FUNCTION_NAME, true); @@ -328,7 +326,6 @@ public void testIdentifierOf() { assertEquals(FunctionIdentifier.of(FUNCTION_NAME, true, Boolean.class, Number.class), identifier); } - @Test public void testIdentifierSignatureOf() { SimpleJavaFunction function = new SimpleJavaFunction<>(FUNCTION_NAME, diff --git a/src/test/skript/tests/misc/function overloading.sk b/src/test/skript/tests/misc/function overloading.sk index f1674b38bd4..b5406ae7f03 100644 --- a/src/test/skript/tests/misc/function overloading.sk +++ b/src/test/skript/tests/misc/function overloading.sk @@ -44,4 +44,4 @@ test "function overloading with 2 parameters": parse: overloaded({_y}, {_x}) - assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments using the 'value within' expression." \ No newline at end of file + assert last parse logs contains "Skript cannot determine which function named 'overloaded' to call. Try clarifying the type of the arguments using the 'value within' expression." From 42c1b2e110d9dd1b238af6621fe584abd93defef Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:26:28 +0200 Subject: [PATCH 17/19] address reviews --- .../function/DynamicFunctionReference.java | 3 +- .../lang/function/FunctionReference.java | 28 ++-- .../lang/function/FunctionRegistry.java | 157 ++++++++++-------- .../lang/function/FunctionRegistryTest.java | 3 - 4 files changed, 108 insertions(+), 83 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index 60215ebf739..d9cdfac5b35 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -66,8 +66,7 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { function = (Function) FunctionRegistry.function(null, name); } if (function == null) { - Skript.error(("Skript cannot determine which function named '%s' to call. " + - "Try clarifying the type of the arguments using the value within expression.").formatted(name)); + Skript.error(FunctionReference.AMBIGUOUS_ERROR.formatted(name)); } this.resolved = function != null; this.function = new WeakReference<>(function); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index ad0cb1eafd6..fa8ae7bb00d 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -27,7 +27,7 @@ */ public class FunctionReference implements Contract, Executable { - private static final String AMBIGUOUS_ERROR = "Skript cannot determine which function named '%s' to call. " + + static final String AMBIGUOUS_ERROR = "Skript cannot determine which function named '%s' to call. " + "Try clarifying the type of the arguments using the 'value within' expression."; /** @@ -40,7 +40,7 @@ public class FunctionReference implements Contract, Executable { * succeeds, this is not null. */ private @Nullable Signature signature; - + /** * Actual function reference. Null before the function is called for first * time. @@ -88,8 +88,8 @@ public class FunctionReference implements Contract, Executable { private Contract contract; public FunctionReference( - String functionName, @Nullable Node node, @Nullable String script, - @Nullable Class[] returnTypes, Expression[] params + String functionName, @Nullable Node node, @Nullable String script, + @Nullable Class[] returnTypes, Expression[] params ) { this.functionName = functionName; this.node = node; @@ -116,8 +116,9 @@ public boolean validateParameterArity(boolean first) { /** * Validates this function reference. Prints errors if needed. + * * @param first True if this is called while loading a script. False when - * this is called when the function signature changes. + * this is called when the function signature changes. * @return True if validation succeeded. */ public boolean validateFunction(boolean first) { @@ -169,7 +170,7 @@ public boolean validateFunction(boolean first) { single = sign.single; } else if (single && !sign.single) { Skript.error("The function '" + functionName + "' was redefined with a different, incompatible return type, but is still used in other script(s)." - + " These will continue to use the old version of the function until Skript restarts."); + + " These will continue to use the old version of the function until Skript restarts."); function = previousFunction; return false; } @@ -280,8 +281,10 @@ private void parseParameters() { private Signature getRegisteredSignature() { parseParameters(); - Skript.debug("Getting signature for '%s' with types %s", - functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + if (Skript.debug()) { + Skript.debug("Getting signature for '%s' with types %s", + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + } Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); @@ -299,13 +302,15 @@ private Signature getRegisteredSignature() { } /** - * Attempts to get this function's signature. + * Attempts to get this function's registered implementation. */ private Function getRegisteredFunction() { parseParameters(); - Skript.debug("Getting function '%s' with types %s", - functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + if (Skript.debug()) { + Skript.debug("Getting function '%s' with types %s", + functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); + } Function function = FunctionRegistry.function(script, functionName, parameterTypes); // if we can't find a signature based on param types, try to match any function @@ -394,6 +399,7 @@ public boolean isSingle(Expression... arguments) { /** * The contract is used in preference to the function for determining return type, etc. + * * @return The contract determining this function's parse-time hints, potentially this reference */ public Contract getContract() { diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index b19a892be64..728e30ad48f 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -52,40 +52,42 @@ private FunctionRegistry() { /** * Registers a signature. * - * @param script The script to register the signature in. If script is null, will register this signature globally. + * @param namespace The namespace to register the signature in. + * If namespace is null, will register this signature globally. + * Usually represents the path of the script this signature is registered in. * @param signature The signature to register. - * @throws SkriptAPIException if a function with the same name and parameters is already registered + * @throws SkriptAPIException if a signature with the same name and parameters is already registered * in this namespace. */ - public static void register(@Nullable String script, @NotNull Signature signature) { + public static void register(@Nullable String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature is null"); Skript.debug("Registering signature '" + signature.getName() + "'"); // namespace - Namespace namespace = GLOBAL_NAMESPACE; - if (script != null && signature.isLocal()) { - namespace = new Namespace(Scope.LOCAL, script); + Namespace ns = GLOBAL_NAMESPACE; + if (namespace != null && signature.isLocal()) { + ns = new Namespace(Scope.LOCAL, namespace); } FunctionIdentifier identifier = FunctionIdentifier.of(signature); // register - Map> javaIdentifiers = identifiers.getOrDefault(namespace, new HashMap<>()); + Map> javaIdentifiers = identifiers.getOrDefault(ns, new HashMap<>()); Set identifiersWithName = javaIdentifiers.getOrDefault(identifier.name, new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - return; + alreadyRegisteredError(signature.getName(), identifier, ns); } javaIdentifiers.put(identifier.name, identifiersWithName); - identifiers.put(namespace, javaIdentifiers); + identifiers.put(ns, javaIdentifiers); - Map> orDefault = signatures.getOrDefault(namespace, new HashMap<>()); + Map> orDefault = signatures.getOrDefault(ns, new HashMap<>()); orDefault.put(identifier, signature); - signatures.put(namespace, orDefault); + signatures.put(ns, orDefault); } /** - * Registers a function. + * Registers a global function. * * @param function The function to register. * @throws SkriptAPIException if the function name is invalid or if @@ -99,13 +101,15 @@ public static void register(@NotNull Function function) { /** * Registers a function. * - * @param script The script to register the function in. If script is null, will register this function globally. - * @param function The function to register. + * @param namespace The namespace to register the function in. + * If namespace is null, will register this function globally. + * Usually represents the path of the script this function is registered in. + * @param function The function to register. * @throws SkriptAPIException if the function name is invalid or if * a function with the same name and parameters is already registered * in this namespace. */ - public static void register(@Nullable String script, @NotNull Function function) { + public static void register(@Nullable String namespace, @NotNull Function function) { Preconditions.checkNotNull(function, "function is null"); Skript.debug("Registering function '" + function.getName() + "'"); @@ -115,24 +119,24 @@ public static void register(@Nullable String script, @NotNull Function functi } // namespace - Namespace namespace = GLOBAL_NAMESPACE; - if (script != null && function.getSignature().isLocal()) { - namespace = new Namespace(Scope.LOCAL, script); + Namespace ns = GLOBAL_NAMESPACE; + if (namespace != null && function.getSignature().isLocal()) { + ns = new Namespace(Scope.LOCAL, namespace); } FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); - if (!signatureExists(namespace, identifier)) { - register(script, function.getSignature()); + if (!signatureExists(ns, identifier)) { + register(namespace, function.getSignature()); } // register - Map> identifierToFunction = functions.getOrDefault(namespace, new HashMap<>()); + Map> identifierToFunction = functions.getOrDefault(ns, new HashMap<>()); Function existing = identifierToFunction.put(identifier, function); if (existing != null) { - alreadyRegisteredError(name, identifier, namespace); + alreadyRegisteredError(name, identifier, ns); } - functions.put(namespace, identifierToFunction); + functions.put(ns, identifierToFunction); } private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, Namespace namespace) { @@ -142,25 +146,26 @@ private static void alreadyRegisteredError(String name, FunctionIdentifier ident } /** - * Checks if a signature with the given name and arguments exists in the given script. + * Checks if a signature with the given name and arguments exists in the given namespace. * *
    - *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code namespace} is null, only global signatures will be checked.
  • *
  • If {@code args} is null or empty, * the first function with the same name as the {@code name} param will be returned.
  • *
* - * @param script The script to check in. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param namespace The namespace to check in. + * Usually represents the path of the script a signature is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return True if a signature with the given name and argument types exists in the script, false otherwise. */ - public static boolean signatureExists(@Nullable String script, @NotNull String name, Class... args) { - if (script == null) { + public static boolean signatureExists(@Nullable String namespace, @NotNull String name, Class... args) { + if (namespace == null) { return signatureExists(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - return signatureExists(new Namespace(Scope.LOCAL, script.toLowerCase()), FunctionIdentifier.of(name, true, args)); + return signatureExists(new Namespace(Scope.LOCAL, namespace.toLowerCase()), FunctionIdentifier.of(name, true, args)); } /** @@ -193,22 +198,23 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu * Gets a function from a script. If no local function is found, checks for global functions. * *
    - *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code namespace} is null, only global functions will be checked.
  • *
  • If {@code args} is null or empty, * the first function with the same name as the {@code name} param will be returned.
  • *
* - * @param script The script to get the function from. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return The function with the given name and argument types, or null if no such function exists. */ - public static Function function(@Nullable String script, @NotNull String name, Class... args) { - if (script == null) { + public static Function function(@Nullable String namespace, @NotNull String name, Class... args) { + if (namespace == null) { return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Function function = function(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, true, args)); + Function function = function(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); if (function == null) { return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } @@ -219,6 +225,7 @@ public static Function function(@Nullable String script, @NotNull String name * Gets a function from a namespace. * * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. * @param provided The provided identifier of the function. * @return The function with the given name and argument types, or null if no such function exists. */ @@ -260,22 +267,23 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct * checks for global functions. * *
    - *
  • If {@code script} is null, only global functions will be checked.
  • + *
  • If {@code namespace} is null, only global signatures will be checked.
  • *
  • If {@code args} is null or empty, * the first function with the same name as the {@code name} param will be returned.
  • *
* - * @param script The script to get the function from. If null, only global functions will be checked. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - public static Signature signature(@Nullable String script, @NotNull String name, Class... args) { - if (script == null) { + public static Signature signature(@Nullable String namespace, @NotNull String name, Class... args) { + if (namespace == null) { return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Signature signature = signature(new Namespace(Scope.LOCAL, script), FunctionIdentifier.of(name, true, args)); + Signature signature = signature(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); if (signature == null) { return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } @@ -319,7 +327,7 @@ private static Signature signature(@NotNull Namespace namespace, @NotNull Fun } /** - * Returns a list of candidates for the provided function. + * Returns a list of candidates for the provided function identifier. * * @param provided The provided function. * @param existing The existing functions with the same name. @@ -421,33 +429,48 @@ public static void remove(Signature signature) { continue; } - nameToIdentifiers.computeIfPresent(name, (k, set) -> { - if (set.remove(other)) { - Skript.debug("Removed identifier '%s' from %s", other, namespace); - } - return set.isEmpty() ? null : set; - }); - identifiers.put(namespace, nameToIdentifiers); - - functions.computeIfPresent(namespace, (ns, map) -> { - if (map.remove(other) != null) { - Skript.debug("Removed function '%s' from %s", other, namespace); - } - return map.isEmpty() ? null : map; - }); - - signatures.computeIfPresent(namespace, (ns, map) -> { - if (map.remove(other) != null) { - Skript.debug("Removed signature '%s' from %s", other, namespace); - } - return map.isEmpty() ? null : map; - }); + removeUpdateMaps(namespace, other, nameToIdentifiers, name); return; } } } + /** + * Updates the maps by removing the provided function identifier from the maps. + * + * @param namespace The namespace + * @param toRemove The identifier to remove + * @param nameToIdentifiers The map of identifiers to functions + * @param name The name of the function + */ + private static void removeUpdateMaps( + Namespace namespace, FunctionIdentifier toRemove, + Map> nameToIdentifiers, String name + ) { + nameToIdentifiers.computeIfPresent(name, (k, set) -> { + if (set.remove(toRemove)) { + Skript.debug("Removed identifier '%s' from %s", toRemove, namespace); + } + return set.isEmpty() ? null : set; + }); + identifiers.put(namespace, nameToIdentifiers); + + functions.computeIfPresent(namespace, (ns, map) -> { + if (map.remove(toRemove) != null) { + Skript.debug("Removed function '%s' from %s", toRemove, namespace); + } + return map.isEmpty() ? null : map; + }); + + signatures.computeIfPresent(namespace, (ns, map) -> { + if (map.remove(toRemove) != null) { + Skript.debug("Removed signature '%s' from %s", toRemove, namespace); + } + return map.isEmpty() ? null : map; + }); + } + /** * Scope of functions in namespace. */ diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index e263816d961..474f27b28f5 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -1,14 +1,11 @@ package ch.njol.skript.lang.function; -import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import ch.njol.skript.lang.function.FunctionRegistry.FunctionIdentifier; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.registrations.DefaultClasses; import org.jetbrains.annotations.Nullable; import org.junit.Test; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; import static org.junit.Assert.*; From 8831fa751778148962292fd1f9d8f8426b4f127e Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:05:34 +0200 Subject: [PATCH 18/19] move local to package-private --- .../java/ch/njol/skript/lang/function/JavaFunction.java | 7 +++++-- .../ch/njol/skript/lang/function/SimpleJavaFunction.java | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/JavaFunction.java b/src/main/java/ch/njol/skript/lang/function/JavaFunction.java index fdc334644e7..735d666ab86 100644 --- a/src/main/java/ch/njol/skript/lang/function/JavaFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/JavaFunction.java @@ -2,6 +2,7 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.util.Contract; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Nullable; /** @@ -17,7 +18,8 @@ public JavaFunction(String name, Parameter[] parameters, ClassInfo returnT this(name, parameters, returnType, single, null); } - public JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { + @Internal + JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { this(name, parameters, returnType, single, local, null); } @@ -25,7 +27,8 @@ public JavaFunction(String name, Parameter[] parameters, ClassInfo returnT this(name, parameters, returnType, single, false, contract); } - public JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local, @Nullable Contract contract) { + @Internal + JavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local, @Nullable Contract contract) { this(new Signature<>("none", name, parameters, local, returnType, single, Thread.currentThread().getStackTrace()[3].getClassName(), contract)); } diff --git a/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java b/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java index f0c89da61c9..a877d17bb7a 100644 --- a/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/SimpleJavaFunction.java @@ -2,6 +2,7 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.util.Contract; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Nullable; /** @@ -19,7 +20,8 @@ public SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo r super(name, parameters, returnType, single); } - public SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { + @Internal + SimpleJavaFunction(String name, Parameter[] parameters, ClassInfo returnType, boolean single, boolean local) { super(name, parameters, returnType, single, local); } From 4c5b4c75d85cb6d0b2341ede51b8791f8936cf8a Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:09:55 +0200 Subject: [PATCH 19/19] rename --- .../function/DynamicFunctionReference.java | 4 +- .../lang/function/FunctionReference.java | 8 +- .../lang/function/FunctionRegistry.java | 20 +-- .../lang/function/FunctionRegistryTest.java | 156 +++++++++--------- 4 files changed, 94 insertions(+), 94 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java index d9cdfac5b35..2e67f986fc9 100644 --- a/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/DynamicFunctionReference.java @@ -60,10 +60,10 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) { // will return the first function found that matches name. // TODO: add a way to specify param types //noinspection unchecked - function = (Function) FunctionRegistry.function(source.getConfig().getFileName(), name); + function = (Function) FunctionRegistry.getFunction(source.getConfig().getFileName(), name); } else { //noinspection unchecked - function = (Function) FunctionRegistry.function(null, name); + function = (Function) FunctionRegistry.getFunction(null, name); } if (function == null) { Skript.error(FunctionReference.AMBIGUOUS_ERROR.formatted(name)); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index fa8ae7bb00d..ac361f846e1 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -285,12 +285,12 @@ private Signature getRegisteredSignature() { Skript.debug("Getting signature for '%s' with types %s", functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); } - Signature sign = FunctionRegistry.signature(script, functionName, parameterTypes); + Signature sign = FunctionRegistry.getSignature(script, functionName, parameterTypes); // if we can't find a signature based on param types, try to match any function if (sign == null) { - sign = FunctionRegistry.signature(script, functionName); + sign = FunctionRegistry.getSignature(script, functionName); } if (sign == null) { @@ -311,11 +311,11 @@ private Function getRegisteredFunction() { Skript.debug("Getting function '%s' with types %s", functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); } - Function function = FunctionRegistry.function(script, functionName, parameterTypes); + Function function = FunctionRegistry.getFunction(script, functionName, parameterTypes); // if we can't find a signature based on param types, try to match any function if (function == null) { - function = FunctionRegistry.function(script, functionName); + function = FunctionRegistry.getFunction(script, functionName); } if (function == null) { diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 728e30ad48f..78cca5f760b 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -209,14 +209,14 @@ private static boolean signatureExists(@NotNull Namespace namespace, @NotNull Fu * @param args The types of the arguments of the function. * @return The function with the given name and argument types, or null if no such function exists. */ - public static Function function(@Nullable String namespace, @NotNull String name, Class... args) { + public static Function getFunction(@Nullable String namespace, @NotNull String name, Class... args) { if (namespace == null) { - return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); + return getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Function function = function(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); + Function function = getFunction(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); if (function == null) { - return function(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); + return getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } return function; } @@ -229,7 +229,7 @@ public static Function function(@Nullable String namespace, @NotNull String n * @param provided The provided identifier of the function. * @return The function with the given name and argument types, or null if no such function exists. */ - private static Function function(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { + private static Function getFunction(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { Preconditions.checkNotNull(namespace, "namespace is null"); Preconditions.checkNotNull(provided, "provided is null"); @@ -278,14 +278,14 @@ private static Function function(@NotNull Namespace namespace, @NotNull Funct * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - public static Signature signature(@Nullable String namespace, @NotNull String name, Class... args) { + public static Signature getSignature(@Nullable String namespace, @NotNull String name, Class... args) { if (namespace == null) { - return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); + return getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } - Signature signature = signature(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); + Signature signature = getSignature(new Namespace(Scope.LOCAL, namespace), FunctionIdentifier.of(name, true, args)); if (signature == null) { - return signature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); + return getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } return signature; } @@ -298,7 +298,7 @@ public static Signature signature(@Nullable String namespace, @NotNull String * @return The signature for the function with the given name and argument types, or null if no such signature exists * in the specified namespace. */ - private static Signature signature(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { + private static Signature getSignature(@NotNull Namespace namespace, @NotNull FunctionIdentifier provided) { Preconditions.checkNotNull(namespace, "namespace is null"); Preconditions.checkNotNull(provided, "provided is null"); diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index 474f27b28f5..05448c36a1f 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -25,20 +25,20 @@ public class FunctionRegistryTest { @Test public void testSimpleMultipleRegistrationsFunction() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.getFunction(null, FUNCTION_NAME)); assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.remove(TEST_FUNCTION.getSignature()); } @@ -46,26 +46,26 @@ public void testSimpleMultipleRegistrationsFunction() { @Test public void testSimpleRegisterRemoveRegisterGlobal() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.remove(TEST_FUNCTION.getSignature()); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.remove(TEST_FUNCTION.getSignature()); } @@ -82,32 +82,32 @@ public void testSimpleRegisterRemoveRegisterGlobal() { public void testSimpleRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); FunctionRegistry.register(TEST_FUNCTION); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME)); - assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME)); + assertEquals(LOCAL_TEST_FUNCTION, FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME)); - assertEquals(TEST_FUNCTION, FunctionRegistry.function(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME)); + assertEquals(TEST_FUNCTION, FunctionRegistry.getFunction(null, FUNCTION_NAME)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION.getSignature()); FunctionRegistry.remove(TEST_FUNCTION.getSignature()); @@ -136,39 +136,39 @@ public void testSimpleRegisterRemoveRegisterLocal() { @Test public void testMultipleRegistrations() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION_B)); assertThrows(SkriptAPIException.class, () -> FunctionRegistry.register(TEST_FUNCTION_N)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); @@ -177,47 +177,47 @@ public void testMultipleRegistrations() { @Test public void testRegisterRemoveRegisterGlobal() { assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_FUNCTION_N); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertEquals(TEST_FUNCTION_N, FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertEquals(TEST_FUNCTION_N, FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(null, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(null, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature()); FunctionRegistry.remove(TEST_FUNCTION_N.getSignature()); @@ -246,25 +246,25 @@ public void testRegisterRemoveRegisterGlobal() { @Test public void testRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertEquals(LOCAL_TEST_FUNCTION_B, FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); @@ -273,27 +273,27 @@ public void testRegisterRemoveRegisterLocal() { assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Number.class)); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); + assertNull(FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class)); assertFalse(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertNull(FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertNull(FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class)); FunctionRegistry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); FunctionRegistry.register(null, TEST_FUNCTION_B); assertTrue(FunctionRegistry.signatureExists(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.signature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); - assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.function(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), FunctionRegistry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class)); + assertEquals(LOCAL_TEST_FUNCTION_N, FunctionRegistry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class)); assertTrue(FunctionRegistry.signatureExists(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.signature(null, FUNCTION_NAME, Boolean.class)); - assertEquals(TEST_FUNCTION_B, FunctionRegistry.function(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B.getSignature(), FunctionRegistry.getSignature(null, FUNCTION_NAME, Boolean.class)); + assertEquals(TEST_FUNCTION_B, FunctionRegistry.getFunction(null, FUNCTION_NAME, Boolean.class)); FunctionRegistry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); FunctionRegistry.remove(TEST_FUNCTION_B.getSignature());