Skip to content

Function overloading #7757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: dev/feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
019341d
init commit
Efnilite Mar 25, 2025
395ad19
fix some errors
Efnilite Mar 25, 2025
4b253f3
liftoff
Efnilite Mar 30, 2025
54e2270
cleanup
Efnilite Mar 30, 2025
aa78c7b
Merge branch 'dev/feature' into feature/fun-overloading
Efnilite Mar 30, 2025
fd7e6a5
yes thank you ai good point
Efnilite Mar 30, 2025
1453949
Merge remote-tracking branch 'origin/feature/fun-overloading' into fe…
Efnilite Mar 30, 2025
02f3233
improve docs
Efnilite Apr 1, 2025
ec8325c
add function unloading
Efnilite Apr 1, 2025
aae5341
fix error placement
Efnilite Apr 2, 2025
3229c09
improve error & fix reloading
Efnilite Apr 2, 2025
8b49784
add all the testing
Efnilite Apr 2, 2025
7bf3846
add way to add local javafunctions
Efnilite Apr 7, 2025
78cf7d8
improved reliability with java functions
Efnilite Apr 7, 2025
f11dc40
improve global/local conflict resolution
Efnilite Apr 7, 2025
f303ff7
fix tests
Efnilite Apr 7, 2025
dd5e6c7
woo tests run
Efnilite Apr 8, 2025
7f34b51
cleanup
Efnilite Apr 8, 2025
ba0156c
Merge branch 'dev/feature' into feature/fun-overloading
Efnilite Apr 8, 2025
42c1b2e
address reviews
Efnilite Apr 9, 2025
b8ffd0a
Merge remote-tracking branch 'origin/feature/fun-overloading' into fe…
Efnilite Apr 9, 2025
8831fa7
move local to package-private
Efnilite Apr 22, 2025
4c5b4c7
rename
Efnilite Apr 22, 2025
2479d6b
Merge branch 'dev/feature' into feature/fun-overloading
Efnilite Apr 22, 2025
4c870d9
Merge remote-tracking branch 'origin/feature/fun-overloading' into fe…
Efnilite Apr 22, 2025
5f5fc1e
Merge branch 'dev/feature' into feature/fun-overloading
Efnilite May 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -56,11 +57,16 @@ public DynamicFunctionReference(@NotNull String name, @Nullable Script source) {
this.name = name;
Function<? extends Result> 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<? extends Result>) Functions.getFunction(name, source.getConfig().getFileName());
function = (Function<? extends Result>) FunctionRegistry.function(source.getConfig().getFileName(), name);
} else {
//noinspection unchecked
function = (Function<? extends Result>) Functions.getFunction(name, null);
function = (Function<? extends Result>) FunctionRegistry.function(null, name);
}
if (function == null) {
Skript.error(FunctionReference.AMBIGUOUS_ERROR.formatted(name));
}
this.resolved = function != null;
this.function = new WeakReference<>(function);
Expand Down
94 changes: 84 additions & 10 deletions src/main/java/ch/njol/skript/lang/function/FunctionReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +27,9 @@
*/
public class FunctionReference<T> implements Contract, Executable<Event, T[]> {

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.
*/
Expand All @@ -37,7 +40,7 @@ public class FunctionReference<T> implements Contract, Executable<Event, T[]> {
* succeeds, this is not null.
*/
private @Nullable Signature<? extends T> signature;

/**
* Actual function reference. Null before the function is called for first
* time.
Expand Down Expand Up @@ -85,8 +88,8 @@ public class FunctionReference<T> implements Contract, Executable<Event, T[]> {
private Contract contract;

public FunctionReference(
String functionName, @Nullable Node node, @Nullable String script,
@Nullable Class<? extends T>[] returnTypes, Expression<?>[] params
String functionName, @Nullable Node node, @Nullable String script,
@Nullable Class<? extends T>[] returnTypes, Expression<?>[] params
) {
this.functionName = functionName;
this.node = node;
Expand All @@ -99,17 +102,23 @@ public FunctionReference(
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;

// Not enough parameters
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
* 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) {
Expand All @@ -119,7 +128,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) {
Expand Down Expand Up @@ -161,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;
}
Expand Down Expand Up @@ -253,6 +262,70 @@ public boolean validateFunction(boolean first) {
return 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++) {
Expression<?> parsed = LiteralUtils.defendExpression(parameters[i]);
parameterTypes[i] = parsed.getReturnType();
}
}

/**
* Attempts to get this function's signature.
*/
private Signature<?> getRegisteredSignature() {
parseParameters();

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);


// 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;
}

return sign;
}

/**
* Attempts to get this function's registered implementation.
*/
private Function<?> getRegisteredFunction() {
parseParameters();

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
if (function == null) {
function = FunctionRegistry.function(script, functionName);
}

if (function == null) {
Skript.error(AMBIGUOUS_ERROR.formatted(functionName));
return null;
}

return function;
}

public @Nullable Function<? extends T> getFunction() {
return function;
}
Expand All @@ -267,7 +340,7 @@ public boolean resetReturnValue() {
// If needed, acquire the function reference
if (function == null)
//noinspection unchecked
function = (Function<? extends T>) Functions.getFunction(functionName, script);
function = (Function<? extends T>) getRegisteredFunction();

if (function == null) { // It might be impossible to resolve functions in some cases!
Skript.error("Couldn't resolve call for '" + functionName + "'.");
Expand Down Expand Up @@ -326,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() {
Expand All @@ -348,7 +422,7 @@ public T[] execute(Event event, Object... arguments) {
// If needed, acquire the function reference
if (function == null)
//noinspection unchecked
function = (Function<? extends T>) Functions.getFunction(functionName, script);
function = (Function<? extends T>) getRegisteredFunction();

if (function == null) { // It might be impossible to resolve functions in some cases!
Skript.error("Couldn't resolve call for '" + functionName + "'.");
Expand Down
Loading