enhancement
major
major
major
#29783
TLScriptFunctions: a script function is bound to an arbitrary overload of the same name, including a private one
Scenario
A TLScriptFunctions subclass declares a public static script function and, in the same class, a private helper method with the same name but other parameters:
{{{#!java @ScriptPrefix("cmsApi") public class CmsApi extends TLScriptFunctions {
public static Object updateTemplate(String name, Map<?, ?> body) { ... updateTemplate(template, body); ... }
private static void updateTemplate(Template template, Map<?, ?> body) { ... } } }}}
Calling cmsApiUpdateTemplate(...) from TL-Script sometimes invokes the private helper (wrong argument count/types, failure at runtime), depending on the order in which the JVM reports the declared methods.
Found in the TopLogic release notes application (Gitea TopLogic/tl-releasenotes), with tl-engine 8.0.0-alpha9.
Analysis
TLScriptMethodResolver selects the candidate methods correctly: it iterates getDeclaredMethods() and accepts only public static ones. For each, it creates a TLScriptMethod.Builder configuration that identifies the method only by ClassName#methodName.
TLScriptMethod.Builder then resolves that string again, by name only:
{{{#!java _method = Arrays.stream(implClass.getDeclaredMethods()) .filter(m -> m.getName().equals(methodName)).findAny().get(); }}}
All declared methods take part, including private and non-static ones and every overload, and findAny() picks one at random. So the method the resolver checked is not necessarily the one that gets bound. A <method> registration through TLScriptMethod.Builder with an overloaded name has the same problem.
The same applies to two public overloads that the resolver offers as separate script functions through different @Name annotations: a lookup by Java method name alone cannot tell them apart.
Lösung
- Die Methodenangabe von TLScriptMethod.Builder (<java method="..."/>) kann die Parametertypen enthalten: fq.ClassName#methodName(java.lang.String,java.util.Map). Die Typen werden mit ihrem binären Namen (Class.getName()) angegeben, z.B. int oder [Ljava.lang.String; für ein Array. Die Form ohne Parameterliste (fq.ClassName#methodName) bleibt für handgeschriebene Registrierungen erlaubt.
- Der Builder berücksichtigt nur nicht-synthetische public static Methoden (TLScriptMethod.Builder.isScriptFunction(Method), dasselbe Prädikat wie in TLScriptMethodResolver). Mit Parameterliste wird genau diese Methode gebunden; ohne Parameterliste muss der Name eindeutig sein. Andernfalls (keine oder mehrere passende Überladungen) wird ein Konfigurationsfehler mit den gefundenen Kandidaten gemeldet, statt eine beliebige Methode zu wählen.
- TLScriptMethodResolver schreibt immer die vollständige Signatur (TLScriptMethod.Builder.signature(Method)), so dass genau die geprüfte Methode gebunden wird. Da die Signatur den binären Klassennamen verwendet, werden jetzt auch geschachtelte TLScriptFunctions-Klassen aufgelöst.
Test
- test.com.top_logic.model.search.expr.config.operations.TestScriptFunctionOverloads