/* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2013 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. */ package com.jogamp.gluegen.runtime; import com.jogamp.common.os.DynamicLookupHelper; import com.jogamp.common.util.SecurityUtil; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; /** * Superclass for all generated ProcAddressTables. * * A ProcAddressTable is a cache of pointers to the dynamically-linkable C * functions this autogenerated Java binding has exposed. Some * libraries such as OpenGL, OpenAL and others define function pointer * signatures rather than statically linkable entry points for the * purposes of being able to query at run-time whether a particular * extension is available. This table acts as a cache of these * function pointers. Each function pointer is typically looked up at * run-time by a platform-dependent mechanism such as dlsym(), * wgl/glXGetProcAddress(), or alGetProcAddress(). If the field containing the function * pointer is 0, the function is considered to be unavailable and can * not be called. * * @author Kenneth Russel * @author Michael Bien * @author Sven Gothel * * @see FunctionAddressResolver * @see DynamicLookupHelper */ public abstract class ProcAddressTable { private static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; private static final int PROCADDRESS_VAR_PREFIX_LEN = PROCADDRESS_VAR_PREFIX.length(); protected static boolean DEBUG; protected static String DEBUG_PREFIX; protected static int debugNum; private final FunctionAddressResolver resolver; static { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { DEBUG = (System.getProperty("jogamp.debug.ProcAddressHelper") != null); if (DEBUG) { DEBUG_PREFIX = System.getProperty("jogamp.debug.ProcAddressHelper.prefix"); } return null; } }); } public ProcAddressTable() { this(new One2OneResolver()); } public ProcAddressTable(final FunctionAddressResolver resolver) { this.resolver = resolver; } /** * Resets the complete table. * <p> * If a {@link SecurityManager} is installed, user needs link permissions * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! * </p> * @throws SecurityException if user is not granted access for all libraries. */ public void reset(final DynamicLookupHelper lookup) throws SecurityException, RuntimeException { if(null==lookup) { throw new RuntimeException("Passed null DynamicLookupHelper"); } final Field[] fields = getClass().getDeclaredFields(); final PrintStream dout; if (DEBUG) { dout = getDebugOutStream(); dout.println(getClass().getName()+".reset() (w/ "+fields.length+" prospective fields)"); } else { dout = null; } // All at once - performance. AccessibleObject.setAccessible(fields, true); lookup.claimAllLinkPermission(); try { for (int i = 0; i < fields.length; ++i) { final String fieldName = fields[i].getName(); if ( isAddressField(fieldName) ) { final String funcName = fieldToFunctionName(fieldName); setEntry(fields[i], funcName, lookup); } } } finally { lookup.releaseAllLinkPermission(); } if (DEBUG) { dout.flush(); if (DEBUG_PREFIX != null) { dout.close(); } } } /** * Initializes the mapping for a single function. * <p> * If a {@link SecurityManager} is installed, user needs link permissions * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! * </p> * * @throws IllegalArgumentException if this function is not in this table. * @throws SecurityException if user is not granted access for all libraries. */ public void initEntry(final String name, final DynamicLookupHelper lookup) throws SecurityException, IllegalArgumentException { final Field addressField = fieldForFunction(name); addressField.setAccessible(true); setEntry(addressField, name, lookup); } private final void setEntry(final Field addressField, final String funcName, final DynamicLookupHelper lookup) throws SecurityException { try { assert (addressField.getType() == Long.TYPE); final long newProcAddress = resolver.resolve(funcName, lookup); // issues SecurityUtil.checkLinkPermission(String) addressField.setLong(this, newProcAddress); if (DEBUG) { getDebugOutStream().println(" " + addressField.getName() + " -> 0x" + Long.toHexString(newProcAddress)); } } catch (final Exception e) { throw new RuntimeException("Can not get proc address for method \"" + funcName + "\": Couldn't set value of field \"" + addressField, e); } } private final String fieldToFunctionName(final String addressFieldName) { return addressFieldName.substring(PROCADDRESS_VAR_PREFIX_LEN); } private final Field fieldForFunction(final String name) throws IllegalArgumentException { try { return getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name); } catch (final NoSuchFieldException ex) { throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex); } } /** * Warning: Returns an accessible probably protected field! * <p> * Caller should have checked link permissions * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code> * <i>if</i> exposing the field or address! * </p> */ private final Field fieldForFunctionInSec(final String name) throws IllegalArgumentException { return AccessController.doPrivileged(new PrivilegedAction<Field>() { @Override public Field run() { try { final Field addressField = ProcAddressTable.this.getClass().getDeclaredField(PROCADDRESS_VAR_PREFIX + name); addressField.setAccessible(true); // we need to read the protected value! return addressField; } catch (final NoSuchFieldException ex) { throw new IllegalArgumentException(getClass().getName() +" has no entry for the function '"+name+"'.", ex); } } } ); } private final boolean isAddressField(final String fieldName) { return fieldName.startsWith(PROCADDRESS_VAR_PREFIX); } private final static PrintStream getDebugOutStream() { PrintStream out = null; if (DEBUG) { if (DEBUG_PREFIX != null) { try { out = new PrintStream(new BufferedOutputStream(new FileOutputStream(DEBUG_PREFIX + File.separatorChar + "procaddresstable-" + (++debugNum) + ".txt"))); } catch (final IOException e) { e.printStackTrace(); out = System.err; } } else { out = System.err; } } return out; } /** * Returns this table as map with the function name as key and the address as value. */ private final Map<String, Long> toMap() { final SortedMap<String, Long> map = new TreeMap<String, Long>(); final Field[] fields = getClass().getFields(); try { for (int i = 0; i < fields.length; ++i) { final String addressFieldName = fields[i].getName(); if (isAddressField(addressFieldName)) { map.put(fieldToFunctionName(addressFieldName), (Long)fields[i].get(this)); } } } catch (final IllegalArgumentException ex) { throw new RuntimeException(ex); } catch (final IllegalAccessException ex) { throw new RuntimeException(ex); } return map; } /** * Returns true only if non null function pointer to this function exists. */ public final boolean isFunctionAvailable(final String functionName) { try{ return isFunctionAvailableImpl(functionName); } catch (final IllegalArgumentException ex) { return false; } } /** * This is a convenience method to query the native function existence by name. * <p> * It lets you avoid having to * manually compute the "{@link #PROCADDRESS_VAR_PREFIX} + <functionName>" * member variable name and look it up via reflection. * </p> * * @throws IllegalArgumentException if this function is not in this table. */ protected boolean isFunctionAvailableImpl(final String functionName) throws IllegalArgumentException { final Field addressField = fieldForFunctionInSec(functionName); try { return 0 != addressField.getLong(this); } catch (final IllegalAccessException ex) { throw new RuntimeException(ex); } } /** * This is a convenience method to query the native function handle by name. * <p> * It lets you avoid having to * manually compute the "{@link #PROCADDRESS_VAR_PREFIX} + <functionName>" * member variable name and look it up via reflection. * </p> * <p> * If a {@link SecurityManager} is installed, user needs link permissions * for <b>all</b> libraries, i.e. for <code>new RuntimePermission("loadLibrary.*");</code>! * </p> * * @throws IllegalArgumentException if this function is not in this table. * @throws SecurityException if user is not granted access for all libraries. */ public long getAddressFor(final String functionName) throws SecurityException, IllegalArgumentException { SecurityUtil.checkAllLinkPermission(); final Field addressField = fieldForFunctionInSec(functionName); try { return addressField.getLong(this); } catch (final IllegalAccessException ex) { throw new RuntimeException(ex); } } /** * Returns all functions pointing to null. */ public final Set<String> getNullPointerFunctions() { final Map<String, Long> table = toMap(); final Set<String> nullPointers = new LinkedHashSet<String>(); for (final Iterator<Map.Entry<String, Long>> it = table.entrySet().iterator(); it.hasNext();) { final Map.Entry<String, Long> entry = it.next(); final long address = entry.getValue().longValue(); if(address == 0) { nullPointers.add(entry.getKey()); } } return nullPointers; } @Override public final String toString() { return getClass().getName()+""+toMap(); } private static class One2OneResolver implements FunctionAddressResolver { @Override public long resolve(final String name, final DynamicLookupHelper lookup) throws SecurityException { return lookup.dynamicLookupFunction(name); } } }