All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jna.Function Maven / Gradle / Ivy

/* This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 

* This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package com.sun.jna; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * An abstraction for a native function pointer. An instance of * Function represents a pointer to some native function. * {@link #invoke(Class,Object[],Map)} is the primary means to call * the function. * * @author Sheng Liang, originator * @author Todd Fast, suitability modifications * @see Pointer */ public class Function extends Pointer { /** Maximum number of arguments supported by a JNA function call. */ public static final int MAX_NARGS = 256; /** Standard C calling convention. */ public static final int C_CONVENTION = 0; /** First alternate convention (currently used only for w32 stdcall). */ public static final int ALT_CONVENTION = 1; /** * Obtain a Function representing a native * function that follows the standard "C" calling convention. * *

The allocated instance represents a pointer to the named native * function from the named library, called with the standard "C" calling * convention. * * @param libraryName * Library in which to find the native function * @param functionName * Name of the native function to be linked with * @throws {@link UnsatisfiedLinkError} if the library is not found or * the given function name is not found within the library. */ public static Function getFunction(String libraryName, String functionName) { return NativeLibrary.getInstance(libraryName).getFunction(functionName); } /** * Obtain a Function representing a native * function that follows a given calling convention. * *

The allocated instance represents a pointer to the named native * function from the named library, called with the named calling * convention. * * @param libraryName * Library in which to find the function * @param functionName * Name of the native function to be linked with * @param callConvention * Call convention used by the native function * @throws {@link UnsatisfiedLinkError} if the library is not found or * the given function name is not found within the library. */ public static Function getFunction(String libraryName, String functionName, int callConvention) { return NativeLibrary.getInstance(libraryName).getFunction(functionName, callConvention); } // Keep a reference to the NativeLibrary so it does not get garbage collected // until the function is private NativeLibrary library; private String functionName; private int callingConvention; /** For internal JNA use. */ static final String OPTION_INVOKING_METHOD = "invoking-method"; /** * Create a new Function that is linked with a native * function that follows the given calling convention. * *

The allocated instance represents a pointer to the named native * function from the supplied library, called with the given calling * convention. * * @param library * {@link NativeLibrary} in which to find the function * @param functionName * Name of the native function to be linked with * @param callingConvention * Calling convention used by the native function * @throws {@link UnsatisfiedLinkError} if the given function name is * not found within the library. */ Function(NativeLibrary library, String functionName, int callingConvention) { checkCallingConvention(callingConvention); if (functionName == null) throw new NullPointerException("Function name must not be null"); this.library = library; this.functionName = functionName; this.callingConvention = callingConvention; try { this.peer = library.getSymbolAddress(functionName); } catch(UnsatisfiedLinkError e) { throw new UnsatisfiedLinkError("Error looking up function '" + functionName + "': " + e.getMessage()); } } /** * Create a new Function that is linked with a native * function that follows the given calling convention. * *

The allocated instance represents a pointer to the given * function address, called with the given calling * convention. * * @param functionAddress * Address of the native function * @param callingConvention * Calling convention used by the native function */ Function(Pointer functionAddress, int callingConvention) { checkCallingConvention(callingConvention); if (functionAddress == null || functionAddress.peer == 0) { throw new NullPointerException("Function address may not be null"); } this.functionName = functionAddress.toString(); this.callingConvention = callingConvention; this.peer = functionAddress.peer; } private void checkCallingConvention(int convention) throws IllegalArgumentException { switch(convention) { case C_CONVENTION: case ALT_CONVENTION: break; default: throw new IllegalArgumentException("Unrecognized calling convention: " + convention); } } public String getName() { return functionName; } public int getCallingConvention() { return callingConvention; } /** Invoke the native function with the given arguments, returning the * native result as an Object. */ public Object invoke(Class returnType, Object[] inArgs) { return invoke(returnType, inArgs, Collections.EMPTY_MAP); } /** Invoke the native function with the given arguments, returning the * native result as an Object. */ public Object invoke(Class returnType, Object[] inArgs, Map options) { // Clone the argument array to obtain a scratch space for modified // types/values Object[] args = { }; if (inArgs != null) { if (inArgs.length > MAX_NARGS) { throw new UnsupportedOperationException("Maximum argument count is " + MAX_NARGS); } args = new Object[inArgs.length]; System.arraycopy(inArgs, 0, args, 0, args.length); } TypeMapper mapper = (TypeMapper)options.get(Library.OPTION_TYPE_MAPPER); Method invokingMethod = (Method)options.get(OPTION_INVOKING_METHOD); for (int i=0; i < args.length; i++) { args[i] = convertArgument(args, i, invokingMethod, mapper); } Class nativeType = returnType; FromNativeConverter resultConverter = null; if (NativeMapped.class.isAssignableFrom(returnType)) { NativeMappedConverter tc = new NativeMappedConverter(returnType); resultConverter = tc; nativeType = tc.nativeType(); } else if (mapper != null) { resultConverter = mapper.getFromNativeConverter(returnType); if (resultConverter != null) { nativeType = resultConverter.nativeType(); } } Object result = invoke(args, nativeType); // Convert the result to a custom value/type if appropriate if (resultConverter != null) { FromNativeContext context; if (invokingMethod != null) { context = new MethodResultContext(returnType, this, inArgs, invokingMethod); } else { context = new FunctionResultContext(returnType, this, inArgs); } result = resultConverter.fromNative(result, context); } // Sync all memory which might have been modified by the native call if (inArgs != null) { for (int i=0; i < inArgs.length; i++) { Object arg = inArgs[i]; if (arg == null) continue; if (arg instanceof Structure) { if (!(arg instanceof Structure.ByValue)) { ((Structure)arg).read(); } } else if (args[i] instanceof StringArray) { ((StringArray)args[i]).read(); } else if (args[i] instanceof PointerArray) { PointerArray array = (PointerArray)args[i]; array.read(); if (Structure.ByReference[].class.isAssignableFrom(arg.getClass())) { Class type = arg.getClass().getComponentType(); Structure[] ss = (Structure[])arg; for (int si=0;si < ss.length;si++) { Pointer p = array.getPointer(Pointer.SIZE * si); ss[si] = Structure.updateStructureByReference(type, ss[si], p); } } } else if (Structure[].class.isAssignableFrom(arg.getClass())) { Structure[] ss = (Structure[])arg; for (int si=0;si < ss.length;si++) { ss[si].read(); } } } } return result; } /** @see NativeLibrary#NativeLibrary(String,String,long) implementation */ Object invoke(Object[] args, Class returnType) { Object result = null; if (returnType == null || returnType==void.class || returnType==Void.class) { invokeVoid(callingConvention, args); result = null; } else if (returnType==boolean.class || returnType==Boolean.class) { result = Boolean.valueOf(invokeInt(callingConvention, args) != 0); } else if (returnType==byte.class || returnType==Byte.class) { result = new Byte((byte)invokeInt(callingConvention, args)); } else if (returnType==short.class || returnType==Short.class) { result = new Short((short)invokeInt(callingConvention, args)); } else if (returnType==char.class || returnType==Character.class) { result = new Character((char)invokeInt(callingConvention, args)); } else if (returnType==int.class || returnType==Integer.class) { result = new Integer(invokeInt(callingConvention, args)); } else if (returnType==long.class || returnType==Long.class) { result = new Long(invokeLong(callingConvention, args)); } else if (returnType==float.class || returnType==Float.class) { result = new Float(invokeFloat(callingConvention, args)); } else if (returnType==double.class || returnType==Double.class) { result = new Double(invokeDouble(callingConvention, args)); } else if (returnType==String.class) { result = invokeString(callingConvention, args, false); } else if (returnType==WString.class) { String s = invokeString(callingConvention, args, true); result = s != null ? new WString(s) : null; } else if (Pointer.class.isAssignableFrom(returnType)) { result = invokePointer(callingConvention, args); } else if (Structure.class.isAssignableFrom(returnType)) { if (Structure.ByValue.class.isAssignableFrom(returnType)) { Structure s = invokeStructure(callingConvention, args, Structure.newInstance(returnType)); s.read(); result = s; } else { result = invokePointer(callingConvention, args); if (result != null) { Structure s = Structure.newInstance(returnType); s.useMemory((Pointer)result); s.read(); result = s; } } } else if (Callback.class.isAssignableFrom(returnType)) { result = invokePointer(callingConvention, args); if (result != null) { result = CallbackReference.getCallback(returnType, (Pointer)result); } } else { throw new IllegalArgumentException("Unsupported return type " + returnType + " in function " + getName()); } return result; } private Object convertArgument(Object[] args, int index, Method invokingMethod, TypeMapper mapper) { Object arg = args[index]; if (arg != null) { Class type = arg.getClass(); ToNativeConverter converter = null; if (NativeMapped.class.isAssignableFrom(type)) { converter = new NativeMappedConverter(type); } else if (mapper != null) { converter = mapper.getToNativeConverter(type); } if (converter != null) { ToNativeContext context; if (invokingMethod != null) { context = new MethodParameterContext(this, args, index, invokingMethod) ; } else { context = new FunctionParameterContext(this, args, index); } arg = converter.toNative(arg, context); } } if (arg == null || isPrimitiveArray(arg.getClass())) { return arg; } Class argClass = arg.getClass(); // Convert Structures to native pointers if (arg instanceof Structure) { Structure struct = (Structure)arg; struct.write(); if (struct instanceof Structure.ByValue) { return struct; } else { return struct.getPointer(); } } // Convert Callback to Pointer else if (arg instanceof Callback) { return CallbackReference.getFunctionPointer((Callback)arg); } // String arguments are converted to native pointers here rather // than in native code so that the values will be valid until // this method returns. // Convert String to native pointer (const) else if (arg instanceof String) { return new NativeString((String)arg, false).getPointer(); } // Convert WString to native pointer (const) else if (arg instanceof WString) { return new NativeString(arg.toString(), true).getPointer(); } // Default conversion of boolean to int; if you want something // different, use a ToNativeConverter else if (arg instanceof Boolean) { return new Integer(Boolean.TRUE.equals(arg) ? -1 : 0); } else if (String[].class == argClass) { return new StringArray((String[])arg); } else if (WString[].class == argClass) { return new StringArray((WString[])arg); } else if (Pointer[].class == argClass) { return new PointerArray((Pointer[])arg); } else if (Structure[].class.isAssignableFrom(argClass)) { Structure[] ss = (Structure[])arg; Class type = argClass.getComponentType(); boolean byRef = Structure.ByReference.class.isAssignableFrom(type); if (byRef) { Pointer[] pointers = new Pointer[ss.length + 1]; for (int i=0;i < ss.length;i++) { pointers[i] = ss[i].getPointer(); } return new PointerArray(pointers); } else if (ss.length == 0) { throw new IllegalArgumentException("Structure array must have non-zero length"); } else if (ss[0] == null) { // Initialize uninitialized arrays of Structure to point // to a single block of memory Structure struct = Structure.newInstance(type); int size = struct.size(); Memory m = new Memory(size * ss.length); struct.useMemory(m); Structure[] tmp = struct.toArray(ss.length); for (int si=0;si < ss.length;si++) { ss[si] = tmp[si]; } return ss[0].getPointer(); } else { Pointer base = ss[0].getPointer(); int size = ss[0].size(); ss[0].write(); for (int si=1;si < ss.length;si++) { if (ss[si].getPointer().peer != base.peer + size*si) { String msg = "Structure array elements must use" + " contiguous memory (at element index " + si + ")"; throw new IllegalArgumentException(msg); } ss[si].write(); } return base; } } else if (argClass.isArray()){ throw new IllegalArgumentException("Unsupported array argument type: " + argClass.getComponentType()); } if (arg != null && !Native.isSupportedNativeType(arg.getClass())) { throw new IllegalArgumentException("Unsupported argument type " + arg.getClass().getName() + " at parameter " + index + " of function " + getName()); } return arg; } private boolean isPrimitiveArray(Class argClass) { return argClass.isArray() && argClass.getComponentType().isPrimitive(); } /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @return The value returned by the target native function */ private native int invokeInt(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @return The value returned by the target native function */ private native long invokeLong(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param args * Arguments to pass to the native function */ public void invoke(Object[] args) { invoke(Void.class, args); } /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function */ private native void invokeVoid(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @return The value returned by the target native function */ private native float invokeFloat(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @return The value returned by the target native function */ private native double invokeDouble(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @param wide whether the native string uses wchar_t; * if false, char is assumed * @return The value returned by the target native function, as a String */ private String invokeString(int callingConvention, Object[] args, boolean wide) { Pointer ptr = invokePointer(callingConvention, args); String s = null; if (ptr != null) { if (wide) s = ptr.getString(0, wide); else s = ptr.getString(0); } return s; } /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @return The native pointer returned by the target native function */ private native Pointer invokePointer(int callingConvention, Object[] args); /** * Call the native function being represented by this object * * @param callingConvention calling convention to be used * @param args * Arguments to pass to the native function * @param struct Pre-allocated structure to hold the result * @return The native pointer returned by the target native function */ private native Structure invokeStructure(int callingConvention, Object[] args, Structure result); /** Provide a human-readable representation of this object. */ public String toString() { if (library != null) { return "native function " + functionName + "(" + library.getName() + ")@0x" + Long.toHexString(peer); } return "native function@0x" + Long.toHexString(peer); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Pointer.class, args)}. */ public Pointer invokePointer(Object[] args) { return (Pointer)invoke(Pointer.class, args); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(String.class, args)} * or {@link #invoke(Class,Object[]) invoke(WString.class, args)} * @param args Arguments passed to native function * @param wide Whether the return value is of type wchar_t*; * if false, the return value is of type char*. */ public String invokeString(Object[] args, boolean wide) { Object o = invoke(wide ? WString.class : String.class, args); return o != null ? o.toString() : null; } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Integer.class, args)}. */ public int invokeInt(Object[] args) { return ((Integer)invoke(Integer.class, args)).intValue(); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Long.class, args)}. */ public long invokeLong(Object[] args) { return ((Long)invoke(Long.class, args)).longValue(); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Float.class, args)}. */ public float invokeFloat(Object[] args) { return ((Float)invoke(Float.class, args)).floatValue(); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Double.class, args)}. */ public double invokeDouble(Object[] args) { return ((Double)invoke(Double.class, args)).doubleValue(); } /** Convenience method for * {@link #invoke(Class,Object[]) invoke(Void.class, args)}. */ public void invokeVoid(Object[] args) { invoke(Void.class, args); } /** Two function pointers are equal if they share the same peer address * and calling convention. */ public boolean equals(Object o) { if (o instanceof Function) { Function other = (Function)o; return other.callingConvention == this.callingConvention && other.peer == this.peer; } return false; } /** Concatenate varargs with normal args to obtain a simple argument * array. */ static Object[] concatenateVarArgs(Object[] inArgs) { // If the final argument is an array of something other than // primitives, Structure, or String, treat it as varargs and // concatenate the previous arguments with the varargs elements. if (inArgs != null && inArgs.length > 0) { Object lastArg = inArgs[inArgs.length-1]; Class argType = lastArg != null ? lastArg.getClass() : null; if (argType != null && argType.isArray()) { Object[] varArgs = (Object[])lastArg; Object[] fullArgs = new Object[inArgs.length+varArgs.length]; System.arraycopy(inArgs, 0, fullArgs, 0, inArgs.length-1); System.arraycopy(varArgs, 0, fullArgs, inArgs.length-1, varArgs.length); // For convenience, always append a NULL argument to the end // of varargs, whether the called API requires it or not. If // it is not needed, it will be ignored, but if it *is* // required, it avoids forcing the Java client to always // explicitly add it. fullArgs[fullArgs.length-1] = null; inArgs = fullArgs; } } return inArgs; } /** Varargs are only supported on 1.5+. */ static boolean isVarArgs(Method m) { try { Method v = m.getClass().getMethod("isVarArgs", new Class[0]); return Boolean.TRUE.equals(v.invoke(m, new Object[0])); } catch (SecurityException e) { } catch (NoSuchMethodException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } return false; } private static class PointerArray extends Memory { private Pointer[] original; public PointerArray(Pointer[] arg) { super(Pointer.SIZE * (arg.length+1)); this.original = arg; for (int i=0;i < arg.length;i++) { setPointer(i*Pointer.SIZE, arg[i]); } setPointer(Pointer.SIZE*arg.length, null); } public void read() { for (int i=0;i < original.length;i++) { original[i] = getPointer(i * Pointer.SIZE); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy