-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathLibrary.java
More file actions
247 lines (230 loc) · 10.7 KB
/
Library.java
File metadata and controls
247 lines (230 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
/** Derive from this interface for all native library definitions.
*
* Define an instance of your library like this:
* <pre><code>
* MyNativeLibrary INSTANCE = (MyNativeLibrary)
* Native.loadLibrary("mylib", MyNativeLibrary.class);
* </code></pre>
* <p>
* By convention, method names are identical to the native names, although you
* can map java names to different native names by providing a
* {@link FunctionMapper} as a value for key {@link #OPTION_FUNCTION_MAPPER}
* in the options map passed to the
* {@link Native#loadLibrary(String, Class, Map)} call.
* <p>
* Although the names for structures and structure fields may be chosen
* arbitrarily, they should correspond as closely as possible to the native
* definitions. The same is true for parameter names.
* <p>
* This interface supports multiple, concurrent invocations of any library
* methods on the Java side. Check your library documentation for its
* multi-threading requirements on the native side. If a library is not safe
* for simultaneous multi-threaded access, consider using
* {@link Native#synchronizedLibrary} to prevent simultaneous multi-threaded
* access to the native code.
* <p>
* <b>Optional fields</b><br>
* Interface options will be automatically propagated to structures defined
* within the library provided a call to
* {@link Native#loadLibrary(String,Class,Map)} is made prior to instantiating
* any of those structures. One common way of ensuring this is to declare
* an <b>INSTANCE</b> field in the interface which holds the
* <code>loadLibrary</code> result.
* <p>
* <b>OPTIONS</b> (an instance of {@link Map}),
* <b>TYPE_MAPPER</b> (an instance of {@link TypeMapper}),
* <b>STRUCTURE_ALIGNMENT</b> (one of the alignment types defined in
* {@link Structure}), and <b>STRING_ENCODING</b> (a {@link String}) may also
* be defined. If no instance of the interface has been instantiated, these
* fields will be used to determine customization settings for structures and
* methods defined within the interface.
* <p>
*
* @author Todd Fast, [email protected]
* @author Timothy Wall, [email protected]
*/
public interface Library {
/** Option key for a {@link TypeMapper} for the library. */
String OPTION_TYPE_MAPPER = "type-mapper";
/** Option key for a {@link FunctionMapper} for the library. */
String OPTION_FUNCTION_MAPPER = "function-mapper";
/** Option key for an {@link InvocationMapper} for the library. */
String OPTION_INVOCATION_MAPPER = "invocation-mapper";
/** Option key for structure alignment type ({@link Integer}), which should
* be one of the predefined alignment types in {@link Structure}.
*/
String OPTION_STRUCTURE_ALIGNMENT = "structure-alignment";
/** <p>Option key for per-library String encoding. This affects conversions
* between Java unicode and native (<code>const char*</code>) strings (as
* arguments or Structure fields).
* </p>
* Defaults to {@link Native#getDefaultStringEncoding()}.
*/
String OPTION_STRING_ENCODING = "string-encoding";
/** Option key for a boolean flag to allow any Java class instance as a
parameter. If no type mapper is found, the object is passed as a
pointer.
<em>NOTE:</em> This is for use with raw JNI interactions via the
JNIEnv data structure.
*/
String OPTION_ALLOW_OBJECTS = "allow-objects";
/** Calling convention for the entire library. */
String OPTION_CALLING_CONVENTION = "calling-convention";
/** Flags to use when opening the native library (see {@link Native#open(String,int)}) */
String OPTION_OPEN_FLAGS = "open-flags";
/** <p>Class loader to use when searching for native libraries on the
* resource path (classpath). If not provided the current thread's
* context class loader is used.</p>
* If extracted from the resource path (i.e. bundled in a jar file), the
* loaded library's lifespan will mirror that of the class loader, which
* means you can use the same library in isolated contexts without
* conflict.
*/
String OPTION_CLASSLOADER = "classloader";
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 Map<String, ?> options;
final Class<?>[] parameterTypes;
FunctionInfo(InvocationHandler handler, Function function, Class<?>[] parameterTypes, boolean isVarArgs, Map<String, ?> options) {
this.handler = handler;
this.function = function;
this.isVarArgs = isVarArgs;
this.options = options;
this.parameterTypes = parameterTypes;
}
}
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<String, ?> 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 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);
functions.put(method, f);
}
}
}
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);
}
}
}