private Object convertArgument( Object[] args, int index, Method invokingMethod, TypeMapper mapper, boolean allowObjects, Class expectedType) { Object arg = args[index]; if (arg != null) { Class type = arg.getClass(); ToNativeConverter converter = null; if (NativeMapped.class.isAssignableFrom(type)) { converter = NativeMappedConverter.getInstance(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.autoWrite(); if (struct instanceof Structure.ByValue) { // Double-check against the method signature, if available Class ptype = struct.getClass(); if (invokingMethod != null) { Class[] ptypes = invokingMethod.getParameterTypes(); if (isVarArgs(invokingMethod)) { if (index < ptypes.length - 1) { ptype = ptypes[index]; } else { Class etype = ptypes[ptypes.length - 1].getComponentType(); if (etype != Object.class) { ptype = etype; } } } else { ptype = ptypes[index]; } } if (Structure.ByValue.class.isAssignableFrom(ptype)) { return struct; } } 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 Boolean.TRUE.equals(arg) ? INTEGER_TRUE : INTEGER_FALSE; } else if (String[].class == argClass) { return new StringArray((String[]) arg, encoding); } else if (WString[].class == argClass) { return new StringArray((WString[]) arg); } else if (Pointer[].class == argClass) { return new PointerArray((Pointer[]) arg); } else if (NativeMapped[].class.isAssignableFrom(argClass)) { return new NativeMappedArray((NativeMapped[]) arg); } else if (Structure[].class.isAssignableFrom(argClass)) { // If the signature is Structure[], disallow // Structure.ByReference[] and Structure.ByReference elements Structure[] ss = (Structure[]) arg; Class type = argClass.getComponentType(); boolean byRef = Structure.ByReference.class.isAssignableFrom(type); if (expectedType != null) { if (!Structure.ByReference[].class.isAssignableFrom(expectedType)) { if (byRef) { throw new IllegalArgumentException( "Function " + getName() + " declared Structure[] at parameter " + index + " but array of " + type + " was passed"); } for (int i = 0; i < ss.length; i++) { if (ss[i] instanceof Structure.ByReference) { throw new IllegalArgumentException( "Function " + getName() + " declared Structure[] at parameter " + index + " but element " + i + " is of Structure.ByReference type"); } } } } if (byRef) { Structure.autoWrite(ss); Pointer[] pointers = new Pointer[ss.length + 1]; for (int i = 0; i < ss.length; i++) { pointers[i] = ss[i] != null ? ss[i].getPointer() : null; } return new PointerArray(pointers); } else if (ss.length == 0) { throw new IllegalArgumentException("Structure array must have non-zero length"); } else if (ss[0] == null) { Structure.newInstance(type).toArray(ss); return ss[0].getPointer(); } else { Structure.autoWrite(ss); return ss[0].getPointer(); } } else if (argClass.isArray()) { throw new IllegalArgumentException( "Unsupported array argument type: " + argClass.getComponentType()); } else if (allowObjects) { return arg; } else if (!Native.isSupportedNativeType(arg.getClass())) { throw new IllegalArgumentException( "Unsupported argument type " + arg.getClass().getName() + " at parameter " + index + " of function " + getName()); } return arg; }
/** * 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); Class[] paramTypes = invokingMethod != null ? invokingMethod.getParameterTypes() : null; boolean allowObjects = Boolean.TRUE.equals(options.get(Library.OPTION_ALLOW_OBJECTS)); boolean isVarArgs = args.length > 0 && invokingMethod != null ? isVarArgs(invokingMethod) : false; for (int i = 0; i < args.length; i++) { Class paramType = invokingMethod != null ? (isVarArgs && i >= paramTypes.length - 1 ? paramTypes[paramTypes.length - 1].getComponentType() : paramTypes[i]) : null; args[i] = convertArgument(args, i, invokingMethod, mapper, allowObjects, paramType); } Class nativeReturnType = returnType; FromNativeConverter resultConverter = null; if (NativeMapped.class.isAssignableFrom(returnType)) { NativeMappedConverter tc = NativeMappedConverter.getInstance(returnType); resultConverter = tc; nativeReturnType = tc.nativeType(); } else if (mapper != null) { resultConverter = mapper.getFromNativeConverter(returnType); if (resultConverter != null) { nativeReturnType = resultConverter.nativeType(); } } Object result = invoke(args, nativeReturnType, allowObjects); // 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 inArg = inArgs[i]; if (inArg == null) continue; if (inArg instanceof Structure) { if (!(inArg instanceof Structure.ByValue)) { ((Structure) inArg).autoRead(); } } else if (args[i] instanceof PostCallRead) { ((PostCallRead) args[i]).read(); if (args[i] instanceof PointerArray) { PointerArray array = (PointerArray) args[i]; if (Structure.ByReference[].class.isAssignableFrom(inArg.getClass())) { Class type = inArg.getClass().getComponentType(); Structure[] ss = (Structure[]) inArg; 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(inArg.getClass())) { Structure.autoRead((Structure[]) inArg); } } } return result; }