Class InvocationUtils

java.lang.Object
org.daiitech.naftah.utils.reflect.InvocationUtils

public final class InvocationUtils extends Object
Utility class for reflective invocation of Java methods and constructors, with support for type conversion and integration with the dynamically typed programming language Naftah.

This class provides methods to:

  • Invoke Java Method and Constructor instances reflectively.
  • Handle named and positional arguments, including conversion between Java types and Naftah types such as NaftahObject and DynamicNumber.
  • Support automatic handling of null, None.get(), and NaN values used in Naftah.
  • Perform deep conversions for arrays, collections, and maps to match expected Java method parameter types .
  • Merge or convert arguments back to their original Naftah representations after method execution.

Typical usage scenarios:

  • Invoking a Java constructor or method from Naftah code, where argument types may not exactly match Java parameter types.
  • Interfacing between Naftah and Java, handling automatic type adaptation for Naftah objects.
  • Performing post-invocation conversion or merging of results back into Naftah objects, arrays, or collections.

This class is designed for internal utility use in reflective invocation workflows, especially when integrating Java APIs with the Naftah programming language.

Author:
Chakib Daii
See Also:
  • Constructor Details

    • InvocationUtils

      private InvocationUtils()
      Private constructor to prevent instantiation. Always throws a NaftahBugError when called.
  • Method Details

    • invokeJvmExecutable

      public static <T extends Executable> Object invokeJvmExecutable(Object instance, T methodOrConstructor, List<Pair<String,Object>> naftahArgs, Class<?> returnType, boolean useNaftahTypes) throws InvocationTargetException, InstantiationException, IllegalAccessException
      Dynamically invokes a JVM Method or Constructor using reflection.

      This unified utility abstracts the complexity of calling either a Method or Constructor at runtime by automatically handling:

      • Argument conversion to match JVM parameter types, including primitives, arrays, collections, and generic types
      • Primitive boxing and unboxing
      • Naftah-specific type handling and conversions (when enabled)
      • Varargs executables, including automatic construction of the trailing vararg array when possible

      This method is designed for runtime environments that need to dynamically invoke JVM executables without compile-time type information.

      Supported executable types

      Argument handling

      • The number of provided arguments must match the executable’s parameter count
      • If the executable is varargs, a missing final array argument may be synthesized automatically
      • Arguments are converted before invocation and may be converted back into naftahArgs after invocation

      Return handling

      The raw result of the underlying reflective call is returned. Any Naftah-specific wrapping or post-processing is handled by the delegated invocation logic.

      Type Parameters:
      T - the type of executable being invoked
      Parameters:
      instance - the target object for instance method calls, or null for static methods or constructors
      methodOrConstructor - the Executable to invoke (either a Method or a Constructor)
      naftahArgs - a list of Pair<String, Object> representing argument names and values; this list may be mutated during invocation (e.g. for varargs handling or argument back-conversion)
      returnType - the expected return type; use Void.TYPE or Void for void executables
      useNaftahTypes - whether Naftah-specific type semantics and conversions should be applied
      Returns:
      the result of the invocation
      Throws:
      InvocationTargetException - if the underlying executable throws an exception
      InstantiationException - if a constructor fails to create a new instance
      IllegalAccessException - if the executable cannot be accessed due to Java access control
      IllegalArgumentException - if argument count or types do not match the executable signature
      See Also:
    • getVarargArrayIfPossible

      private static Object getVarargArrayIfPossible(List<Pair<String,Object>> naftahArgs, Class<?>[] paramTypes)
      Creates a varargs array from a list of arguments if the method parameters indicate a varargs parameter.

      This method checks if the last parameter in the paramTypes array is an array (indicating a varargs parameter) and if there are enough arguments in naftahArgs to populate the normal parameters. If so, it creates a new array for the varargs portion.

      Example usage:

       
       List<Pair<String, Object>> args = List.of(Pair.of("arg1", value1), Pair.of("arg2", value2));
       Class<?>[] paramTypes = { String.class, Integer.class, String[].class }; // last is varargs
       Object varargsArray = getVarargArrayIfPossible(args, paramTypes);
       // varargsArray will be a String[0] if no extra arguments, or filled with remaining args otherwise
       
       

      Parameters:
      naftahArgs - the list of arguments as pairs of name and value
      paramTypes - the array of parameter types for the target method, where the last element may be a varargs array
      Returns:
      a newly instantiated array of the varargs component type containing the extra arguments, or null if there are not enough arguments to reach the varargs parameter
      Throws:
      NullPointerException - if naftahArgs or paramTypes is null
      See Also:
    • invokeJvmExecutable

      public static <T extends Executable> Object invokeJvmExecutable(Object instance, T methodOrConstructor, Object[] executableArgs, List<Pair<String,Object>> naftahArgs, Class<?> returnType) throws InvocationTargetException, InstantiationException, IllegalAccessException
      Invokes a given Method or Constructor reflectively with specified argument values.

      This low-level helper performs:

      Type Parameters:
      T - the type of executable
      Parameters:
      instance - the target object for instance methods, or null for static methods or constructors
      methodOrConstructor - the Executable to invoke
      executableArgs - the argument values in declared parameter order
      naftahArgs - the original argument name/value pairs (used for back-conversion)
      returnType - the expected return type
      Returns:
      the invocation result, or None.get() if the result is null or void
      Throws:
      InvocationTargetException - if the underlying executable throws an exception
      InstantiationException - if a constructor fails to instantiate a new object
      IllegalAccessException - if the executable cannot be accessed
      See Also:
    • invokeJvmConstructor

      public static <T extends Executable> Object invokeJvmConstructor(T methodOrConstructor, List<Pair<String,Object>> args, Class<?> returnType, boolean useNaftahTypes) throws InvocationTargetException, InstantiationException, IllegalAccessException
      Convenience method to invoke a Constructor reflectively using a list of arguments.

      This method is a thin wrapper around invokeJvmExecutable(Object, Executable, List, Class, boolean) that automatically supplies a null instance, as constructors do not require one.

      Constructor arguments are converted automatically to match the constructor’s parameter types, including primitives, arrays, collections, and generic types. Naftah-specific type semantics and conversions are applied when enabled.

      The provided argument list may be mutated during invocation (for example, to support varargs handling or argument back-conversion).

      Type Parameters:
      T - the type of executable (constructor) being invoked
      Parameters:
      methodOrConstructor - the Constructor to invoke
      args - the constructor arguments as a list of Pair<String, Object>
      returnType - the expected type of the constructed object
      useNaftahTypes - whether Naftah-specific type semantics and conversions should be applied
      Returns:
      the newly created instance
      Throws:
      InvocationTargetException - if the constructor throws an exception
      InstantiationException - if the constructor fails to instantiate a new object
      IllegalAccessException - if reflective access is not allowed
      IllegalArgumentException - if the argument count or types do not match the constructor signature
      See Also:
    • invokeJvmConstructor

      public static <T extends Executable> Object invokeJvmConstructor(T methodOrConstructor, Object[] executableArgs, List<Pair<String,Object>> naftahArgs, Class<?> returnType) throws InvocationTargetException, InstantiationException, IllegalAccessException
      Invokes a Java Constructor reflectively using the specified argument array.

      This is a convenience wrapper around invokeJvmExecutable(Object, Executable, Object[], List, Class) that specifically targets constructors. Since constructors do not require a target object, this method automatically supplies null for the instance parameter.

      Behavior overview

      Note: This method assumes that all arguments in executableArgs are already type-compatible with the constructor’s parameter types. For automatic type conversion, named arguments, or more complex argument handling, use the invokeJvmConstructor(Executable, List, Class, boolean) variant.

      Type Parameters:
      T - the type of Executable (usually Constructor).
      Parameters:
      methodOrConstructor - the Constructor to invoke reflectively.
      executableArgs - an array of argument values to pass to the constructor.
      naftahArgs - a list of named argument pairs, for optional post-processing.
      returnType - the expected type of the constructed object.
      Returns:
      the newly constructed instance, or None.get() if the result is null.
      Throws:
      InvocationTargetException - if the underlying constructor throws an exception.
      InstantiationException - if the constructor cannot instantiate a new object.
      IllegalAccessException - if reflective access is not permitted.
      See Also:
    • convertArgument

      public static Object convertArgument(Object value, Class<?> targetType, Type genericType, boolean useNaftahTypes)
      Converts a single argument to the target type expected by a reflective method or constructor parameter.

      This method attempts to adapt the supplied value to the specified targetType. When available, genericType information is used to guide recursive, element-wise conversion of arrays, collections, tuples, and maps.

      Supported conversions

      Naftah-specific semantics

      • If value is null or represents None, the result depends on useNaftahTypes:
        • when true, None.get() is returned
        • when false, null is returned
      • When useNaftahTypes is false, NaftahObject values are automatically unwrapped unless the target type is NaftahObject

      If value is already assignable to targetType, it is returned unchanged.

      Parameters:
      value - the original argument value to convert; may be null or None
      targetType - the target class expected by the executable parameter
      genericType - generic type information used to guide recursive conversion of collections, arrays, and maps; may be null
      useNaftahTypes - whether Naftah-specific type semantics and wrappers should be preserved
      Returns:
      a value compatible with targetType, or null if value is null and Naftah types are disabled
      Throws:
      ClassCastException - if the value cannot be converted or cast to targetType
      See Also:
    • convertArgumentsBack

      public static void convertArgumentsBack(Object[] executableArgs, List<Pair<String,Object>> naftahArgs)
      Updates the original argument list with values from the executed arguments array.

      This method iterates through the provided executableArgs array and compares each element with the corresponding element in naftahArgs. If the original argument and the converted value differ (either by reference or logical equality), the method attempts to merge the changes back into the original object using convertArgumentBack(Object, Object).

      This is useful when invoking a method or constructor reflectively and wanting to propagate modifications of mutable arguments (like arrays, collections, or maps) back to the original argument references.

      Parameters:
      executableArgs - the array of argument values used during the reflective call.
      naftahArgs - the list of original named arguments as Pair<String, Object>, where the value part may be updated with the converted value.
      See Also:
    • convertArgumentBack

      public static Object convertArgumentBack(Object original, Object converted)
      Merges a converted argument value back into its original representation.

      This method reconciles a value produced by a reflective invocation (converted) with the original argument value (original). Where possible, mutable objects are updated in place; otherwise, the original value or a replacement object is returned.

      Supported merge behaviors

      If converted is already assignable to the runtime type of original, it is returned directly.

      This method is primarily intended to propagate argument mutations made during reflective method or constructor execution back to the original argument objects.

      Parameters:
      original - the original argument value supplied to the executable
      converted - the value produced by the reflective invocation
      Returns:
      the merged value, either the updated original instance or a suitable replacement object
      Throws:
      NaftahBugError - if reflective field updates fail
      See Also:
    • getConvertedElementAt

      private static Object getConvertedElementAt(Object converted, Class<?> convertedType, int i)
      Retrieves the element at the specified index from a converted composite value.

      Supports both array and Collection representations. If convertedType represents an array, the element is obtained via Array.get(Object, int); otherwise the value is retrieved from the collection using index-based access.

      Parameters:
      converted - the converted composite value (array or Collection)
      convertedType - the runtime type of converted, used to distinguish arrays from collections
      i - the zero-based index of the element to retrieve
      Returns:
      the element at the specified index
      Throws:
      IndexOutOfBoundsException - if the index is out of range
      ClassCastException - if converted is not compatible with convertedType
    • findBestExecutable

      public static <T extends JvmExecutable> Pair<T,Object[]> findBestExecutable(Collection<T> candidates, List<Pair<String,Object>> args) throws NoSuchMethodException
      Attempts to find the most suitable JvmExecutable (method or constructor) from a collection of candidates, based on the provided argument list.

      This overload delegates to findBestExecutable(Collection, List, boolean) with removeInstanceArg = false.

      Type Parameters:
      T - the type of JvmExecutable (e.g. JvmFunction, BuiltinFunction).
      Parameters:
      candidates - the collection of available JvmExecutable instances to evaluate.
      args - the argument list to match, represented as Pair<String, Object> where each pair contains the argument name and its value.
      Returns:
      a Pair containing the best-matching JvmExecutable and its prepared argument array; or null if no suitable match is found.
      Throws:
      NoSuchMethodException
      See Also:
    • findBestExecutable

      public static <T extends JvmExecutable> Pair<T,Object[]> findBestExecutable(Collection<T> candidates, List<Pair<String,Object>> args, boolean removeInstanceArg) throws NoSuchMethodException
      Determines the best-matching JvmExecutable (method or constructor) from a given collection of candidates based on argument compatibility.

      The matching process computes a “score” for each executable using matchScore(Executable, Class[], List, boolean), where lower scores indicate a closer match. The executable with the lowest non-negative score is returned along with its converted argument array.

      If removeInstanceArg is true, and the executable represents a non-static JvmFunction, the first argument in args is removed, as it corresponds to the instance already passed during invocation.

      Type Parameters:
      T - the type of JvmExecutable.
      Parameters:
      candidates - the collection of JvmExecutable candidates to search through.
      args - the argument list as Pair<String, Object> entries.
      removeInstanceArg - whether to remove the first argument when matching non-static functions (useful when the instance is already supplied).
      Returns:
      a Pair containing:
      • the best-matching JvmExecutable, and
      • the prepared and type-converted argument array for invocation.
      Returns null if no compatible executable is found.
      Throws:
      NoSuchMethodException
      See Also:
    • matchScore

      private static <T extends Executable> Pair<Integer,Object[]> matchScore(T executable, Class<?>[] params, List<Pair<String,Object>> args, boolean useNaftahTypes)
      Computes a compatibility score describing how well a set of provided arguments matches the parameter types of a given Executable.

      Each argument is tentatively converted to the corresponding parameter type using convertArgument(Object, Class, Type, boolean). If any argument fails conversion, the executable is considered incompatible and a score of -1 is returned.

      Scoring semantics

      Lower scores indicate a better match. The total score is the sum of per-parameter penalties based on how closely the converted argument matches the target parameter type:

      • null argument: +10
      • Exact runtime type match: +0
      • Assignable match (after conversion): +1
      • Boxed numeric to boxed numeric: +2
      • Boxed numeric to matching primitive: +3
      • Numeric or character primitive mismatch: +4
      • Other non-null compatible conversions: +5
      • Converted value is null: +6

      If a converted argument is null but the corresponding parameter is primitive, the match fails immediately.

      Naftah-specific behavior

      The useNaftahTypes flag controls whether None and NaftahObject values are preserved or unwrapped during argument conversion.

      Type Parameters:
      T - the type of Executable
      Parameters:
      executable - the method or constructor being evaluated
      params - the raw parameter types of the executable
      args - the provided arguments as a list of Pair<String, Object>
      useNaftahTypes - whether Naftah-specific type semantics should be applied during conversion
      Returns:
      a Pair containing:
      • the computed compatibility score (lower is better, -1 indicates incompatibility)
      • an array of converted argument values suitable for invocation
    • getGenericType

      private static <T extends Executable> Type getGenericType(T executable, int index)
      Retrieves the generic parameter type for the specified parameter index of a given Executable.

      If the executable declares generic parameter types, the corresponding one is returned; otherwise, the raw parameter type is used as a fallback.

      Type Parameters:
      T - the type of the Executable.
      Parameters:
      executable - the Executable whose parameter type should be retrieved.
      index - the parameter index.
      Returns:
      the Type representing the parameter’s declared or generic type.