package org.dynjs.runtime.modules;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.dynjs.exception.DynJSException;
import org.dynjs.runtime.AbstractNativeFunction;
import org.dynjs.runtime.ExecutionContext;
import org.dynjs.runtime.GlobalContext;
public class JavaFunction extends AbstractNativeFunction {
public JavaFunction(GlobalContext globalContext, Object object, Method method) throws IllegalAccessException {
super(globalContext);
this.object = object;
this.method = method;
this.handle = MethodHandles.lookup().unreflect(method).bindTo(this.object);
int trueNumberOfArgs = -1;
Class<?>[] methodParamTypes = this.method.getParameterTypes();
if (methodParamTypes.length >= 2) {
if (methodParamTypes[0].equals(ExecutionContext.class)) {
trueNumberOfArgs = methodParamTypes.length - 2;
}
}
if (trueNumberOfArgs < 0) {
trueNumberOfArgs = methodParamTypes.length;
}
String[] formalParams = new String[trueNumberOfArgs];
for (int i = 0; i < trueNumberOfArgs; ++i) {
formalParams[i] = "arg" + i;
}
setFormalParamters(formalParams);
}
@Override
public Object call(ExecutionContext context, Object self, Object... arguments) {
try {
List<Object> newArgs = buildArguments(context, self, arguments);
return this.handle.invokeWithArguments(newArgs.toArray());
} catch (Throwable e) {
throw new DynJSException(e);
}
}
private List<Object> buildArguments(ExecutionContext context, Object self, Object... args) {
List<Object> newArgs = new ArrayList<Object>();
Class<?>[] methodParamTypes = this.method.getParameterTypes();
if (methodParamTypes.length > 0) {
if (methodParamTypes[0].equals(ExecutionContext.class)) {
newArgs.add(context);
newArgs.add(self);
}
if (methodParamTypes[methodParamTypes.length-1].equals(Object[].class)) {
newArgs.add(args);
} else {
for (Object arg : args) {
newArgs.add(arg);
}
}
}
int additionalNulls = methodParamTypes.length - newArgs.size();
for (int i = 0; i < additionalNulls; ++i) {
newArgs.add(null);
}
return newArgs;
}
public String getFileName() {
return this.object.getClass().getName().replace(".", "/") + ".java";
}
private Object object;
private Method method;
private MethodHandle handle;
}