GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
ProcAddressTable.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2013 JogAmp Community. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistribution in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use
34 * in the design, construction, operation or maintenance of any nuclear
35 * facility.
36 */
37package com.jogamp.gluegen.runtime;
38
39import com.jogamp.common.os.DynamicLookupHelper;
40import com.jogamp.common.util.SecurityUtil;
41
42import java.io.BufferedOutputStream;
43import java.io.File;
44import java.io.FileOutputStream;
45import java.io.IOException;
46import java.io.PrintStream;
47import java.lang.reflect.AccessibleObject;
48import java.lang.reflect.Field;
49import java.security.PrivilegedAction;
50import java.util.Iterator;
51import java.util.LinkedHashSet;
52import java.util.Map;
53import java.util.Set;
54import java.util.SortedMap;
55import java.util.TreeMap;
56
57/**
58 * Superclass for all generated ProcAddressTables.
59 *
60 * A ProcAddressTable is a cache of pointers to the dynamically-linkable C
61 * functions this autogenerated Java binding has exposed. Some
62 * libraries such as OpenGL, OpenAL and others define function pointer
63 * signatures rather than statically linkable entry points for the
64 * purposes of being able to query at run-time whether a particular
65 * extension is available. This table acts as a cache of these
66 * function pointers. Each function pointer is typically looked up at
67 * run-time by a platform-dependent mechanism such as dlsym(),
68 * wgl/glXGetProcAddress(), or alGetProcAddress(). If the field containing the function
69 * pointer is 0, the function is considered to be unavailable and can
70 * not be called.
71 *
72 * @author Kenneth Russel
73 * @author Michael Bien
74 * @author Sven Gothel
75 *
76 * @see FunctionAddressResolver
77 * @see DynamicLookupHelper
78 */
79public abstract class ProcAddressTable {
80
81 private static final String PROCADDRESS_VAR_PREFIX = "_addressof_";
82 private static final int PROCADDRESS_VAR_PREFIX_LEN = PROCADDRESS_VAR_PREFIX.length();
83
84 protected static boolean DEBUG;
85 protected static String DEBUG_PREFIX;
86 protected static int debugNum;
87
88 private final FunctionAddressResolver resolver;
89
90 static {
91 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
92 @Override
93 public Object run() {
94 DEBUG = (System.getProperty("jogamp.debug.ProcAddressHelper") != null);
95 if (DEBUG) {
96 DEBUG_PREFIX = System.getProperty("jogamp.debug.ProcAddressHelper.prefix");
97 }
98 return null;
99 }
100 });
101 }
102
104 this(new One2OneResolver());
105 }
106
108 this.resolver = resolver;
109 }
110
111
112 /**
113 * Resets the complete table.
114 * <p>
115 * If a {@link SecurityManager} is installed, user needs link permissions
116 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>!
117 * </p>
118 * @throws SecurityException if user is not granted access for all libraries.
119 */
120 public void reset(final DynamicLookupHelper lookup) throws SecurityException, RuntimeException {
121 if(null==lookup) {
122 throw new RuntimeException("Passed null DynamicLookupHelper");
123 }
124
125 final Field[] fields = getClass().getDeclaredFields();
126
127 final PrintStream dout;
128 if (DEBUG) {
129 dout = getDebugOutStream();
130 dout.println(getClass().getName()+".reset() (w/ "+fields.length+" prospective fields)");
131 } else {
132 dout = null;
133 }
134
135 // All at once - performance.
136 AccessibleObject.setAccessible(fields, true);
137 lookup.claimAllLinkPermission();
138 try {
139 for (int i = 0; i < fields.length; ++i) {
140 final String fieldName = fields[i].getName();
141 if ( isAddressField(fieldName) ) {
142 final String funcName = fieldToFunctionName(fieldName);
143 setEntry(fields[i], funcName, lookup);
144 }
145 }
146 } finally {
147 lookup.releaseAllLinkPermission();
148 }
149
150 if (DEBUG) {
151 dout.flush();
152 if (DEBUG_PREFIX != null) {
153 dout.close();
154 }
155 }
156 }
157
158 /**
159 * Initializes the mapping for a single function.
160 * <p>
161 * If a {@link SecurityManager} is installed, user needs link permissions
162 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>!
163 * </p>
164 *
165 * @throws IllegalArgumentException if this function is not in this table.
166 * @throws SecurityException if user is not granted access for all libraries.
167 */
168 public void initEntry(final String name, final DynamicLookupHelper lookup) throws SecurityException, IllegalArgumentException {
169 final Field addressField = fieldForFunction(name);
170 addressField.setAccessible(true);
171 setEntry(addressField, name, lookup);
172 }
173
174 private final void setEntry(final Field addressField, final String funcName, final DynamicLookupHelper lookup) throws SecurityException {
175 try {
176 assert (addressField.getType() == Long.TYPE);
177 final long newProcAddress = resolver.resolve(funcName, lookup); // issues SecurityUtil.checkLinkPermission(String)
178 addressField.setLong(this, newProcAddress);
179 if (DEBUG) {
180 getDebugOutStream().println(" " + addressField.getName() + " -> 0x" + Long.toHexString(newProcAddress));
181 }
182 } catch (final Exception e) {
183 throw new RuntimeException("Can not get proc address for method \""
184 + funcName + "\": Couldn't set value of field \"" + addressField, e);
185 }
186 }
187
188 private final String fieldToFunctionName(final String addressFieldName) {
189 return addressFieldName.substring(PROCADDRESS_VAR_PREFIX_LEN);
190 }
191
192 private final Field fieldForFunction(final String name) throws IllegalArgumentException {
193 try {
194 return getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name);
195 } catch (final NoSuchFieldException ex) {
196 throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex);
197 }
198 }
199
200 /**
201 * Warning: Returns an accessible probably protected field!
202 * <p>
203 * Caller should have checked link permissions
204 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>
205 * <i>if</i> exposing the field or address!
206 * </p>
207 */
208 private final Field fieldForFunctionInSec(final String name) throws IllegalArgumentException {
209 return SecurityUtil.doPrivileged(new PrivilegedAction<Field>() {
210 @Override
211 public Field run() {
212 try {
213 final Field addressField = ProcAddressTable.this.getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name);
214 addressField.setAccessible(true); // we need to read the protected value!
215 return addressField;
216 } catch (final NoSuchFieldException ex) {
217 throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex);
218 }
219 }
220 } );
221 }
222
223 private final boolean isAddressField(final String fieldName) {
224 return fieldName.startsWith(PROCADDRESS_VAR_PREFIX);
225 }
226
227 private final static PrintStream getDebugOutStream() {
228 PrintStream out = null;
229 if (DEBUG) {
230 if (DEBUG_PREFIX != null) {
231 try {
232 out = new PrintStream(new BufferedOutputStream(new FileOutputStream(DEBUG_PREFIX + File.separatorChar
233 + "procaddresstable-" + (++debugNum) + ".txt")));
234 } catch (final IOException e) {
235 e.printStackTrace();
236 out = System.err;
237 }
238 } else {
239 out = System.err;
240 }
241 }
242 return out;
243 }
244
245 /**
246 * Returns this table as map with the function name as key and the address as value.
247 */
248 private final Map<String, Long> toMap() {
249 final SortedMap<String, Long> map = new TreeMap<String, Long>();
250
251 final Field[] fields = getClass().getFields();
252 try {
253 for (int i = 0; i < fields.length; ++i) {
254 final String addressFieldName = fields[i].getName();
255 if (isAddressField(addressFieldName)) {
256 map.put(fieldToFunctionName(addressFieldName), (Long)fields[i].get(this));
257 }
258 }
259 } catch (final IllegalArgumentException ex) {
260 throw new RuntimeException(ex);
261 } catch (final IllegalAccessException ex) {
262 throw new RuntimeException(ex);
263 }
264
265 return map;
266 }
267
268 /**
269 * Returns true only if non null function pointer to this function exists.
270 */
271 public final boolean isFunctionAvailable(final String functionName) {
272 try{
273 return isFunctionAvailableImpl(functionName);
274 } catch (final IllegalArgumentException ex) {
275 return false;
276 }
277 }
278
279 /**
280 * This is a convenience method to query the native function existence by name.
281 * <p>
282 * It lets you avoid having to
283 * manually compute the &quot;{@link #PROCADDRESS_VAR_PREFIX} + &lt;functionName&gt;&quot;
284 * member variable name and look it up via reflection.
285 * </p>
286 *
287 * @throws IllegalArgumentException if this function is not in this table.
288 */
289 protected boolean isFunctionAvailableImpl(final String functionName) throws IllegalArgumentException {
290 final Field addressField = fieldForFunctionInSec(functionName);
291 try {
292 return 0 != addressField.getLong(this);
293 } catch (final IllegalAccessException ex) {
294 throw new RuntimeException(ex);
295 }
296 }
297
298 /**
299 * This is a convenience method to query the native function handle by name.
300 * <p>
301 * It lets you avoid having to
302 * manually compute the &quot;{@link #PROCADDRESS_VAR_PREFIX} + &lt;functionName&gt;&quot;
303 * member variable name and look it up via reflection.
304 * </p>
305 * <p>
306 * If a {@link SecurityManager} is installed, user needs link permissions
307 * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>!
308 * </p>
309 *
310 * @throws IllegalArgumentException if this function is not in this table.
311 * @throws SecurityException if user is not granted access for all libraries.
312 */
313 public long getAddressFor(final String functionName) throws SecurityException, IllegalArgumentException {
315 final Field addressField = fieldForFunctionInSec(functionName);
316 try {
317 return addressField.getLong(this);
318 } catch (final IllegalAccessException ex) {
319 throw new RuntimeException(ex);
320 }
321 }
322
323 /**
324 * Returns all functions pointing to null.
325 */
326 public final Set<String> getNullPointerFunctions() {
327 final Map<String, Long> table = toMap();
328 final Set<String> nullPointers = new LinkedHashSet<String>();
329 for (final Iterator<Map.Entry<String, Long>> it = table.entrySet().iterator(); it.hasNext();) {
330 final Map.Entry<String, Long> entry = it.next();
331 final long address = entry.getValue().longValue();
332 if(address == 0) {
333 nullPointers.add(entry.getKey());
334 }
335 }
336 return nullPointers;
337 }
338
339 @Override
340 public final String toString() {
341 return getClass().getName()+""+toMap();
342 }
343
344 private static class One2OneResolver implements FunctionAddressResolver {
345 @Override
346 public long resolve(final String name, final DynamicLookupHelper lookup) throws SecurityException {
347 return lookup.dynamicLookupFunction(name);
348 }
349 }
350
351
352}
static< T > T doPrivileged(final PrivilegedAction< T > o)
Call wrapper for java.security.AccessController#doPrivileged(PrivilegedAction).
static final void checkAllLinkPermission()
Throws an SecurityException if an installed SecurityManager does not permit to dynamically link to al...
Superclass for all generated ProcAddressTables.
void reset(final DynamicLookupHelper lookup)
Resets the complete table.
long getAddressFor(final String functionName)
This is a convenience method to query the native function handle by name.
boolean isFunctionAvailableImpl(final String functionName)
This is a convenience method to query the native function existence by name.
void initEntry(final String name, final DynamicLookupHelper lookup)
Initializes the mapping for a single function.
final boolean isFunctionAvailable(final String functionName)
Returns true only if non null function pointer to this function exists.
ProcAddressTable(final FunctionAddressResolver resolver)
final Set< String > getNullPointerFunctions()
Returns all functions pointing to null.
Interface callers may use ProcAddressHelper's reset helper method to install function pointers into a...
long resolve(String name, DynamicLookupHelper lookup)
Resolves the name of the function bound to the method and returns the address.