Adds support for a custom SymbolProvider in NativeLibrary & Library by soywiz · Pull Request #1490 · java-native-access/jna (original) (raw)

static class Handler implements InvocationHandler {
static final Method OBJECT_TOSTRING;
static final Method OBJECT_HASHCODE;
static final Method OBJECT_EQUALS;
static {
try {
OBJECT_TOSTRING = Object.class.getMethod("toString");
OBJECT_HASHCODE= Object.class.getMethod("hashCode");
OBJECT_EQUALS = Object.class.getMethod("equals", Object.class);
} catch (Exception e) {
throw new Error("Error retrieving Object.toString() method");
}
}
/**
* FunctionInfo has to be immutable to to make the object visible
* to other threads fully initialized. This is a prerequisite for
* using the class in the double checked locking scenario of {@link Handler#invoke(Object, Method, Object[])}
*/
private static final class FunctionInfo {
final InvocationHandler handler;
final Function function;
final boolean isVarArgs;
final Object methodHandle;
final Map<String, ?> options;
final Class<?>[] parameterTypes;
FunctionInfo(Object mh) {
this.handler = null;
this.function = null;
this.isVarArgs = false;
this.options = null;
this.parameterTypes = null;
this.methodHandle = mh;
}
FunctionInfo(InvocationHandler handler, Function function, Class\[\] parameterTypes, boolean isVarArgs, Map options) {
this.handler = handler;
this.function = function;
this.isVarArgs = isVarArgs;
this.options = options;
this.parameterTypes = parameterTypes;
this.methodHandle = null;
}
}
private final NativeLibrary nativeLibrary;
private final Class<?> interfaceClass;
// Library invocation options
private final Map<String, Object> options;
private final InvocationMapper invocationMapper;
private final Map<Method, FunctionInfo> functions = new WeakHashMap<Method, FunctionInfo>();
public Handler(String libname, Class interfaceClass, Map options) {
if (libname != null && "".equals(libname.trim())) {
throw new IllegalArgumentException("Invalid library name \"" + libname + "\"");
}
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(libname + " does not implement an interface: " + interfaceClass.getName());
}
this.interfaceClass = interfaceClass;
this.options = new HashMap<String, Object>(options);
int callingConvention = AltCallingConvention.class.isAssignableFrom(interfaceClass)
? Function.ALT_CONVENTION
: Function.C_CONVENTION;
if (this.options.get(OPTION_CALLING_CONVENTION) == null) {
this.options.put(OPTION_CALLING_CONVENTION, Integer.valueOf(callingConvention));
}
if (this.options.get(OPTION_CLASSLOADER) == null) {
this.options.put(OPTION_CLASSLOADER, interfaceClass.getClassLoader());
}
this.nativeLibrary = NativeLibrary.getInstance(libname, this.options);
invocationMapper = (InvocationMapper)this.options.get(OPTION_INVOCATION_MAPPER);
}
public NativeLibrary getNativeLibrary() {
return nativeLibrary;
}
public String getLibraryName() {
return nativeLibrary.getName();
}
public Class<?> getInterfaceClass() {
return interfaceClass;
}
@Override
public Object invoke(Object proxy, Method method, Object[] inArgs)
throws Throwable {
// Intercept Object methods
if (OBJECT_TOSTRING.equals(method)) {
return "Proxy interface to " + nativeLibrary;
} else if (OBJECT_HASHCODE.equals(method)) {
return Integer.valueOf(hashCode());
} else if (OBJECT_EQUALS.equals(method)) {
Object o = inArgs[0];
if (o != null && Proxy.isProxyClass(o.getClass())) {
return Function.valueOf(Proxy.getInvocationHandler(o) == this);
}
return Boolean.FALSE;
}
// Using the double-checked locking pattern to speed up function calls
FunctionInfo f = functions.get(method);
if(f == null) {
synchronized(functions) {
f = functions.get(method);
if (f == null) {
boolean isDefault = ReflectionUtils.isDefault(method);
if(! isDefault) {
boolean isVarArgs = Function.isVarArgs(method);
InvocationHandler handler = null;
if (invocationMapper != null) {
handler = invocationMapper.getInvocationHandler(nativeLibrary, method);
}
Function function = null;
Class<?>[] parameterTypes = null;
Map<String, Object> options = null;
if (handler == null) {
// Find the function to invoke
function = nativeLibrary.getFunction(method.getName(), method);
parameterTypes = method.getParameterTypes();
options = new HashMap<String, Object>(this.options);
options.put(Function.OPTION_INVOKING_METHOD, method);
}
f = new FunctionInfo(handler, function, parameterTypes, isVarArgs, options);
} else {
f = new FunctionInfo(ReflectionUtils.getMethodHandle(method));
}
functions.put(method, f);
}
}
}
if (f.methodHandle != null) {
return ReflectionUtils.invokeDefaultMethod(proxy, f.methodHandle, inArgs);
} else {
if (f.isVarArgs) {
inArgs = Function.concatenateVarArgs(inArgs);
}
if (f.handler != null) {
return f.handler.invoke(proxy, method, inArgs);
}
return f.function.invoke(method, f.parameterTypes, method.getReturnType(), inArgs, f.options);
}
}
}