Skip to content

Commit

Permalink
Fix varargs type resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
btk5h committed Sep 18, 2018
1 parent dec5584 commit 9d7f5c3
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 39 deletions.
108 changes: 69 additions & 39 deletions src/main/java/com/btk5h/skriptmirror/skript/reflect/ExprJavaCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ private T invoke(Object target, Object[] arguments, Descriptor baseDescriptor) {

MethodHandle mh = method.get();

convertTypes(mh, argumentsCopy);
argumentsCopy = convertTypes(mh, argumentsCopy);

try {
returnedValue = (T) mh.invokeWithArguments(argumentsCopy);
Expand Down Expand Up @@ -487,81 +487,107 @@ private Optional<MethodHandle> findCompatibleMethod(Descriptor descriptor, Objec

private static boolean matchesArgs(Object[] args, MethodHandle mh) {
MethodType mt = mh.type();
int parameterCount = mt.parameterCount();
Class<?>[] params = mt.parameterArray();
int varargsIndex = params.length - 1;
boolean hasVarargs = mh.isVarargsCollector();

// Fail early if there is an arity mismatch
// If the method has varargs, make sure args has the minimum arity (exclude the varargs parameter)
if (args.length != parameterCount
&& !(hasVarargs && args.length >= (parameterCount - 1))) {
if (args.length != params.length
&& !(hasVarargs && args.length >= varargsIndex)) {
return false;
}

Class<?>[] params = mt.parameterArray();

for (int i = 0; i < args.length; i++) {
Class<?> param;
if (hasVarargs && i >= (params.length - 1)) {
param = params[params.length - 1].getComponentType();
boolean loopAtVarargs = hasVarargs && i >= varargsIndex;

if (loopAtVarargs) {
param = params[varargsIndex].getComponentType();
} else {
param = params[i];
}

Object arg = ObjectWrapper.unwrapIfNecessary(args[i]);

if (!param.isInstance(arg)) {
if (arg instanceof Number && JavaUtil.NUMERIC_CLASSES.contains(param)) {
if (!canCoerceType(arg, param)) {
if (loopAtVarargs && args.length == params.length && canCoerceType(arg, params[i])) {
continue;
}

if (param.isArray() && JavaUtil.getArrayDepth(param) == JavaUtil.getArrayDepth(arg.getClass())) {
Class<?> paramComponent = JavaUtil.getBaseComponent(param);
Class<?> argComponent = JavaUtil.getBaseComponent(arg.getClass());

if ((Number.class.isAssignableFrom(paramComponent) || JavaUtil.NUMERIC_CLASSES.contains(paramComponent))
&& (Number.class.isAssignableFrom(argComponent) || JavaUtil.NUMERIC_CLASSES.contains(argComponent))) {
continue;
}
}
return false;
}
}

if (param.isPrimitive() && JavaUtil.WRAPPER_CLASSES.get(param).isInstance(arg)) {
continue;
}
return true;
}

if (arg instanceof String
&& (param == char.class || param == Character.class)
&& ((String) arg).length() == 1) {
continue;
}
private static boolean canCoerceType(Object o, Class<?> to) {
if (to.isInstance(o)) {
return true;
}

if (param == Class.class && (arg instanceof JavaType || arg instanceof ClassInfo)) {
continue;
}
if (o instanceof Number && JavaUtil.NUMERIC_CLASSES.contains(to)) {
return true;
}

if (!param.isPrimitive() && arg instanceof Null) {
continue;
}
if (to.isArray() && JavaUtil.getArrayDepth(to) == JavaUtil.getArrayDepth(o.getClass())) {
Class<?> paramComponent = JavaUtil.getBaseComponent(to);
Class<?> argComponent = JavaUtil.getBaseComponent(o.getClass());

return false;
if (JavaUtil.isNumericClass(paramComponent) && JavaUtil.isNumericClass(argComponent)) {
return true;
}
}

return true;
if (to.isPrimitive() && JavaUtil.WRAPPER_CLASSES.get(to).isInstance(o)) {
return true;
}

if (o instanceof String
&& (to == char.class || to == Character.class)
&& ((String) o).length() == 1) {
return true;
}

if (to == Class.class && (o instanceof JavaType || o instanceof ClassInfo)) {
return true;
}

if (!to.isPrimitive() && o instanceof Null) {
return true;
}

return false;
}

private static void convertTypes(MethodHandle mh, Object[] args) {
private static Object[] convertTypes(MethodHandle mh, Object[] args) {
Class<?>[] params = mh.type().parameterArray();
int varargsIndex = params.length - 1;
boolean hasVarargs = mh.isVarargsCollector();

for (int i = 0; i < args.length; i++) {
Class<?> param;
if (mh.isVarargsCollector() && i >= (params.length - 1)) {
param = params[params.length - 1].getComponentType();
boolean loopAtVarargs = hasVarargs && i >= varargsIndex;

if (loopAtVarargs) {
param = params[varargsIndex].getComponentType();
} else {
param = params[i];
}

args[i] = ObjectWrapper.unwrapIfNecessary(args[i]);

if (loopAtVarargs && args.length == params.length && params[i].isInstance(args[i])) {
Object varargsArray = args[i];
int varargsLength = Array.getLength(varargsArray);

args = Arrays.copyOf(args, args.length - 1 + varargsLength);

//noinspection SuspiciousSystemArraycopy
System.arraycopy(varargsArray, 0, args, varargsIndex, varargsLength);
}

if (param.isPrimitive() && args[i] instanceof Number) {
if (param == byte.class) {
args[i] = ((Number) args[i]).byteValue();
Expand All @@ -578,7 +604,9 @@ private static void convertTypes(MethodHandle mh, Object[] args) {
}
}

if (param.isArray() && param != args[i].getClass()) {
if (param.isArray()
&& JavaUtil.getArrayDepth(param) == JavaUtil.getArrayDepth(args[i].getClass())
&& JavaUtil.isNumericClass(JavaUtil.getBaseComponent(param))) {
args[i] = JavaUtil.convertNumericArray(args[i], JavaUtil.getBaseComponent(param));
}

Expand All @@ -599,6 +627,8 @@ private static void convertTypes(MethodHandle mh, Object[] args) {
args[i] = null;
}
}

return args;
}

private String argumentsMessage(Object... arguments) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/btk5h/skriptmirror/util/JavaUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ public static <T, R> Function<T, R> propagateErrors(ExceptionalFunction<T, R> fu
};
}

public static boolean isNumericClass(Class<?> cls) {
return Number.class.isAssignableFrom(cls) || JavaUtil.NUMERIC_CLASSES.contains(cls);
}

@SuppressWarnings("unchecked")
public static <T> T[] newArray(Class<? extends T> type, int length) {
return (T[]) Array.newInstance(type, length);
Expand Down

0 comments on commit 9d7f5c3

Please sign in to comment.