diff options
Diffstat (limited to 'src/java')
88 files changed, 7841 insertions, 2588 deletions
diff --git a/src/java/com/jogamp/common/ExceptionUtils.java b/src/java/com/jogamp/common/ExceptionUtils.java index c848a99..386f42b 100644 --- a/src/java/com/jogamp/common/ExceptionUtils.java +++ b/src/java/com/jogamp/common/ExceptionUtils.java @@ -58,21 +58,97 @@ public class ExceptionUtils { } /** - * Dumps a {@link Throwable} in a decorating message including the current thread name, + * Interface allowing {@link Throwable} specializations to provide their custom stack trace presentation. + * @since 2.3.2 + */ + public static interface CustomStackTrace { + /** + * Prints this {@link Throwable} as a cause to the output {@link PrintStream} {@code s}, + * not iterating over all inner causes! + * @param s output stream + * @param causeStr the cause title + * @param causeIdx the cause index over all causes known by caller + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth); + /** + * Custom {@code printStackTrace} method, similar to {@link Throwable#printStackTrace(PrintStream, int, int)}. + * @param s output stream + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + */ + void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth); + } + + /** + * Prints the given {@link Throwable} cause to the output {@link PrintStream} {@code s}. + * @param s output stream + * @param causeStr the cause title + * @param cause the {@link Throwable} cause for output + * @param causeIdx the cause index over all causes known by caller + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static int printCause(final PrintStream s, final String causeStr, Throwable cause, final int causeIdx, final int causeDepth, final int stackDepth) { + int i=causeIdx; + for(; null != cause && ( -1 == causeDepth || i < causeDepth ); cause = cause.getCause()) { + if( cause instanceof CustomStackTrace ) { + ((CustomStackTrace)cause).printCauseStack(s, causeStr, i, stackDepth); + } else { + s.println(causeStr+"["+i+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName()); + dumpStack(s, cause.getStackTrace(), 0, stackDepth); + } + i++; + } + return i; + } + + /** + * Prints the given {@link Throwable} to the output {@link PrintStream} {@code s}. + * @param s output stream + * @param t the {@link Throwable} for output + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static void printStackTrace(final PrintStream s, final Throwable t, final int causeDepth, final int stackDepth) { + if( t instanceof CustomStackTrace ) { + ((CustomStackTrace)t).printStackTrace(s, causeDepth, stackDepth); + } else { + s.println(t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); + dumpStack(s, t.getStackTrace(), 0, stackDepth); + printCause(s, "Caused", t.getCause(), 0, causeDepth, stackDepth); + } + } + + /** + * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name, * and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}. * <p> * Implementation will iterate through all {@link Throwable#getCause() causes}. * </p> + * @param additionalDescr additional text placed before the {@link Throwable} details. + * @param t the {@link Throwable} for output */ public static void dumpThrowable(final String additionalDescr, final Throwable t) { - System.err.println("Caught "+additionalDescr+" "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); - dumpStack(System.err, t.getStackTrace(), 0, -1); - int causeDepth = 1; - for( Throwable cause = t.getCause(); null != cause; cause = cause.getCause() ) { - System.err.println("Caused["+causeDepth+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName()); - dumpStack(System.err, cause.getStackTrace(), 0, -1); - causeDepth++; - } + dumpThrowable(additionalDescr, t, -1, -1); + } + /** + * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name, + * and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}. + * <p> + * Implementation will iterate through all {@link Throwable#getCause() causes}. + * </p> + * @param additionalDescr additional text placed before the {@link Throwable} details. + * @param t the {@link Throwable} for output + * @param causeDepth the maximum depth for causes, or {@code -1} for all + * @param stackDepth the maximum depth for stack entries, or {@code -1} for all + * @since 2.3.2 + */ + public static void dumpThrowable(final String additionalDescr, final Throwable t, final int causeDepth, final int stackDepth) { + System.err.print("Caught "+additionalDescr+" "); + printStackTrace(System.err, t, causeDepth, stackDepth); } - } diff --git a/src/java/com/jogamp/common/JogampRuntimeException.java b/src/java/com/jogamp/common/JogampRuntimeException.java index d33d498..524bb93 100644 --- a/src/java/com/jogamp/common/JogampRuntimeException.java +++ b/src/java/com/jogamp/common/JogampRuntimeException.java @@ -28,9 +28,10 @@ package com.jogamp.common; -/** A generic exception for Jogamp errors used throughout the binding - as a substitute for {@link RuntimeException}. */ - +/** + * A generic <i>unchecked exception</i> for Jogamp errors used throughout the binding + * as a substitute for {@link RuntimeException}. + */ @SuppressWarnings("serial") public class JogampRuntimeException extends RuntimeException { /** Constructs a JogampRuntimeException object. */ diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 9b1865f..3ba8dff 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -61,7 +61,18 @@ import jogamp.common.Debug; import jogamp.common.os.PlatformPropsImpl; public class JNILibLoaderBase { - public static final boolean DEBUG = Debug.debug("JNILibLoader"); + public static final boolean DEBUG; + protected static final boolean PERF; + + static { + Debug.initSingleton(); + DEBUG = Debug.debug("JNILibLoader"); + PERF = DEBUG || PropertyAccess.isPropertyDefined("jogamp.debug.JNILibLoader.Perf", true); + } + + private static final Object perfSync = new Object(); + private static long perfTotal = 0; + private static long perfCount = 0; public interface LoaderAction { /** @@ -177,6 +188,7 @@ public class JNILibLoaderBase { msg.append(")"); System.err.println(msg.toString()); } + final long t0 = PERF ? System.currentTimeMillis() : 0; // 'Platform.currentTimeMillis()' not yet available! boolean ok = false; @@ -195,28 +207,9 @@ public class JNILibLoaderBase { if (DEBUG) { System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: nativeLibraryPath: %s%n", nativeLibraryPath); } - final ClassLoader cl = classFromJavaJar.getClassLoader(); - final URL nativeLibraryURI = cl.getResource(nativeLibraryPath); - if (null != nativeLibraryURI) { - // We probably have one big-fat jar file, containing java classes - // and all native platform libraries under 'natives/os.and.arch'! - final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) ); - try { - if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) { - ok = true; - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI); - } - } - } catch(final Exception e) { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); - e.printStackTrace(); - } - } - } - if (!ok) { - // We assume one slim native jar file per 'os.and.arch'! + { + // Attempt-1 a 'one slim native jar file' per 'os.and.arch' layout + // with native platform libraries under 'natives/os.and.arch'! final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(nativeJarBasename) ); if (DEBUG) { @@ -224,7 +217,7 @@ public class JNILibLoaderBase { } try { - ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, null /* nativeLibraryPath */); + ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath); } catch(final Exception e) { if(DEBUG) { System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); @@ -233,40 +226,75 @@ public class JNILibLoaderBase { } } if (!ok) { - // Attempt to find via ClassLoader and Native-Jar-Tag, - // assuming one slim native jar file per 'os.and.arch'! - final String moduleName; + final ClassLoader cl = classFromJavaJar.getClassLoader(); { - final String packageName = classFromJavaJar.getPackage().getName(); - final int idx = packageName.lastIndexOf('.'); - if( 0 <= idx ) { - moduleName = packageName.substring(idx+1); - } else { - moduleName = packageName; + // Attempt-2 a 'one big-fat jar file' layout, containing java classes + // and all native platform libraries under 'natives/os.and.arch' per platform! + final URL nativeLibraryURI = cl.getResource(nativeLibraryPath); + if (null != nativeLibraryURI) { + final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) ); + try { + if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) { + ok = true; + if (DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI); + } + } + } catch(final Exception e) { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); + e.printStackTrace(); + } + } } } - final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.'); - final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml - try { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n", - moduleName, os_and_arch_dot, nativeJarTagClassName); - } - final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl); - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI); + if (!ok) { + // Attempt-3 to find via ClassLoader and Native-Jar-Tag, + // assuming one slim native jar file per 'os.and.arch' + // and native platform libraries under 'natives/os.and.arch'! + final String moduleName; + { + final String packageName = classFromJavaJar.getPackage().getName(); + final int idx = packageName.lastIndexOf('.'); + if( 0 <= idx ) { + moduleName = packageName.substring(idx+1); + } else { + moduleName = packageName; + } } - ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, null /* nativeLibraryPath */); - } catch (final Exception e ) { - if(DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); - e.printStackTrace(); + final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.'); + final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml + try { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n", + moduleName, os_and_arch_dot, nativeJarTagClassName); + } + final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl); + if (DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI); + } + ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, nativeLibraryPath); + } catch (final Exception e ) { + if(DEBUG) { + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage()); + e.printStackTrace(); + } } } } - if (DEBUG) { - System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ok: %b%n", ok); + if (DEBUG || PERF) { + final long tNow = System.currentTimeMillis() - t0; + final long tTotal, tCount; + synchronized(perfSync) { + tCount = perfCount+1; + tTotal = perfTotal + tNow; + perfTotal = tTotal; + perfCount = tCount; + } + final double tAvrg = tTotal / (double)tCount; + System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl.X: %s / %s -> ok: %b; duration: now %d ms, total %d ms (count %d, avrg %.3f ms)%n", + jarBasename, nativeJarBasename, ok, tNow, tTotal, tCount, tAvrg); } return ok; } @@ -585,7 +613,7 @@ public class JNILibLoaderBase { if(DEBUG) { System.err.println("ERROR (retry w/ enumLibPath) - "+ex1.getMessage()); } - final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, true, cl); + final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, cl); // Iterate down these and see which one if any we can actually find. for (final Iterator<String> iter = possiblePaths.iterator(); 0 == mode && iter.hasNext(); ) { final String path = iter.next(); diff --git a/src/java/com/jogamp/common/net/AssetURLContext.java b/src/java/com/jogamp/common/net/AssetURLContext.java index af90c01..8462a41 100644 --- a/src/java/com/jogamp/common/net/AssetURLContext.java +++ b/src/java/com/jogamp/common/net/AssetURLContext.java @@ -164,7 +164,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { url = new URL(path); conn = open(url); type = null != conn ? 1 : -1; - } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("ERR(0): "+e1.getMessage()); } } + } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("FAIL(1): "+e1.getMessage()); } } if(null == conn && null != cl) { // lookup via ClassLoader .. cleanup leading '/' @@ -189,7 +189,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { conn = open(url); type = null != conn ? 3 : -1; } - } catch (final Throwable e) { if(DEBUG) { System.err.println("ERR(1): "+e.getMessage()); } } + } catch (final Throwable e) { if(DEBUG) { System.err.println("FAIL(3): "+e.getMessage()); } } } if(DEBUG) { @@ -209,7 +209,7 @@ public abstract class AssetURLContext implements PiggybackURLContext { final URLConnection c = url.openConnection(); c.connect(); // redundant return c; - } catch (final IOException ioe) { if(DEBUG) { System.err.println("ERR: "+ioe.getMessage()); } } + } catch (final IOException ioe) { if(DEBUG) { System.err.println("FAIL(2): "+ioe.getMessage()); } } return null; } diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java index 6bafba2..bca90bf 100644 --- a/src/java/com/jogamp/common/net/Uri.java +++ b/src/java/com/jogamp/common/net/Uri.java @@ -1232,7 +1232,15 @@ public class Uri { /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */ public final boolean isFileScheme() { - return FILE_SCHEME.equals( scheme.get() ); + return null != scheme && FILE_SCHEME.equals( scheme.get() ); + } + + /** + * Returns true, if this instance is a {@code jar} {@code scheme}, otherwise false. + * @since 2.3.2 + */ + public final boolean isJarScheme() { + return null != scheme && JAR_SCHEME.equals( scheme.get() ); } /** @@ -1386,7 +1394,7 @@ public class Uri { if( !emptyString(schemeSpecificPart) ) { final StringBuilder sb = new StringBuilder(); - if( scheme.equals(JAR_SCHEME) ) { + if( isJarScheme() ) { final int idx = schemeSpecificPart.lastIndexOf(JAR_SCHEME_SEPARATOR); if (0 > idx) { throw new URISyntaxException(input.get(), "missing jar separator"); diff --git a/src/java/com/jogamp/common/nio/Buffers.java b/src/java/com/jogamp/common/nio/Buffers.java index aae2be8..fb23627 100644 --- a/src/java/com/jogamp/common/nio/Buffers.java +++ b/src/java/com/jogamp/common/nio/Buffers.java @@ -39,6 +39,7 @@ */ package com.jogamp.common.nio; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -48,9 +49,14 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.ValueConv; +import jogamp.common.Debug; + /** * Utility methods allowing easy {@link java.nio.Buffer} manipulations. * @@ -60,6 +66,11 @@ import com.jogamp.common.util.ValueConv; */ public class Buffers { + static final boolean DEBUG; + static { + DEBUG = Debug.debug("Buffers"); + } + public static final int SIZEOF_BYTE = 1; public static final int SIZEOF_SHORT = 2; public static final int SIZEOF_CHAR = 2; @@ -1150,4 +1161,64 @@ public class Buffers { return sb; } + /** + * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}. + */ + public static class Cleaner { + private static final Method mbbCleaner; + private static final Method cClean; + private static final boolean hasCleaner; + /** OK to be lazy on thread synchronization, just for early out **/ + private static volatile boolean cleanerError; + static { + final Method[] _mbbCleaner = { null }; + final Method[] _cClean = { null }; + if( AccessController.doPrivileged(new PrivilegedAction<Boolean>() { + @Override + public Boolean run() { + try { + _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader()); + _mbbCleaner[0].setAccessible(true); + _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); + _cClean[0].setAccessible(true); + return Boolean.TRUE; + } catch(final Throwable t) { + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return Boolean.FALSE; + } } } ).booleanValue() ) { + mbbCleaner = _mbbCleaner[0]; + cClean = _cClean[0]; + hasCleaner = null != mbbCleaner && null != cClean; + } else { + mbbCleaner = null; + cClean = null; + hasCleaner = false; + } + cleanerError = !hasCleaner; + } + /** + * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer}, + * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method. + * @return {@code true} if successful, otherwise {@code false}. + */ + public static boolean clean(final Buffer b) { + if( !hasCleaner || cleanerError || !b.isDirect() ) { + return false; + } + try { + cClean.invoke(mbbCleaner.invoke(b)); + return true; + } catch(final Throwable t) { + cleanerError = true; + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return false; + } + } + } } diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java index f8d5857..6a56d6e 100644 --- a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java +++ b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java @@ -33,13 +33,10 @@ import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; -import java.security.AccessController; -import java.security.PrivilegedAction; import jogamp.common.Debug; @@ -163,10 +160,6 @@ public class MappedByteBufferInputStream extends InputStream { private int refCount; - private Method mbbCleaner; - private Method cClean; - private boolean cleanerInit; - private boolean hasCleaner; private CacheMode cmode; private int sliceIdx; @@ -191,10 +184,12 @@ public class MappedByteBufferInputStream extends InputStream { } } long fcSz = 0, pos = 0, rem = 0; - try { - fcSz = fc.size(); - } catch (final IOException e) { - e.printStackTrace(); + if( fc.isOpen() ) { + try { + fcSz = fc.size(); + } catch (final IOException e) { + e.printStackTrace(); + } } if( 0 < refCount ) { try { @@ -229,14 +224,16 @@ public class MappedByteBufferInputStream extends InputStream { notifyLengthChange( totalSize ); this.refCount = 1; - this.cleanerInit = false; - this.hasCleaner = false; this.cmode = cmode; this.sliceIdx = currSliceIdx; this.mark = -1; currentSlice().position(0); + + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("CTOR", System.err); + } } /** @@ -330,6 +327,9 @@ public class MappedByteBufferInputStream extends InputStream { } } } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("Close", System.err); + } } final FileChannel.MapMode getMapMode() { return mmode; } @@ -441,10 +441,9 @@ public class MappedByteBufferInputStream extends InputStream { } position2( Math.min(prePosition, newTotalSize) ); // -> clipped position (set currSlice and re-map/-pos buffer) } - /* if( DEBUG ) { - System.err.println("notifyLengthChange.X: "+slices[currSlice]); - dbgDump("notifyLengthChange.X:", System.err); - } */ + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("NotifyLengthChange", System.err); + } } /** @@ -551,6 +550,21 @@ public class MappedByteBufferInputStream extends InputStream { } } + /** + * Releases the mapped {@link ByteBuffer} slices. + * @throws IOException if a buffer slice operation failed. + */ + public final synchronized void flushSlices() throws IOException { + if( null != slices ) { + for(int i=0; i<sliceCount; i++) { + flushSlice(i, synchronous); + } + } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("FlushSlices", System.err); + } + } + synchronized void syncSlice(final ByteBuffer s) throws IOException { syncSlice(s, synchronous); } @@ -630,58 +644,16 @@ public class MappedByteBufferInputStream extends InputStream { } } private synchronized boolean cleanBuffer(final ByteBuffer mbb, final boolean syncBuffer) throws IOException { - if( !cleanerInit ) { - initCleaner(mbb); - } syncSlice(mbb, syncBuffer); if( !mbb.isDirect() ) { return false; } - boolean res = false; - if ( hasCleaner ) { - try { - cClean.invoke(mbbCleaner.invoke(mbb)); - res = true; - } catch(final Throwable t) { - hasCleaner = false; - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - } - if( !res && CacheMode.FLUSH_PRE_HARD == cmode ) { + if( !Buffers.Cleaner.clean(mbb) && CacheMode.FLUSH_PRE_HARD == cmode ) { cmode = CacheMode.FLUSH_PRE_SOFT; + return false; + } else { + return true; } - return res; - } - private synchronized void initCleaner(final ByteBuffer bb) { - final Method[] _mbbCleaner = { null }; - final Method[] _cClean = { null }; - AccessController.doPrivileged(new PrivilegedAction<Object>() { - @Override - public Object run() { - try { - _mbbCleaner[0] = bb.getClass().getMethod("cleaner"); - _mbbCleaner[0].setAccessible(true); - _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); - _cClean[0].setAccessible(true); - } catch(final Throwable t) { - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - return null; - } } ); - mbbCleaner = _mbbCleaner[0]; - cClean = _cClean[0]; - final boolean res = null != mbbCleaner && null != cClean; - if( DEBUG ) { - System.err.println("initCleaner: Has cleaner: "+res+", mbbCleaner "+mbbCleaner+", cClean "+cClean); - } - hasCleaner = res; - cleanerInit = true; } /** diff --git a/src/java/com/jogamp/common/nio/StructAccessor.java b/src/java/com/jogamp/common/nio/StructAccessor.java index af7b6d1..8ae0c29 100644 --- a/src/java/com/jogamp/common/nio/StructAccessor.java +++ b/src/java/com/jogamp/common/nio/StructAccessor.java @@ -83,6 +83,16 @@ public class StructAccessor { bb.put(byteOffset, v); } + /** Retrieves the boolean at the specified byteOffset. */ + public final boolean getBooleanAt(final int byteOffset) { + return (byte)0 != bb.get(byteOffset); + } + + /** Puts a boolean at the specified byteOffset. */ + public final void setBooleanAt(final int byteOffset, final boolean v) { + bb.put(byteOffset, v?(byte)1:(byte)0); + } + /** Retrieves the char at the specified byteOffset. */ public final char getCharAt(final int byteOffset) { return bb.getChar(byteOffset); @@ -213,6 +223,19 @@ public class StructAccessor { return v; } + public final void setBooleansAt(int byteOffset, final boolean[] v) { + for (int i = 0; i < v.length; i++) { + bb.put(byteOffset++, v[i]?(byte)1:(byte)0); + } + } + + public final boolean[] getBooleansAt(int byteOffset, final boolean[] v) { + for (int i = 0; i < v.length; i++) { + v[i] = (byte)0 != bb.get(byteOffset++); + } + return v; + } + public final void setCharsAt(int byteOffset, final char[] v) { for (int i = 0; i < v.length; i++, byteOffset+=2) { bb.putChar(byteOffset, v[i]); diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java index c578565..a3d6198 100644 --- a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java +++ b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java @@ -189,7 +189,10 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { * @see DynamicLibraryBundleInfo#getToolLibNames() */ public final boolean isToolLibComplete() { - return toolGetProcAddressComplete && null != dynLinkGlobal && getToolLibNumber() == getToolLibLoadedNumber(); + final int toolLibNumber = getToolLibNumber(); + return toolGetProcAddressComplete && + ( 0 == toolLibNumber || null != dynLinkGlobal ) && + toolLibNumber == getToolLibLoadedNumber(); } public final boolean isToolLibLoaded() { @@ -246,9 +249,12 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { return aptr; } - protected static final NativeLibrary loadFirstAvailable(final List<String> libNames, final ClassLoader loader, final boolean global) throws SecurityException { + protected static final NativeLibrary loadFirstAvailable(final List<String> libNames, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader, final boolean global) throws SecurityException { for (int i=0; i < libNames.size(); i++) { - final NativeLibrary lib = NativeLibrary.open(libNames.get(i), loader, global); + final NativeLibrary lib = NativeLibrary.open(libNames.get(i), searchSystemPath, searchSystemPathFirst, loader, global); if (lib != null) { return lib; } @@ -266,7 +272,10 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { for (i=0; i < toolLibNames.size(); i++) { final List<String> libNames = toolLibNames.get(i); if( null != libNames && libNames.size() > 0 ) { - lib = loadFirstAvailable(libNames, cl, info.shallLinkGlobal()); + lib = loadFirstAvailable(libNames, + info.searchToolLibInSystemPath(), + info.searchToolLibSystemPathFirst(), + cl, info.shallLinkGlobal()); if ( null == lib ) { if(DEBUG) { System.err.println("Unable to load any Tool library of: "+libNames); @@ -358,7 +367,7 @@ public class DynamicLibraryBundle implements DynamicLookupHelper { final long addr = info.toolGetProcAddress(toolGetProcAddressHandle, funcName); if(DEBUG_LOOKUP) { if(0!=addr) { - System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr)); + System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr)+", via tool 0x"+Long.toHexString(toolGetProcAddressHandle)); } } return addr; diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java index 7be5f25..01068b4 100644 --- a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java +++ b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java @@ -37,6 +37,21 @@ public interface DynamicLibraryBundleInfo { public static final boolean DEBUG = DynamicLibraryBundle.DEBUG; /** + * Returns {@code true} if tool libraries shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @since 2.4.0 + */ + public boolean searchToolLibInSystemPath(); + + /** + * Returns {@code true} if system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * <p> + * If {@link #searchToolLibInSystemPath()} is {@code false} the return value is ignored. + * </p> + * @since 2.4.0 + */ + public boolean searchToolLibSystemPathFirst(); + + /** * If a {@link SecurityManager} is installed, user needs link permissions * for the named libraries. * diff --git a/src/java/com/jogamp/common/os/NativeLibrary.java b/src/java/com/jogamp/common/os/NativeLibrary.java index 747f92d..2ba2581 100644 --- a/src/java/com/jogamp/common/os/NativeLibrary.java +++ b/src/java/com/jogamp/common/os/NativeLibrary.java @@ -138,32 +138,47 @@ public final class NativeLibrary implements DynamicLookupHelper { } /** Opens the given native library, assuming it has the same base - name on all platforms, looking first in the system's search - path, and in the context of the specified ClassLoader, which is - used to help find the library in the case of e.g. Java Web Start. - * @throws SecurityException if user is not granted access for the named library. - */ - public static final NativeLibrary open(final String libName, final ClassLoader loader) throws SecurityException { - return open(libName, libName, libName, true, loader, true); - } - - /** Opens the given native library, assuming it has the same base - name on all platforms, looking first in the system's search - path, and in the context of the specified ClassLoader, which is - used to help find the library in the case of e.g. Java Web Start. + name on all platforms. + <p> + The {@code searchSystemPath} argument changes the behavior to + either use the default system path or not at all. + </p> + <p> + Assuming {@code searchSystemPath} is {@code true}, + the {@code searchSystemPathFirst} argument changes the behavior to first + search the default system path rather than searching it last. + </p> + * @param libName library name, with or without prefix and suffix + * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * if {@code searchSystemPath} is {@code false} this argument is ignored. + * @param loader {@link ClassLoader} to locate the library + * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process. + * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded. * @throws SecurityException if user is not granted access for the named library. + * @since 2.4.0 */ - public static final NativeLibrary open(final String libName, final ClassLoader loader, final boolean global) throws SecurityException { - return open(libName, libName, libName, true, loader, global); + public static final NativeLibrary open(final String libName, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader, final boolean global) throws SecurityException { + return open(libName, libName, libName, searchSystemPath, searchSystemPathFirst, loader, global); } /** Opens the given native library, assuming it has the given base names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the Windows, Unix and Mac OS X platforms, respectively, and in the context of the specified ClassLoader, which is used to help find - the library in the case of e.g. Java Web Start. The - searchSystemPathFirst argument changes the behavior to first + the library in the case of e.g. Java Web Start. + <p> + The {@code searchSystemPath} argument changes the behavior to + either use the default system path or not at all. + </p> + <p> + Assuming {@code searchSystemPath} is {@code true}, + the {@code searchSystemPathFirst} argument changes the behavior to first search the default system path rather than searching it last. + </p> Note that we do not currently handle DSO versioning on Unix. Experience with JOAL and OpenAL has shown that it is extremely problematic to rely on a specific .so version (for one thing, @@ -171,28 +186,27 @@ public final class NativeLibrary implements DynamicLookupHelper { ending in .so, for example .so.0), and in general if this dynamic loading facility is used correctly the version number will be irrelevant. + * @param windowsLibName windows library name, with or without prefix and suffix + * @param unixLibName unix library name, with or without prefix and suffix + * @param macOSXLibName mac-osx library name, with or without prefix and suffix + * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}. + * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last. + * if {@code searchSystemPath} is {@code false} this argument is ignored. + * @param loader {@link ClassLoader} to locate the library + * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process. + * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded. * @throws SecurityException if user is not granted access for the named library. */ public static final NativeLibrary open(final String windowsLibName, final String unixLibName, final String macOSXLibName, - final boolean searchSystemPathFirst, - final ClassLoader loader) throws SecurityException { - return open(windowsLibName, unixLibName, macOSXLibName, searchSystemPathFirst, loader, true); - } - - /** - * @throws SecurityException if user is not granted access for the named library. - */ - public static final NativeLibrary open(final String windowsLibName, - final String unixLibName, - final String macOSXLibName, + final boolean searchSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global) throws SecurityException { final List<String> possiblePaths = enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, - searchSystemPathFirst, + searchSystemPath, searchSystemPathFirst, loader); Platform.initSingleton(); // loads native gluegen-rt library @@ -367,12 +381,33 @@ public final class NativeLibrary implements DynamicLookupHelper { /** Given the base library names (no prefixes/suffixes) for the various platforms, enumerate the possible locations and names of - the indicated native library on the system. */ + the indicated native library on the system not using the system path. */ + public static final List<String> enumerateLibraryPaths(final String windowsLibName, + final String unixLibName, + final String macOSXLibName, + final ClassLoader loader) { + return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, + false /* searchSystemPath */, false /* searchSystemPathFirst */, + loader); + } + /** Given the base library names (no prefixes/suffixes) for the + various platforms, enumerate the possible locations and names of + the indicated native library on the system using the system path. */ public static final List<String> enumerateLibraryPaths(final String windowsLibName, final String unixLibName, final String macOSXLibName, final boolean searchSystemPathFirst, final ClassLoader loader) { + return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName, + true /* searchSystemPath */, searchSystemPathFirst, + loader); + } + private static final List<String> enumerateLibraryPaths(final String windowsLibName, + final String unixLibName, + final String macOSXLibName, + final boolean searchSystemPath, + final boolean searchSystemPathFirst, + final ClassLoader loader) { final List<String> paths = new ArrayList<String>(); final String libName = selectName(windowsLibName, unixLibName, macOSXLibName); if (libName == null) { @@ -388,11 +423,18 @@ public final class NativeLibrary implements DynamicLookupHelper { final String[] baseNames = buildNames(libName); - if (searchSystemPathFirst) { - // Add just the library names to use the OS's search algorithm - for (int i = 0; i < baseNames.length; i++) { - paths.add(baseNames[i]); - } + if( searchSystemPath && searchSystemPathFirst ) { + // Add just the library names to use the OS's search algorithm + for (int i = 0; i < baseNames.length; i++) { + paths.add(baseNames[i]); + } + // Add probable Mac OS X-specific paths + if ( isOSX ) { + // Add historical location + addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + // Add current location + addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + } } // The idea to ask the ClassLoader to find the library is borrowed @@ -412,24 +454,25 @@ public final class NativeLibrary implements DynamicLookupHelper { if(null != usrPath) { count++; } - final String sysPath = System.getProperty("sun.boot.library.path"); - if(null != sysPath) { - count++; + final String sysPath; + if( searchSystemPath ) { + sysPath = System.getProperty("sun.boot.library.path"); + if(null != sysPath) { + count++; + } + } else { + sysPath = null; } final String[] res = new String[count]; int i=0; - if (searchSystemPathFirst) { - if(null != sysPath) { - res[i++] = sysPath; - } + if( null != sysPath && searchSystemPathFirst ) { + res[i++] = sysPath; } if(null != usrPath) { res[i++] = usrPath; } - if (!searchSystemPathFirst) { - if(null != sysPath) { - res[i++] = sysPath; - } + if( null != sysPath && !searchSystemPathFirst ) { + res[i++] = sysPath; } return res; } @@ -453,19 +496,22 @@ public final class NativeLibrary implements DynamicLookupHelper { }); addPaths(userDir, baseNames, paths); - if (!searchSystemPathFirst) { - // Add just the library names to use the OS's search algorithm - for (int i = 0; i < baseNames.length; i++) { - paths.add(baseNames[i]); - } - } + // Add current working directory + natives/os-arch/ + library names + // to handle Bug 1145 cc1 using an unpacked fat-jar + addPaths(userDir+File.separator+"natives"+File.separator+PlatformPropsImpl.os_and_arch+File.separator, baseNames, paths); - // Add probable Mac OS X-specific paths - if ( isOSX ) { - // Add historical location - addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); - // Add current location - addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + if( searchSystemPath && !searchSystemPathFirst ) { + // Add just the library names to use the OS's search algorithm + for (int i = 0; i < baseNames.length; i++) { + paths.add(baseNames[i]); + } + // Add probable Mac OS X-specific paths + if ( isOSX ) { + // Add historical location + addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + // Add current location + addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths); + } } return paths; diff --git a/src/java/com/jogamp/common/util/ArrayHashMap.java b/src/java/com/jogamp/common/util/ArrayHashMap.java new file mode 100644 index 0000000..35a484b --- /dev/null +++ b/src/java/com/jogamp/common/util/ArrayHashMap.java @@ -0,0 +1,305 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * {@link HashMap} implementation backed by an {@link ArrayList} to preserve order of values. + * + * Implementation properties are: + * <ul> + * <li> Unique elements utilizing {@link java.lang.Object#hashCode()} for O(1) operations, see below.</li> + * <li> Java 1.5 compatible</li> + * </ul> + * + * O(1) operations: + * <ul> + * <li> put new key-value-pair(s) </li> + * <li> test for containment </li> + * <li> trying to remove non existent elements </li> + * </ul> + * + * O(n) operations: + * <ul> + * <li> put existing key-value-pair(s) </li> + * <li> removing existing elements</li> + * </ul> + * + * For thread safety, the application shall decorate access to instances via + * {@link com.jogamp.common.util.locks.RecursiveLock}. + * +*/ +public class ArrayHashMap<K, V> + implements Cloneable, Map<K, V> +{ + /** + * Default load factor: {@value} + */ + public static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** + * The default initial capacity: {@value} + */ + public static final int DEFAULT_INITIAL_CAPACITY = 16; + + private final HashMap<K,V> map; // key -> object + private final ArrayList<V> data; // list of objects + private final boolean supportNullValue; + + /** + * + * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value. + * Use {@code false} if {@code null} is not a valid value, + * here {@link #put(Object, Object)} and {@link #remove(Object)} will be optimized. + * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default + * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default + * @see #supportsNullValue() + */ + public ArrayHashMap(final boolean supportNullValue, final int initialCapacity, final float loadFactor) { + this.map = new HashMap<K,V>(initialCapacity, loadFactor); + this.data = new ArrayList<V>(initialCapacity); + this.supportNullValue = supportNullValue; + } + + /** + * @return a shallow copy of this ArrayHashMap, elements are not copied. + */ + public ArrayHashMap(final ArrayHashMap<K, V> o) { + map = new HashMap<K, V>(o.map); + data = new ArrayList<V>(o.data); + supportNullValue = o.supportNullValue; + } + + /** + * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value. + * <p> + * Returns {@code false} if {@code null} is not a valid value, + * here {@link #put(Object, Object)} and {@link #remove(Object)} are optimized operations. + * </p> + * @see #ArrayHashMap(boolean, int, float) + */ + public final boolean supportsNullValue() { return supportNullValue; } + + // + // Cloneable + // + + /** + * Implementation uses {@link #ArrayHashMap(ArrayHashMap)}. + * @return a shallow copy of this ArrayHashMap, elements are not copied. + */ + @Override + public final Object clone() { + return new ArrayHashMap<K, V>(this); + } + + /** + * Returns this object ordered ArrayList. Use w/ care, it's not a copy. + * @see #toArrayList() + */ + public final ArrayList<V> getData() { return data; } + + /** + * @return a shallow copy of this ArrayHashMap's ArrayList, elements are not copied. + * @see #getData() + */ + public final ArrayList<V> toArrayList() { + return new ArrayList<V>(data); + } + + /** Returns this object hash map. Use w/ care, it's not a copy. */ + public final HashMap<K,V> getMap() { return map; } + + @Override + public final String toString() { return data.toString(); } + + // + // Map + // + + @Override + public final void clear() { + data.clear(); + map.clear(); + } + + @Override + public Set<K> keySet() { + return map.keySet(); + } + + /** + * {@inheritDoc} + * <p> + * See {@link #getData()} and {@link #toArrayList()}. + * </p> + * @see #getData() + * @see #toArrayList() + */ + @Override + public Collection<V> values() { + return map.values(); + } + + @Override + public Set<java.util.Map.Entry<K, V>> entrySet() { + return map.entrySet(); + } + + @Override + public final V get(final Object key) { + return map.get(key); + } + + /** + * {@inheritDoc} + * <p> + * This is an O(1) operation, in case the key does not exist, + * otherwise O(n). + * </p> + * @throws NullPointerException if {@code value} is {@code null} but {@link #supportsNullValue()} == {@code false} + */ + @Override + public final V put(final K key, final V value) throws NullPointerException { + final V oldValue; + if( supportNullValue ) { + // slow path + final boolean exists = map.containsKey(key); + if(!exists) { + // !exists + if( null != ( oldValue = map.put(key, value) ) ) { + // slips a valid null .. + throw new InternalError("Already existing, but checked before: "+key+" -> "+oldValue); + } + } else { + // exists + oldValue = map.put(key, value); + if( !data.remove(oldValue) ) { + throw new InternalError("Already existing, but not in list: "+oldValue); + } + } + } else { + checkNullValue(value); + // fast path + if( null != ( oldValue = map.put(key, value) ) ) { + // exists + if( !data.remove(oldValue) ) { + throw new InternalError("Already existing, but not in list: "+oldValue); + } + } + } + if(!data.add(value)) { + throw new InternalError("Couldn't add value to list: "+value); + } + return oldValue; + } + + @Override + public void putAll(final Map<? extends K, ? extends V> m) { + for (final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { + final Map.Entry<? extends K, ? extends V> e = i.next(); + put(e.getKey(), e.getValue()); + } + } + + /** + * {@inheritDoc} + * <p> + * This is an O(1) operation, in case the key does not exist, + * otherwise O(n). + * </p> + */ + @Override + public final V remove(final Object key) { + if( supportNullValue ) { + if( map.containsKey(key) ) { + // exists + final V oldValue = map.remove(key); + if ( !data.remove(oldValue) ) { + throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue); + } + return oldValue; + } + } else { + final V oldValue; + if ( null != (oldValue = map.remove(key) ) ) { + // exists + if ( !data.remove(oldValue) ) { + throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue); + } + } + return oldValue; + } + return null; + } + + @Override + public final boolean containsKey(final Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(final Object value) { + return map.containsValue(value); + } + + @Override + public final boolean equals(final Object arrayHashMap) { + if ( !(arrayHashMap instanceof ArrayHashMap) ) { + return false; + } + return map.equals(((ArrayHashMap<?,?>)arrayHashMap).map); + } + + @Override + public final int hashCode() { + return map.hashCode(); + } + + @Override + public final boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public final int size() { + return data.size(); + } + + private static final void checkNullValue(final Object value) throws NullPointerException { + if( null == value ) { + throw new NullPointerException("Null value not supported"); + } + } +} diff --git a/src/java/com/jogamp/common/util/ArrayHashSet.java b/src/java/com/jogamp/common/util/ArrayHashSet.java index 34e84c4..c0ac2fa 100644 --- a/src/java/com/jogamp/common/util/ArrayHashSet.java +++ b/src/java/com/jogamp/common/util/ArrayHashSet.java @@ -68,23 +68,52 @@ import java.util.ListIterator; public class ArrayHashSet<E> implements Cloneable, Collection<E>, List<E> { + /** + * Default load factor: {@value} + */ + public static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** + * The default initial capacity: {@value} + */ + public static final int DEFAULT_INITIAL_CAPACITY = 16; + private final HashMap<E,E> map; // object -> object private final ArrayList<E> data; // list of objects + private final boolean supportNullValue; - public ArrayHashSet() { - map = new HashMap<E,E>(); - data = new ArrayList<E>(); + /** + * + * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value. + * Use {@code false} if {@code null} is not a valid value, + * here {@link #remove(E)} and {@link #getOrAdd(Object)} will be optimized. + * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default + * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default + * @see #supportsNullValue() + */ + public ArrayHashSet(final boolean supportNullValue, final int initialCapacity, final float loadFactor) { + this.map = new HashMap<E,E>(initialCapacity, loadFactor); + this.data = new ArrayList<E>(initialCapacity); + this.supportNullValue = supportNullValue; } - public ArrayHashSet(final int initialCapacity) { - map = new HashMap<E,E>(initialCapacity); - data = new ArrayList<E>(initialCapacity); + /** + * @return a shallow copy of this ArrayHashSet, elements are not copied. + */ + public ArrayHashSet(final ArrayHashSet<E> o) { + map = new HashMap<E, E>(o.map); + data = new ArrayList<E>(o.data); + supportNullValue = o.supportNullValue; } - public ArrayHashSet(final int initialCapacity, final float loadFactor) { - map = new HashMap<E,E>(initialCapacity, loadFactor); - data = new ArrayList<E>(initialCapacity); - } + /** + * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value. + * <p> + * Returns {@code false} if {@code null} is not a valid value, + * here {@link #remove(E)} and {@link #getOrAdd(Object)} are optimized operations. + * </p> + * @see #ArrayHashSet(boolean, int, float) + */ + public final boolean supportsNullValue() { return supportNullValue; } // // Cloneable @@ -95,12 +124,7 @@ public class ArrayHashSet<E> */ @Override public final Object clone() { - final ArrayList<E> clonedList = new ArrayList<E>(data); - - final ArrayHashSet<E> newObj = new ArrayHashSet<E>(); - newObj.addAll(clonedList); - - return newObj; + return new ArrayHashSet<E>(this); } /** Returns this object ordered ArrayList. Use w/ care, it's not a copy. */ @@ -125,40 +149,66 @@ public class ArrayHashSet<E> * Add element at the end of this list, if it is not contained yet. * <br> * This is an O(1) operation + * <p> + * {@inheritDoc} + * </p> * * @return true if the element was added to this list, * otherwise false (already contained). + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final boolean add(final E element) { - final boolean exists = map.containsKey(element); - if(!exists) { + public final boolean add(final E element) throws NullPointerException { + if( !supportNullValue ) { + checkNull(element); + } + if( !map.containsKey(element) ) { + // !exists if(null != map.put(element, element)) { + // slips a valid null .. throw new InternalError("Already existing, but checked before: "+element); } if(!data.add(element)) { throw new InternalError("Couldn't add element: "+element); } + return true; } - return !exists; + return false; } /** * Remove element from this list. * <br> - * This is an O(1) operation, in case it does not exist, + * This is an O(1) operation, in case the element does not exist, * otherwise O(n). + * <p> + * {@inheritDoc} + * </p> * * @return true if the element was removed from this list, * otherwise false (not contained). + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final boolean remove(final Object element) { - if ( null != map.remove(element) ) { - if ( ! data.remove(element) ) { - throw new InternalError("Couldn't remove prev mapped element: "+element); + public final boolean remove(final Object element) throws NullPointerException { + if( supportNullValue ) { + if( map.containsKey(element) ) { + // exists + map.remove(element); + if ( !data.remove(element) ) { + throw new InternalError("Couldn't remove prev mapped element: "+element); + } + return true; + } + } else { + checkNull(element); + if ( null != map.remove(element) ) { + // exists + if ( !data.remove(element) ) { + throw new InternalError("Couldn't remove prev mapped element: "+element); + } + return true; } - return true; } return false; } @@ -167,6 +217,9 @@ public class ArrayHashSet<E> * Add all elements of given {@link java.util.Collection} at the end of this list. * <br> * This is an O(n) operation, over the given Collection size. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element was added to this list, * otherwise false (completely container). @@ -184,6 +237,9 @@ public class ArrayHashSet<E> * Test for containment * <br> * This is an O(1) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if the given element is contained by this list using fast hash map, * otherwise false. @@ -197,6 +253,9 @@ public class ArrayHashSet<E> * Test for containment of given {@link java.util.Collection} * <br> * This is an O(n) operation, over the given Collection size. + * <p> + * {@inheritDoc} + * </p> * * @return true if the given Collection is completly contained by this list using hash map, * otherwise false. @@ -215,6 +274,9 @@ public class ArrayHashSet<E> * Remove all elements of given {@link java.util.Collection} from this list. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element of this list was removed, * otherwise false. @@ -233,6 +295,9 @@ public class ArrayHashSet<E> * remove all elements not contained by the given {@link java.util.Collection} c. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if at least one element of this list was removed, * otherwise false. @@ -250,6 +315,9 @@ public class ArrayHashSet<E> /** * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return true if arrayHashSet is of type ArrayHashSet and all entries are equal * Performance: arrayHashSet(1) @@ -264,6 +332,9 @@ public class ArrayHashSet<E> /** * This is an O(n) operation over the size of this list. + * <p> + * {@inheritDoc} + * </p> * * @return the hash code of this list as define in {@link java.util.List#hashCode()}, * ie hashing all elements of this list. @@ -316,30 +387,44 @@ public class ArrayHashSet<E> * Add element at the given index in this list, if it is not contained yet. * <br> * This is an O(1) operation + * <p> + * {@inheritDoc} + * </p> * * @throws IllegalArgumentException if the given element was already contained + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ @Override - public final void add(final int index, final E element) { + public final void add(final int index, final E element) throws IllegalArgumentException, NullPointerException { + if( !supportNullValue ) { + checkNull(element); + } if ( map.containsKey(element) ) { throw new IllegalArgumentException("Element "+element+" is already contained"); } if(null != map.put(element, element)) { + // slips a valid null .. throw new InternalError("Already existing, but checked before: "+element); } + // !exists data.add(index, element); } /** + * <p> + * {@inheritDoc} + * </p> * @throws UnsupportedOperationException */ @Override - public final boolean addAll(final int index, final Collection<? extends E> c) { + public final boolean addAll(final int index, final Collection<? extends E> c) throws UnsupportedOperationException { throw new UnsupportedOperationException("Not supported yet."); } /** - * @throws UnsupportedOperationException + * <p> + * {@inheritDoc} + * </p> */ @Override public final E set(final int index, final E element) { @@ -354,6 +439,9 @@ public class ArrayHashSet<E> * Remove element at given index from this list. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return the removed object */ @@ -370,6 +458,9 @@ public class ArrayHashSet<E> * Since this list is unique, equivalent to {@link #indexOf(java.lang.Object)}. * <br> * This is an O(n) operation. + * <p> + * {@inheritDoc} + * </p> * * @return index of element, or -1 if not found */ @@ -409,34 +500,44 @@ public class ArrayHashSet<E> * <br> * This is an O(1) operation. * - * @param key hash source to find the identical Object within this list + * @param element hash source to find the identical Object within this list * @return object from this list, identical to the given <code>key</code> hash code, * or null if not contained */ - public final E get(final Object key) { - return map.get(key); + public final E get(final Object element) { + return map.get(element); } /** * Identity method allowing to get the identical object, using the internal hash map.<br> - * If the <code>key</code> is not yet contained, add it. + * If the <code>element</code> is not yet contained, add it. * <br> * This is an O(1) operation. * - * @param key hash source to find the identical Object within this list + * @param element hash source to find the identical Object within this list * @return object from this list, identical to the given <code>key</code> hash code, * or add the given <code>key</code> and return it. + * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false} */ - public final E getOrAdd(final E key) { - final E identity = get(key); - if(null == identity) { - // object not contained yet, add it - if(!this.add(key)) { - throw new InternalError("Key not mapped, but contained in list: "+key); + public final E getOrAdd(final E element) throws NullPointerException { + if( supportNullValue ) { + if( map.containsKey(element) ) { + // existent + return map.get(element); + } + } else { + checkNull(element); + final E identity = map.get(element); + if(null != identity) { + // existent + return identity; } - return key; } - return identity; + // !existent + if(!this.add(element)) { + throw new InternalError("Element not mapped, but contained in list: "+element); + } + return element; } /** @@ -455,4 +556,9 @@ public class ArrayHashSet<E> return data.contains(element); } + private static final void checkNull(final Object element) throws NullPointerException { + if( null == element ) { + throw new NullPointerException("Null element not supported"); + } + } } diff --git a/src/java/com/jogamp/common/util/Bitfield.java b/src/java/com/jogamp/common/util/Bitfield.java new file mode 100644 index 0000000..4b2b9d5 --- /dev/null +++ b/src/java/com/jogamp/common/util/Bitfield.java @@ -0,0 +1,208 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import jogamp.common.util.SyncedBitfield; + +/** + * Simple bitfield interface for efficient bit storage access in O(1). + * @since 2.3.2 + */ +public interface Bitfield { + /** Maximum 32 bit Unsigned Integer Value: {@code 0xffffffff} == {@value}. */ + public static final int UNSIGNED_INT_MAX_VALUE = 0xffffffff; + + /** + * Bit operation utilities (static). + */ + public static class Util { + /** + * Returns the 32 bit mask of n-bits, i.e. n low order 1’s. + * <p> + * Implementation handles n == 32. + * </p> + * @throws IndexOutOfBoundsException if {@code b} is out of bounds, i.e. > 32 + */ + public static int getBitMask(final int n) { + if( 32 > n ) { + return ( 1 << n ) - 1; + } else if ( 32 == n ) { + return UNSIGNED_INT_MAX_VALUE; + } else { + throw new IndexOutOfBoundsException("n <= 32 expected, is "+n); + } + } + + /** + * Returns the number of set bits within given 32bit integer in O(1) + * using a <i>HAKEM 169 Bit Count</i> inspired implementation: + * <pre> + * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html + * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169 + * http://tekpool.wordpress.com/category/bit-count/ + * http://www.hackersdelight.org/ + * </pre> + */ + public static final int bitCount(int n) { + // Note: Original used 'unsigned int', + // hence we use the unsigned right-shift '>>>' + /** + * Original does not work due to lack of 'unsigned' right-shift and modulo, + * we need 2-complementary solution, i.e. 'signed'. + int c = n; + c -= (n >>> 1) & 033333333333; + c -= (n >>> 2) & 011111111111; + return ( (c + ( c >>> 3 ) ) & 030707070707 ) & 0x3f; // % 63 + */ + // Hackers Delight, Figure 5-2, pop1 of pop.c.txt + n = n - ((n >>> 1) & 0x55555555); + n = (n & 0x33333333) + ((n >>> 2) & 0x33333333); + n = (n + (n >>> 4)) & 0x0f0f0f0f; + n = n + (n >>> 8); + n = n + (n >>> 16); + return n & 0x3f; + } + } + /** + * Simple {@link Bitfield} factory for returning the efficient implementation. + */ + public static class Factory { + /** + * Creates am efficient {@link Bitfield} instance based on the requested {@code storageBitSize}. + * <p> + * Implementation returns a plain 32 bit integer field implementation for + * {@code storageBitSize} ≤ 32 bits or an 32 bit integer array implementation otherwise. + * </p> + */ + public static Bitfield create(final int storageBitSize) { + if( 32 >= storageBitSize ) { + return new jogamp.common.util.Int32Bitfield(); + } else { + return new jogamp.common.util.Int32ArrayBitfield(storageBitSize); + } + } + /** + * Creates a synchronized {@link Bitfield} by wrapping the given {@link Bitfield} instance. + */ + public static Bitfield synchronize(final Bitfield impl) { + return new SyncedBitfield(impl); + } + } + /** + * Returns the storage size in bit units, e.g. 32 bit for implementations using one {@code int} field. + */ + int size(); + + + /** + * Set all bits of this bitfield to the given value {@code bit}. + */ + void clearField(final boolean bit); + + /** + * Returns {@code length} bits from this storage, + * starting with the lowest bit from the storage position {@code lowBitnum}. + * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}]. + * @param length number of bits to read, constrained to [0..32]. + * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds + * @see #put32(int, int, int) + */ + int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException; + + /** + * Puts {@code length} bits of given {@code data} into this storage, + * starting w/ the lowest bit to the storage position {@code lowBitnum}. + * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}]. + * @param length number of bits to write, constrained to [0..32]. + * @param data the actual bits to be put into this storage + * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds + * @see #get32(int, int) + */ + void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException; + + /** + * Copies {@code length} bits at position {@code srcLowBitnum} to position {@code dstLowBitnum} + * and returning the bits. + * <p> + * Implementation shall operate as if invoking {@link #get32(int, int)} + * and then {@link #put32(int, int, int)} sequentially. + * </p> + * @param srcLowBitnum source bit number, restricted to [0..{@link #size()}-1]. + * @param dstLowBitnum destination bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + * @see #get32(int, int) + * @see #put32(int, int, int) + */ + int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException; + + /** + * Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean get(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> + * and return the previous value. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException; + + /** + * Set the bit at position <code>bitnum</code> according to <code>bit</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + void set(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Clear the bit at position <code>bitnum</code> according to <code>bit</code>. + * @param bitnum bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + void clear(final int bitnum) throws IndexOutOfBoundsException; + + /** + * Copies the bit at position {@code srcBitnum} to position {@code dstBitnum} + * and returning <code>true</code> if the bit is set, otherwise <code>false</code>. + * @param srcBitnum source bit number, restricted to [0..{@link #size()}-1]. + * @param dstBitnum destination bit number, restricted to [0..{@link #size()}-1]. + * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds + */ + boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException; + + /** + * Returns the number of one bits within this bitfield. + * <p> + * Utilizes {#link {@link Bitfield.Util#bitCount(int)}}. + * </p> + */ + int bitCount(); +} diff --git a/src/java/com/jogamp/common/util/CustomCompress.java b/src/java/com/jogamp/common/util/CustomCompress.java new file mode 100644 index 0000000..6bca095 --- /dev/null +++ b/src/java/com/jogamp/common/util/CustomCompress.java @@ -0,0 +1,167 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +/** + * All in memory inflater / deflator for small chunks using streams + * <p> + * Stream header of deflated data: + * <ul> + * <li>4 bytes magic 0xDEF1A7E0 (Big Endian)</li> + * <li>4 bytes integer deflated-size (Big Endian)</li> + * <li>4 bytes integer inflated-size (Big Endian)</li> + * <li>deflated bytes</li> + * </ul> + * </p> + */ +public class CustomCompress { + /** Start of stream header for deflated data */ + public static final int MAGIC = 0xDEF1A7E0; + + /** + * + * @param in {@link InputStream} at start of stream header, i.e. position {@link #MAGIC}. + * @return the inflated bytes from the stream + * @throws IOException if an I/O or deflation exception occurs + * @throws IllegalArgumentException if {@code inLen} ≤ 0 or {@code outLen} ≤ 0, as read from header + */ + public static byte[] inflateFromStream(final InputStream in) + throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException + { + final int inLen; + final int outLen; + { + final DataInputStream din = new DataInputStream(in); + final int _magic = din.readInt(); + if( _magic != MAGIC ) { + throw new IOException("wrong magic: "+Integer.toHexString(_magic)+", expected "+Integer.toHexString(MAGIC)); + } + inLen = din.readInt(); + outLen = din.readInt(); + } + return inflateFromStream(in, inLen, outLen, new byte[outLen], 0); + } + + /** + * + * @param in {@link InputStream} at start of deflated bytes, i.e. after the stream header. + * @param inLen number of deflated bytes in stream {@code in} + * @param outLen number of inflated {@code output} bytes at {@code outOff} + * @param output sink for deflated bytes + * @param outOff offset to {@code output} + * @return the inflated bytes from the stream, passing {@code output} for chaining + * @throws IOException if an I/O or deflation exception occurs + * @throws ArrayIndexOutOfBoundsException if {@code outOff} and {@code outLen} exceeds {@code output} + * @throws IllegalArgumentException if {@code inLen} ≤ 0 or {@code outLen} ≤ 0 + */ + public static byte[] inflateFromStream(final InputStream in, final int inLen, final int outLen, + final byte[] output, final int outOff) + throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException + { + if (inLen <= 0 || outLen <= 0 ) { + throw new IllegalArgumentException("Length[input "+inLen+", output "+outLen+"]"); + } + if (outOff < 0 || output.length < outOff + outLen) { + throw new ArrayIndexOutOfBoundsException("output.length "+output.length+", offset "+outOff+", length "+outLen); + } + final byte[] input = new byte[inLen]; + int numBytes = 0; + try { + while (true) { + final int remBytes = inLen - numBytes; + int count; + if ( 0 >= remBytes || (count = in.read(input, numBytes, remBytes)) == -1 ) { + break; + } + numBytes += count; + } + } finally { + in.close(); + } + if( inLen != numBytes ) { + throw new IOException("Got "+numBytes+" bytes != expected "+inLen); + } + try { + final Inflater inflater = new Inflater(); + inflater.setInput(input, 0, inLen); + final int outSize = inflater.inflate(output, outOff, outLen); + inflater.end(); + if( outLen != outSize ) { + throw new IOException("Got inflated "+outSize+" bytes != expected "+outLen); + } + } catch(final DataFormatException dfe) { + throw new IOException(dfe); + } + return output; + } + + /** + * @param input raw input bytes + * @param inOff offset to {@code input} + * @param inLen number of {@code input} bytes at {@code inOff} + * @param level compression level 0-9 or {@link Deflater#DEFAULT_COMPRESSION} + * @param out sink for deflated bytes + * @return number of deflated bytes written, not including the header. + * @throws IOException if an I/O or deflation exception occurs + * @throws ArrayIndexOutOfBoundsException if {@code inOff} and {@code inLen} exceeds {@code input} + * @throws IllegalArgumentException if {@code inLen} ≤ 0 + */ + public static int deflateToStream(final byte[] input, final int inOff, final int inLen, + final int level, final OutputStream out) throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException { + if (inLen <= 0 ) { + throw new IllegalArgumentException("Length[input "+inLen+"]"); + } + if (inOff < 0 || input.length < inOff + inLen) { + throw new ArrayIndexOutOfBoundsException("input.length "+input.length+", offset "+inOff+", length "+inLen); + } + final byte[] output = new byte[inLen]; + final Deflater deflater = new Deflater(level); + deflater.setInput(input, inOff, inLen); + deflater.finish(); + final int outSize = deflater.deflate(output, 0, inLen); + deflater.end(); + { + final DataOutputStream dout = new DataOutputStream(out); + dout.writeInt(CustomCompress.MAGIC); + dout.writeInt(outSize); + dout.writeInt(inLen); + } + out.write(output, 0, outSize); + return outSize; + } + +} diff --git a/src/java/com/jogamp/common/util/FunctionTask.java b/src/java/com/jogamp/common/util/FunctionTask.java index 4ac64d3..9eb1ca5 100644 --- a/src/java/com/jogamp/common/util/FunctionTask.java +++ b/src/java/com/jogamp/common/util/FunctionTask.java @@ -30,6 +30,8 @@ package com.jogamp.common.util; import java.io.PrintStream; +import com.jogamp.common.JogampRuntimeException; + /** * Helper class to provide a Runnable queue implementation with a Runnable wrapper * which notifies after execution for the <code>invokeAndWait()</code> semantics. @@ -40,34 +42,67 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { protected A[] args; /** - * Invokes <code>func</code>. + * Invokes <code>func</code> on the current {@link Thread}. + * <p> + * The result can be retrieved via {@link FunctionTask#getResult()}, + * using the returned instance. + * </p> + * @param func the {@link Function} to execute. + * @param args the {@link Function} arguments + * @return the newly created and invoked {@link FunctionTask} + * @since 2.4.0 + */ + public static <U,V> FunctionTask<U,V> invokeOnCurrentThread(final Function<U,V> func, final V... args) { + final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, null, false, null); + rt.args = args; + rt.run(); + return rt; + } + + /** + * Invokes <code>func</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. + * <p> + * The result can be retrieved via {@link FunctionTask#getResult()}, + * using the returned instance. + * </p> + * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>func</code> execution is completed, otherwise returns immediately. * @param func the {@link Function} to execute. * @param args the {@link Function} arguments - * @return the {@link Function} return value + * @return the newly created and invoked {@link FunctionTask} + * @since 2.3.2 */ - public static <U,V> U invoke(final boolean waitUntilDone, final Function<U,V> func, final V... args) { - Throwable throwable = null; - final Object sync = new Object(); - final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - final U res; - synchronized(sync) { - res = rt.eval(args); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); + public static <U,V> FunctionTask<U,V> invokeOnNewThread(final ThreadGroup tg, final String threadName, + final boolean waitUntilDone, final Function<U,V> func, final V... args) { + final FunctionTask<U,V> rt; + if( !waitUntilDone ) { + rt = new FunctionTask<U,V>( func, null, true, System.err ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + rt.args = args; + t.start(); + } else { + final Object sync = new Object(); + rt = new FunctionTask<U,V>( func, sync, true, null ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + synchronized(sync) { + rt.args = args; + t.start(); + while( rt.isInQueue() ) { + try { + sync.wait(); + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rt.getThrowable(); + if(null!=throwable) { + throw new JogampRuntimeException(throwable); + } } } } - return res; + return rt; } /** @@ -124,6 +159,8 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { */ @Override public final void run() { + execThread = Thread.currentThread(); + final A[] args = this.args; this.args = null; this.result = null; @@ -144,6 +181,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; } } else { synchronized (syncObject) { @@ -161,6 +199,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; syncObject.notifyAll(); } } diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index c773b21..0381ebc 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -39,18 +39,25 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; +import java.io.Reader; +import java.io.SyncFailedException; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.regex.Pattern; import jogamp.common.Debug; import jogamp.common.os.AndroidUtils; import jogamp.common.os.PlatformPropsImpl; +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.JogampRuntimeException; import com.jogamp.common.net.AssetURLContext; import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; @@ -58,7 +65,61 @@ import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.os.Platform; public class IOUtil { - public static final boolean DEBUG = Debug.debug("IOUtil"); + public static final boolean DEBUG; + private static final boolean DEBUG_EXE; + private static final boolean DEBUG_EXE_NOSTREAM; + private static final boolean DEBUG_EXE_EXISTING_FILE; + private static final boolean testTempDirExec; + private static final Method fileToPathGetter; + private static final Method isExecutableQuery; + private static final boolean useNativeExeFile; + + static { + final boolean _props[] = { false, false, false, false, false, false }; + final Method[] res = AccessController.doPrivileged(new PrivilegedAction<Method[]>() { + @Override + public Method[] run() { + final Method[] res = new Method[] { null, null }; + try { + int i=0; + _props[i++] = Debug.debug("IOUtil"); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true); + // For security reasons, we have to hardcode this, i.e. disable this manual debug feature! + _props[i++] = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.TestTempDirExec", true, true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.UseNativeExeFile", true, false); + + // Java 1.7 + i=0; + res[i] = File.class.getDeclaredMethod("toPath"); + res[i++].setAccessible(true); + final Class<?> nioPathClz = ReflectionUtil.getClass("java.nio.file.Path", false, IOUtil.class.getClassLoader()); + final Class<?> nioFilesClz = ReflectionUtil.getClass("java.nio.file.Files", false, IOUtil.class.getClassLoader()); + res[i] = nioFilesClz.getDeclaredMethod("isExecutable", nioPathClz); + res[i++].setAccessible(true); + } catch (final Throwable t) { + if(_props[0]) { + ExceptionUtils.dumpThrowable("ioutil-init", t); + } + } + return res; + } + }); + { + int i=0; + DEBUG = _props[i++]; + DEBUG_EXE = _props[i++]; + DEBUG_EXE_NOSTREAM = _props[i++]; + DEBUG_EXE_EXISTING_FILE = _props[i++]; + testTempDirExec = _props[i++]; + useNativeExeFile = _props[i++]; + + i=0; + fileToPathGetter = res[i++]; + isExecutableQuery = res[i++]; + } + } /** Std. temporary directory property key <code>java.io.tmpdir</code>. */ private static final String java_io_tmpdir_propkey = "java.io.tmpdir"; @@ -188,6 +249,15 @@ public class IOUtil { return numBytes; } + public static StringBuilder appendCharStream(final StringBuilder sb, final Reader r) throws IOException { + final char[] cbuf = new char[1024]; + int count; + while( 0 < ( count = r.read(cbuf) ) ) { + sb.append(cbuf, 0, count); + } + return sb; + } + /** * Copy the specified input stream to a byte array, which is being returned. */ @@ -408,7 +478,7 @@ public class IOUtil { /*** * - * RESOURCE LOCATION STUFF + * RESOURCE LOCATION HELPER * */ @@ -417,7 +487,10 @@ public class IOUtil { * to be {@link #resolve(int) resolved} at a later time. */ public static class ClassResources { - /** Class instance used to {@link #resolve(int)} the {@link #resourcePaths}. */ + /** Optional {@link ClassLoader} used to {@link #resolve(int)} {@link #resourcePaths}. */ + public final ClassLoader classLoader; + + /** Optional class instance used to {@link #resolve(int)} relative {@link #resourcePaths}. */ public final Class<?> contextCL; /** Resource paths, see {@link #resolve(int)}. */ @@ -427,67 +500,73 @@ public class IOUtil { public final int resourceCount() { return resourcePaths.length; } /** - * @param contextCL class instance to {@link #resolve(int)} {@link #resourcePaths}. - * @param resourcePaths array of strings denominating multiple resource paths. None shall be null. + * @param resourcePaths multiple relative or absolute resource locations + * @param classLoader optional {@link ClassLoader}, see {@link IOUtil#getResource(String, ClassLoader, Class)} + * @param relContext optional relative context, see {@link IOUtil#getResource(String, ClassLoader, Class)} */ - public ClassResources(final Class<?> contextCL, final String[] resourcePaths) { + public ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext) { for(int i=resourcePaths.length-1; i>=0; i--) { if( null == resourcePaths[i] ) { throw new IllegalArgumentException("resourcePath["+i+"] is null"); } } - this.contextCL = contextCL; + this.classLoader = classLoader; + this.contextCL = relContext; this.resourcePaths = resourcePaths; } /** - * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using {@link #contextCL} and {@link IOUtil#getResource(Class, String)}. + * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using + * {@link #classLoader}, {@link #contextCL} through {@link IOUtil#getResource(String, ClassLoader, Class)}. * @throws ArrayIndexOutOfBoundsException if <code>uriIndex</code> is < 0 or >= {@link #resourceCount()}. */ public URLConnection resolve(final int uriIndex) throws ArrayIndexOutOfBoundsException { - return getResource(contextCL, resourcePaths[uriIndex]); + return getResource(resourcePaths[uriIndex], classLoader, contextCL); } } /** * Locating a resource using {@link #getResource(String, ClassLoader)}: * <ul> - * <li><i>relative</i>: <code>context</code>'s package name-path plus <code>resourcePath</code> via <code>context</code>'s ClassLoader. + * <li><i>relative</i>: <code>relContext</code>'s package name-path plus <code>resourcePath</code> via <code>classLoader</code>. * This allows locations relative to JAR- and other URLs. - * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.</li> - * <li><i>absolute</i>: <code>context</code>'s ClassLoader and the <code>resourcePath</code> as is (filesystem)</li> + * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder. + * This attempt is skipped if {@code relContext} is {@code null}.</li> + * <li><i>absolute</i>: <code>resourcePath</code> as is via <code>classLoader</code>. * </ul> - * * <p> * Returns the resolved and open URLConnection or null if not found. * </p> * + * @param resourcePath the resource path to locate relative or absolute + * @param classLoader the optional {@link ClassLoader}, recommended + * @param relContext relative context, i.e. position, of the {@code resourcePath}, + * to perform the relative lookup, if not {@code null}. * @see #getResource(String, ClassLoader) * @see ClassLoader#getResource(String) * @see ClassLoader#getSystemResource(String) */ - public static URLConnection getResource(final Class<?> context, final String resourcePath) { + public static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext) { if(null == resourcePath) { return null; } - final ClassLoader contextCL = (null!=context)?context.getClassLoader():IOUtil.class.getClassLoader(); URLConnection conn = null; - if(null != context) { + if(null != relContext) { // scoping the path within the class's package - final String className = context.getName().replace('.', '/'); + final String className = relContext.getName().replace('.', '/'); final int lastSlash = className.lastIndexOf('/'); if (lastSlash >= 0) { final String pkgName = className.substring(0, lastSlash + 1); - conn = getResource(pkgName + resourcePath, contextCL); + conn = getResource(pkgName + resourcePath, classLoader); if(DEBUG) { - System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+context.getName()+">: "+(null!=conn)); + System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+relContext.getName()+">: "+(null!=conn)); } } } else if(DEBUG) { - System.err.println("IOUtil: null context"); + System.err.println("IOUtil: null context, skip rel. lookup"); } if(null == conn) { - conn = getResource(resourcePath, contextCL); + conn = getResource(resourcePath, classLoader); if(DEBUG) { System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn)); } @@ -518,8 +597,7 @@ public class IOUtil { return AssetURLContext.createURL(resourcePath, cl).openConnection(); } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: Caught Exception:"); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil", ioe); } return null; } @@ -528,8 +606,7 @@ public class IOUtil { return AssetURLContext.resolve(resourcePath, cl); } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: Caught Exception:"); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil", ioe); } } } @@ -557,7 +634,7 @@ public class IOUtil { } /** - * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory. + * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute. * @return parent of path * @throws URISyntaxException if path is empty or has no parent directory available */ @@ -569,11 +646,11 @@ public class IOUtil { final int e = path.lastIndexOf("/"); if( e < 0 ) { - throw new URISyntaxException(path, "path contains no '/'"); + throw new URISyntaxException(path, "path contains no '/': <"+path+">"); } if( e == 0 ) { // path is root directory - throw new URISyntaxException(path, "path has no parents"); + throw new URISyntaxException(path, "path has no parents: <"+path+">"); } if( e < pl - 1 ) { // path is file, return it's parent directory @@ -583,23 +660,45 @@ public class IOUtil { // path is a directory .. final int p = path.lastIndexOf("/", e-1); if( p >= j) { + // parent itself has '/' - post '!' or no '!' at all return path.substring(0, p+1); + } else { + // parent itself has no '/' + final String parent = path.substring(j, e); + if( parent.equals("..") ) { + throw new URISyntaxException(path, "parent is unresolved: <"+path+">"); + } else { + // parent is '!' or empty (relative path) + return path.substring(0, j); + } } - throw new URISyntaxException(path, "parent of path contains no '/'"); } /** - * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory. - * @return clean path string where <code>../</code> and <code>./</code> is resolved. + * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute. + * @return clean path string where {@code ./} and {@code ../} is resolved, + * while keeping a starting {@code ../} at the beginning of a relative path. * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code> */ public static String cleanPathString(String path) throws URISyntaxException { - int idx; - while ( ( idx = path.indexOf("../") ) >= 0 ) { - path = getParentOf(path.substring(0, idx)) + path.substring(idx+3); + // Resolve './' before '../' to handle case 'parent/./../a.txt' properly. + int idx = path.length() - 1; + while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) { + if( 0 < idx && path.charAt(idx-1) == '.' ) { + idx-=2; // skip '../' -> idx upfront + } else { + path = path.substring(0, idx) + path.substring(idx+2); + idx--; // idx upfront + } } - while ( ( idx = path.indexOf("./") ) >= 0 ) { - path = path.substring(0, idx) + path.substring(idx+2); + idx = 0; + while ( ( idx = path.indexOf("../", idx) ) >= 0 ) { + if( 0 == idx ) { + idx += 3; // skip starting '../' + } else { + path = getParentOf(path.substring(0, idx)) + path.substring(idx+3); + idx = 0; + } } return path; } @@ -642,8 +741,7 @@ public class IOUtil { return c; } catch (final IOException ioe) { if(DEBUG) { - System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false - "+ioe.getClass().getSimpleName()+": "+ioe.getMessage()); - ioe.printStackTrace(); + ExceptionUtils.dumpThrowable("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false -", ioe); } } } else if(DEBUG) { @@ -656,7 +754,7 @@ public class IOUtil { private static String getExeTestFileSuffix() { switch(PlatformPropsImpl.OS_TYPE) { case WINDOWS: - if( Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { + if( useNativeExeFile && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { return ".exe"; } else { return ".bat"; @@ -670,7 +768,7 @@ public class IOUtil { case WINDOWS: return "echo off"+PlatformPropsImpl.NEWLINE; default: - return null; + return "#!/bin/true"+PlatformPropsImpl.NEWLINE; } } private static String[] getExeTestCommandArgs(final String scriptFile) { @@ -681,48 +779,50 @@ public class IOUtil { return new String[] { scriptFile }; } } - private static final byte[] getBytesFromRelFile(final byte[] res, final String fname, final int size) throws IOException { - final URLConnection con = IOUtil.getResource(IOUtil.class, fname); + + private static final byte[] readCode(final String fname) throws IOException { + final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class); final InputStream in = con.getInputStream(); - int numBytes = 0; + byte[] output = null; try { - while (true) { - final int remBytes = size - numBytes; - int count; - if ( 0 >= remBytes || (count = in.read(res, numBytes, remBytes)) == -1 ) { - break; - } - numBytes += count; - } + output = CustomCompress.inflateFromStream(in); } finally { in.close(); } - if( size != numBytes ) { - throw new IOException("Got "+numBytes+" bytes != expected "+size); - } - return res; + return output; } - private static final Object exeTestBytesLock = new Object(); - private static WeakReference<byte[]> exeTestBytesRef = null; + private static final Object exeTestLock = new Object(); + private static WeakReference<byte[]> exeTestCodeRef = null; private static void fillExeTestFile(final File exefile) throws IOException { - if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && + if( useNativeExeFile && + Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { - final int codeSize = 268; - final byte[] code; - synchronized ( exeTestBytesLock ) { - byte[] _code; - if( null == exeTestBytesRef || null == ( _code = exeTestBytesRef.get() ) ) { - code = getBytesFromRelFile(new byte[512], "bin/exe-windows-i586-268b.bin", codeSize); - exeTestBytesRef = new WeakReference<byte[]>(code); + final byte[] exeTestCode; + synchronized ( exeTestLock ) { + byte[] _exeTestCode = null; + if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) { + final String fname; + if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) { + fname = "bin/exe-windows-x86_64.defl"; + } else { + fname = "bin/exe-windows-i386.defl"; + } + exeTestCode = readCode(fname); + exeTestCodeRef = new WeakReference<byte[]>(exeTestCode); } else { - code = _code; + exeTestCode = _exeTestCode; } } - final OutputStream out = new FileOutputStream(exefile); + final FileOutputStream out = new FileOutputStream(exefile); try { - out.write(code, 0, codeSize); + out.write(exeTestCode, 0, exeTestCode.length); + try { + out.getFD().sync(); + } catch (final SyncFailedException sfe) { + ExceptionUtils.dumpThrowable("", sfe); + } } finally { out.close(); } @@ -732,6 +832,11 @@ public class IOUtil { final FileWriter fout = new FileWriter(exefile); try { fout.write(shellCode); + try { + fout.flush(); + } catch (final IOException sfe) { + ExceptionUtils.dumpThrowable("", sfe); + } } finally { fout.close(); } @@ -796,50 +901,70 @@ public class IOUtil { public static class StreamMonitor implements Runnable { private final InputStream[] istreams; + private final boolean[] eos; private final PrintStream ostream; private final String prefix; public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) { this.istreams = streams; + this.eos = new boolean[streams.length]; this.ostream = ostream; this.prefix = prefix; - new Thread(this, "StreamMonitor-"+Thread.currentThread().getName()).start(); + final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName()); + t.setDaemon(true); + t.start(); } + @Override public void run() { final byte[] buffer = new byte[4096]; try { - int numRead; + final int streamCount = istreams.length; + int eosCount = 0; do { - numRead = 0; for(int i=0; i<istreams.length; i++) { - final int numReadI = istreams[i].read(buffer); - if (numReadI > 0) { - if( null != ostream ) { - if( null != prefix ) { - ostream.write(prefix.getBytes()); + if( !eos[i] ) { + final int numReadI = istreams[i].read(buffer); + if (numReadI > 0) { + if( null != ostream ) { + if( null != prefix ) { + ostream.write(prefix.getBytes()); + } + ostream.write(buffer, 0, numReadI); } - ostream.write(buffer, 0, numReadI); + } else { + // numReadI == -1 + eosCount++; + eos[i] = true; } - numRead += numReadI; } } if( null != ostream ) { ostream.flush(); } - } while (numRead >= 0); - } - catch (final IOException e) { - for(int i=0; i<istreams.length; i++) { - try { - istreams[i].close(); - } catch (final IOException e2) { } + } while ( eosCount < streamCount ); + } catch (final IOException e) { + } finally { + if( null != ostream ) { + ostream.flush(); } // Should allow clean exit when process shuts down } } } + private static final Boolean isNioExecutableFile(final File file) { + if( null != fileToPathGetter && null != isExecutableQuery ) { + try { + return (Boolean) isExecutableQuery.invoke(null, fileToPathGetter.invoke(file)); + } catch (final Throwable t) { + throw new JogampRuntimeException("error invoking Files.isExecutable(file.toPath())", t); + } + } else { + return null; + } + } + /** * Returns true if the given {@code dir} * <ol> @@ -855,65 +980,114 @@ public class IOUtil { public static boolean testDirExec(final File dir) throws SecurityException { - if (!testFile(dir, true, true)) { + final boolean debug = DEBUG_EXE || DEBUG; + + if( !testTempDirExec ) { if(DEBUG) { + System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled TestTempDirExec"); + } + return false; + } + if (!testFile(dir, true, true)) { + if( debug ) { System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir"); } return false; } if(!getOSHasNoexecFS()) { - if(DEBUG) { + if( debug ) { System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable"); } return true; } - final long t0 = DEBUG ? System.currentTimeMillis() : 0; + final long t0 = debug ? System.currentTimeMillis() : 0; final File exeTestFile; + final boolean existingExe; try { - exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir); + final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null; + if( null != permExeTestFile && permExeTestFile.exists() ) { + exeTestFile = permExeTestFile; + existingExe = true; + } else { + exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir); + existingExe = false; + fillExeTestFile(exeTestFile); + } } catch (final SecurityException se) { throw se; // fwd Security exception } catch (final IOException e) { - if(DEBUG) { + if( debug ) { e.printStackTrace(); } return false; } - final long t1 = DEBUG ? System.currentTimeMillis() : 0; + final long t1 = debug ? System.currentTimeMillis() : 0; + long t2; int res = -1; - if(exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */)) { - try { - fillExeTestFile(exeTestFile); - - // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)' - // and hence splitting up command by spaces! - final Process pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ) ); - /** - * Disable StreamMonitor, which throttles exec-test performance a lot! - * - * if( isStringSet(shellCode) ) { - new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: "); - } - */ - pr.waitFor() ; - res = pr.exitValue(); - } catch (final SecurityException se) { - throw se; // fwd Security exception - } catch (final Throwable t) { - res = -2; - if(DEBUG) { - System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage()); - t.printStackTrace(); + int exitValue = -1; + Boolean isNioExec = null; + if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) { + t2 = debug ? System.currentTimeMillis() : 0; + // First soft exec test via NIO's ACL check, if available + isNioExec = isNioExecutableFile(exeTestFile); + if( null != isNioExec ) { + res = isNioExec.booleanValue() ? 0 : -1; + } + if( null == isNioExec || 0 <= res ) { + // Hard exec test via actual execution, if NIO's ACL check succeeded or not available. + // Required, since Windows 'Software Restriction Policies (SRP)' won't be triggered merely by NIO's ACL check. + Process pr = null; + try { + // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)' + // and hence splitting up command by spaces! + // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call! + pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ), null, null ); + if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) { + new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: "); + } + pr.waitFor(); + exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned + if( 0 == exitValue ) { + res++; // file has been executed and exited normally + } else { + res = -2; // abnormal termination + } + } catch (final SecurityException se) { + throw se; // fwd Security exception + } catch (final Throwable t) { + t2 = debug ? System.currentTimeMillis() : 0; + res = -3; + if( debug ) { + System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage()); + t.printStackTrace(); + } + } finally { + if( null != pr ) { + // Bug 1219 Comment 58: Ensure that the launched process gets terminated! + // This is Process implementation specific and varies on different platforms, + // hence it may be required. + try { + pr.destroy(); + } catch (final Throwable t) { + ExceptionUtils.dumpThrowable("", t); + } + } } } + } else { + t2 = debug ? System.currentTimeMillis() : 0; + } + + final boolean ok = 0 <= res; + if( !DEBUG_EXE && !existingExe ) { + exeTestFile.delete(); } - final boolean ok = 0 == res; - final long t2 = DEBUG ? System.currentTimeMillis() : 0; - exeTestFile.delete(); - if( DEBUG) { - System.err.println("IOUtil.testDirExec(): <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok); - System.err.println("IOUtil.testDirExec(): total "+(t2-t0)+"ms, create "+(t1-t0)+"ms, execute "+(t2-t1)+"ms"); + if( debug ) { + final long t3 = System.currentTimeMillis(); + System.err.println("IOUtil.testDirExec(): test-exe <"+exeTestFile.getAbsolutePath()+">, existingFile "+existingExe+", isNioExec "+isNioExec+", returned "+exitValue); + System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok); + System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms"); } return ok; } diff --git a/src/java/com/jogamp/common/util/IntBitfield.java b/src/java/com/jogamp/common/util/IntBitfield.java deleted file mode 100644 index 74e37ac..0000000 --- a/src/java/com/jogamp/common/util/IntBitfield.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2012 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: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions 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. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.common.util; - -/** - * Simple bitfield holder class using an int[] storage. - * <p> - * IntBitfield allows convenient access of a wide field of transient bits using efficient storage in O(1). - * </p> - * <p> - * It can be used e.g. to map key-codes to pressed-state etc. - * </p> - */ -public class IntBitfield { - /** Unit size in bits, here 32 bits for one int unit. */ - public static final int UNIT_SIZE = 32; - - private static final long UNIT_SHIFT_L = 5L; - private static final int UNIT_SHIFT_I = 5; - - private final int[] storage; - private final long bitsCountL; - private final int bitsCountI; - - /** - * @param bitCount - */ - public IntBitfield(final long bitCount) { - final int units = (int) Math.max(1L, ( bitCount + 7L ) >>> UNIT_SHIFT_L); - this.storage = new int[units]; - this.bitsCountL = (long)units << UNIT_SHIFT_L ; - this.bitsCountI = bitsCountL > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)bitsCountL; - } - - /** - * @param bitCount - */ - public IntBitfield(final int bitCount) { - final int units = Math.max(1, ( bitCount + 7 ) >>> UNIT_SHIFT_I); - this.storage = new int[units]; - this.bitsCountI = units << UNIT_SHIFT_I; - this.bitsCountL = bitsCountI; - } - - private final void check(final long bitnum) { - if( 0 > bitnum || bitnum >= bitsCountL ) { - throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountL-1)+"], but is "+bitnum); - } - } - private final void check(final int bitnum) { - if( 0 > bitnum || bitnum >= bitsCountI ) { - throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountI-1)+"], but is "+bitnum); - } - } - - /** Return the capacity of this bit field, i.e. the number of bits stored int this field. */ - public final long capacity() { return bitsCountL; } - - /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */ - public final boolean get(final long bitnum) { - check(bitnum); - final int u = (int) ( bitnum >>> UNIT_SHIFT_L ); - final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) ); - return 0 != ( storage[u] & ( 1 << b ) ) ; - } - - /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */ - public final boolean get(final int bitnum) { - check(bitnum); - final int u = bitnum >>> UNIT_SHIFT_I; - final int b = bitnum - ( u << UNIT_SHIFT_I ); - return 0 != ( storage[u] & ( 1 << b ) ) ; - } - - /** - * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> - * and return the previous value. - */ - public final boolean put(final long bitnum, final boolean bit) { - check(bitnum); - final int u = (int) ( bitnum >>> UNIT_SHIFT_L ); - final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) ); - final int m = 1 << b; - final boolean prev = 0 != ( storage[u] & m ) ; - if( prev != bit ) { - if( bit ) { - storage[u] |= m; - } else { - storage[u] &= ~m; - } - } - return prev; - } - - /** - * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code> - * and return the previous value. - */ - public final boolean put(final int bitnum, final boolean bit) { - check(bitnum); - final int u = bitnum >>> UNIT_SHIFT_I; - final int b = bitnum - ( u << UNIT_SHIFT_I ); - final int m = 1 << b; - final boolean prev = 0 != ( storage[u] & m ) ; - if( prev != bit ) { - if( bit ) { - storage[u] |= m; - } else { - storage[u] &= ~m; - } - } - return prev; - } - /** - * Returns the number of set bits within given 32bit integer in O(1) - * using <i>HAKEM Bit Count</i>: - * <pre> - * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html - * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169 - * http://tekpool.wordpress.com/category/bit-count/ - * </pre> - */ - public static final int getBitCount(final int n) { - // Note: Original used 'unsigned int', - // hence we use the unsigned right-shift '>>>' - int c = n; - c -= (n >>> 1) & 033333333333; - c -= (n >>> 2) & 011111111111; - return ( (c + ( c >>> 3 ) ) & 030707070707 ) % 63; - } - - /** - * Returns the number of set bits within this bitfield. - * <p> - * Utilizes {#link {@link #getBitCount(int)}}. - * </p> - */ - public long getBitCount() { - long c = 0; - for(int i = storage.length-1; i>=0; i--) { - c += getBitCount(storage[i]); - } - return c; - } -} diff --git a/src/java/com/jogamp/common/util/InterruptSource.java b/src/java/com/jogamp/common/util/InterruptSource.java new file mode 100644 index 0000000..01fcdb5 --- /dev/null +++ b/src/java/com/jogamp/common/util/InterruptSource.java @@ -0,0 +1,157 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +/** + * Interface exposing {@link java.lang.Thread#interrupt()} source, + * intended for {@link java.lang.Thread} specializations. + * @since 2.3.2 + */ +public interface InterruptSource { + /** + * Returns the source of the last {@link #interrupt()} call. + * @param clear if true, issues {@link #clearInterruptSource()} + */ + Throwable getInterruptSource(final boolean clear); + + /** + * Returns the count of {@link java.lang.Thread#interrupt()} calls. + * @param clear if true, issues {@link #clearInterruptSource()} + */ + int getInterruptCounter(final boolean clear); + + /** + * Clears source and count of {@link java.lang.Thread#interrupt()} calls, if any. + */ + void clearInterruptSource(); + + public static class Util { + /** + * Casts given {@link java.lang.Thread} to {@link InterruptSource} + * if applicable, otherwise returns {@code null}. + */ + public static InterruptSource get(final java.lang.Thread t) { + if(t instanceof InterruptSource) { + return (InterruptSource)t; + } else { + return null; + } + } + /** + * Casts current {@link java.lang.Thread} to {@link InterruptSource} + * if applicable, otherwise returns {@code null}. + */ + public static InterruptSource currentThread() { + return get(java.lang.Thread.currentThread()); + } + } + + /** + * {@link java.lang.Thread} specialization implementing {@link InterruptSource} + * to track {@link java.lang.Thread#interrupt()} calls. + * @since 2.3.2 + */ + public static class Thread extends java.lang.Thread implements InterruptSource { + volatile Throwable interruptSource = null; + volatile int interruptCounter = 0; + final Object sync = new Object(); + + /** + * See {@link Thread#Thread(} for details. + */ + public Thread() { + super(); + } + /** + * See {@link Thread#Thread(ThreadGroup, Runnable)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + */ + public Thread(final ThreadGroup tg, final Runnable target) { + super(tg, target); + } + /** + * See {@link Thread#Thread(ThreadGroup, Runnable, String)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, must not be {@code null} + */ + public Thread(final ThreadGroup tg, final Runnable target, final String name) { + super(tg, target, name); + } + + /** + * Depending on whether {@code name} is null, either + * {@link #Thread(ThreadGroup, Runnable, String)} or + * {@link #Thread(ThreadGroup, Runnable)} is being utilized. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, may be {@code null} + */ + public static Thread create(final ThreadGroup tg, final Runnable target, final String name) { + return null != name ? new Thread(tg, target, name) : new Thread(tg, target); + } + + @Override + public final Throwable getInterruptSource(final boolean clear) { + synchronized(sync) { + final Throwable r = interruptSource; + if( clear ) { + clearInterruptSource(); + } + return r; + } + } + @Override + public final int getInterruptCounter(final boolean clear) { + synchronized(sync) { + final int r = interruptCounter; + if( clear ) { + clearInterruptSource(); + } + return r; + } + } + @Override + public final void clearInterruptSource() { + synchronized(sync) { + interruptCounter = 0; + interruptSource = null; + } + } + @Override + public final void interrupt() { + synchronized(sync) { + interruptCounter++; + interruptSource = new Throwable(getName()+".interrupt() #"+interruptCounter); + } + super.interrupt(); + } + } +} diff --git a/src/java/com/jogamp/common/util/InterruptedRuntimeException.java b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java new file mode 100644 index 0000000..af6fea8 --- /dev/null +++ b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java @@ -0,0 +1,80 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import com.jogamp.common.JogampRuntimeException; + +/** + * <i>Unchecked exception</i> propagating an {@link InterruptedException} + * where handling of the latter is not desired. + * <p> + * {@link InterruptedRuntimeException} may be thrown either by waiting for any {@link Runnable} + * to be completed, or during its execution. + * </p> + * <p> + * The propagated {@link InterruptedException} may be of type {@link SourcedInterruptedException}. + * </p> + * <p> + * </p> + */ +@SuppressWarnings("serial") +public class InterruptedRuntimeException extends JogampRuntimeException { + + /** + * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap} + * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}. + * + * @param message the message of this exception + * @param cause the propagated {@link InterruptedException} + */ + public InterruptedRuntimeException(final String message, final InterruptedException cause) { + super(message, SourcedInterruptedException.wrap(cause)); + } + + /** + * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap} + * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}. + * + * @param cause the propagated {@link InterruptedException} + */ + public InterruptedRuntimeException(final InterruptedException cause) { + super(SourcedInterruptedException.wrap(cause)); + } + + /** + * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + public InterruptedException getCause() { + return (InterruptedException)super.getCause(); + } +} diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 745dd12..d6c8fd4 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -163,7 +163,7 @@ public class JarUtil { } } } - if( !uri.scheme.equals( Uri.JAR_SCHEME ) ) { + if( !uri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+uri+">"); } if(DEBUG) { @@ -190,7 +190,7 @@ public class JarUtil { if(null == classJarUri) { throw new IllegalArgumentException("Uri is null"); } - if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) { + if( !classJarUri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">"); } Uri.Encoded ssp = classJarUri.schemeSpecificPart; @@ -262,7 +262,7 @@ public class JarUtil { if(null == classJarUri) { throw new IllegalArgumentException("Uri is null"); } - if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) { + if( !classJarUri.isJarScheme() ) { throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">"); } final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart; @@ -413,20 +413,6 @@ public class JarUtil { } /** - * See {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}. - * @param classFromJavaJar URI encoded! - * @param cutOffInclSubDir URI encoded! - * @param relResPath URI encoded! - * @return - * @throws IllegalArgumentException - * @throws IOException - * @throws URISyntaxException - * @deprecated Use {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}. - */ - public static java.net.URI getRelativeOf(final Class<?> classFromJavaJar, final String cutOffInclSubDir, final String relResPath) throws IllegalArgumentException, IOException, URISyntaxException { - return getRelativeOf(classFromJavaJar, Uri.Encoded.cast(cutOffInclSubDir), Uri.Encoded.cast(relResPath)).toURI(); - } - /** * Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource * relative to a given class's Jar's Uri. * <pre> diff --git a/src/java/com/jogamp/common/util/JogampVersion.java b/src/java/com/jogamp/common/util/JogampVersion.java index e9becc6..e06ce1f 100644 --- a/src/java/com/jogamp/common/util/JogampVersion.java +++ b/src/java/com/jogamp/common/util/JogampVersion.java @@ -46,6 +46,9 @@ public class JogampVersion { /** See {@link #getImplementationCommit()} */ public static final Attributes.Name IMPLEMENTATION_COMMIT = new Attributes.Name("Implementation-Commit"); + /** For FAT JogAmp jar files */ + private static final String packageNameFAT = "com.jogamp"; + private final String packageName; private final Manifest mf; private final int hash; @@ -55,12 +58,27 @@ public class JogampVersion { private final String androidPackageVersionName; protected JogampVersion(final String packageName, final Manifest mf) { - this.packageName = packageName; - this.mf = ( null != mf ) ? mf : new Manifest(); + if( null != mf ) { + // use provided valid data + this.mf = mf; + this.packageName = packageName; + } else { + // try FAT jar file + final Manifest fatMF = VersionUtil.getManifest(JogampVersion.class.getClassLoader(), packageNameFAT); + if( null != fatMF ) { + // use FAT jar file + this.mf = fatMF; + this.packageName = packageNameFAT; + } else { + // use faulty data, unresolvable .. + this.mf = new Manifest(); + this.packageName = packageName; + } + } this.hash = this.mf.hashCode(); mainAttributes = this.mf.getMainAttributes(); mainAttributeNames = mainAttributes.keySet(); - androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(packageName); // null if !Android + androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(this.packageName); // null if !Android } @Override diff --git a/src/java/com/jogamp/common/util/RunnableTask.java b/src/java/com/jogamp/common/util/RunnableTask.java index 6fb98de..2689de1 100644 --- a/src/java/com/jogamp/common/util/RunnableTask.java +++ b/src/java/com/jogamp/common/util/RunnableTask.java @@ -30,6 +30,8 @@ package com.jogamp.common.util; import java.io.PrintStream; +import com.jogamp.common.JogampRuntimeException; + /** * Helper class to provide a Runnable queue implementation with a Runnable wrapper * which notifies after execution for the <code>invokeAndWait()</code> semantics. @@ -38,70 +40,58 @@ public class RunnableTask extends TaskBase { protected final Runnable runnable; /** - * Invokes <code>runnable</code> on the current thread. - * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. - * @param runnable the {@link Runnable} to execute. + * Invokes <code>runnable</code> on the current {@link Thread}. + * @param runnable the {@link Runnable} to execute on the current thread. + * The runnable <b>must exit</b>, i.e. not loop forever. + * @return the newly created and invoked {@link RunnableTask} + * @since 2.4.0 */ - public static void invoke(final boolean waitUntilDone, final Runnable runnable) { - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - synchronized(sync) { - rt.run(); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } - } - } + public static RunnableTask invokeOnCurrentThread(final Runnable runnable) { + final RunnableTask rt = new RunnableTask( runnable, null, false, null ); + rt.run(); + return rt; } /** - * Invokes <code>runnable</code> on a new thread belonging to the given {@link ThreadGroup}. + * Invokes <code>runnable</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. * @param runnable the {@link Runnable} to execute on the new thread. If <code>waitUntilDone</code> is <code>true</code>, - * the runnable <b>must exist</b>, i.e. not loop forever. - * @param threadName the name for the new thread - * @return the newly created {@link Thread} + * the runnable <b>must exit</b>, i.e. not loop forever. + * @return the newly created and invoked {@link RunnableTask} + * @since 2.3.2 */ - public static Thread invokeOnNewThread(final ThreadGroup tg, final boolean waitUntilDone, final Runnable runnable, final String threadName) { - final Thread t = new Thread(tg, threadName) { - @Override - public void run() { - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err ); - synchronized(sync) { - rt.run(); - if( waitUntilDone ) { - try { - sync.wait(); - } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rt.getThrowable(); - } - if(null!=throwable) { - throw new RuntimeException(throwable); - } + public static RunnableTask invokeOnNewThread(final ThreadGroup tg, final String threadName, + final boolean waitUntilDone, final Runnable runnable) { + final RunnableTask rt; + if( !waitUntilDone ) { + rt = new RunnableTask( runnable, null, true, System.err ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + t.start(); + } else { + final Object sync = new Object(); + rt = new RunnableTask( runnable, sync, true, null ); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); + synchronized(sync) { + t.start(); + while( rt.isInQueue() ) { + try { + sync.wait(); + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rt.getThrowable(); + if(null!=throwable) { + throw new JogampRuntimeException(throwable); } } - } }; - t.start(); - return t; + } + } + return rt; } - /** * Create a RunnableTask object w/ synchronization, * ie. suitable for <code>invokeAndWait()</code>, i.e. {@link #invoke(boolean, Runnable) invoke(true, runnable)}. @@ -126,6 +116,8 @@ public class RunnableTask extends TaskBase { @Override public final void run() { + execThread = Thread.currentThread(); + runnableException = null; tStarted = System.currentTimeMillis(); if(null == syncObject) { @@ -143,6 +135,7 @@ public class RunnableTask extends TaskBase { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; } } else { synchronized (syncObject) { @@ -160,6 +153,7 @@ public class RunnableTask extends TaskBase { } } finally { tExecuted = System.currentTimeMillis(); + isExecuted = true; syncObject.notifyAll(); } } diff --git a/src/java/com/jogamp/common/util/SourcedInterruptedException.java b/src/java/com/jogamp/common/util/SourcedInterruptedException.java new file mode 100644 index 0000000..530f1e7 --- /dev/null +++ b/src/java/com/jogamp/common/util/SourcedInterruptedException.java @@ -0,0 +1,166 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.common.util; + +import java.io.PrintStream; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.ExceptionUtils.CustomStackTrace; + +/** + * {@link InterruptedException}, which may include the source, see {@link #getInterruptSource()}. + * <p> + * This exception may be created directly where {@link #getCause()} returns {@code null}, + * or by propagating an existing {@link InterruptedException} as returned by {@link #getCause()}. + * </p> + * @since 2.3.2 + */ +@SuppressWarnings("serial") +public class SourcedInterruptedException extends InterruptedException implements CustomStackTrace { + final Throwable interruptSource; + + /** + * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException} + * if it is not yet of the desired type and + * if the current thread if a {@link InterruptSource}, i.e. the source is known. + * <p> + * Otherwise the given {@link InterruptedException} instance is returned. + * </p> + * <p> + * In case method is creating a new wrapping instance, + * {@link InterruptSource#clearInterruptSource()} is being issued. + * </p> + * + * @param ie the to be wrapped {@link InterruptedException} + */ + public static InterruptedException wrap(final InterruptedException ie) { + return wrap(ie, InterruptSource.Util.currentThread()); + } + + /** + * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException} + * if it is not yet of the same type and if {@code source} is not {@code null}. + * <p> + * Otherwise the given {@link InterruptedException} instance is returned. + * </p> + * <p> + * In case method is creating a new wrapping instance, + * {@link InterruptSource#clearInterruptSource()} is being issued. + * </p> + * + * @param ie the to be wrapped {@link InterruptedException} + * @param source the {@link InterruptSource} + */ + public static InterruptedException wrap(final InterruptedException ie, final InterruptSource source) { + if( !(ie instanceof SourcedInterruptedException) && null != source ) { + return new SourcedInterruptedException(ie, source.getInterruptSource(true)); + } else { + return ie; + } + } + + /** + * @param message mandatory message of this exception + * @param cause optional propagated cause + * @param interruptSource optional propagated source of {@link Thread#interrupt()} call + */ + public SourcedInterruptedException(final String message, final InterruptedException cause, final Throwable interruptSource) { + super(message); + if( null != cause ) { + initCause(cause); + } + this.interruptSource = interruptSource; + } + + /** + * @param cause mandatory propagated cause + * @param interruptSource optional propagated source of {@link Thread#interrupt()} call + */ + public SourcedInterruptedException(final InterruptedException cause, final Throwable interruptSource) { + super(cause.getMessage()); + initCause(cause); + this.interruptSource = interruptSource; + } + + /** + * Returns the source of the {@link Thread#interrupt()} call if known, + * otherwise {@code null} is returned. + */ + public final Throwable getInterruptSource() { + return interruptSource; + } + + /** + * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception, + * or {@code null} if not applicable. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + public InterruptedException getCause() { + return (InterruptedException)super.getCause(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(256); + sb.append(getClass().getSimpleName()).append(": "); + if (null != interruptSource) { + sb.append("[sourced]"); + } else { + sb.append("[unknown]"); + } + final String m = getLocalizedMessage(); + if( null != m ) { + sb.append(" ").append(m); + } + return sb.toString(); + } + + @Override + public final void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth) { + final String s0 = causeStr+"["+causeIdx+"]"; + s.println(s0+" by "+getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth); + if( null != interruptSource ) { + ExceptionUtils.printCause(s, s0, interruptSource, 0, 1, stackDepth); + } + } + + @Override + public final void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth) { + s.println(getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth); + ExceptionUtils.printCause(s, "Caused", getCause(), 0, causeDepth, stackDepth); + if( null != interruptSource ) { + ExceptionUtils.printCause(s, "InterruptSource", interruptSource, 0, causeDepth, stackDepth); + } + } +} diff --git a/src/java/com/jogamp/common/util/TaskBase.java b/src/java/com/jogamp/common/util/TaskBase.java index 59b86c3..64a8313 100644 --- a/src/java/com/jogamp/common/util/TaskBase.java +++ b/src/java/com/jogamp/common/util/TaskBase.java @@ -54,17 +54,29 @@ public abstract class TaskBase implements Runnable { protected Throwable runnableException; protected long tCreated, tStarted; protected volatile long tExecuted; + protected volatile boolean isExecuted; protected volatile boolean isFlushed; + protected volatile Thread execThread; + /** + * @param syncObject The synchronization object if caller wait until <code>runnable</code> execution is completed, + * or <code>null</code> if waiting is not desired. + * @param catchExceptions Influence an occurring exception during <code>runnable</code> execution. + * If <code>true</code>, the exception is silenced and can be retrieved via {@link #getThrowable()}, + * otherwise the exception is thrown. + * @param exceptionOut If not <code>null</code>, exceptions are written to this {@link PrintStream}. + */ protected TaskBase(final Object syncObject, final boolean catchExceptions, final PrintStream exceptionOut) { this.syncObject = syncObject; this.catchExceptions = catchExceptions; this.exceptionOut = exceptionOut; this.sourceStack = TRACE_SOURCE ? new Throwable("Creation @") : null; - tCreated = System.currentTimeMillis(); - tStarted = 0; - tExecuted = 0; - isFlushed = false; + this.tCreated = System.currentTimeMillis(); + this.tStarted = 0; + this.tExecuted = 0; + this.isExecuted = false; + this.isFlushed = false; + this.execThread = null; } protected final String getExceptionOutIntro() { @@ -77,6 +89,14 @@ public abstract class TaskBase implements Runnable { } /** + * Returns the execution thread or {@code null} if not yet {@link #run()}. + * @since 2.3.2 + */ + public final Thread getExecutionThread() { + return execThread; + } + + /** * Return the synchronization object if any. * @see #RunnableTask(Runnable, Object, boolean) */ @@ -126,12 +146,12 @@ public abstract class TaskBase implements Runnable { /** * @return !{@link #isExecuted()} && !{@link #isFlushed()} */ - public final boolean isInQueue() { return 0 != tExecuted && !isFlushed; } + public final boolean isInQueue() { return !isExecuted && !isFlushed; } /** * @return True if executed, otherwise false; */ - public final boolean isExecuted() { return 0 != tExecuted ; } + public final boolean isExecuted() { return isExecuted; } /** * @return True if flushed, otherwise false; @@ -159,7 +179,16 @@ public abstract class TaskBase implements Runnable { @Override public String toString() { - return "RunnableTask[executed "+isExecuted()+", tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]"; + final String etn; + final String eth; + if( null != execThread ) { + etn = execThread.getName(); + eth = "0x"+Integer.toHexString(execThread.hashCode()); + } else { + etn = "n/a"; + eth = "n/a"; + } + return "RunnableTask[enqueued "+isInQueue()+"[executed "+isExecuted()+", flushed "+isFlushed()+", thread["+eth+", "+etn+"]], tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]"; } } diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl Binary files differnew file mode 100644 index 0000000..d8f1716 --- /dev/null +++ b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin b/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin Binary files differdeleted file mode 100644 index b0d5f63..0000000 --- a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin +++ /dev/null diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl Binary files differnew file mode 100644 index 0000000..be0998b --- /dev/null +++ b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index 24f0237..44c7a11 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -35,6 +35,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.InterruptSource; import jogamp.common.Debug; @@ -238,7 +239,7 @@ public class TempFileCache { // Add shutdown hook to cleanup the OutputStream, FileChannel, // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. // We do this so that the locks never get garbage-collected. - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { /* @Override */ @Override public void run() { @@ -265,7 +266,7 @@ public class TempFileCache { } // Start a new Reaper thread to do stuff... - final Thread reaperThread = new Thread() { + final Thread reaperThread = new InterruptSource.Thread() { /* @Override */ @Override public void run() { diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index ed69ddc..2ff5140 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -273,19 +273,6 @@ public class TempJarCache { } /** - * See {@link #addResources(Class, Uri)} - * @param certClass - * @param jarURI - * @throws IOException - * @throws SecurityException - * @throws IllegalArgumentException - * @throws URISyntaxException - * @deprecated Use {@link #addResources(Class, Uri)} - */ - public synchronized static final void addResources(final Class<?> certClass, final java.net.URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException { - addResources(certClass, Uri.valueOf(jarURI)); - } - /** * Adds native resources, if not yet added. * * @param certClass if class is certified, the JarFile entries needs to have the same certificate @@ -421,14 +408,6 @@ public class TempJarCache { return null; } - /** - * See {@link #getResourceUri(String)} - * @deprecated Use {@link #getResourceUri(String)} - */ - public synchronized static final java.net.URI getResource(final String name) throws URISyntaxException { - return getResourceUri(name).toURI(); - } - /** Similar to {@link ClassLoader#getResource(String)}. */ public synchronized static final Uri getResourceUri(final String name) throws URISyntaxException { checkInitialized(); diff --git a/src/java/com/jogamp/gluegen/ASTLocusTag.java b/src/java/com/jogamp/gluegen/ASTLocusTag.java new file mode 100644 index 0000000..aea7699 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ASTLocusTag.java @@ -0,0 +1,99 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +/** + * An AST location tag. + */ +public class ASTLocusTag { + /** Source object, might be {@link String}. */ + public final Object source; + /** Line number, {@code -1} if undefined */ + public final int line; + /** Column number, {@code -1} if undefined */ + public final int column; + /** Source text reflecting current location, {@code null} if undefined */ + public final String text; + + public ASTLocusTag(final Object source, final int line, final int column, final String text) { + this.source = source; + this.line = line; + this.column = column; + this.text = text; + } + + public String toString() { + return toString(new StringBuilder(), null, true).toString(); + } + public StringBuilder toString(final StringBuilder sb, final String level, final boolean inclText) { + boolean preCol = false; + if (source != null) { + sb.append(source); + preCol = true; + } + if (line != -1) { + if( preCol ) { + sb.append(":"); + } else { + sb.append("line "); + } + sb.append(line); + if (column != -1) { + sb.append(":" + column); + } + preCol = true; + } + if( null != level && level.length()>0 ) { + if( preCol ) { + sb.append(": "); + } + sb.append(level); + preCol = true; + } + if( inclText && null != text && text.length()>0 ) { + if( preCol ) { + sb.append(": "); + } else { + sb.append("text "); + } + sb.append("'").append(text).append("'"); + } + return sb; + } + + /** + * Interface tag for {@link ASTLocusTag} provider. + */ + public static interface ASTLocusTagProvider { + /** + * Returns this instance's {@link ASTLocusTag}, if available, + * otherwise returns {@code null}. + */ + ASTLocusTag getASTLocusTag(); + } +} diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index 93a1ecc..7c88c37 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -44,20 +44,20 @@ import java.io.*; import java.text.MessageFormat; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.Logging.LoggerIf; import com.jogamp.gluegen.cgram.types.*; -import java.util.logging.Logger; - /** Emits the C-side component of the Java<->C JNI binding. */ public class CMethodBindingEmitter extends FunctionEmitter { - protected static final Logger LOG = Logger.getLogger(CMethodBindingEmitter.class.getPackage().getName()); protected static final CommentEmitter defaultCommentEmitter = new DefaultCommentEmitter(); protected static final String arrayResLength = "_array_res_length"; protected static final String arrayRes = "_array_res"; protected static final String arrayIdx = "_array_idx"; + protected final LoggerIf LOG; + protected MethodBinding binding; /** Name of the package in which the corresponding Java method resides.*/ @@ -124,9 +124,11 @@ public class CMethodBindingEmitter extends FunctionEmitter { final boolean isJavaMethodStatic, final boolean forImplementingMethodCall, final boolean forIndirectBufferAndArrayImplementation, - final MachineDataInfo machDesc) + final MachineDataInfo machDesc, + final JavaConfiguration configuration) { - super(output, false); + super(output, false, configuration); + LOG = Logging.getLogger(CMethodBindingEmitter.class.getPackage().getName(), CMethodBindingEmitter.class.getSimpleName()); assert(binding != null); assert(javaClassName != null); @@ -148,8 +150,21 @@ public class CMethodBindingEmitter extends FunctionEmitter { public final MethodBinding getBinding() { return binding; } @Override - public String getName() { - return binding.getName(); + public String getInterfaceName() { + return binding.getInterfaceName(); + } + @Override + public String getImplName() { + return binding.getImplName(); + } + @Override + public String getNativeName() { + return binding.getNativeName(); + } + + @Override + public FunctionSymbol getCSymbol() { + return binding.getCSymbol(); } /** @@ -306,12 +321,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.print("_"); if (isOverloadedBinding) { writer.print(jniMangle(binding)); - //System.err.println("OVERLOADED MANGLING FOR " + getName() + - // " = " + jniMangle(binding)); } else { - writer.print(JavaEmitter.jniMangle(getName())); - //System.err.println(" NORMAL MANGLING FOR " + binding.getName() + - // " = " + jniMangle(getName())); + writer.print(JavaEmitter.jniMangle(getImplName())); } } @@ -450,8 +461,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { final JavaType javaReturnType = binding.getJavaReturnType(); if (!cReturnType.isVoid()) { writer.print(" "); - // Note we must respect const/volatile for return argument - writer.print(binding.getCSymbol().getReturnType().getName(true)); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See cast in emitBodyCallCFunction(..)! + writer.print(binding.getCSymbol().getReturnType().getCName(false)); writer.println(" _res;"); if (javaReturnType.isNIOByteBufferArray() || javaReturnType.isArrayOfCompoundTypeWrappers()) { @@ -569,7 +582,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" if ( NULL != " + javaArgName + " ) {"); final Type cArgType = binding.getCArgumentType(i); - String cArgTypeName = cArgType.getName(); + String cArgTypeName = cArgType.getCName(); final String convName = pointerConversionArgumentName(javaArgName); @@ -595,14 +608,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { // // Note that we properly handle only the case of an array of // compound type wrappers in emitBodyVariablePostCallCleanup below - if (!isBaseTypeConst(cArgType) && + if (!cArgType.isBaseTypeConst() && !javaArgType.isArrayOfCompoundTypeWrappers()) { // FIXME: if the arg type is non-const, the sematics might be that // the function modifies the argument -- we don't yet support // this. - throw new RuntimeException( - "Cannot copy data for ptr-to-ptr arg type \"" + cArgType + - "\": support for non-const ptr-to-ptr types not implemented."); + throw new GlueGenException( + "Cannot copy data for ptr-to-ptr arg type \"" + cArgType.getDebugString() + + "\": support for non-const ptr-to-ptr types not implemented: "+binding, binding.getCSymbol().getASTLocusTag()); } writer.println(); @@ -646,16 +659,17 @@ public class CMethodBindingEmitter extends FunctionEmitter { error = 100; } if( 0 < error ) { - throw new RuntimeException( - "Could not copy data for type \"" + cArgType + - "\"; currently only pointer- and array-types are supported. (error "+error+")"); + throw new GlueGenException( + "Could not copy data for type \"" + cArgType.getDebugString() + + "\"; currently only pointer- and array-types are supported. (error "+error+"): "+binding, + binding.getCSymbol().getASTLocusTag()); } } emitMalloc( writer, convName+"_copy", - cArgElementType.getName(), - isBaseTypeConst(cArgType), + cArgElementType.getCName(), + cArgType.isBaseTypeConst(), arrayLenName, "Could not allocate buffer for copying data in argument \\\""+javaArgName+"\\\""); @@ -692,7 +706,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { in the method binding. */ emitGetDirectBufferAddress(writer, "_tmpObj", - cArgElementType.getName(), + cArgElementType.getCName(), convName + "_copy[_copyIndex]", true, "_offsetHandle[_copyIndex]", true); @@ -702,13 +716,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { // offset argument emitGetDirectBufferAddress(writer, "_tmpObj", - cArgElementType.getName(), + cArgElementType.getCName(), "("+convName + "_copy + _copyIndex)", false /* !receivingIsPtrPtr -> linear layout -> use memcpy */, null, true); } else { if( null == cArgElementType2 ) { - throw new RuntimeException("XXX: Type "+cArgType+" not properly handled as ptr-to-ptr"); + throw new GlueGenException("XXX: Type "+cArgType.getDebugString()+" not properly handled as ptr-to-ptr: "+binding, + binding.getCSymbol().getASTLocusTag()); } // Question: do we always need to copy the sub-arrays, or just // GetPrimitiveArrayCritical on each jobjectarray element and @@ -719,14 +734,15 @@ public class CMethodBindingEmitter extends FunctionEmitter { emitMalloc( writer, convName+"_copy[_copyIndex]", - cArgElementType2.getName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! - isBaseTypeConst(cArgType), + cArgElementType2.getCName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! + cArgType.isBaseTypeConst(), "(*env)->GetArrayLength(env, _tmpObj)", "Could not allocate buffer during copying of data in argument \\\""+javaArgName+"\\\""); // FIXME: copy the data (use matched Get/ReleasePrimitiveArrayCritical() calls) if (true) { - throw new RuntimeException("Cannot yet handle type \"" + cArgType.getName() + - "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays"); + throw new GlueGenException("Cannot yet handle type \"" + cArgType.getDebugString() + + "\"; need to add support for copying ptr-to-ptr-to-primitiveType subarrays: "+binding, + binding.getCSymbol().getASTLocusTag()); } } @@ -781,7 +797,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" if ( JNI_FALSE == " + isNIOArgName(i) + " && NULL != " + javaArgName + " ) {"); // Release array - final String modeFlag = isBaseTypeConst(cArgType) ? "JNI_ABORT" : "0" ; + final String modeFlag = cArgType.isBaseTypeConst() ? "JNI_ABORT" : "0" ; writer.print(" (*env)->ReleasePrimitiveArrayCritical(env, " + javaArgName + ", " + convName + ", "+modeFlag+");"); } else { writer.println(" if ( NULL != " + javaArgName + " ) {"); @@ -792,7 +808,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { // // FIXME: should factor out this whole block of code into a separate // method for clarity and maintenance purposes - if (!isBaseTypeConst(cArgType)) { + if (!cArgType.isBaseTypeConst()) { // FIXME: handle any cleanup from treatment of non-const args, // assuming they were treated differently in // emitBodyVariablePreCallSetup() (see the similar section in that @@ -804,15 +820,16 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" _tmpObj = (*env)->GetObjectArrayElement(env, " + javaArgName + ", _copyIndex);"); emitReturnDirectBufferAddress(writer, "_tmpObj", - cArgType.asArray().getBaseElementType().getName(), + cArgType.asArray().getBaseElementType().getCName(), "("+convName + "_copy + _copyIndex)", false /* receivingIsPtrPtr */, null); writer.println(" }"); } else { - throw new RuntimeException( - "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType + - "\": support for cleaning up most non-const ptr-to-ptr types not implemented."); + throw new GlueGenException( + "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType.getDebugString() + + "\": support for cleaning up most non-const ptr-to-ptr types not implemented.", + binding.getCSymbol().getASTLocusTag()); } } @@ -833,9 +850,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { // free each element final PointerType cArgPtrType = cArgType.asPointer(); if (cArgPtrType == null) { - throw new RuntimeException( - "Could not copy data for type \"" + cArgType + - "\"; currently only pointer types supported."); + throw new GlueGenException( + "Could not copy data for type \"" + cArgType.getDebugString() + + "\"; currently only pointer types supported.", + binding.getCSymbol().getASTLocusTag()); } // process each element in the array @@ -854,9 +872,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.print(convName+"_copy[_copyIndex]"); writer.println(");"); } else { - if (true) throw new RuntimeException( - "Cannot yet handle type \"" + cArgType.getName() + - "\"; need to add support for cleaning up copied ptr-to-ptr-to-primitiveType subarrays"); + throw new GlueGenException( + "Cannot yet handle type \"" + cArgType.getDebugString() + + "\"; need to add support for cleaning up copied ptr-to-ptr-to-primitiveType subarrays", + binding.getCSymbol().getASTLocusTag()); } writer.println(" }"); } @@ -915,20 +934,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { javaArgType.isArray() || javaArgType.isArrayOfCompoundTypeWrappers() || ( javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation ) ); - if (isBaseTypeConst(cArgType)) { - writer.print("const "); - } - - // if this is a pointer to an unsigned type, add unsigned to the name to avoid compiler warnings - if(cArgType.isPointer()) { - final Type baseType = cArgType.getBaseElementType(); - if(baseType.isInt() && (((IntType)baseType).isPrimitiveUnsigned())) { - writer.print("unsigned "); - } - } - - writer.print(cArgType.getName()); + writer.print(cArgType.getCName(true)); writer.print(") "); + if (cArgType.isPointer() && javaArgType.isPrimitive()) { writer.print("(intptr_t) "); } @@ -975,13 +983,18 @@ public class CMethodBindingEmitter extends FunctionEmitter { final Type cReturnType = binding.getCReturnType(); if (!cReturnType.isVoid()) { - writer.print("_res = "); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See return type in emitBodyVariableDeclarations(..)! + writer.print("_res = ("); + writer.print(cReturnType.getCName(false)); + writer.print(") "); } if ( isCStructFunctionPointer && binding.hasContainingType() ) { // Call through function pointer writer.print(CMethodBindingEmitter.cThisArgumentName() + "->"); } - writer.print(binding.getCSymbol().getName()); + writer.print(getNativeName()); writer.print("("); emitBodyPassCArguments(writer); writer.println(");"); @@ -1020,7 +1033,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { if (returnValueCapacityExpression != null) { returnSizeOf = returnValueCapacityExpression.format(argumentNameArray()); } else { - returnSizeOf = "sizeof(" + cReturnType.getName() + ")"; + returnSizeOf = "sizeof(" + cReturnType.getCName() + ")"; } writer.println(" return JVMUtil_NewDirectByteBufferCopy(env, &_res, "+returnSizeOf+");"); } else if (javaReturnType.isNIOBuffer() || javaReturnType.isCompoundTypeWrapper()) { @@ -1029,36 +1042,76 @@ public class CMethodBindingEmitter extends FunctionEmitter { // See whether capacity has been specified if (returnValueCapacityExpression != null) { - writer.print( returnValueCapacityExpression.format( argumentNameArray() ) ); + writer.println( returnValueCapacityExpression.format( argumentNameArray() ) + ");"); } else { - if (cReturnType.isPointer() && - cReturnType.asPointer().getTargetType().isCompound()) { - if (cReturnType.asPointer().getTargetType().getSize() == null) { - throw new RuntimeException( - "Error emitting code for compound return type "+ - "for function \"" + binding + "\": " + - "Structs to be emitted should have been laid out by this point " + - "(type " + cReturnType.asPointer().getTargetType().getName() + " / " + - cReturnType.asPointer().getTargetType() + " was not) for "+binding - ); + final Type cReturnTargetType = cReturnType.isPointer() ? cReturnType.getTargetType() : null; + int mode = 0; + if ( 1 == cReturnType.pointerDepth() && null != cReturnTargetType ) { + if( cReturnTargetType.isCompound() ) { + if( !cReturnTargetType.isAnon() && + cReturnTargetType.asCompound().getNumFields() > 0 ) + { + // fully declared non-anonymous struct pointer: pass content + if ( cReturnTargetType.getSize() == null ) { + throw new GlueGenException( + "Error emitting code for compound return type "+ + "for function \"" + binding + "\": " + + "Structs to be emitted should have been laid out by this point " + + "(type " + cReturnTargetType.getCName() + " / " + + cReturnTargetType.getDebugString() + " was not) for "+binding.getCSymbol(), + binding.getCSymbol().getASTLocusTag() + ); + } + writer.println("sizeof(" + cReturnTargetType.getCName() + ") );"); + mode = 10; + } else if( cReturnTargetType.asCompound().getNumFields() == 0 ) { + // anonymous struct pointer: pass pointer + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 11; + } + } + if( 0 == mode ) { + if( cReturnTargetType.isPrimitive() ) { + // primitive pointer: pass primitive + writer.println("sizeof(" + cReturnTargetType.getCName() + ") );"); + mode = 20; + } else if( cReturnTargetType.isVoid() ) { + // void pointer: pass pointer + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 21; + } + } + } + if( 0 == mode ) { + if( null != cfg.typeInfo(cReturnType) ) { // javaReturnType.isOpaqued() covered above via isPrimitive() + // Opaque + writer.println("sizeof(" + cReturnType.getCName() + ") );"); + mode = 88; + } else { + final String wmsg = "Assumed return size of equivalent C return type"; + writer.println("sizeof(" + cReturnType.getCName() + ") ); // WARNING: "+wmsg); + mode = 99; + LOG.warning(binding.getCSymbol().getASTLocusTag(), + "No capacity specified for java.nio.Buffer return " + + "value for function \"" + binding.getName() + "\". " + wmsg + " (sizeof(" + cReturnType.getCName() + ")): " + binding); } } - writer.print("sizeof(" + cReturnType.getName() + ")"); - LOG.warning( - "No capacity specified for java.nio.Buffer return " + - "value for function \"" + binding.getName() + "\"" + - " assuming size of equivalent C return type (sizeof(" + cReturnType.getName() + ")): " + binding); + writer.println(" /** "); + writer.println(" * mode: "+mode); + writer.println(" * cReturnType: "+cReturnType.getDebugString()); + writer.println(" * cReturnTargetType: "+cReturnTargetType.getDebugString()); + writer.println(" * javaReturnType: "+javaReturnType.getDebugString()); + writer.println(" */"); } - writer.println(");"); } else if (javaReturnType.isString()) { writer.println(" if (NULL == _res) return NULL;"); - writer.println(" return (*env)->NewStringUTF(env, _res);"); + writer.println(" return (*env)->NewStringUTF(env, (const char *)_res);"); } else if (javaReturnType.isArrayOfCompoundTypeWrappers() || (javaReturnType.isArray() && javaReturnType.isNIOByteBufferArray())) { writer.println(" if (NULL == _res) return NULL;"); if (returnValueLengthExpression == null) { - throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + - binding); + throw new GlueGenException("Error while generating C code: no length specified for array returned from function " + + binding, binding.getCSymbol().getASTLocusTag()); } writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNameArray()) + ";"); writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); @@ -1071,7 +1124,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { pointerType = retType.asArray().getBaseElementType(); } writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + - ", (*env)->NewDirectByteBuffer(env, (void *)_res[" + arrayIdx + "], sizeof(" + pointerType.getName() + ")));"); + ", (*env)->NewDirectByteBuffer(env, (void *)_res[" + arrayIdx + "], sizeof(" + pointerType.getCName() + ")));"); writer.println(" }"); writer.println(" return " + arrayRes + ";"); } else if (javaReturnType.isArray()) { @@ -1080,9 +1133,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { // expression which computes the array size (already present // as ReturnValueCapacity, not yet implemented / tested here) - throw new RuntimeException( + throw new GlueGenException( "Could not emit native code for function \"" + binding + - "\": array return values for non-char types not implemented yet, for "+binding); + "\": array return values for non-char types not implemented yet, for "+binding, + binding.getCSymbol().getASTLocusTag()); // FIXME: This is approximately what will be required here // @@ -1104,8 +1158,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { //writer.print(arrayRes); //writer.println(";"); } else { - System.err.print("Unhandled return type: "+javaReturnType.getDebugString()); - throw new RuntimeException("Unhandled return type: "+javaReturnType.getDebugString()+" for "+binding); + throw new GlueGenException("Unhandled return type: "+javaReturnType.getDebugString()+" for "+binding, + binding.getCSymbol().getReturnType().getASTLocusTag()); } } } @@ -1116,7 +1170,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected String jniMangle(final MethodBinding binding) { final StringBuilder buf = new StringBuilder(); - buf.append(JavaEmitter.jniMangle(getName())); + buf.append(JavaEmitter.jniMangle(getImplName())); buf.append(getImplSuffix()); buf.append("__"); if (binding.hasContainingType()) { @@ -1132,7 +1186,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { // We should only see "void" as the first argument of a 1-argument function // FIXME: should normalize this in the parser if ((i != 0) || (binding.getNumArguments() > 1)) { - throw new RuntimeException("Saw illegal \"void\" argument while emitting \"" + getName() + "\""); + throw new GlueGenException("Saw illegal \"void\" argument while emitting arg "+i+" of "+binding, + binding.getCArgumentType(i).getASTLocusTag()); } } else { Class<?> c = type.getJavaClass(); @@ -1164,7 +1219,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { // These are not exposed at the Java level } else { // FIXME: add support for char* -> String conversion - throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); + throw new GlueGenException("Unknown kind of JavaType: arg "+i+", name="+type.getName()+" of "+binding, + binding.getCArgumentType(i).getASTLocusTag()); } } } @@ -1225,7 +1281,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryError\"),"); writer.print(" \"" + errorMessage); writer.print(" in native dispatcher for \\\""); - writer.print(getName()); + writer.print(getInterfaceName()); writer.println("\\\"\");"); writer.print(" return"); if (!binding.getJavaReturnType().isVoid()) { @@ -1386,33 +1442,34 @@ public class CMethodBindingEmitter extends FunctionEmitter { // Note that we don't need to obey const/volatile for outgoing arguments // if (javaType.isNIOBuffer()) { - ptrTypeString = cType.getName(); + // primitive NIO object + ptrTypeString = cType.getCName(); } else if (javaType.isArray() || javaType.isArrayOfCompoundTypeWrappers()) { needsDataCopy = javaArgTypeNeedsDataCopy(javaType); if (javaType.isPrimitiveArray() || javaType.isNIOBufferArray() || javaType.isArrayOfCompoundTypeWrappers()) { - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } else if (!javaType.isStringArray()) { final Class<?> elementType = javaType.getJavaClass().getComponentType(); if (elementType.isArray()) { final Class<?> subElementType = elementType.getComponentType(); if (subElementType.isPrimitive()) { // type is pointer to pointer to primitive - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } else { // type is pointer to pointer of some type we don't support (maybe // it's an array of pointers to structs?) - throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + throw new GlueGenException("Unsupported pointer type: \"" + cType.getDebugString() + "\"", cType.getASTLocusTag()); } } else { // type is pointer to pointer of some type we don't support (maybe // it's an array of pointers to structs?) - throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + throw new GlueGenException("Unsupported pointer type: \"" + cType.getDebugString() + "\"", cType.getASTLocusTag()); } } } else { - ptrTypeString = cType.getName(); + ptrTypeString = cType.getCName(); } writer.print(" "); @@ -1434,14 +1491,14 @@ public class CMethodBindingEmitter extends FunctionEmitter { String cElementTypeName = "char *"; final PointerType cPtrType = cType.asPointer(); if (cPtrType != null) { - cElementTypeName = cPtrType.getTargetType().asPointer().getName(); + cElementTypeName = cPtrType.getTargetType().asPointer().getCName(); } - if (isBaseTypeConst(cType)) { + if (cType.isBaseTypeConst()) { writer.print("const "); } writer.print(cElementTypeName+" *"); } else { - if (isBaseTypeConst(cType)) { + if (cType.isBaseTypeConst()) { writer.print("const "); } writer.print(ptrTypeString); @@ -1470,9 +1527,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { final String cVariableType; if( !cType.isPointer() && type.isCompoundTypeWrapper() ) { // FIXME: Compound call-by-value - cVariableType = cType.getName()+" *"; + cVariableType = cType.getCName()+" *"; } else { - cVariableType = cType.getName(); + cVariableType = cType.getCName(); } emitGetDirectBufferAddress(writer, incomingArgumentName, diff --git a/src/java/com/jogamp/gluegen/ConstantDefinition.java b/src/java/com/jogamp/gluegen/ConstantDefinition.java index ca67001..675c6d7 100644 --- a/src/java/com/jogamp/gluegen/ConstantDefinition.java +++ b/src/java/com/jogamp/gluegen/ConstantDefinition.java @@ -1,71 +1,481 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. +/** + * Copyright 2015 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 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. + * 1. Redistributions 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. + * 2. Redistributions 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. + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. */ - package com.jogamp.gluegen; -import java.util.*; +import java.math.BigInteger; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + +/** + * Represents a [native] constant expression, + * comprises the [native] expression, see {@link #getNativeExpr()} + * and the optional {@link CNumber} representation, see {@link #getNumber()}. + * <p> + * The representation of the equivalent java expression including + * the result type is covered by {@link JavaExpr}, + * which can be computed via {@link #computeJavaExpr(Map)}. + * </p> + * <p> + * This class and its sub-classes define and convert all native expressions + * to Java space. + * </p> + */ +public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider { + public static final long UNSIGNED_INT_MAX_VALUE = 0xffffffffL; + public static final BigInteger UNSIGNED_LONG_MAX_VALUE = new BigInteger("ffffffffffffffff", 16); + + /** + * A Number, either integer, optionally [long, unsigned], + * or floating point, optionally [double]. + */ + public static class CNumber { + /** + * {@code true} if number is integer and value stored in {@link #i}, + * otherwise {@code false} for floating point and value stored in {@link #f}. + */ + public final boolean isInteger; + /** {@code true} if number is a {@code long} {@link #isInteger}. */ + public final boolean isLong; + /** {@code true} if number is an {@code unsigned} {@link #isInteger}. */ + public final boolean isUnsigned; + /** The value if {@link #isInteger} */ + public final long i; + + /** {@code true} if number is a {@code double precision} {@code floating point}, i.e. !{@link #isInteger}. */ + public final boolean isDouble; + /** The value if !{@link #isInteger} */ + public final double f; + + /** ctor for integer number */ + public CNumber(final boolean isLong, final boolean isUnsigned, final long value) { + this.isInteger = true; + this.isLong = isLong; + this.isUnsigned = isUnsigned; + this.i = value; + this.isDouble = false; + this.f = 0.0; + } + /** ctor for floating point number */ + public CNumber(final boolean isDouble, final double value) { + this.isInteger = false; + this.isLong = false; + this.isUnsigned = false; + this.i = 0; + this.isDouble = isDouble; + this.f = value; + } + @Override + public int hashCode() { + return isInteger ? Long.valueOf(i).hashCode() : Double.valueOf(f).hashCode(); + } + @Override + public boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof CNumber) ) { + return false; + } + final CNumber t = (CNumber) arg; + return isInteger == t.isInteger && + ( isInteger ? i == t.i : f == t.f ); + } + public final String toJavaString() { + if( isInteger ) { + if( i >= 0 || isUnsigned ) { + if( isLong ) { + return "0x"+Long.toHexString(i)+"L"; + } else { + return "0x"+Integer.toHexString((int)i); + } + } else { + if( isLong ) { + return String.valueOf(i)+"L"; + } else { + return String.valueOf((int)i); + } + } + } else { + return String.valueOf(f) + ( !isDouble ? "f" : ""); + } + } + public final String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + if( isInteger ) { + if( isUnsigned ) { + sb.append("unsigned "); + } + if( isLong) { + sb.append("long: "); + } else { + sb.append("int: "); + } + sb.append(i); + } else { + if( isDouble ) { + sb.append("double: "); + } else { + sb.append("float: "); + } + sb.append(f); + } + sb.append("]"); + return sb.toString(); + } + } + + /** + * A valid java expression, including its result type, + * usually generated from a native [C] expression, + * see {@link JavaExpr#create(ConstantDefinition)}. + */ + public static class JavaExpr { + public final String javaExpression; + public final CNumber resultType; + public final Number resultJavaType; + public final String resultJavaTypeName; + public JavaExpr(final String javaExpression, final CNumber resultType) { + this.javaExpression = javaExpression; + this.resultType = resultType; + if( resultType.isDouble ) { + resultJavaTypeName = "double"; + resultJavaType = Double.valueOf(resultType.f); + } else if( !resultType.isInteger ) { + resultJavaTypeName = "float"; + resultJavaType = Double.valueOf(resultType.f).floatValue(); + } else if( resultType.isLong ) { + resultJavaTypeName = "long"; + resultJavaType = Long.valueOf(resultType.i); + } else /* if( resultType.isInteger ) */ { + resultJavaTypeName = "int"; + resultJavaType = Long.valueOf(resultType.i).intValue(); + } + } + /** + * Computes a valid {@link JavaExpr java expression} based on the given {@link ConstantDefinition}, + * which may either be a single {@link CNumber}, see {@link ConstantDefinition#getNumber()}, + * or represents a native expression, see {@link ConstantDefinition#getExpr()}. + */ + public static JavaExpr compute(final ConstantDefinition constDef, + final Map<String, ConstantDefinition.JavaExpr> constMap) { + final boolean debug = GlueGen.debug(); + if( debug ) { + System.err.println("ConstJavaExpr.create: "+constDef); + } + if( constDef.hasNumber() ) { + // Already parsed as CNumber completely! + if( debug ) { + System.err.printf("V %s (isCNumber)%n", constDef); + } + return new JavaExpr(constDef.getNumber().toJavaString(), constDef.getNumber()); + } + final StringBuilder javaExpr = new StringBuilder(); + final String nativeExpr = constDef.getNativeExpr(); -/** Represents the definition of a constant which was provided either - via a #define statement or through an enum definition. */ -public class ConstantDefinition { + // "calculates" the result type of a simple expression + // example: (2+3)-(2.0f-3.0) -> Double + // example: (1 << 2) -> Integer + CNumber resultType = null; + final Matcher matcher = patternCPPOperand.matcher(nativeExpr); + int preStartIdx = 0; + int opEndIdx = 0; + while ( matcher.find() ) { + final int opStartIdx = matcher.start(); + if( opStartIdx > preStartIdx ) { + final String sValue = nativeExpr.substring(preStartIdx, opStartIdx).trim(); + if( sValue.length() > 0 ) { + if( debug ) { + System.err.printf("V %03d-%03d: %s%n", preStartIdx, opStartIdx, sValue); + } + resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); + javaExpr.append(" "); + } + } + opEndIdx = matcher.end(); + final String op = nativeExpr.substring(opStartIdx, opEndIdx); + if( debug ) { + System.err.printf("O %03d-%03d: %s%n", opStartIdx, opEndIdx, op); + } + javaExpr.append(op).append(" "); + preStartIdx = opEndIdx; + } + if( opEndIdx < nativeExpr.length() ) { + // tail .. + final String sValue = nativeExpr.substring(opEndIdx).trim(); + if( sValue.length() > 0 ) { + if( debug ) { + System.err.printf("V %03d %03d-%03d: %s (tail)%n", preStartIdx, opEndIdx, nativeExpr.length(), sValue); + } + resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); + } + } + final String javaExprS = javaExpr.toString().trim(); + if( null == resultType ) { + throw new GlueGenException("Cannot emit const \""+constDef.getName()+"\": value \""+nativeExpr+ + "\", parsed \""+javaExprS+"\" does not contain a constant number", constDef.getASTLocusTag()); + } + return new JavaExpr(javaExprS, resultType); + } + private static CNumber processValue(final ConstantDefinition constDef, + final String sValue, + final Map<String, ConstantDefinition.JavaExpr> constMap, + CNumber resultType, + final StringBuilder javaExpr) { + final CNumber nValue = getANumber(constDef, sValue); + if( null != nValue ) { + resultType = evalType(resultType , nValue); + javaExpr.append(nValue.toJavaString()); + } else { + // Lookup CNumber type in const-map, to evaluate this result type + final JavaExpr cje = constMap.get(sValue); + if( null != cje ) { + resultType = evalType(resultType , cje.resultType); + } + javaExpr.append(sValue); + } + return resultType; + } + private static CNumber getANumber(final ConstantDefinition constDef, final String value) { + try { + final CNumber number = decodeANumber(value); + if( null != number ) { + return number; + } + } catch( final Throwable _t ) { + final String msg = "Cannot emit const \""+constDef.getName()+"\": value \""+value+ + "\" cannot be assigned to a int, long, float, or double"; + throw new GlueGenException(msg, constDef.getASTLocusTag(), _t); + } + return null; + } + private static CNumber evalType(final CNumber resultType, final CNumber type) { + //fast path + if( type.isDouble ) { + return type; + } + if( null != resultType ) { + if( resultType.isInteger ) { + if( resultType.isLong ) { + /* resultType is Long */ + if( !type.isInteger ) { + /* resultType: Long -> [ Float || Double ] */ + return type; + } + } else if( type.isLong || !type.isInteger ) { + /* resultType: Integer -> [ Long || Float || Double ] */ + return type; + } + } else if( !resultType.isInteger && !resultType.isDouble ) { + if( type.isDouble ) { + /* resultType: Float -> Double */ + return type; + } + } + } else { + return type; + } + return resultType; + } + } - private final String origName; - private final HashSet<String> aliasedNames; - private String name; - private final String value; + private final boolean relaxedEqSem; + private final String nativeExpr; + private final CNumber number; private final boolean isEnum; private final String enumName; - private Set<String> aliases; + private final ASTLocusTag astLocus; + /** + * Constructor for plain const-values, non-enumerates. + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param astLocus AST location of the represented constant. + */ + public ConstantDefinition(final String name, + final String nativeExpr, + final CNumber number, + final ASTLocusTag astLocus) { + this(name, nativeExpr, number, false, null, astLocus); + } + /** + * Constructor for enumerates + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param enumName optional name of the represented enumeration + * @param astLocus AST location of the represented constant. + */ public ConstantDefinition(final String name, - final String value, - final boolean isEnum, - final String enumName) { - this.origName = name; - this.name = name; - this.value = value; + final String nativeExpr, + final CNumber number, + final String enumName, final ASTLocusTag astLocus) { + this(name, nativeExpr, number, true, enumName, astLocus); + } + /** + * @param name unique name of this constant expression + * @param nativeExpr original [native] expression + * @param number optional {@link CNumber} representing this constant. + * If {@code null}, implementation attempts to derive a {@link CNumber} + * of the given {@code nativeExpr}. + * @param isEnum {@code true} if this constant is an enumerate, otherwise {@code false}. + * @param enumName optional name of the represented enumeration + * @param astLocus AST location of the represented constant. + */ + private ConstantDefinition(final String name, + final String nativeExpr, + final CNumber number, + final boolean isEnum, final String enumName, final ASTLocusTag astLocus) { + super(name); + this.nativeExpr = nativeExpr; + this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest(); + if( null != number ) { + this.number = number; + } else { + // Attempt to parse define string as number + final CNumber iNum = decodeIntegerNumber(nativeExpr); + if( null != iNum ) { + this.number = iNum; + } else { + final CNumber fNum = decodeDecimalNumber(nativeExpr); + if( null != fNum ) { + this.number = fNum; + } else { + this.number = null; + } + } + } this.isEnum = isEnum; this.enumName = enumName; - this.aliasedNames=new HashSet<String>(); + this.astLocus = astLocus; } - public boolean equals(final ConstantDefinition other) { - return (equals(name, other.name) && - equals(value, other.value) && - equals(enumName, other.enumName)); + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } + + /** + * Hash by its given {@link #getName() name}. + */ + @Override + public final int hashCode() { + return getName().hashCode(); } - private boolean equals(final String s1, final String s2) { + /** + * Equality test by its given {@link #getName() name}. + */ + @Override + public final boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof ConstantDefinition) ) { + return false; + } else { + final ConstantDefinition t = (ConstantDefinition)arg; + return equals(getName(), t.getName()); + } + } + + @Override + public final int hashCodeSemantics() { + // 31 * x == (x << 5) - x + int hash = 31 + ( null != getName() ? getName().hashCode() : 0 ); + hash = ((hash << 5) - hash) + ( isEnum ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( null != enumName ? enumName.hashCode() : 0 ); + hash = ((hash << 5) - hash) + ( null != number ? number.hashCode() : 0 ); + return ((hash << 5) - hash) + ( !relaxedEqSem && null != nativeExpr ? nativeExpr.hashCode() : 0 ); + } + + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof ConstantDefinition) ) { + return false; + } else { + final ConstantDefinition t = (ConstantDefinition) arg; + if( !equals(getName(), t.getName()) || + isEnum != t.isEnum || + !equals(enumName, t.enumName) ) { + return false; + } + if( null != number ) { + if( number.isInteger ) { + return number.i == t.number.i; + } else { + return number.f == t.number.f; + } + } else { + // define's string value may be semantical equal .. but formatted differently! + return relaxedEqSem || equals(nativeExpr, t.nativeExpr); + } + } + } + + /** Returns the original [native] expression. */ + public String getNativeExpr() { return nativeExpr; } + /** + * Returns the parsed {@link CNumber} of the {@link #getNativeExpr() native expression}, + * or {@code null} if the latter does not comprise a single number, + * i.e. is a complex expression. + */ + public CNumber getNumber() { return number; } + /** + * Returns {@code true} if this instance represents has a {@link #getNumber() number}, + * otherwise {@code false}. + */ + public boolean hasNumber() { return null != number; } + + /** Returns {@code null} if this definition was not part of an + enumeration, or if the enumeration is anonymous. */ + public String getEnumName() { return enumName; } + + public boolean isEnum() { return isEnum; } + + @Override + public String toString() { + return "ConstantDefinition [name \"" + getName() + + "\", expression \"" + nativeExpr + + "\", number "+number + + "], enum[is " + isEnum + ", name \"" + enumName + "\"]]"; + } + + private static boolean equals(final String s1, final String s2) { if (s1 == null || s2 == null) { if (s1 == null && s2 == null) { return true; @@ -76,57 +486,469 @@ public class ConstantDefinition { return s1.equals(s2); } - @Override - public int hashCode() { - return name.hashCode(); + /** + * Computes the {@link JavaExpr java expression} based on this instance, + * see {@link JavaExpr#create(ConstantDefinition)}. + */ + public final JavaExpr computeJavaExpr(final Map<String, ConstantDefinition.JavaExpr> constMap) { + return JavaExpr.compute(this, constMap); } - /** Supports renaming in Java binding. */ - public void rename(final String name) { - if(null!=name) { - this.name = name; - aliasedNames.add(origName); - } + // + // Static utility functions for type detection + // + + public static boolean isConstantExpression(final String value) { + if( null != value && value.length() > 0 ) { + // Single numeric value + if ( isNumber(value) ) { + return true; + } + // Find constant expressions like (1 << 3) + // if found just pass them through, they will most likely work in java too + // expressions containing identifiers are currently ignored (casts too) + final String[] values = value.split("[\\s\\(\\)]"); // [ whitespace '(' ')' ] + int numberCount = 0; + for (final String s : values) { + if( s.length() > 0 ) { + if( isCPPOperand(s) ) { + // OK + } else if ( isNumber(s) ) { + // OK + numberCount++; + } else { + return false; + } + } + } + final boolean res = numberCount > 0; + return res; + } + return false; + } + + public static boolean isIdentifier(final String value) { + boolean identifier = false; + + final char[] chars = value.toCharArray(); + + for (int i = 0; i < chars.length; i++) { + final char c = chars[i]; + if (i == 0) { + if (Character.isJavaIdentifierStart(c)) { + identifier = true; + } + } else { + if (!Character.isJavaIdentifierPart(c)) { + identifier = false; + break; + } + } + } + return identifier; + } + + /** + * Returns either {@link #decodeIntegerNumber(String)}, + * {@link #decodeDecimalNumber(String)} or {@code null}. + * @param v + */ + public static CNumber decodeANumber(final String v) { + final CNumber iNumber = ConstantDefinition.decodeIntegerNumber(v); + if( null != iNumber ) { + return iNumber; + } + return ConstantDefinition.decodeDecimalNumber(v); } - public void addAliasedName(final String name) { - aliasedNames.add(name); + /** + * If the given string {@link #isIntegerNumber(String)}, + * return the decoded integer value, represented as a {@code ANumber}, + * otherwise returns {@code null}. + * <p> + * Method strips off sign prefix {@code +} + * and integer modifier suffixes {@code [uUlL]} + * before utilizing {@link Long#decode(String)}. + * </p> + * @param v + */ + public static CNumber decodeIntegerNumber(final String v) { + if( null == v || !isIntegerNumber(v) ) { + return null; + } + String s0 = v.trim(); + if( 0 == s0.length() ) { + return null; + } + if (s0.startsWith("+")) { + s0 = s0.substring(1, s0.length()).trim(); + if( 0 == s0.length() ) { + return null; + } + } + final boolean neg; + if (s0.startsWith("-")) { + s0 = s0.substring(1, s0.length()).trim(); + if( 0 == s0.length() ) { + return null; + } + neg = true; + } else { + neg = false; + } + + // Test last two chars for [lL] and [uU] modifiers! + boolean isUnsigned = false; + boolean isLong = false; + final int j = s0.length() - 2; + for(int i = s0.length() - 1; i >= 0 && i >= j; i--) { + final char lastChar = s0.charAt(s0.length()-1); + if( lastChar == 'u' || lastChar == 'U' ) { + s0 = s0.substring(0, s0.length()-1); + isUnsigned = true; + } else if( lastChar == 'l' || lastChar == 'L' ) { + s0 = s0.substring(0, s0.length()-1); + isLong = true; + } else { + // early out, no modifier match! + break; + } + } + if( 0 == s0.length() ) { + return null; + } + final long res; + if( isLong && isUnsigned ) { + res = decodeULong(s0, neg); + } else { + if( neg ) { + s0 = "-" + s0; + } + res = Long.decode(s0).longValue(); + } + final boolean isLong2 = isLong || + ( !isUnsigned && ( Integer.MIN_VALUE > res || res > Integer.MAX_VALUE ) ) || + ( isUnsigned && res > UNSIGNED_INT_MAX_VALUE ); + return new CNumber(isLong2, isUnsigned, res); } - public Collection<String> getAliasedNames() { - return aliasedNames; + private static long decodeULong(final String v, final boolean neg) throws NumberFormatException { + final int radix; + final int idx; + if (v.startsWith("0x") || v.startsWith("0X")) { + idx = 2; + radix = 16; + } else if (v.startsWith("#")) { + idx = 1; + radix = 16; + } else if (v.startsWith("0") && v.length() > 1) { + idx = 1; + radix = 8; + } else { + idx = 0; + radix = 10; + } + final String s0 = ( neg ? "-" : "" ) + v.substring(idx); + final BigInteger res = new BigInteger(s0, radix); + if( res.compareTo(UNSIGNED_LONG_MAX_VALUE) > 0 ) { + throw new NumberFormatException("Value \""+v+"\" is > UNSIGNED_LONG_MAX"); + } + return res.longValue(); } - public String getOrigName() { - return origName; + /** + * If the given string {@link #isDecimalNumber(String)}, + * return the decoded floating-point value, represented as a {@code ANumber} object, + * otherwise returns {@code null}. + * <p> + * Method utilizes {@link Double#valueOf(String)}. + * </p> + * @param v + * @param isDouble return value for {@code double} flag + */ + public static CNumber decodeDecimalNumber(final String v) { + if( null == v || !isDecimalNumber(v) ) { + return null; + } + final String s0 = v.trim(); + if( 0 == s0.length() ) { + return null; + } + boolean _isDouble = false; + final char lastChar = s0.charAt(s0.length()-1); + if( lastChar == 'd' || lastChar == 'D' ) { + _isDouble = true; + } + final double res = Double.valueOf(s0).doubleValue(); + final double ares = Math.abs(res); + return new CNumber(_isDouble || Float.MIN_VALUE > ares || ares > Float.MAX_VALUE, res); } - public String getName() { - return name; + /** + * Matches {@link #isHexNumber(String)} or {@link #isDecimalOrIntNumber(String)}. + */ + public static boolean isNumber(final String s) { + if( isHexNumber(s) ) { + return true; + } else { + return isDecimalOrIntNumber(s); + } } - public String getValue() { return value; } - /** Returns null if this definition was not part of an - enumeration, or if the enum was anonymous. */ - public String getEnumName() { return enumName; } + /** + * Matches {@link #isHexNumber(String)} or {@link #patternIntegerNumber}. + */ + public static boolean isIntegerNumber(final String s) { + if( isHexNumber(s) ) { + return true; + } else { + return patternIntegerNumber.matcher(s).matches(); + } + } - public boolean isEnum() { return isEnum; } + /** + * Matches {@link #patternHexNumber}. + */ + public static boolean isHexNumber(final String s) { + return patternHexNumber.matcher(s).matches(); + } - public Set<String> getAliases() { - return aliases; + /** + * Matches pattern for <code>floating point</code> number, + * compatible and described in {@link Double#valueOf(String)}. + */ + public static boolean isDecimalNumber(final String s) { + return patternDecimalNumber.matcher(s).matches(); } - public void addAlias(final String alias) { - if (aliases == null) { - aliases = new LinkedHashSet<String>(); - } - aliases.add(alias); + /** + * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number, + * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}. + */ + public static boolean isDecimalOrIntNumber(final String s) { + return patternDecimalOrIntNumber.matcher(s).matches(); } - @Override - public String toString() { - return "ConstantDefinition [name " + name + " origName " + origName + " value " + value - + " aliasedNames " + aliasedNames + " aliases " + aliases - + " enumName " + enumName + " isEnum " + isEnum + "]"; + /** + * Matches pattern for valid CPP operands, see {@link #patternCPPOperand}. + */ + public static boolean isCPPOperand(final String s) { + return patternCPPOperand.matcher(s).matches(); } + /** + * Complete pattern for <code>hexadecimal</code> number, + * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. + */ + public static Pattern patternHexNumber; + + /** + * Complete pattern for <code>floating point</code> number, + * compatible and described in {@link Double#valueOf(String)}. + */ + public final static Pattern patternDecimalNumber; + + /** + * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number, + * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}. + */ + public final static Pattern patternDecimalOrIntNumber; + + /** + * Complete pattern for <code>integer</code> number, + * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. + */ + public final static Pattern patternIntegerNumber; + + /** + * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>} + * <p> + * Expression excludes {@link #patternDecimalOrIntNumber}. + * </p> + */ + public static Pattern patternCPPOperand; + + static { + final String WhiteSpace = "[\\x00-\\x20]*"; + final String Digits = "(\\p{Digit}+)"; + final String HexDigits = "(\\p{XDigit}+)"; + final String IntTypeSuffix = + "(" + + "[uU]|" + + "([uU][lL])|" + + "[lL]|" + + "([lL][uU])" + + ")"; + + final String hexRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + // HexDigits IntTypeSuffix_opt + "0[xX]" + HexDigits + IntTypeSuffix + "?" + + WhiteSpace // Optional trailing "whitespace" + ; + patternHexNumber = Pattern.compile(hexRegex); + + final String intRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + // Digits IntTypeSuffix_opt + Digits + IntTypeSuffix + "?" + + WhiteSpace // Optional trailing "whitespace" + ; + patternIntegerNumber = Pattern.compile(intRegex); + + // an exponent is 'e' or 'E' followed by an optionally + // signed decimal integer. + final String Exp = "[eE][+-]?"+Digits; + final String fpRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.("+Digits+")("+Exp+")?)|"+ + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternDecimalNumber = Pattern.compile(fpRegex); + + final String fpOrIntRegex = + WhiteSpace + // Optional leading "whitespace" + "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // Matching integers w/ IntTypeSuffix, + // which are otherwise not matched by the below floating point matcher! + // Digits IntTypeSuffix + "(" + Digits + IntTypeSuffix +")|" + + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.(" + Digits + ")(" + Exp + ")?)|" + + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternDecimalOrIntNumber = Pattern.compile(fpOrIntRegex); + + final String fpOrIntRegex2 = + WhiteSpace + // Optional leading "whitespace" + // "[+-]?" + // Optional sign character + "("+ + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // Matching integers w/ IntTypeSuffix, + // which are otherwise not matched by the below floating point matcher! + // Digits IntTypeSuffix + "(" + Digits + IntTypeSuffix +")|" + + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from the Java Language Specification, 2nd + // edition, section 3.10.2. + + "("+ + "("+ + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.(" + Digits + ")(" + Exp + ")?)|" + + + // Hexadecimal w/ binary exponent + "(" + + "(" + + // Hexadecimal strings + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + ")" + + + // binary exponent + "[pP][+-]?" + Digits + + ")" + + ")" + + "[fFdD]?"+ + ")"+ + ")" + + WhiteSpace // Optional trailing "whitespace" + ; + patternCPPOperand = Pattern.compile("(?!"+fpOrIntRegex2+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)"); + } } diff --git a/src/java/com/jogamp/gluegen/DebugEmitter.java b/src/java/com/jogamp/gluegen/DebugEmitter.java index 6381c8c..046c2b6 100644 --- a/src/java/com/jogamp/gluegen/DebugEmitter.java +++ b/src/java/com/jogamp/gluegen/DebugEmitter.java @@ -39,6 +39,7 @@ package com.jogamp.gluegen; +import java.io.IOException; import java.util.*; import com.jogamp.gluegen.cgram.types.*; @@ -46,9 +47,16 @@ import com.jogamp.gluegen.cgram.types.*; /** Debug emitter which prints the parsing results to standard output. */ public class DebugEmitter implements GlueEmitter { + protected JavaConfiguration cfg; @Override - public void readConfigurationFile(final String filename) {} + public void readConfigurationFile(final String filename) throws IOException { + cfg = createConfig(); + cfg.read(filename); + } + + @Override + public JavaConfiguration getConfiguration() { return cfg; } @Override public void beginEmission(final GlueEmitterControls controls) { @@ -66,7 +74,7 @@ public class DebugEmitter implements GlueEmitter { @Override public void emitDefine(final ConstantDefinition def, final String optionalComment) { final String name = def.getName(); - final String value = def.getValue(); + final String value = def.getNativeExpr(); System.out.println("#define " + name + " " + value + (optionalComment != null ? ("// " + optionalComment) : "")); } @@ -110,10 +118,10 @@ public class DebugEmitter implements GlueEmitter { } @Override - public void emitStruct(final CompoundType t, final String alternateName) { + public void emitStruct(final CompoundType t, final Type typedefType) { String name = t.getName(); - if (name == null && alternateName != null) { - name = alternateName; + if (name == null && typedefType != null) { + name = typedefType.getName(); } System.out.println("Referenced type \"" + name + "\""); @@ -121,4 +129,13 @@ public class DebugEmitter implements GlueEmitter { @Override public void endStructs() {} + + /** + * Create the object that will read and store configuration information for + * this JavaEmitter. + */ + protected JavaConfiguration createConfig() { + return new JavaConfiguration(); + } + } diff --git a/src/java/com/jogamp/gluegen/FunctionEmitter.java b/src/java/com/jogamp/gluegen/FunctionEmitter.java index 8e9d306..bfbb73b 100644 --- a/src/java/com/jogamp/gluegen/FunctionEmitter.java +++ b/src/java/com/jogamp/gluegen/FunctionEmitter.java @@ -42,57 +42,43 @@ package com.jogamp.gluegen; import java.util.*; import java.io.*; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; public abstract class FunctionEmitter { public static final EmissionModifier STATIC = new EmissionModifier("static"); - private final boolean isInterfaceVal; + private final boolean isInterface; private final ArrayList<EmissionModifier> modifiers; private CommentEmitter commentEmitter = null; private final PrintWriter defaultOutput; + // Only present to provide more clear comments + protected final JavaConfiguration cfg; /** * Constructs the FunctionEmitter with a CommentEmitter that emits nothing. */ - public FunctionEmitter(final PrintWriter defaultOutput, final boolean isInterface) { + public FunctionEmitter(final PrintWriter defaultOutput, final boolean isInterface, final JavaConfiguration configuration) { assert(defaultOutput != null); + this.isInterface = isInterface; this.modifiers = new ArrayList<EmissionModifier>(); this.defaultOutput = defaultOutput; - this.isInterfaceVal = isInterface; + this.cfg = configuration; } /** * Makes this FunctionEmitter a copy of the passed one. */ public FunctionEmitter(final FunctionEmitter arg) { + isInterface = arg.isInterface; modifiers = new ArrayList<EmissionModifier>(arg.modifiers); commentEmitter = arg.commentEmitter; defaultOutput = arg.defaultOutput; - isInterfaceVal = arg.isInterfaceVal; + cfg = arg.cfg; } - public boolean isInterface() { return isInterfaceVal; } - - /** - * Checks the base type of pointer-to-pointer, pointer, array or plain for const-ness. - * <p> - * Note: Implementation walks down to the base type and returns it's const-ness. - * Intermediate 'const' qualifier are not considered, e.g. const pointer. - * </p> - */ - protected final boolean isBaseTypeConst(final Type type) { - if ( 2 == type.pointerDepth() ) { - return type.asPointer().getTargetType().asPointer().getTargetType().isConst(); - } else if ( 1 == type.pointerDepth() ) { - return type.asPointer().getTargetType().isConst(); - } else if( type.isArray() ) { - return type.asArray().getBaseElementType().isConst(); - } else { - return type.isConst(); - } - } + public boolean isInterface() { return isInterface; } public PrintWriter getDefaultOutput() { return defaultOutput; } @@ -111,7 +97,11 @@ public abstract class FunctionEmitter { public Iterator<EmissionModifier> getModifiers() { return modifiers.iterator(); } - public abstract String getName(); + public abstract String getInterfaceName(); + public abstract String getImplName(); + public abstract String getNativeName(); + + public abstract FunctionSymbol getCSymbol(); /** * Emit the function to the specified output (instead of the default diff --git a/src/java/com/jogamp/gluegen/GenericCPP.java b/src/java/com/jogamp/gluegen/GenericCPP.java new file mode 100644 index 0000000..db414d9 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GenericCPP.java @@ -0,0 +1,63 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +import java.io.OutputStream; +import java.io.Reader; +import java.util.List; + +import com.jogamp.gluegen.jcpp.LexerException; + +/** + * Generic C preprocessor interface for GlueGen + */ +public interface GenericCPP { + + public void addDefine(String name, String value) throws LexerException; + + public String findFile(String filename); + + public OutputStream out(); + public void setOut(OutputStream out); + + public void run(Reader reader, String filename) throws GlueGenException; + + /** + * Returns a list of {@link ConstantDefinition}, i.e. + * <i>non-function-like</i> and <i>non-empty</i> macros w/ <i>constant-value</i>, + * as derived during parsing. + * <p> + * May return an empty list, in case this preprocessor does not + * store {@link ConstantDefinition}s. + * </p> + * @throws GlueGenException + */ + public List<ConstantDefinition> getConstantDefinitions() throws GlueGenException; + + +}
\ No newline at end of file diff --git a/src/java/com/jogamp/gluegen/GlueEmitter.java b/src/java/com/jogamp/gluegen/GlueEmitter.java index bb46cf5..0e8d61f 100644 --- a/src/java/com/jogamp/gluegen/GlueEmitter.java +++ b/src/java/com/jogamp/gluegen/GlueEmitter.java @@ -50,6 +50,7 @@ import com.jogamp.gluegen.cgram.types.*; public interface GlueEmitter { public void readConfigurationFile(String filename) throws Exception; + public JavaConfiguration getConfiguration(); /** * Begin the emission of glue code. This might include opening files, @@ -91,11 +92,11 @@ public interface GlueEmitter { public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map<Type, Type> canonMap) throws Exception; - /** Emit glue code for the given CompoundType. alternateName is + /** Emit glue code for the given CompoundType. typedefType is provided when the CompoundType (e.g. "struct foo_t") has not been typedefed to anything but the type of "pointer to struct foo_t" has (e.g. "typedef struct foo_t {} *Foo"); in this case - alternateName would be set to Foo. */ - public void emitStruct(CompoundType t, String alternateName) throws Exception; + typedefType would be set to pointer type Foo. */ + public void emitStruct(CompoundType t, Type typedefType) throws Exception; public void endStructs() throws Exception; } diff --git a/src/java/com/jogamp/gluegen/GlueGen.java b/src/java/com/jogamp/gluegen/GlueGen.java index e123910..6dee6f0 100644 --- a/src/java/com/jogamp/gluegen/GlueGen.java +++ b/src/java/com/jogamp/gluegen/GlueGen.java @@ -43,11 +43,13 @@ import com.jogamp.common.GlueGenVersion; import java.io.*; import java.util.*; +import java.util.logging.Level; import antlr.*; + import com.jogamp.gluegen.cgram.*; import com.jogamp.gluegen.cgram.types.*; -import com.jogamp.gluegen.pcpp.*; +import com.jogamp.gluegen.jcpp.JCPP; import static java.lang.System.*; @@ -61,14 +63,18 @@ public class GlueGen implements GlueEmitterControls { } private final List<String> forcedStructNames = new ArrayList<String>(); - private PCPP preprocessor; + private GenericCPP preprocessor; // State for SymbolFilters - private List<ConstantDefinition> constants; - private List<FunctionSymbol> functions; + private List<ConstantDefinition> allConstants; + private List<FunctionSymbol> allFunctions; private static boolean debug = false; + private static Level logLevel = null; + + public static void setDebug(final boolean v) { debug=v; } + public static void setLogLevel(final Level l) { logLevel=l; } public static boolean debug() { return debug; } @Override @@ -83,38 +89,66 @@ public class GlueGen implements GlueEmitterControls { @Override public void runSymbolFilter(final SymbolFilter filter) { - filter.filterSymbols(constants, functions); + filter.filterSymbols(allConstants, allFunctions); final List<ConstantDefinition> newConstants = filter.getConstants(); final List<FunctionSymbol> newFunctions = filter.getFunctions(); if (newConstants != null) { - constants = newConstants; + allConstants = newConstants; } if (newFunctions != null) { - functions = newFunctions; + allFunctions = newFunctions; } } + /** GlueGen's build in macro name {@value}, when compiling w/ GlueGen. */ + public static final String __GLUEGEN__ = "__GLUEGEN__"; @SuppressWarnings("unchecked") - public void run(final Reader reader, final String filename, final Class<?> emitterClass, final List<String> includePaths, final List<String> cfgFiles, final String outputRootDir, final boolean copyPCPPOutput2Stderr) { + public void run(final Reader reader, final String filename, final Class<?> emitterClass, final List<String> includePaths, final List<String> cfgFiles, final String outputRootDir, final boolean copyCPPOutput2Stderr) { try { - final File out = File.createTempFile("PCPPTemp", ".pcpp"); + if(debug) { + Logging.getLogger().setLevel(Level.ALL); + } else if( null != logLevel ) { + Logging.getLogger().setLevel(logLevel); + } + final GlueEmitter emit; + if (emitterClass == null) { + emit = new JavaEmitter(); + } else { + try { + emit = (GlueEmitter) emitterClass.newInstance(); + } catch (final Exception e) { + throw new RuntimeException("Exception occurred while instantiating emitter class.", e); + } + } + + for (final String config : cfgFiles) { + emit.readConfigurationFile(config); + } + final JavaConfiguration cfg = emit.getConfiguration(); + + final File out = File.createTempFile("CPPTemp", ".cpp"); final FileOutputStream outStream = new FileOutputStream(out); + // preprocessor = new PCPP(includePaths, debug, copyCPPOutput2Stderr); + preprocessor = new JCPP(includePaths, debug, copyCPPOutput2Stderr); + final String cppName = preprocessor.getClass().getSimpleName(); if(debug) { - System.err.println("PCPP output at (persistent): " + out.getAbsolutePath()); + System.err.println("CPP <"+cppName+"> output at (persistent): " + out.getAbsolutePath()); } else { out.deleteOnExit(); } - preprocessor = new PCPP(includePaths, debug, copyPCPPOutput2Stderr); - preprocessor.addDefine("__GLUEGEN__", "2"); + preprocessor.addDefine(__GLUEGEN__, "2"); preprocessor.setOut(outStream); preprocessor.run(reader, filename); outStream.flush(); outStream.close(); + if(debug) { + System.err.println("CPP <"+cppName+"> done"); + } final FileInputStream inStream = new FileInputStream(out); final DataInputStream dis = new DataInputStream(inStream); @@ -140,6 +174,7 @@ public class GlueGen implements GlueEmitterControls { final HeaderParser headerParser = new HeaderParser(); headerParser.setDebug(debug); + headerParser.setJavaConfiguration(cfg); final TypeDictionary td = new TypeDictionary(); headerParser.setTypedefDictionary(td); final TypeDictionary sd = new TypeDictionary(); @@ -162,21 +197,6 @@ public class GlueGen implements GlueEmitterControls { // generate glue code: the #defines to constants, the set of // typedefs, and the set of functions. - GlueEmitter emit = null; - if (emitterClass == null) { - emit = new JavaEmitter(); - } else { - try { - emit = (GlueEmitter) emitterClass.newInstance(); - } catch (final Exception e) { - throw new RuntimeException("Exception occurred while instantiating emitter class.", e); - } - } - - for (final String config : cfgFiles) { - emit.readConfigurationFile(config); - } - if (null != outputRootDir && outputRootDir.trim().length() > 0) { if (emit instanceof JavaEmitter) { // FIXME: hack to interfere with the *Configuration setting via commandlines @@ -189,7 +209,7 @@ public class GlueGen implements GlueEmitterControls { // Repackage the enum and #define statements from the parser into a common format // so that SymbolFilters can operate upon both identically - constants = new ArrayList<ConstantDefinition>(); + allConstants = new ArrayList<ConstantDefinition>(); for (final EnumType enumeration : headerParser.getEnums()) { String enumName = enumeration.getName(); if (enumName.equals("<anonymous>")) { @@ -197,63 +217,93 @@ public class GlueGen implements GlueEmitterControls { } // iterate over all values in the enumeration for (int i = 0; i < enumeration.getNumEnumerates(); ++i) { - final String enumElementName = enumeration.getEnumName(i); - final String value = String.valueOf(enumeration.getEnumValue(i)); - constants.add(new ConstantDefinition(enumElementName, value, true, enumName)); + final EnumType.Enumerator enumerate = enumeration.getEnum(i); + final ConstantDefinition def = + new ConstantDefinition(enumerate.getName(), enumerate.getExpr(), + enumerate.getNumber(), + enumName, enumeration.getASTLocusTag()); + allConstants.add(def); } } for (final Object elem : lexer.getDefines()) { final Define def = (Define) elem; - constants.add(new ConstantDefinition(def.getName(), def.getValue(), false, null)); + allConstants.add(new ConstantDefinition(def.getName(), def.getValue(), null, def.getASTLocusTag())); } + allConstants.addAll(preprocessor.getConstantDefinitions()); - functions = headerParser.getParsedFunctions(); + allFunctions = headerParser.getParsedFunctions(); - // begin emission of glue code + // begin emission of glue code, + // incl. firing up 'runSymbolFilter(SymbolFilter)' calls, which: + // - filters all ConstantDefinition + // - filters all FunctionSymbol emit.beginEmission(this); - emit.beginDefines(); - final Set<String> emittedDefines = new HashSet<String>(100); - // emit java equivalent of enum { ... } statements - final StringBuilder comment = new StringBuilder(); - for (final ConstantDefinition def : constants) { - if (!emittedDefines.contains(def.getName())) { - emittedDefines.add(def.getName()); - final Set<String> aliases = def.getAliases(); - if (aliases != null) { - comment.append("Alias for: <code>"); - for (final String alias : aliases) { - comment.append(" ").append(alias); - } - comment.append("</code>"); + if( debug() ) { + int i=0; + System.err.println("Filtered Constants: "+allConstants.size()); + for (final ConstantDefinition def : allConstants) { + if( debug() ) { + System.err.println("Filtered ["+i+"]: "+def.getAliasedString()); + i++; } - if (def.getEnumName() != null) { - if (comment.length() > 0) - comment.append("<br>\n"); + } + i=0; + System.err.println("Filtered Functions: "+allFunctions.size()); + for (final FunctionSymbol cFunc : allFunctions) { + System.err.println("Filtered ["+i+"]: "+cFunc.getAliasedString()); + i++; + } + } - comment.append("Defined as part of enum type \""); - comment.append(def.getEnumName()); - comment.append("\""); - } - if (comment.length() > 0) { - emit.emitDefine(def, comment.toString()); - comment.setLength(0); - } - else { - emit.emitDefine(def, null); + if ( !cfg.structsOnly() ) { + emit.beginDefines(); + final Set<String> emittedDefines = new HashSet<String>(100); + // emit java equivalent of enum { ... } statements + final StringBuilder comment = new StringBuilder(); + for (final ConstantDefinition def : allConstants) { + if (!emittedDefines.contains(def.getName())) { + emittedDefines.add(def.getName()); + final Set<String> aliases = cfg.getAliasedDocNames(def); + if (aliases != null && aliases.size() > 0 ) { + int i=0; + comment.append("Alias for: <code>"); + for (final String alias : aliases) { + if(0 < i) { + comment.append("</code>, <code>"); + } + comment.append(alias); + i++; + } + comment.append("</code>"); + } + if (def.getEnumName() != null) { + if (comment.length() > 0) + comment.append("<br>\n"); + + comment.append("Defined as part of enum type \""); + comment.append(def.getEnumName()); + comment.append("\""); + } + if (comment.length() > 0) { + emit.emitDefine(def, comment.toString()); + comment.setLength(0); + } + else { + emit.emitDefine(def, null); + } } } + emit.endDefines(); } - emit.endDefines(); // Iterate through the functions finding structs that are referenced in // the function signatures; these will be remembered for later emission final ReferencedStructs referencedStructs = new ReferencedStructs(); - for (final FunctionSymbol sym : functions) { + for (final FunctionSymbol sym : allFunctions) { // FIXME: this doesn't take into account the possibility that some of // the functions we send to emitMethodBindings() might not actually be - // emitted (e.g., if an Ignore directive in the JavaEmitter causes it - // to be skipped). + // emitted (e.g., if an Ignore directive in the JavaEmitter causes it to be skipped). sym.getType().visit(referencedStructs); } @@ -273,13 +323,9 @@ public class GlueGen implements GlueEmitterControls { // Lay out structs emit.beginStructLayout(); - for (final Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { - final Type t = iter.next(); - if (t.isCompound()) { - emit.layoutStruct(t.asCompound()); - } else if (t.isPointer()) { - final PointerType p = t.asPointer(); - final CompoundType c = p.getTargetType().asCompound(); + for (final Iterator<CompoundType> iter = referencedStructs.layouts(); iter.hasNext();) { + final CompoundType c = iter.next(); + if( !c.isLayouted() ) { emit.layoutStruct(c); } } @@ -290,20 +336,23 @@ public class GlueGen implements GlueEmitterControls { for (final Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { final Type t = iter.next(); if (t.isCompound()) { + assert t.isTypedef() && t.getName() == null : "ReferencedStructs incorrectly recorded compound type " + t; emit.emitStruct(t.asCompound(), null); } else if (t.isPointer()) { final PointerType p = t.asPointer(); final CompoundType c = p.getTargetType().asCompound(); - assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; - emit.emitStruct(c, p.getName()); + assert p.isTypedef() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p); } } emit.endStructs(); - // emit java and C code to interface with the native functions - emit.beginFunctions(td, sd, headerParser.getCanonMap()); - emit.emitFunctions(functions); - emit.endFunctions(); + if ( !cfg.structsOnly() ) { + // emit java and C code to interface with the native functions + emit.beginFunctions(td, sd, headerParser.getCanonMap()); + emit.emitFunctions(allFunctions); + emit.endFunctions(); + } // end emission of glue code emit.endEmission(); @@ -340,6 +389,9 @@ public class GlueGen implements GlueEmitterControls { emitterFQN = arg.substring(2); } else if (arg.startsWith("-C")) { cfgFiles.add(arg.substring(2)); + } else if (arg.equals("--logLevel")) { + i++; + logLevel = Level.parse(args[i]); } else if (arg.equals("--debug")) { debug=true; } else if (arg.equals("--dumpCPP")) { @@ -392,7 +444,7 @@ public class GlueGen implements GlueEmitterControls { out.println("file or files can be specified with -C option; e.g,"); out.println("-Cjava-emitter.cfg."); out.println(" --debug enables debug mode"); - out.println(" --dumpCPP directs PCPP to dump all output to stderr as well"); + out.println(" --dumpCPP directs CPP to dump all output to stderr as well"); exit(1); } } diff --git a/src/java/com/jogamp/gluegen/GlueGenException.java b/src/java/com/jogamp/gluegen/GlueGenException.java new file mode 100644 index 0000000..b6713e1 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GlueGenException.java @@ -0,0 +1,92 @@ +/** + * Copyright 2010 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.gluegen; + +import com.jogamp.common.JogampRuntimeException; + +/** A generic exception for Jogamp errors used throughout the binding + as a substitute for {@link RuntimeException}. */ + +@SuppressWarnings("serial") +public class GlueGenException extends JogampRuntimeException { + final ASTLocusTag locus; + + public ASTLocusTag getASTLocusTag() { return locus; } + + /** Constructs a GlueGenException object. */ + public GlueGenException() { + super(); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message. */ + public GlueGenException(final String message) { + super(message); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final Throwable cause) { + super(message, cause); + locus = null; + } + + /** Constructs a GlueGenException object with the specified root + cause. */ + public GlueGenException(final Throwable cause) { + super(cause); + locus = null; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final ASTLocusTag locusTag) { + super(message); + this.locus = locusTag; + } + + /** Constructs a GlueGenException object with the specified detail + message and root cause. */ + public GlueGenException(final String message, final ASTLocusTag locusTag, final Throwable cause) { + super(message, cause); + this.locus = locusTag; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(256); + if (null != locus) { + locus.toString(sb, "error", true).append(": "); + } + sb.append(getClass().getSimpleName()).append(": ").append(getLocalizedMessage()); + return sb.toString(); + } + +} diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 346920d..019ca2d 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -40,21 +40,19 @@ package com.jogamp.gluegen; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; import com.jogamp.gluegen.JavaEmitter.EmissionStyle; import com.jogamp.gluegen.JavaEmitter.MethodAccess; +import com.jogamp.gluegen.Logging.LoggerIf; import java.io.*; import java.lang.reflect.Array; import java.util.*; -import java.util.Map.Entry; import java.util.regex.*; import com.jogamp.gluegen.jgram.*; import com.jogamp.gluegen.cgram.types.*; -import java.util.logging.Logger; - -import jogamp.common.os.MachineDataInfoRuntime; import static java.util.logging.Level.*; import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*; @@ -63,17 +61,13 @@ import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*; JavaEmitter. */ public class JavaConfiguration { - - public static final boolean DEBUG_IGNORES = GlueGen.debug() || false; - public static final boolean DEBUG_RENAMES = GlueGen.debug() || false; - private int nestedReads; private String packageName; private String implPackageName; private String className; private String implClassName; - protected static final Logger LOG = Logger.getLogger(JavaConfiguration.class.getPackage().getName()); + protected final LoggerIf LOG; public static String NEWLINE = System.getProperty("line.separator"); @@ -108,6 +102,13 @@ public class JavaConfiguration { private boolean tagNativeBinding; /** + * If true, {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * will attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifiers. + * Otherwise a full semantic equality test will be performed. + */ + private boolean relaxedEqualSemanticsTest; + + /** * Style of code emission. Can emit everything into one class * (AllStatic), separate interface and implementing classes * (InterfaceAndImpl), only the interface (InterfaceOnly), or only @@ -137,6 +138,7 @@ public class JavaConfiguration { private final Map<String, MethodAccess> accessControl = new HashMap<String, MethodAccess>(); private final Map<String, TypeInfo> typeInfoMap = new HashMap<String, TypeInfo>(); private final Set<String> returnsString = new HashSet<String>(); + private final Map<String, JavaType> returnsOpaqueJType = new HashMap<String, JavaType>(); private final Map<String, String> returnedArrayLengths = new HashMap<String, String>(); /** @@ -158,6 +160,7 @@ public class JavaConfiguration { private boolean forceUseNIODirectOnly4All = false; private final Set<String> useNIODirectOnly = new HashSet<String>(); private final Set<String> manuallyImplement = new HashSet<String>(); + private final Map<String, String> delegatedImplementation = new HashMap<String, String>(); private final Set<String> manualStaticInitCall = new HashSet<String>(); private final Set<String> forceStaticInitCode = new HashSet<String>(); private final Map<String, List<String>> customJavaCode = new HashMap<String, List<String>>(); @@ -180,6 +183,10 @@ public class JavaConfiguration { private final Map<String, List<String>> javaPrologues = new HashMap<String, List<String>>(); private final Map<String, List<String>> javaEpilogues = new HashMap<String, List<String>>(); + public JavaConfiguration() { + LOG = Logging.getLogger(JavaConfiguration.class.getPackage().getName(), JavaConfiguration.class.getSimpleName()); + } + /** Reads the configuration file. @param filename path to file that should be read */ @@ -317,6 +324,15 @@ public class JavaConfiguration { return tagNativeBinding; } + /** + * Returns whether {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * shall attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifier + * - or not. + */ + public boolean relaxedEqualSemanticsTest() { + return relaxedEqualSemanticsTest; + } + /** Returns the code emission style (constants in JavaEmitter) parsed from the configuration file. */ public EmissionStyle emissionStyle() { return emissionStyle; @@ -333,7 +349,7 @@ public class JavaConfiguration { } // Default access control is public return PUBLIC; - } + } /** Returns the package in which the generated glue code expects to find its run-time helper classes (Buffers, Platform, @@ -358,15 +374,31 @@ public class JavaConfiguration { } private static final boolean DEBUG_TYPE_INFO = false; + + /** + * If the given {@code canonicalName} should be considered opaque, + * returns the TypeInfo describing the replacement type. + * <p> + * Returns null if this type should not be considered opaque. + * </p> + * <p> + * If symbol references a struct fields, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + */ + public TypeInfo canonicalNameOpaque(final String canonicalName) { + return typeInfoMap.get(canonicalName); + } + /** If this type should be considered opaque, returns the TypeInfo describing the replacement type. Returns null if this type should not be considered opaque. */ - public TypeInfo typeInfo(Type type, final TypeDictionary typedefDictionary) { + public TypeInfo typeInfo(Type type) { // Because typedefs of pointer types can show up at any point, // walk the pointer chain looking for a typedef name that is in // the TypeInfo map. if (DEBUG_TYPE_INFO) - System.err.println("Incoming type = " + type); + System.err.println("Incoming type = " + type + ", " + type.getDebugString()); final int pointerDepth = type.pointerDepth(); for (int i = 0; i <= pointerDepth; i++) { String name = type.getName(); @@ -377,12 +409,13 @@ public class JavaConfiguration { if (name != null) { final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); if (info != null) { + final TypeInfo res = promoteTypeInfo(info, i); if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + + System.err.println(" [1] info.name=" + info.name() + ", name=" + name + ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); + ", type.pointerDepth=" + type.pointerDepth() + " -> "+res); } - return promoteTypeInfo(info, i); + return res; } } @@ -392,33 +425,13 @@ public class JavaConfiguration { if (name != null) { final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); if (info != null) { + final TypeInfo res = promoteTypeInfo(info, i); if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + - ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); - } - return promoteTypeInfo(info, i); - } - } - } - - // Try all typedef names that map to this type - final Set<Entry<String, Type>> entrySet = typedefDictionary.entrySet(); - for (final Map.Entry<String, Type> entry : entrySet) { - // "eq" equality is OK to use here since all types have been canonicalized - if (entry.getValue() == type) { - name = entry.getKey(); - if (DEBUG_TYPE_INFO) { - System.err.println("Looking under typedef name " + name); - } - final TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); - if (info != null) { - if (DEBUG_TYPE_INFO) { - System.err.println(" info.name=" + info.name() + ", name=" + name + + System.err.println(" [2] info.name=" + info.name() + ", name=" + name + ", info.pointerDepth=" + info.pointerDepth() + - ", type.pointerDepth=" + type.pointerDepth()); + ", type.pointerDepth=" + type.pointerDepth() + " -> "+res); } - return promoteTypeInfo(info, i); + return res; } } } @@ -427,7 +440,9 @@ public class JavaConfiguration { type = type.asPointer().getTargetType(); } } - + if (DEBUG_TYPE_INFO) { + System.err.println(" [X] NULL"); + } return null; } @@ -497,6 +512,13 @@ public class JavaConfiguration { public boolean returnsString(final String functionName) { return returnsString.contains(functionName); } + /** Indicates whether the given function (which returns a + <code>char*</code> in C) should be translated as returning a + <code>java.lang.String</code>. */ + public boolean returnsString(final AliasedSymbol symbol) { + return returnsString.contains( symbol.getName() ) || + oneInSet(returnsString, symbol.getAliasedNames()); + } /** * Returns a MessageFormat string of the Java expression calculating @@ -549,17 +571,6 @@ public class JavaConfiguration { return forceUseNIODirectOnly4All || useNIODirectOnly.contains(functionName); } - /** Returns true if the glue code for the given function will be - manually implemented by the end user. - * <p> - * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, - * it describes field's array-length or element-count referenced by a pointer. - * </p> - */ - public boolean manuallyImplement(final String functionName) { - return manuallyImplement.contains(functionName); - } - /** * Returns true if the static initialization java code calling <code>initializeImpl()</code> * for the given class will be manually implemented by the end user @@ -727,52 +738,94 @@ public class JavaConfiguration { return parentClass.get(className); } - public void dumpIgnoresOnce() { - if(!dumpedIgnores) { - dumpedIgnores = true; - dumpIgnores(); + public void logIgnoresOnce() { + if(!loggedIgnores) { + loggedIgnores = true; + logIgnores(); } } - private static boolean dumpedIgnores = false; + private static boolean loggedIgnores = false; - public void dumpIgnores() { - System.err.println("Extended Intf: "); + public void logIgnores() { + LOG.log(INFO, "Extended Intf: {0}", extendedIntfSymbolsIgnore.size()); for (final String str : extendedIntfSymbolsIgnore) { - System.err.println("\t"+str); + LOG.log(INFO, "\t{0}", str); } - System.err.println("Extended Impl: "); + LOG.log(INFO, "Extended Impl: {0}", extendedImplSymbolsIgnore.size()); for (final String str : extendedImplSymbolsIgnore) { - System.err.println("\t"+str); + LOG.log(INFO, "\t{0}", str); } - System.err.println("Ignores (All): "); + LOG.log(INFO, "Ignores (All): {0}", ignores.size()); for (final Pattern pattern : ignores) { - System.err.println("\t"+pattern); + LOG.log(INFO, "\t{0}", pattern); } } - public void dumpRenamesOnce() { - if(!dumpedRenames) { - dumpedRenames = true; - dumpRenames(); + public void logRenamesOnce() { + if(!loggedRenames) { + loggedRenames = true; + logRenames(); } } - private static boolean dumpedRenames = false; + private static boolean loggedRenames = false; - public void dumpRenames() { - System.err.println("Symbol Renames: "); + public void logRenames() { + LOG.log(INFO, "Symbol Renames: {0}", javaSymbolRenames.size()); for (final String key : javaSymbolRenames.keySet()) { - System.err.println("\t"+key+" -> "+javaSymbolRenames.get(key)); + LOG.log(INFO, "\t{0} -> {1}", key, javaSymbolRenames.get(key)); } - System.err.println("Symbol Aliasing (through renaming): "); + LOG.log(INFO, "Symbol Aliasing (through renaming): {0}", javaSymbolRenames.size()); for(final String newName : javaSymbolRenames.values()) { final Set<String> origNames = javaRenamedSymbols.get(newName); if(null!=origNames) { - System.err.println("\t"+newName+" <- "+origNames); + LOG.log(INFO, "\t{0} <- {1}", newName, origNames); } } } + public static <K,V> V oneInMap(final Map<K, V> map, final Set<K> symbols) { + if( null != map && map.size() > 0 && + null != symbols && symbols.size() > 0 ) { + for(final K sym : symbols) { + final V v = map.get(sym); + if( null != v ) { + return v; + } + } + } + return null; + } + public static <K> boolean oneInSet(final Set<K> set1, final Set<K> set2) { + if( null != set1 && set1.size() > 0 && + null != set2 && set2.size() > 0 ) { + for(final K sym : set2) { + if( set1.contains( sym ) ) { + return true; + } + } + } + return false; + } + private static boolean onePatternMatch(final Pattern ignoreRegexp, final Set<String> set) { + if( null != ignoreRegexp && null != set && set.size() > 0 ) { + for(final String sym : set) { + final Matcher matcher = ignoreRegexp.matcher(sym); + if (matcher.matches()) { + return true; + } + } + } + return false; + } + protected static ASTLocusTag getASTLocusTag(final AliasedSymbol s) { + if( s instanceof ASTLocusTagProvider ) { + return ((ASTLocusTagProvider)s).getASTLocusTag(); + } else { + return null; + } + } + /** * Returns the canonical configuration name for a struct field name, * i.e. 'struct-name'.'field-name' @@ -782,136 +835,286 @@ public class JavaConfiguration { } /** - * Returns true if this #define, function, struct, or field within - * a struct should be ignored during glue code generation of interfaces and implementation. + * Variant of {@link #manuallyImplement(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public boolean manuallyImplement(final String functionName) { + if( manuallyImplement.contains(functionName) ) { + LOG.log(INFO, "ManuallyImplement: \"{0}\"", functionName); + return true; + } else { + return false; + } + } + + /** + * Returns true if the glue code for the given aliased function will be + * manually implemented by the end user. * <p> - * For struct fields see {@link #canonicalStructFieldSymbol(String, String)}. + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. * </p> + * <p> + * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + * @see #manuallyImplement(String) */ - public boolean shouldIgnoreInInterface(final String symbol) { - if(DEBUG_IGNORES) { - dumpIgnoresOnce(); - } - // Simple case-1; the entire symbol (orig or renamed) is in the interface ignore table - final String renamedSymbol = getJavaSymbolRename(symbol); - if ( extendedIntfSymbolsIgnore.contains( symbol ) || - extendedIntfSymbolsIgnore.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Intf ignore : "+symbol); + public boolean manuallyImplement(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + if ( manuallyImplement.contains( name ) || + oneInSet(manuallyImplement, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "ManuallyImplement: {0}", symbol); + return true; + } else { + return false; } - return true; - } - // Simple case-2; the entire symbol (orig or renamed) is _not_ in the not-empty interface only table - if ( !extendedIntfSymbolsOnly.isEmpty() && - !extendedIntfSymbolsOnly.contains( symbol ) && - !extendedIntfSymbolsOnly.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Intf !extended: " + symbol); + } + + /** + * Variant of {@link #getDelegatedImplementation(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public String getDelegatedImplementation(final String functionName) { + final String res = delegatedImplementation.get(functionName); + if( null == res ) { + return null; + } + LOG.log(INFO, "DelegatedImplementation: {0} -> {1}", functionName, res); + return res; + } + + /** + * Returns the {@code RENAMED-IMPL-SYMBOL} if the implementation of the glue code + * of the given function shall be manually delegated by the end user. + * <p> + * {@code DelegateImplementation <ORIG-SYMBOL> <RENAMED-IMPL-SYMBOL>} + * </p> + * <p> + * The interface is emitted unchanged. + * </p> + * <p> + * The Java and native-code implementation is renamed to {@code RENAMED-IMPL-SYMBOL}. + * The user's manual implementation of {@code ORIG-SYMBOL} + * may delegate to {@code RENAMED-IMPL-SYMBOL}. + * </p> + * <p> + * If symbol references a struct field or method, see {@link #canonicalStructFieldSymbol(String, String)}, + * it describes field's array-length or element-count referenced by a pointer. + * </p> + */ + public String getDelegatedImplementation(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + String res = delegatedImplementation.get(name); + if( null == res ) { + res = oneInMap(delegatedImplementation, aliases); + if( null == res ) { + return null; } - return true; - } - return shouldIgnoreInImpl_Int(symbol); + } + LOG.log(INFO, getASTLocusTag(symbol), "DelegatedImplementation: {0} -> {1}", symbol, res); + return res; + } + + /** + * Variant of {@link #getOpaqueReturnType(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public JavaType getOpaqueReturnType(final String functionName) { + final JavaType res = returnsOpaqueJType.get(functionName); + if( null == res ) { + return null; + } + LOG.log(INFO, "ReturnsOpaque: {0} -> {1}", functionName, res); + return res; } /** - * Returns true if this #define, function, struct, or field within - * a struct should be ignored during glue code generation of implementation only. + * Returns the opaque {@link JavaType} for the given function {@link AliasedSymbol} + * or {@code null} if not opaque. * <p> - * For struct fields see {@link #canonicalStructFieldSymbol(String, String)}. + * {@code ReturnsOpaque <Primitive Java Type> <Function Name>} * </p> */ - public boolean shouldIgnoreInImpl(final String symbol) { - return shouldIgnoreInImpl_Int(symbol); + public JavaType getOpaqueReturnType(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + JavaType res = returnsOpaqueJType.get(name); + if( null == res ) { + res = oneInMap(returnsOpaqueJType, aliases); + if( null == res ) { + return null; + } + } + LOG.log(INFO, getASTLocusTag(symbol), "ReturnsOpaque: {0} -> {1}", symbol, res); + return res; } - private boolean shouldIgnoreInImpl_Int(final String symbol) { + /** + * Variant of {@link #shouldIgnoreInInterface(AliasedSymbol)}, + * where this method only considers the {@link AliasedSymbol#getName() current-name} + * of the given symbol, not the {@link #getJavaSymbolRename(String) renamed-symbol}. + */ + public final boolean shouldIgnoreInInterface(final String symbol) { + return shouldIgnoreInInterface( new AliasedSymbol.NoneAliasedSymbol(symbol) ); + } + /** + * Returns true if this aliased symbol should be ignored + * during glue code generation of interfaces and implementation. + * <p> + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. + * </p> + * <p> + * Implementation calls {@link #shouldIgnoreInInterface_Int(AliasedSymbol)} + * and overriding implementations shall ensure its being called as well! + * </p> + * @param symbol the symbolic aliased name to check for exclusion + */ + public boolean shouldIgnoreInInterface(final AliasedSymbol symbol) { + return shouldIgnoreInInterface_Int(symbol); + } - if(DEBUG_IGNORES) { - dumpIgnoresOnce(); - } + protected final boolean shouldIgnoreInInterface_Int(final AliasedSymbol symbol) { + if( GlueGen.debug() ) { + logIgnoresOnce(); + } + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); - // Simple case-1; the entire symbol (orig or renamed) is in the implementation ignore table - final String renamedSymbol = getJavaSymbolRename(symbol); - if ( extendedImplSymbolsIgnore.contains( symbol ) || - extendedImplSymbolsIgnore.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl ignore : "+symbol); + // Simple case-1; the symbol (orig or renamed) is in the interface ignore table + if ( extendedIntfSymbolsIgnore.contains( name ) || + oneInSet(extendedIntfSymbolsIgnore, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Intf ignore (one): {0}", symbol); + return true; } - return true; - } - // Simple case-2; the entire symbol (orig or renamed) is _not_ in the not-empty implementation only table - if ( !extendedImplSymbolsOnly.isEmpty() && - !extendedImplSymbolsOnly.contains( symbol ) && - !extendedImplSymbolsOnly.contains( renamedSymbol ) ) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl !extended: " + symbol); - } + // Simple case-2; the entire symbol (orig and renamed) is _not_ in the not-empty interface only table + if ( !extendedIntfSymbolsOnly.isEmpty() && + !extendedIntfSymbolsOnly.contains( name ) && + !oneInSet(extendedIntfSymbolsOnly, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Intf !extended (all): {0}", symbol); return true; - } + } + return shouldIgnoreInImpl_Int(symbol); + } - // Ok, the slow case. We need to check the entire table, in case the table - // contains an regular expression that matches the symbol. - for (final Pattern regexp : ignores) { - final Matcher matcher = regexp.matcher(symbol); - if (matcher.matches()) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl RegEx: "+symbol); - } - return true; + /** + * Returns true if this aliased symbol should be ignored + * during glue code generation of implementation only. + * <p> + * Both, the {@link AliasedSymbol#getName() current-name} + * and all {@link AliasedSymbol#getAliasedNames() aliases} shall be considered. + * </p> + * <p> + * Implementation calls {@link #shouldIgnoreInImpl_Int(AliasedSymbol)} + * and overriding implementations shall ensure its being called as well! + * </p> + * @param symbol the symbolic aliased name to check for exclusion + */ + public boolean shouldIgnoreInImpl(final AliasedSymbol symbol) { + return shouldIgnoreInImpl_Int(symbol); + } + + protected final boolean shouldIgnoreInImpl_Int(final AliasedSymbol symbol) { + final String name = symbol.getName(); + final Set<String> aliases = symbol.getAliasedNames(); + + // Simple case-1; the symbol (orig or renamed) is in the interface ignore table + if ( extendedImplSymbolsIgnore.contains( name ) || + oneInSet(extendedImplSymbolsIgnore, aliases) + ) + { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl ignore (one): {0}", symbol); + return true; + } + // Simple case-2; the entire symbol (orig and renamed) is _not_ in the not-empty interface only table + if ( !extendedImplSymbolsOnly.isEmpty() && + !extendedImplSymbolsOnly.contains( name ) && + !oneInSet(extendedImplSymbolsOnly, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl !extended (all): {0}", symbol); + return true; } - } - // Check negated ignore table if not empty - if (ignoreNots.size() > 0) { // Ok, the slow case. We need to check the entire table, in case the table // contains an regular expression that matches the symbol. - for (final Pattern regexp : ignoreNots) { - final Matcher matcher = regexp.matcher(symbol); - if (!matcher.matches()) { - // Special case as this is most often likely to be the case. - // Unignores are not used very often. - if(unignores.isEmpty()) { - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl unignores==0: "+symbol); - } - return true; + for (final Pattern ignoreRegexp : ignores) { + final Matcher matcher = ignoreRegexp.matcher(name); + if ( matcher.matches() || onePatternMatch(ignoreRegexp, aliases) ) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl RegEx: {0}", symbol); + return true; } + } - boolean unignoreFound = false; - for (final Pattern unignoreRegexp : unignores) { - final Matcher unignoreMatcher = unignoreRegexp.matcher(symbol); - if (unignoreMatcher.matches()) { - unignoreFound = true; - break; - } + // Check negated ignore table if not empty + if (ignoreNots.size() > 0) { + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (final Pattern ignoreNotRegexp : ignoreNots) { + final Matcher matcher = ignoreNotRegexp.matcher(name); + if ( !matcher.matches() && !onePatternMatch(ignoreNotRegexp, aliases) ) { + // Special case as this is most often likely to be the case. + // Unignores are not used very often. + if(unignores.isEmpty()) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl unignores==0: {0} -> {1}", symbol, name); + return true; + } + boolean unignoreFound = false; + for (final Pattern unignoreRegexp : unignores) { + final Matcher unignoreMatcher = unignoreRegexp.matcher(name); + if ( unignoreMatcher.matches() || onePatternMatch(unignoreRegexp, aliases) ) { + unignoreFound = true; + break; + } + } + + if (!unignoreFound) { + LOG.log(INFO, getASTLocusTag(symbol), "Ignore Impl !unignore: {0} -> {1}", symbol, name); + return true; + } + } } - - if (!unignoreFound) - if(DEBUG_IGNORES) { - System.err.println("Ignore Impl !unignore: "+symbol); - } - return true; - } } - } - - return false; + return false; } /** Returns true if this function should be given a body which throws a run-time exception with an "unimplemented" message during glue code generation. */ - public boolean isUnimplemented(final String symbol) { - // Ok, the slow case. We need to check the entire table, in case the table - // contains an regular expression that matches the symbol. - for (final Pattern regexp : unimplemented) { - final Matcher matcher = regexp.matcher(symbol); - if (matcher.matches()) { - return true; + public boolean isUnimplemented(final AliasedSymbol symbol) { + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (final Pattern unimplRegexp : unimplemented) { + final Matcher matcher = unimplRegexp.matcher(symbol.getName()); + if ( matcher.matches() || onePatternMatch(unimplRegexp, symbol.getAliasedNames()) ) { + return true; + } } - } + return false; + } - return false; + + /** + * Return a set of aliased-name for comment in docs. + * <p> + * This is usually {@link AliasedSymbol#addAliasedName(String)}, + * however an implementation may choose otherwise. + * </p> + * @param symbol the aliased symbol to retrieve the aliases + * @return set of aliased-names or {@code null}. + */ + public Set<String> getAliasedDocNames(final AliasedSymbol symbol) { + return symbol.getAliasedNames(); } /** Returns a replacement name for this type, which should be the @@ -932,8 +1135,8 @@ public class JavaConfiguration { function under the hood. Returns null if this symbol has not been explicitly renamed. */ public String getJavaSymbolRename(final String origName) { - if(DEBUG_RENAMES) { - dumpRenamesOnce(); + if( LOG.isLoggable(INFO) ) { + logRenamesOnce(); } return javaSymbolRenames.get(origName); } @@ -945,18 +1148,12 @@ public class JavaConfiguration { /** Programmatically adds a rename directive for the given symbol. */ public void addJavaSymbolRename(final String origName, final String newName) { - if(DEBUG_RENAMES) { - System.err.print("\tRename "+origName+" -> "+newName); - } + LOG.log(INFO, "\tRename {0} -> {1}", origName, newName); final String prevValue = javaSymbolRenames.put(origName, newName); if(null != prevValue && !prevValue.equals(newName)) { throw new RuntimeException("Rename-Override Attampt: "+origName+" -> "+newName+ ", but "+origName+" -> "+prevValue+" already exist. Run in 'debug' mode to analyze!"); } - if(DEBUG_RENAMES) { - System.err.println(); - } - Set<String> origNames = javaRenamedSymbols.get(newName); if(null == origNames) { origNames = new HashSet<String>(); @@ -965,6 +1162,16 @@ public class JavaConfiguration { origNames.add(origName); } + /** Programmatically adds a delegate implementation directive for the given symbol. */ + public void addDelegateImplementation(final String origName, final String renamedImpl) { + LOG.log(INFO, "\tDelegateImplementation {0} -> {1}", origName, renamedImpl); + final String prevValue = delegatedImplementation.put(origName, renamedImpl); + if(null != prevValue && !prevValue.equals(renamedImpl)) { + throw new RuntimeException("Rename-Override Attampt: "+origName+" -> "+renamedImpl+ + ", but "+origName+" -> "+prevValue+" already exist. Run in 'debug' mode to analyze!"); + } + } + /** Returns true if the emission style is AllStatic. */ public boolean allStatic() { return emissionStyle == AllStatic; @@ -1033,11 +1240,14 @@ public class JavaConfiguration { nativeOutputUsesJavaHierarchy = Boolean.valueOf(tmp).booleanValue(); } else if (cmd.equalsIgnoreCase("TagNativeBinding")) { tagNativeBinding = readBoolean("TagNativeBinding", tok, filename, lineNo).booleanValue(); + } else if (cmd.equalsIgnoreCase("RelaxedEqualSemanticsTest")) { + relaxedEqualSemanticsTest = readBoolean("RelaxedEqualSemanticsTest", tok, filename, lineNo).booleanValue(); + TypeConfig.setRelaxedEqualSemanticsTest(relaxedEqualSemanticsTest); // propagate .. } else if (cmd.equalsIgnoreCase("Style")) { try{ emissionStyle = EmissionStyle.valueOf(readString("Style", tok, filename, lineNo)); }catch(final IllegalArgumentException ex) { - LOG.log(WARNING, "Error parsing \"style\" command at line {0} in file \"{1}\"", new Object[]{lineNo, filename}); + LOG.log(WARNING, "Error parsing \"style\" command at line {0} in file \"{1}\"", lineNo, filename); } } else if (cmd.equalsIgnoreCase("AccessControl")) { readAccessControl(tok, filename, lineNo); @@ -1047,6 +1257,8 @@ public class JavaConfiguration { readOpaque(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ReturnsString")) { readReturnsString(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ReturnsOpaque")) { + readReturnsOpaque(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ReturnedArrayLength")) { readReturnedArrayLength(tok, filename, lineNo); // Warning: make sure delimiters are reset at the top of this loop @@ -1147,10 +1359,10 @@ public class JavaConfiguration { readParentClass(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("RenameJavaType")) { readRenameJavaType(tok, filename, lineNo); - } else if (cmd.equalsIgnoreCase("RenameJavaSymbol") || - // Backward compatibility - cmd.equalsIgnoreCase("RenameJavaMethod")) { + } else if (cmd.equalsIgnoreCase("RenameJavaSymbol")) { readRenameJavaSymbol(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("DelegateImplementation")) { + readDelegateImplementation(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("RuntimeExceptionType")) { runtimeExceptionType = readString("RuntimeExceptionType", tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("UnsupportedExceptionType")) { @@ -1222,7 +1434,7 @@ public class JavaConfiguration { protected void readOpaque(final StringTokenizer tok, final String filename, final int lineNo) { try { - final JavaType javaType = JavaType.createForClass(stringToPrimitiveType(tok.nextToken())); + final JavaType javaType = JavaType.createForOpaqueClass(stringToPrimitiveType(tok.nextToken())); String cType = null; while (tok.hasMoreTokens()) { if (cType == null) { @@ -1243,6 +1455,17 @@ public class JavaConfiguration { } } + protected void readReturnsOpaque(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final JavaType javaType = JavaType.createForOpaqueClass(stringToPrimitiveType(tok.nextToken())); + final String funcName = tok.nextToken(); + returnsOpaqueJType.put(funcName, javaType); + } catch (final Exception e) { + throw new RuntimeException("Error parsing \"ReturnsOpaque\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readReturnsString(final StringTokenizer tok, final String filename, final int lineNo) { try { final String name = tok.nextToken(); @@ -1684,6 +1907,17 @@ public class JavaConfiguration { } } + public void readDelegateImplementation(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final String fromName = tok.nextToken(); + final String toName = tok.nextToken(); + addDelegateImplementation(fromName, toName); + } catch (final NoSuchElementException e) { + throw new RuntimeException("Error parsing \"DelegateImplementation\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + protected void readJavaPrologueOrEpilogue(final StringTokenizer tok, final String filename, final int lineNo, final boolean prologue) { try { String methodName = tok.nextToken(); @@ -1755,6 +1989,16 @@ public class JavaConfiguration { return new TypeInfo(typeName, pointerDepth, javaType); } + public TypeInfo addTypeInfo(final String alias, final Type superType) { + final TypeInfo superInfo = typeInfo(superType); + if( null != superInfo ) { + final TypeInfo res = new TypeInfo(alias, superInfo.pointerDepth(), superInfo.javaType()); + addTypeInfo(res); + return res; + } else { + return null; + } + } protected void addTypeInfo(final TypeInfo info) { TypeInfo tmp = typeInfoMap.get(info.name()); if (tmp == null) { diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index d2dc4ba..02e56a4 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -40,22 +40,56 @@ package com.jogamp.gluegen; -import com.jogamp.common.nio.Buffers; -import com.jogamp.common.os.DynamicLookupHelper; -import com.jogamp.common.os.MachineDataInfo; - -import java.io.*; -import java.util.*; -import java.text.MessageFormat; - -import com.jogamp.gluegen.cgram.types.*; - +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PACKAGE_PRIVATE; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PRIVATE; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PROTECTED; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC_ABSTRACT; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.nio.Buffer; -import java.util.logging.Logger; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; import jogamp.common.os.MachineDataInfoRuntime; -import static java.util.logging.Level.*; -import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.DynamicLookupHelper; +import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.common.util.ArrayHashMap; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.Logging.LoggerIf; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.ArrayType; +import com.jogamp.gluegen.cgram.types.CVAttributes; +import com.jogamp.gluegen.cgram.types.CompoundType; +import com.jogamp.gluegen.cgram.types.Field; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; +import com.jogamp.gluegen.cgram.types.FunctionType; +import com.jogamp.gluegen.cgram.types.IntType; +import com.jogamp.gluegen.cgram.types.PointerType; +import com.jogamp.gluegen.cgram.types.SizeThunk; +import com.jogamp.gluegen.cgram.types.StructLayout; +import com.jogamp.gluegen.cgram.types.Type; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeDictionary; // PROBLEMS: // - what if something returns 'const int *'? Could we @@ -70,7 +104,6 @@ import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; public class JavaEmitter implements GlueEmitter { private StructLayout layout; - private TypeDictionary typedefDictionary; private Map<Type, Type> canonMap; protected JavaConfiguration cfg; private boolean requiresStaticInitialization = false; @@ -97,13 +130,19 @@ public class JavaEmitter implements GlueEmitter { private final String javaName; } - private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything + private String javaFileName; // of javaWriter or javaImplWriter + private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything private PrintWriter javaImplWriter; // Only used in non-AllStatic modes for impl class + private String cFileName; // of cWriter private PrintWriter cWriter; private final MachineDataInfo machDescJava = MachineDataInfo.StaticConfig.LP64_UNIX.md; private final MachineDataInfo.StaticConfig[] machDescTargetConfigs = MachineDataInfo.StaticConfig.values(); - protected final static Logger LOG = Logger.getLogger(JavaEmitter.class.getPackage().getName()); + protected final LoggerIf LOG; + + public JavaEmitter() { + LOG = Logging.getLogger(JavaEmitter.class.getPackage().getName(), JavaEmitter.class.getSimpleName()); + } @Override public void readConfigurationFile(final String filename) throws Exception { @@ -111,61 +150,129 @@ public class JavaEmitter implements GlueEmitter { cfg.read(filename); } - class ConstantRenamer implements SymbolFilter { + @Override + public JavaConfiguration getConfiguration() { return cfg; } + class ConstFuncRenamer implements SymbolFilter { private List<ConstantDefinition> constants; - - @Override - public void filterSymbols(final List<ConstantDefinition> constants, final List<FunctionSymbol> functions) { - this.constants = constants; - doWork(); - } + private List<FunctionSymbol> functions; @Override public List<ConstantDefinition> getConstants() { return constants; } - @Override public List<FunctionSymbol> getFunctions() { - return null; + return functions; + } + + private <T extends AliasedSemanticSymbol> List<T> filterSymbolsInt(final List<T> inList, + final boolean preserveOrder, + final List<T> outList) { + final JavaConfiguration cfg = getConfig(); + final ArrayHashMap<String, T> symMap = + new ArrayHashMap<String, T>(false, 100, ArrayHashMap.DEFAULT_LOAD_FACTOR); + for (final T sym : inList) { + final String origName = sym.getName(); + final String newName = cfg.getJavaSymbolRename(origName); + final T dupSym; + if( null != newName ) { + // Alias Name + dupSym = symMap.get(newName); + if( null != dupSym ) { + // only rename to allow 'equalSemantics' to not care .. + sym.rename(newName); + } + } else { + // Original Name + dupSym = symMap.get(origName); + } + if( null != dupSym ) { + // Duplicate alias .. check + if( !dupSym.equalSemantics(sym) ) { + final ASTLocusTag loc; + final String preLoc; + if( sym instanceof ASTLocusTagProvider ) { + loc = ((ASTLocusTagProvider)sym).getASTLocusTag(); + } else { + loc = null; + } + if( dupSym instanceof ASTLocusTagProvider ) { + preLoc = String.format(",%n %s: previous definition is here", + ((ASTLocusTagProvider)dupSym).getASTLocusTag().toString(new StringBuilder(), "note", true)); + } else { + preLoc = ""; + } + final String mode = null != newName ? "alias" : "orig"; + final String message = + String.format("Duplicate Name (%s) w/ incompatible value:%n this '%s',%n have '%s'%s", + mode, sym.getAliasedString(), dupSym.getAliasedString(), preLoc); + throw new GlueGenException(message, loc); + } + } + if( null != newName ) { + // Alias Name + if( null != dupSym ) { + // Duplicate alias .. add aliased name + dupSym.addAliasedName(origName); + } else { + // No duplicate .. rename and add + sym.rename(newName); + symMap.put(newName, sym); + } + } else { + // Original Name + if( null != dupSym ) { + // Duplicate orig .. drop + } else { + // No duplicate orig .. add + symMap.put(origName, sym); + } + } + } + outList.addAll(symMap.getData()); + if( !preserveOrder ) { + // sort constants to make them easier to find in native code + Collections.sort(outList, new Comparator<T>() { + @Override + public int compare(final T o1, final T o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + } + return outList; } - private void doWork() { - final List<ConstantDefinition> newConstants = new ArrayList<ConstantDefinition>(); - final JavaConfiguration cfg = getConfig(); - for (final ConstantDefinition def : constants) { - def.rename(cfg.getJavaSymbolRename(def.getName())); - newConstants.add(def); - } - constants = newConstants; + @Override + public void filterSymbols(final List<ConstantDefinition> inConstList, final List<FunctionSymbol> inFuncList) { + constants = filterSymbolsInt(inConstList, true, new ArrayList<ConstantDefinition>(100)); + functions = filterSymbolsInt(inFuncList, true, new ArrayList<FunctionSymbol>(100)); } } @Override public void beginEmission(final GlueEmitterControls controls) throws IOException { + // Handle renaming of constants and functions + controls.runSymbolFilter(new ConstFuncRenamer()); // Request emission of any structs requested for (final String structs : cfg.forcedStructs()) { controls.forceStructEmission(structs); } - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { try { openWriters(); } catch (final Exception e) { throw new RuntimeException("Unable to open files for writing", e); } emitAllFileHeaders(); - - // Handle renaming of constants - controls.runSymbolFilter(new ConstantRenamer()); } } @Override public void endEmission() { - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { emitAllFileFooters(); try { @@ -178,177 +285,11 @@ public class JavaEmitter implements GlueEmitter { @Override public void beginDefines() throws Exception { - if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { javaWriter().println(); } } - protected static int getJavaRadix(final String name, final String value) { - // FIXME: need to handle when type specifier is in last char (e.g., - // "1.0d or 2759L", because parseXXX() methods don't allow the type - // specifier character in the string. - // - //char lastChar = value.charAt(value.length()-1); - - try { - // see if it's a long or int - int radix; - String parseValue; - // FIXME: are you allowed to specify hex/octal constants with - // negation, e.g. "-0xFF" or "-056"? If so, need to modify the - // following "if(..)" checks and parseValue computation - if (value.startsWith("0x") || value.startsWith("0X")) { - radix = 16; - parseValue = value.substring(2); - } - else if (value.startsWith("0") && value.length() > 1) { - // TODO: is "0" the prefix in C to indicate octal??? - radix = 8; - parseValue = value.substring(1); - } - else { - radix = 10; - parseValue = value; - } - //System.err.println("parsing " + value + " as long w/ radix " + radix); - Long.parseLong(parseValue, radix); - return radix; - } catch (final NumberFormatException e) { - try { - // see if it's a double or float - Double.parseDouble(value); - return 10; - } catch (final NumberFormatException e2) { - throw new RuntimeException( - "Cannot emit define \""+name+"\": value \""+value+ - "\" cannot be assigned to a int, long, float, or double", e2); - } - } - } - - protected static Object getJavaValue(final String name, final String value) { - - // "calculates" the result type of a simple expression - // example: (2+3)-(2.0f-3.0) -> Double - // example: (1 << 2) -> Integer - - final Scanner scanner = new Scanner(value).useDelimiter("[+-/*/></(/)]"); - - Object resultType = null; - - while (scanner.hasNext()) { - - final String t = scanner.next().trim(); - - if(0<t.length()) { - final Object type = getJavaValue2(name, t); - - //fast path - if(type instanceof Double) - return type; - - if(resultType != null) { - - if(resultType instanceof Integer) { - if(type instanceof Long || type instanceof Float || type instanceof Double) - resultType = type; - }else if(resultType instanceof Long) { - if(type instanceof Float || type instanceof Double) - resultType = type; - }else if(resultType instanceof Float) { - if(type instanceof Float) - resultType = type; - } - }else{ - resultType = type; - } - - //fast path - if(resultType instanceof Double) - return type; - } - } - - return resultType; - } - - private static Object getJavaValue2(final String name, final String value) { - // FIXME: need to handle when type specifier is in last char (e.g., - // "1.0d or 2759L", because parseXXX() methods don't allow the type - // specifier character in the string. - // - final char lastChar = value.charAt(value.length()-1); - - try { - // see if it's a long or int - int radix; - String parseValue; - // FIXME: are you allowed to specify hex/octal constants with - // negation, e.g. "-0xFF" or "-056"? If so, need to modify the - // following "if(..)" checks and parseValue computation - if (value.startsWith("0x") || value.startsWith("0X")) { - radix = 16; - parseValue = value.substring(2); - } else if (value.startsWith("0") && value.length() > 1) { - // TODO: is "0" the prefix in C to indicate octal??? - radix = 8; - parseValue = value.substring(1); - } else { - radix = 10; - parseValue = value; - } - if(lastChar == 'u' || lastChar == 'U') { - parseValue = parseValue.substring(0, parseValue.length()-1); - } - - //System.err.println("parsing " + value + " as long w/ radix " + radix); - final long longVal = Long.parseLong(parseValue, radix); - // if constant is small enough, store it as an int instead of a long - if (longVal > Integer.MIN_VALUE && longVal < Integer.MAX_VALUE) { - return (int)longVal; - } - return longVal; - - } catch (final NumberFormatException e) { - try { - // see if it's a double or float - final double dVal = Double.parseDouble(value); - final double absVal = Math.abs(dVal); - // if constant is small enough, store it as a float instead of a double - if (absVal < Float.MIN_VALUE || absVal > Float.MAX_VALUE) { - return new Double(dVal); - } - return new Float((float) dVal); - } catch (final NumberFormatException e2) { - throw new RuntimeException( - "Cannot emit define \""+name+"\": value \""+value+ - "\" cannot be assigned to a int, long, float, or double", e2); - } - } - } - - - protected static String getJavaType(final String name, final String value) { - final Object oval = getJavaValue(name, value); - return getJavaType(name, oval); - } - - protected static String getJavaType(final String name, final Object oval) { - if(oval instanceof Integer) { - return "int"; - } else if(oval instanceof Long) { - return "long"; - } else if(oval instanceof Float) { - return "float"; - } else if(oval instanceof Double) { - return "double"; - } - - throw new RuntimeException( - "Cannot emit define (2) \""+name+"\": value \""+oval+ - "\" cannot be assigned to a int, long, float, or double"); - } - /** Mangle a class, package or function name for JNI usage, i.e. replace all '.' w/ '_' */ protected static String jniMangle(final String name) { return name.replaceAll("_", "_1").replace('.', '_'); @@ -358,10 +299,12 @@ public class JavaEmitter implements GlueEmitter { return "Java_"+jniMangle(javaPackageName)+"_"+jniMangle(javaClassName); } + private final Map<String, ConstantDefinition.JavaExpr> constMap = + new HashMap<String, ConstantDefinition.JavaExpr>(); + @Override public void emitDefine(final ConstantDefinition def, final String optionalComment) throws Exception { - - if (cfg.allStatic() || cfg.emitInterface()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { // TODO: Some defines (e.g., GL_DOUBLE_EXT in gl.h) are defined in terms // of other defines -- should we emit them as references to the original // define (not even sure if the lexer supports this)? Right now they're @@ -371,24 +314,22 @@ public class JavaEmitter implements GlueEmitter { // currently only emits only numeric defines -- if it handled #define'd // objects it would make a bigger difference. - final String name = def.getName(); - String value = def.getValue(); - - if (!cfg.shouldIgnoreInInterface(name)) { - final String type = getJavaType(name, value); + if ( !cfg.shouldIgnoreInInterface(def) ) { + final ConstantDefinition.JavaExpr constExpr = def.computeJavaExpr(constMap); + constMap.put(def.getName(), constExpr); + javaWriter().print(" /** "); if (optionalComment != null && optionalComment.length() != 0) { - javaWriter().println(" /** " + optionalComment + " */"); + javaWriter().print(optionalComment); + javaWriter().print(" - "); } - String suffix = ""; - if(!value.endsWith(")")) { - if (type.equals("float") && !value.endsWith("f")) { - suffix = "f"; - }else if(value.endsWith("u") || value.endsWith("U")) { - value = value.substring(0, value.length()-1); - } + javaWriter().print("CType: "); + if( constExpr.resultType.isUnsigned ) { + javaWriter().print("unsigned "); } - - javaWriter().println(" public static final " + type + " " + name + " = " + value + suffix + ";"); + javaWriter().print(constExpr.resultJavaTypeName); + javaWriter().println(" */"); + javaWriter().println(" public static final " + constExpr.resultJavaTypeName + + " " + def.getName() + " = " + constExpr.javaExpression + ";"); } } } @@ -402,67 +343,51 @@ public class JavaEmitter implements GlueEmitter { final TypeDictionary structDictionary, final Map<Type, Type> canonMap) throws Exception { - this.typedefDictionary = typedefDictionary; + // this.typedefDictionary = typedefDictionary; this.canonMap = canonMap; this.requiresStaticInitialization = false; // reset - if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) { javaWriter().println(); } } @Override - public Iterator<FunctionSymbol> emitFunctions(final List<FunctionSymbol> originalCFunctions) throws Exception { - - // Sometimes headers will have the same function prototype twice, once - // with the argument names and once without. We'll remember the signatures - // we've already processed we don't generate duplicate bindings. - // - // Note: this code assumes that on the equals() method in FunctionSymbol - // only considers function name and argument types (i.e., it does not - // consider argument *names*) when comparing FunctionSymbols for equality - final Set<FunctionSymbol> funcsToBindSet = new HashSet<FunctionSymbol>(100); - for (final FunctionSymbol cFunc : originalCFunctions) { - if (!funcsToBindSet.contains(cFunc)) { - funcsToBindSet.add(cFunc); - } - } - - // validateFunctionsToBind(funcsToBindSet); + public Iterator<FunctionSymbol> emitFunctions(final List<FunctionSymbol> funcsToBind) throws Exception { + if ( !cfg.structsOnly() ) { + // Bind all the C funcs to Java methods + final ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size()); + { + int i=0; + for (final FunctionSymbol cFunc : funcsToBind) { + // Check to see whether this function should be ignored + if ( !cfg.shouldIgnoreInImpl(cFunc) ) { + methodBindingEmitters.addAll(generateMethodBindingEmitters(cFunc)); + LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Impl[{0}]: {1}", i++, cFunc); + } - final ArrayList<FunctionSymbol> funcsToBind = new ArrayList<FunctionSymbol>(funcsToBindSet); - // sort functions to make them easier to find in native code - Collections.sort(funcsToBind, new Comparator<FunctionSymbol>() { - @Override - public int compare(final FunctionSymbol o1, final FunctionSymbol o2) { - return o1.getName().compareTo(o2.getName()); } - }); - - // Bind all the C funcs to Java methods - final HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>(); - final ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size()); - for (final FunctionSymbol cFunc : funcsToBind) { - // Check to see whether this function should be ignored - if (!cfg.shouldIgnoreInImpl(cFunc.getName())) { - methodBindingEmitters.addAll(generateMethodBindingEmitters(methodBindingSet, cFunc)); - } - - } + } - // Emit all the methods - for (final FunctionEmitter emitter : methodBindingEmitters) { - try { - if (!emitter.isInterface() || !cfg.shouldIgnoreInInterface(emitter.getName())) { - emitter.emit(); - emitter.getDefaultOutput().println(); // put newline after method body + // Emit all the methods + { + int i=0; + for (final FunctionEmitter emitter : methodBindingEmitters) { + try { + final FunctionSymbol cFunc = emitter.getCSymbol(); + if ( !emitter.isInterface() || !cfg.shouldIgnoreInInterface(cFunc) ) { + emitter.emit(); + emitter.getDefaultOutput().println(); // put newline after method body + LOG.log(INFO, cFunc.getASTLocusTag(), "Non-Ignored Intf[{0}]: {1}", i++, cFunc); + } + } catch (final Exception e) { + throw new GlueGenException( + "Error while emitting binding for \"" + emitter.getCSymbol().getAliasedString() + "\"", + emitter.getCSymbol().getASTLocusTag(), e); + } + } } - } catch (final Exception e) { - throw new RuntimeException( - "Error while emitting binding for \"" + emitter.getName() + "\"", e); - } } - // Return the list of FunctionSymbols that we generated gluecode for return funcsToBind.iterator(); } @@ -506,25 +431,33 @@ public class JavaEmitter implements GlueEmitter { * native code because it doesn't need any processing of the * outgoing arguments). */ - protected void generatePublicEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters, final boolean signatureOnly) { - if (cfg.manuallyImplement(binding.getName()) && !signatureOnly) { + protected void generatePublicEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters, + final boolean signatureOnly) { + final FunctionSymbol cSymbol = binding.getCSymbol(); + if ( !signatureOnly && cfg.manuallyImplement(cSymbol) ) { // We only generate signatures for manually-implemented methods; // user provides the implementation return; } - final MethodAccess accessControl = cfg.accessControl(binding.getName()); + final MethodAccess accessControl; + + if ( !signatureOnly && null != binding.getDelegationImplName() ) { + // private access for delegation implementation methods + accessControl = PRIVATE; + } else { + accessControl = cfg.accessControl(binding.getName()); + } + // We should not emit anything except public APIs into interfaces - if (signatureOnly && (accessControl != PUBLIC)) { + if ( signatureOnly && PUBLIC != accessControl ) { return; } - final PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); - // It's possible we may not need a body even if signatureOnly is // set to false; for example, if the routine doesn't take any // arrays or buffers as arguments - final boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); + final boolean isUnimplemented = cfg.isUnimplemented(cSymbol); final List<String> prologue = cfg.javaPrologueForMethod(binding, false, false); final List<String> epilogue = cfg.javaEpilogueForMethod(binding, false, false); final boolean needsBody = isUnimplemented || @@ -536,25 +469,31 @@ public class JavaEmitter implements GlueEmitter { if( !requiresStaticInitialization ) { requiresStaticInitialization = binding.signatureRequiresStaticInitialization(); if( requiresStaticInitialization ) { - LOG.log(INFO, "StaticInit Trigger.1 \"{0}\"", binding); + LOG.log(INFO, cSymbol.getASTLocusTag(), "StaticInit Trigger.1 \"{0}\"", binding); } } + final boolean emitBody = !signatureOnly && needsBody; + final boolean isNativeMethod = !isUnimplemented && !needsBody && !signatureOnly; + + final PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); + final JavaMethodBindingEmitter emitter = new JavaMethodBindingEmitter(binding, writer, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - !signatureOnly && needsBody, + emitBody, // emitBody cfg.tagNativeBinding(), - false, // eraseBufferAndArrayTypes + false, // eraseBufferAndArrayTypes cfg.useNIOOnly(binding.getName()), cfg.useNIODirectOnly(binding.getName()), - false, - false, - false, - isUnimplemented, - signatureOnly, + false, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + isUnimplemented, // isUnimplemented + signatureOnly, // isInterface + isNativeMethod, // isNativeMethod + false, // isPrivateNativeMethod cfg); switch (accessControl) { case PUBLIC: emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); break; @@ -565,7 +504,7 @@ public class JavaEmitter implements GlueEmitter { if (cfg.allStatic()) { emitter.addModifier(FunctionEmitter.STATIC); } - if (!isUnimplemented && !needsBody && !signatureOnly) { + if (isNativeMethod) { emitter.addModifier(JavaMethodBindingEmitter.NATIVE); } emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); @@ -585,7 +524,8 @@ public class JavaEmitter implements GlueEmitter { */ protected void generatePrivateEmitters(final MethodBinding binding, final List<FunctionEmitter> allEmitters) { - if (cfg.manuallyImplement(binding.getName())) { + final FunctionSymbol cSymbol = binding.getCSymbol(); + if (cfg.manuallyImplement(cSymbol)) { // Don't produce emitters for the implementation class return; } @@ -594,11 +534,11 @@ public class JavaEmitter implements GlueEmitter { cfg.javaPrologueForMethod(binding, false, false) != null || cfg.javaEpilogueForMethod(binding, false, false) != null ; - if ( !cfg.isUnimplemented( binding.getName() ) ) { + if ( !cfg.isUnimplemented( cSymbol ) ) { if( !requiresStaticInitialization ) { requiresStaticInitialization = binding.signatureRequiresStaticInitialization(); if( requiresStaticInitialization ) { - LOG.log(INFO, "StaticInit Trigger.2 \"{0}\"", binding); + LOG.log(INFO, cSymbol.getASTLocusTag(), "StaticInit Trigger.2 \"{0}\"", binding); } } @@ -621,16 +561,17 @@ public class JavaEmitter implements GlueEmitter { writer, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, + false, // emitBody cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + true, // eraseBufferAndArrayTypes cfg.useNIOOnly(binding.getName()), cfg.useNIODirectOnly(binding.getName()), - true, - true, - false, - false, - false, + true, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + false, // isUnimplemented + false, // isInterface + true, // isNativeMethod + true, // isPrivateNativeMethod cfg); emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); if (cfg.allStatic()) { @@ -658,7 +599,7 @@ public class JavaEmitter implements GlueEmitter { cfg.allStatic(), (binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue), !cfg.useNIODirectOnly(binding.getName()), - machDescJava); + machDescJava, getConfiguration()); prepCEmitter(binding.getName(), binding.getJavaReturnType(), cEmitter); allEmitters.add(cEmitter); } @@ -700,18 +641,34 @@ public class JavaEmitter implements GlueEmitter { * Generate all appropriate Java bindings for the specified C function * symbols. */ - protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final FunctionSymbol sym) throws Exception { final ArrayList<FunctionEmitter> allEmitters = new ArrayList<FunctionEmitter>(); - try { + if( cfg.emitInterface() ) { + generateMethodBindingEmittersImpl(allEmitters, sym, true); + } + if( cfg.emitImpl() ) { + generateMethodBindingEmittersImpl(allEmitters, sym, false); + } + } catch (final Exception e) { + throw new GlueGenException("Error while generating bindings for \"" + sym + "\"", sym.getASTLocusTag(), e); + } + + return allEmitters; + } + private void generateMethodBindingEmittersImpl(final ArrayList<FunctionEmitter> allEmitters, + final FunctionSymbol sym, + final boolean forInterface) throws Exception + { // Get Java binding for the function - final MethodBinding mb = bindFunction(sym, null, null, machDescJava); + final MethodBinding mb = bindFunction(sym, forInterface, machDescJava, null, null); // JavaTypes representing C pointers in the initial // MethodBinding have not been lowered yet to concrete types final List<MethodBinding> bindings = expandMethodBinding(mb); + final HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>(); + for (final MethodBinding binding : bindings) { if(!methodBindingSet.add(binding)) { @@ -772,25 +729,19 @@ public class JavaEmitter implements GlueEmitter { // Note in particular that the public entry point taking an // array is merely a special case of the indirect buffer case. - if (cfg.emitInterface()) { + if ( forInterface ) { generatePublicEmitters(binding, allEmitters, true); - } - if (cfg.emitImpl()) { + } else { generatePublicEmitters(binding, allEmitters, false); generatePrivateEmitters(binding, allEmitters); } } // end iteration over expanded bindings - } catch (final Exception e) { - throw new RuntimeException("Error while generating bindings for \"" + sym + "\"", e); } - return allEmitters; - } - @Override public void endFunctions() throws Exception { - if (!cfg.structsOnly()) { + if ( !cfg.structsOnly() ) { if (cfg.allStatic() || cfg.emitInterface()) { emitCustomJavaCode(javaWriter(), cfg.className()); } @@ -821,40 +772,89 @@ public class JavaEmitter implements GlueEmitter { public void beginStructs(final TypeDictionary typedefDictionary, final TypeDictionary structDictionary, final Map<Type, Type> canonMap) throws Exception { - this.typedefDictionary = typedefDictionary; + // this.typedefDictionary = typedefDictionary; this.canonMap = canonMap; } @Override - public void emitStruct(final CompoundType structCType, final String alternateName) throws Exception { - final String structCTypeName; + public void emitStruct(final CompoundType structCType, final Type structCTypedefPtr) throws Exception { + final String structCTypeName, typedefedName; { - String _name = structCType.getName(); - if (_name == null && alternateName != null) { - _name = alternateName; + final String _name = structCType.getName(); + if ( null != structCTypedefPtr && null != structCTypedefPtr.getName() ) { + // always use typedef'ed name if available + typedefedName = structCTypedefPtr.getName(); + structCTypeName = typedefedName; + } else { + // fall back to actual struct type name + typedefedName = null; + structCTypeName = _name; } - structCTypeName = _name; - } - - if (structCTypeName == null) { - final String structName = structCType.getStructName(); - if ( null != structName && cfg.shouldIgnoreInInterface(structName) ) { + LOG.log(INFO, structCType.getASTLocusTag(), "Struct emission of structCType {0}", structCType); + LOG.log(INFO, structCType.getASTLocusTag()," structCTypedefPtr {0}", structCTypedefPtr); + LOG.log(INFO, structCType.getASTLocusTag()," : structCTypeName \"{0}\" -> typedefedName \"{1}\" -> \"{2}\"", + _name, typedefedName, structCTypeName); + if ( null == structCTypeName ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of unnamed struct {0} w/o typedef", structCType); + return; + } + final AliasedSymbol.AliasedSymbolImpl aliases = new AliasedSymbol.AliasedSymbolImpl(structCTypeName); + aliases.addAliasedName(_name); + aliases.addAliasedName(typedefedName); + if ( cfg.shouldIgnoreInInterface(aliases) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of ignored \"{0}\": {1}", aliases, structCType); return; } - LOG.log(WARNING, "skipping emission of unnamed struct \"{0}\"", structCType); - return; } - if (cfg.shouldIgnoreInInterface(structCTypeName)) { - return; + if( null != structCTypedefPtr && isOpaque(structCTypedefPtr) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque typedef {0}", structCTypedefPtr); + return; + } + if( isOpaque(structCType) ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque c-struct {0}", structCType); + return; } - final Type containingCType = canonicalize(new PointerType(SizeThunk.POINTER, structCType, 0)); + final Type containingCType; + { + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + final Type aptr; + int mode; + if( null != typedefedName ) { + aptr = structCTypedefPtr; + mode = 1; + } else { + aptr = new PointerType(SizeThunk.POINTER, structCType, 0); + aptr.setTypedefName(typedefedName); + mode = 2; + } + containingCType = canonicalize(aptr); + LOG.log(INFO, structCType.getASTLocusTag(), "containingCType[{0}]: {1} -canon-> {2}", mode, aptr, containingCType); + } final JavaType containingJType = typeToJavaType(containingCType, null); - if (!containingJType.isCompoundTypeWrapper()) { - return; + if( containingJType.isOpaqued() ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "skipping emission of opaque {0}, {1}", containingJType, structCType); + return; + } + if( !containingJType.isCompoundTypeWrapper() ) { + LOG.log(WARNING, structCType.getASTLocusTag(), + "skipping emission of non-compound {0}, {1}", containingJType, structCType); + return; } final String containingJTypeName = containingJType.getName(); + LOG.log(INFO, structCType.getASTLocusTag(), + "perform emission of \"{0}\" -> \"{1}\": {2}", structCTypeName, containingJTypeName, structCType); + + if( 0 == structCType.getNumFields() ) { + LOG.log(INFO, structCType.getASTLocusTag(), + "emission of \"{0}\" with zero fields {1}", containingJTypeName, structCType); + } this.requiresStaticInitialization = false; // reset @@ -884,13 +884,13 @@ public class JavaEmitter implements GlueEmitter { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if (!cfg.shouldIgnoreInInterface(cfgFieldName0)) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = renamed==null ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); if ( fieldType.isFunctionPointer() || fieldType.isPointer() || requiresGetCStringLength(fieldType, cfgFieldName1) ) { needsNativeCode = true; @@ -978,11 +978,11 @@ public class JavaEmitter implements GlueEmitter { for (int i = 0; i < structCType.getNumFields(); i++) { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if ( !cfg.shouldIgnoreInInterface(cfgFieldName0) ) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = null==renamed ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); if (fieldType.isFunctionPointer()) { // no offset/size for function pointer .. if( GlueGen.debug() ) { @@ -993,8 +993,8 @@ public class JavaEmitter implements GlueEmitter { // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate // a name?) if (fieldType.getName() == null) { - throw new RuntimeException("Anonymous structs as fields not supported yet, field \"" + - cfgFieldName1 + "\", "+fieldType.getDebugString()); + throw new GlueGenException("Anonymous structs as fields not supported yet, field \"" + + cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag()); } if( GlueGen.debug() ) { System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "compound"); @@ -1012,11 +1012,11 @@ public class JavaEmitter implements GlueEmitter { try { externalJavaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - throw new RuntimeException("Error occurred while creating accessor for field \"" + - cfgFieldName1 + "\", "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating accessor for field \"" + + cfgFieldName1 + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } if( GlueGen.debug() ) { - System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, fieldName, fieldType.getDebugString(), "MISC"); + System.err.printf("SE.os.%02d: %s / %s, %s (%s)%n", (i+1), field, cfgFieldName1, fieldType.getDebugString(), "MISC"); System.err.printf("SE.os.%02d: javaType %s%n", (i+1), externalJavaType.getDebugString()); } if (externalJavaType.isPrimitive()) { @@ -1040,6 +1040,7 @@ public class JavaEmitter implements GlueEmitter { } } javaWriter.println(); + // getDelegatedImplementation if( !cfg.manuallyImplement(JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, "size")) ) { javaWriter.println(" public static int size() {"); javaWriter.println(" return "+containingJTypeName+"_size[mdIdx];"); @@ -1073,52 +1074,62 @@ public class JavaEmitter implements GlueEmitter { final Field field = structCType.getField(i); final Type fieldType = field.getType(); - final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, field.getName()); + final String cfgFieldName0 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, field.getName()); if (!cfg.shouldIgnoreInInterface(cfgFieldName0)) { final String renamed = cfg.getJavaSymbolRename(cfgFieldName0); final String fieldName = renamed==null ? field.getName() : renamed; - final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(structCTypeName, fieldName); + final String cfgFieldName1 = JavaConfiguration.canonicalStructFieldSymbol(containingJTypeName, fieldName); + final TypeInfo opaqueFieldType = cfg.typeInfo(fieldType); + final boolean isOpaqueFieldType = null != opaqueFieldType; + final TypeInfo opaqueField = cfg.canonicalNameOpaque(cfgFieldName1); + final boolean isOpaqueField = null != opaqueField; if( GlueGen.debug() ) { - System.err.printf("SE.ac.%02d: %s / %s, %s%n", (i+1), field, cfgFieldName1, fieldType.getDebugString()); + System.err.printf("SE.ac.%02d: %s / %s (opaque %b), %s (opaque %b)%n", (i+1), + (i+1), field, cfgFieldName1, isOpaqueField, fieldType.getDebugString(), isOpaqueFieldType); } - if (fieldType.isFunctionPointer()) { + if ( fieldType.isFunctionPointer() && !isOpaqueField ) { + final FunctionSymbol func = new FunctionSymbol(field.getName(), fieldType.asPointer().getTargetType().asFunction()); + func.rename(renamed); // null is OK generateFunctionPointerCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, containingCType, containingJType, i, - new FunctionSymbol(fieldName, fieldType.asPointer().getTargetType().asFunction()), cfgFieldName1); - } else if (fieldType.isCompound()) { + func, cfgFieldName1); + } else if ( fieldType.isCompound() && !isOpaqueField ) { // FIXME: will need to support this at least in order to // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate a name?) if (fieldType.getName() == null) { - throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" + - field + "\" in type \"" + structCTypeName + "\")"); + throw new GlueGenException("Anonymous structs as fields not supported yet (field \"" + + field + "\" in type \"" + structCTypeName + "\")", + fieldType.getASTLocusTag()); } javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, fieldType.getName(), capitalizeString(fieldName), null, null); + generateGetterSignature(javaWriter, fieldType, false, false, fieldType.getName(), fieldName, capitalizeString(fieldName), null, null); javaWriter.println(" {"); javaWriter.println(" return " + fieldType.getName() + ".create( accessor.slice( " + fieldName+"_offset[mdIdx], "+fieldName+"_size[mdIdx] ) );"); javaWriter.println(" }"); - } else if ( fieldType.isArray() || fieldType.isPointer() ) { - generateArrayGetterSetterCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, - containingCType, containingJType, - i, field, fieldName, cfgFieldName1); + } else if ( ( fieldType.isArray() || fieldType.isPointer() ) && !isOpaqueField ) { + generateArrayGetterSetterCode(methodBindingSet, javaWriter, jniWriter, structCType, structCTypeName, + structClassPkgName, containingCType, + containingJType, i, field, fieldName, cfgFieldName1); } else { final JavaType javaType; try { javaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - System.err.println("Error occurred while creating accessor for field \"" + - field.getName() + "\", "+fieldType.getDebugString()); - throw(e); + throw new GlueGenException("Error occurred while creating accessor for field \"" + + field.getName() + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } - if (javaType.isPrimitive()) { + if ( isOpaqueFieldType || isOpaqueField || javaType.isPrimitive()) { // Primitive type final boolean fieldTypeNativeSizeFixed = fieldType.getSize().hasFixedNativeSize(); final String javaTypeName; - if ( isOpaque(fieldType) ) { - javaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType, machDescJava); + if ( isOpaqueFieldType ) { + javaTypeName = opaqueFieldType.javaType().getName(); + } else if ( isOpaqueField ) { + javaTypeName = opaqueField.javaType().getName(); + // javaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType, machDescJava); } else { javaTypeName = javaType.getName(); } @@ -1126,15 +1137,14 @@ public class JavaEmitter implements GlueEmitter { final String capFieldName = capitalizeString(fieldName); final String sizeDenominator = fieldType.isPointer() ? "pointer" : javaTypeName ; - if(GlueGen.debug()) { - System.err.println("Java.StructEmitter.Primitive: "+field.getName()+", "+fieldType.getDebugString()+", "+javaTypeName+", "+ - ", fixedSize "+fieldTypeNativeSizeFixed+", opaque "+isOpaque(fieldType)+", sizeDenominator "+sizeDenominator); - } + LOG.log(FINE, structCType.getASTLocusTag(), + "Java.StructEmitter.Primitive: "+field.getName()+", "+fieldType+", "+javaTypeName+", "+ + ", fixedSize "+fieldTypeNativeSizeFixed+", opaque[t "+isOpaqueFieldType+", f "+isOpaqueField+"], sizeDenominator "+sizeDenominator); if( !fieldType.isConst() ) { // Setter javaWriter.println(); - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capFieldName, null, javaTypeName, null, null); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capFieldName, null, javaTypeName, null, null); javaWriter.println(" {"); if( fieldTypeNativeSizeFixed ) { javaWriter.println(" accessor.set" + capJavaTypeName + "At(" + fieldName+"_offset[mdIdx], val);"); @@ -1147,7 +1157,7 @@ public class JavaEmitter implements GlueEmitter { // Getter javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, javaTypeName, capFieldName, null, null); + generateGetterSignature(javaWriter, fieldType, false, false, javaTypeName, fieldName, capFieldName, null, null); javaWriter.println(" {"); javaWriter.print (" return "); if( fieldTypeNativeSizeFixed ) { @@ -1216,9 +1226,9 @@ public class JavaEmitter implements GlueEmitter { private void generateGetterSignature(final PrintWriter writer, final Type origFieldType, final boolean staticMethod, final boolean abstractMethod, - final String returnTypeName, final String capitalizedFieldName, - final String customArgs, final String arrayLengthExpr) { - writer.print(" /** Getter for native field: "+origFieldType.getDebugString()); + final String returnTypeName, final String fieldName, + final String capitalizedFieldName, final String customArgs, final String arrayLengthExpr) { + writer.print(" /** Getter for native field <code>"+fieldName+"</code>: "+origFieldType.getDebugString()); if( null != arrayLengthExpr ) { writer.print(", with array length of <code>"+arrayLengthExpr+"</code>"); } @@ -1231,10 +1241,10 @@ public class JavaEmitter implements GlueEmitter { } private void generateSetterSignature(final PrintWriter writer, final Type origFieldType, final boolean abstractMethod, - final String returnTypeName, final String capitalizedFieldName, - final String customArgsPre, final String paramTypeName, final String customArgsPost, - final String arrayLengthExpr) { - writer.print(" /** Setter for native field: "+origFieldType.getDebugString()); + final String returnTypeName, final String fieldName, + final String capitalizedFieldName, final String customArgsPre, final String paramTypeName, + final String customArgsPost, final String arrayLengthExpr) { + writer.print(" /** Setter for native field <code>"+fieldName+"</code>: "+origFieldType.getDebugString()); if( null != arrayLengthExpr ) { writer.print(", with array length of <code>"+arrayLengthExpr+"</code>"); } @@ -1288,7 +1298,7 @@ public class JavaEmitter implements GlueEmitter { final Type containingCType, final JavaType containingJType, final int i, final FunctionSymbol funcSym, final String returnSizeLookupName) { // Emit method call and associated native code - final MethodBinding mb = bindFunction(funcSym, containingJType, containingCType, machDescJava); + final MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, containingJType, containingCType); mb.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis // JavaTypes representing C pointers in the initial @@ -1310,16 +1320,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - true, + true, // emitBody cfg.tagNativeBinding(), false, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - false, - false, // FIXME: should unify this with the general emission code + false, // forDirectBufferImplementation false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, + false, // isUnimplemented + false, // isInterface + false, // isNativeMethod + false, // isPrivateNativeMethod cfg); emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); emitter.emit(); @@ -1330,17 +1341,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, + false, // emitBody cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + true, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - true, - true, // FIXME: should unify this with the general emission code + true, // forDirectBufferImplementation false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, - cfg); + false, // isUnimplemented + false, // isInterface + false, // isNativeMethod + true, cfg); emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); emitter.addModifier(JavaMethodBindingEmitter.NATIVE); emitter.emit(); @@ -1355,7 +1366,7 @@ public class JavaEmitter implements GlueEmitter { false, true, false, // forIndirectBufferAndArrayImplementation - machDescJava); + machDescJava, getConfiguration()); cEmitter.setIsCStructFunctionPointer(true); prepCEmitter(returnSizeLookupName, binding.getJavaReturnType(), cEmitter); cEmitter.emit(); @@ -1369,7 +1380,7 @@ public class JavaEmitter implements GlueEmitter { final int i, final FunctionSymbol funcSym, final String returnSizeLookupName, final String docArrayLenExpr, final String nativeArrayLenExpr) { // Emit method call and associated native code - final MethodBinding mb = bindFunction(funcSym, containingJType, containingCType, machDescJava); + final MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, containingJType, containingCType); mb.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis // JavaTypes representing C pointers in the initial @@ -1392,16 +1403,17 @@ public class JavaEmitter implements GlueEmitter { javaWriter, cfg.runtimeExceptionType(), cfg.unsupportedExceptionType(), - false, - cfg.tagNativeBinding(), - true, // eraseBufferAndArrayTypes + false, // emitBody + cfg.tagNativeBinding(), // tagNativeBinding + true, // eraseBufferAndArrayTypes useNIOOnly, useNIODirectOnly, - true, - true, // FIXME: should unify this with the general emission code - false, // forIndirectBufferAndArrayImplementation - false, // FIXME: should unify this with the general emission code - false, + false, // forDirectBufferImplementation + false, // forIndirectBufferAndArrayImplementation + false, // isUnimplemented + true, // isInterface + true, // isNativeMethod + true, // isPrivateNativeMethod cfg); if( null != docArrayLenExpr ) { emitter.setReturnedArrayLengthExpression(docArrayLenExpr, true); @@ -1420,7 +1432,7 @@ public class JavaEmitter implements GlueEmitter { false, true, false, // forIndirectBufferAndArrayImplementation - machDescJava); + machDescJava, getConfiguration()); cEmitter.setIsCStructFunctionPointer(false); final String lenExprSet; if( null != nativeArrayLenExpr ) { @@ -1470,14 +1482,16 @@ public class JavaEmitter implements GlueEmitter { final String cfgVal = cfg.returnedArrayLength(returnSizeLookupName); if( null != cfgVal ) { if( hasFixedTypeLen[0] ) { - System.err.println("WARNING: struct array field '"+returnSizeLookupName+"' of '"+type+"' length '"+Arrays.toString(length)+"' overwritten by cfg-expression: "+cfgVal); + LOG.log(WARNING, type.getASTLocusTag(), + "struct array field '"+returnSizeLookupName+"' of '"+type+"' length '"+Arrays.toString(length)+"' overwritten by cfg-expression: "+cfgVal); } return cfgVal; } if( hasFixedTypeLen[0] ) { return lengthExpr.toString(); } else { - System.err.println("WARNING: struct array field '"+returnSizeLookupName+"' length '"+Arrays.toString(length)+"' without fixed- nor configured-size: "+type.getDebugString()); + LOG.log(WARNING, type.getASTLocusTag(), + "struct array field '"+returnSizeLookupName+"' length '"+Arrays.toString(length)+"' without fixed- nor configured-size: {0}", type); return null; } } @@ -1510,16 +1524,18 @@ public class JavaEmitter implements GlueEmitter { private void generateArrayGetterSetterCode(final Set<MethodBinding> methodBindingSet, final PrintWriter javaWriter, final PrintWriter jniWriter, + final CompoundType structCType, final String structCTypeName, final String structClassPkgName, final Type containingCType, final JavaType containingJType, - final int i, final Field field, final String fieldName, final String returnSizeLookupName) throws Exception { + final int i, final Field field, final String fieldName, + final String returnSizeLookupName) throws Exception { final Type fieldType = field.getType(); final JavaType javaType; try { javaType = typeToJavaType(fieldType, machDescJava); } catch (final Exception e) { - throw new RuntimeException("Error occurred while creating array/pointer accessor for field \"" + - returnSizeLookupName + "\", "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating array/pointer accessor for field \"" + + returnSizeLookupName + "\", "+fieldType.getDebugString(), fieldType.getASTLocusTag(), e); } if( GlueGen.debug() ) { System.err.printf("SE.ac.%02d: javaType %s%n", (i+1), javaType.getDebugString()); @@ -1586,7 +1602,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP ptr-ptr (depth "+pointerType.pointerDepth()+"): "+returnSizeLookupName +": "+fieldType; javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } } @@ -1598,8 +1614,9 @@ public class JavaEmitter implements GlueEmitter { try { baseJElemType = typeToJavaType(baseCElemType, machDescJava); } catch (final Exception e ) { - throw new RuntimeException("Error occurred while creating array/pointer accessor for field \"" + - returnSizeLookupName + "\", baseType "+baseCElemType.getDebugString()+", topType "+fieldType.getDebugString(), e); + throw new GlueGenException("Error occurred while creating array/pointer accessor for field \"" + + returnSizeLookupName + "\", baseType "+baseCElemType.getDebugString()+", topType "+fieldType.getDebugString(), + fieldType.getASTLocusTag(), e); } baseJElemTypeName = baseJElemType.getName(); baseCElemNativeSizeFixed = baseCElemType.isPrimitive() ? baseCElemType.getSize().hasFixedNativeSize() : true; @@ -1609,7 +1626,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP primitive w/ platform dependent sized type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } } @@ -1625,7 +1642,7 @@ public class JavaEmitter implements GlueEmitter { _arrayLengthExpr = "getCStringLengthImpl(pString)+1"; _arrayLengthExprIsConst = false; this.requiresStaticInitialization = true; - LOG.log(INFO, "StaticInit Trigger.3 \"{0}\"", returnSizeLookupName); + LOG.log(INFO, structCType.getASTLocusTag(), "StaticInit Trigger.3 \"{0}\"", returnSizeLookupName); } else { useGetCStringLength = false; } @@ -1635,7 +1652,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(); final String msg = "SKIP unsized array in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("WARNING: "+msg); + LOG.log(WARNING, structCType.getASTLocusTag(), msg); return; } boolean _hasSingleElement=false; @@ -1657,7 +1674,7 @@ public class JavaEmitter implements GlueEmitter { // if( !hasSingleElement && useFixedTypeLen[0] ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, arrayLengthExprIsConst, false, "final int", capitalFieldName+"ArrayLength", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, arrayLengthExprIsConst, false, "final int", fieldName, capitalFieldName+"ArrayLength", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" return "+arrayLengthExpr+";"); javaWriter.println(" }"); @@ -1671,11 +1688,11 @@ public class JavaEmitter implements GlueEmitter { // Setter Primitive Pointer final String msg = "SKIP setter for primitive-pointer type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("INFO: "+msg); + LOG.log(INFO, structCType.getASTLocusTag(), msg); } else { // Setter Primitive Array if( hasSingleElement ) { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); if( baseCElemNativeSizeFixed ) { javaWriter.println(" accessor.set" + baseJElemTypeNameC + "At(" + fieldName+"_offset[mdIdx], val);"); @@ -1685,7 +1702,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); } else { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + val.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + val.length \"+val.length+\" > array-length \"+arrayLength); };"); @@ -1708,11 +1725,11 @@ public class JavaEmitter implements GlueEmitter { // Setter Struct Pointer final String msg = "SKIP setter for complex-pointer type in struct: "+returnSizeLookupName+": "+fieldType.getDebugString(); javaWriter.println(" // "+msg); - System.err.println("INFO: "+msg); + LOG.log(INFO, structCType.getASTLocusTag(), msg); } else { // Setter Struct Array if( hasSingleElement ) { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, null, baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int elemSize = "+baseJElemTypeName+".size();"); javaWriter.println(" final ByteBuffer destB = getBuffer();"); @@ -1728,7 +1745,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); } else { - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int offset", baseJElemTypeName+"[]", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + val.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + val.length \"+val.length+\" > array-length \"+arrayLength); };"); @@ -1750,7 +1767,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return this;"); javaWriter.println(" }"); javaWriter.println(); - generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, capitalFieldName, "final int index", baseJElemTypeName, null, arrayLengthExpr); + generateSetterSignature(javaWriter, fieldType, false, containingJTypeName, fieldName, capitalFieldName, "final int index", baseJElemTypeName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" final int elemSize = "+baseJElemTypeName+".size();"); @@ -1779,12 +1796,12 @@ public class JavaEmitter implements GlueEmitter { if( isPointer ) { // Getter Primitive Pointer final FunctionType ft = new FunctionType(dummyFuncTypeName, SizeThunk.POINTER, fieldType, 0); - ft.addArgument(containingCType.getCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), + ft.addArgument(containingCType.newCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), CMethodBindingEmitter.cThisArgumentName()); ft.addArgument(int32Type, nativeArrayLengthArg); final FunctionSymbol fs = new FunctionSymbol("get"+capitalFieldName, ft); jniWriter.println(); - jniWriter.print("static "+fs.toString()); + jniWriter.print("static "+fs.toString(false)); jniWriter.println("{"); jniWriter.println(" return "+CMethodBindingEmitter.cThisArgumentName()+"->"+field.getName()+";"); jniWriter.println("}"); @@ -1792,7 +1809,7 @@ public class JavaEmitter implements GlueEmitter { generateArrayPointerCode(methodBindingSet, javaWriter, jniWriter, structCTypeName, structClassPkgName, containingCType, containingJType, i, fs, returnSizeLookupName, arrayLengthExpr, nativeArrayLengthArg); javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); if( useGetCStringLength ) { javaWriter.println(" final int arrayLength = get"+capitalFieldName+"ArrayLength();"); @@ -1809,7 +1826,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); if( isString && isByteBuffer ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, "String", capitalFieldName+"AsString", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "String", fieldName, capitalFieldName+"AsString", null, arrayLengthExpr); javaWriter.println(" {"); if( useGetCStringLength ) { javaWriter.println(" final int arrayLength = get"+capitalFieldName+"ArrayLength();"); @@ -1829,7 +1846,7 @@ public class JavaEmitter implements GlueEmitter { } if( useGetCStringLength ) { javaWriter.println(); - generateGetterSignature(javaWriter, fieldType, false, false, "final int", capitalFieldName+"ArrayLength", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "final int", fieldName, capitalFieldName+"ArrayLength", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final long pString = PointerBuffer.wrap( accessor.slice(" + fieldName+"_offset[mdIdx], PointerBuffer.ELEMENT_SIZE) ).get(0);"); javaWriter.println(" return "+arrayLengthExpr+";"); @@ -1838,7 +1855,7 @@ public class JavaEmitter implements GlueEmitter { } else { // Getter Primitive Array if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); if( baseCElemNativeSizeFixed ) { javaWriter.println(" return accessor.get" + baseJElemTypeNameC + "At(" + fieldName+"_offset[mdIdx]);"); @@ -1848,7 +1865,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); javaWriter.println(); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeNameC+"Buffer", fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.print(" return accessor.slice(" + fieldName+"_offset[mdIdx], Buffers.SIZEOF_"+baseJElemTypeNameU+" * "+arrayLengthExpr+")"); if( !isByteBuffer ) { @@ -1858,7 +1875,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" }"); javaWriter.println(); if( isString && isByteBuffer ) { - generateGetterSignature(javaWriter, fieldType, false, false, "String", capitalFieldName+"AsString", null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, "String", fieldName, capitalFieldName+"AsString", null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int offset = " + fieldName+"_offset[mdIdx];"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); @@ -1872,7 +1889,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return new String(ba, 0, i);"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1887,12 +1904,12 @@ public class JavaEmitter implements GlueEmitter { if( isPointer ) { // Getter Struct Pointer final FunctionType ft = new FunctionType(dummyFuncTypeName, SizeThunk.POINTER, fieldType, 0); - ft.addArgument(containingCType.getCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), + ft.addArgument(containingCType.newCVVariant(containingCType.getCVAttributes() | CVAttributes.CONST), CMethodBindingEmitter.cThisArgumentName()); ft.addArgument(int32Type, nativeArrayElemOffsetArg); final FunctionSymbol fs = new FunctionSymbol("get"+capitalFieldName, ft); jniWriter.println(); - jniWriter.print("static "+fs.toString()); + jniWriter.print("static "+fs.toString(false)); jniWriter.println("{"); jniWriter.println(" return "+CMethodBindingEmitter.cThisArgumentName()+"->"+field.getName()+"+"+nativeArrayElemOffsetArg+";"); jniWriter.println("}"); @@ -1901,7 +1918,7 @@ public class JavaEmitter implements GlueEmitter { containingCType, containingJType, i, fs, returnSizeLookupName, arrayLengthExpr, nativeArrayLengthONE); javaWriter.println(); if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final ByteBuffer source = getBuffer();"); javaWriter.println(" final ByteBuffer _res = get"+capitalFieldName+"0(source, 0);"); @@ -1909,7 +1926,7 @@ public class JavaEmitter implements GlueEmitter { javaWriter.println(" return "+baseJElemTypeName+".create(_res);"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1925,12 +1942,12 @@ public class JavaEmitter implements GlueEmitter { } else { // Getter Struct Array if( hasSingleElement ) { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, capitalFieldName, null, arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName, fieldName, capitalFieldName, null, arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" return "+baseJElemTypeName+".create(accessor.slice("+fieldName+"_offset[mdIdx], "+baseJElemTypeName+".size()));"); javaWriter.println(" }"); } else { - generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); + generateGetterSignature(javaWriter, fieldType, false, false, baseJElemTypeName+"[]", fieldName, capitalFieldName, "final int offset, "+baseJElemTypeName+" result[]", arrayLengthExpr); javaWriter.println(" {"); javaWriter.println(" final int arrayLength = "+arrayLengthExpr+";"); javaWriter.println(" if( offset + result.length > arrayLength ) { throw new IndexOutOfBoundsException(\"offset \"+offset+\" + result.length \"+result.length+\" > array-length \"+arrayLength); };"); @@ -1947,12 +1964,9 @@ public class JavaEmitter implements GlueEmitter { } } - private static final boolean DEBUG_TYPEC2JAVA = false; private JavaType typeToJavaType(final Type cType, final MachineDataInfo curMachDesc) { final JavaType jt = typeToJavaTypeImpl(cType, curMachDesc); - if( DEBUG_TYPEC2JAVA ) { - System.err.println("typeToJavaType: "+cType.getDebugString()+" -> "+jt.getDebugString()); - } + LOG.log(FINE, cType.getASTLocusTag(), "typeToJavaType: {0} -> {1}", cType, jt); return jt; } private boolean isJNIEnvPointer(final Type cType) { @@ -1968,7 +1982,7 @@ public class JavaEmitter implements GlueEmitter { } // Opaque specifications override automatic conversions // in case the identity is being used .. not if ptr-ptr - final TypeInfo info = cfg.typeInfo(cType, typedefDictionary); + final TypeInfo info = cfg.typeInfo(cType); if (info != null) { boolean isPointerPointer = false; if (cType.pointerDepth() > 0 || cType.arrayDimension() > 0) { @@ -1986,16 +2000,16 @@ public class JavaEmitter implements GlueEmitter { // target type) if (targetType.isPointer()) { isPointerPointer = true; - - // t is<type>**, targetType is <type>*, we need to get <type> - final Type bottomType = targetType.asPointer().getTargetType(); if( GlueGen.debug() ) { - LOG.log(INFO, "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", new Object[]{cType.getDebugString(), targetType, bottomType}); + // t is<type>**, targetType is <type>*, we need to get <type> + final Type bottomType = targetType.asPointer().getTargetType(); + LOG.log(INFO, cType.getASTLocusTag(), "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", + cType, targetType, bottomType); } } } } - if(!isPointerPointer) { + if( !isPointerPointer ) { return info.javaType(); } } @@ -2006,8 +2020,9 @@ public class JavaEmitter implements GlueEmitter { case 2: return javaType(Short.TYPE); case 4: return javaType(Integer.TYPE); case 8: return javaType(Long.TYPE); - default: throw new RuntimeException("Unknown integer type of size " + - cType.getSize(curMachDesc) + " and name " + cType.getName()); + default: throw new GlueGenException("Unknown integer type of size " + + cType.getSize(curMachDesc) + " and name " + cType.getName(), + cType.getASTLocusTag()); } } else if (cType.isFloat()) { return javaType(Float.TYPE); @@ -2042,8 +2057,9 @@ public class JavaEmitter implements GlueEmitter { case 2: return JavaType.createForCShortPointer(); case 4: return JavaType.createForCInt32Pointer(); case 8: return JavaType.createForCInt64Pointer(); - default: throw new RuntimeException("Unknown integer array type of size " + - cType.getSize(curMachDesc) + " and name " + cType.getName()+", "+cType.getDebugString()); + default: throw new GlueGenException("Unknown integer array type of size " + + cType.getSize(curMachDesc) + " and name " + cType.getName()+", "+cType.getDebugString(), + cType.getASTLocusTag()); } } else if (targetType.isFloat()) { return JavaType.createForCFloatPointer(); @@ -2058,19 +2074,28 @@ public class JavaEmitter implements GlueEmitter { cType.getName().equals("jobject")) { return javaType(java.lang.Object.class); } - String name = targetType.getName(); - if (name == null) { - // Try containing pointer type for any typedefs - name = cType.getName(); - if (name == null) { - throw new RuntimeException("Couldn't find a proper type name for pointer type " + cType.getDebugString()); - } + // NOTE: Struct Name Resolution (JavaEmitter, HeaderParser) + String name; + if( !targetType.isTypedef() && cType.isTypedef() ) { + // If compound is not a typedef _and_ containing pointer is typedef, use the latter. + name = cType.getName(); + } else { + // .. otherwise try compound name + name = targetType.getName(); + if( null == name ) { + // .. fall back to pointer type name + name = cType.getName(); + if (name == null) { + throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString(), + cType.getASTLocusTag()); + } + } } - return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { - throw new RuntimeException("Don't know how to convert pointer/array type \"" + - cType.getDebugString() + "\""); + throw new GlueGenException("Don't know how to convert pointer/array type \"" + + cType.getDebugString() + "\"", + cType.getASTLocusTag()); } } // Handle Types of form pointer-to-pointer-to-type or @@ -2084,19 +2109,22 @@ public class JavaEmitter implements GlueEmitter { // t is<type>**, targetType is <type>*, we need to get <type> bottomType = targetType.asPointer().getTargetType(); if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } return JavaType.forNIOPointerBufferClass(); } else if(targetType.isArray()) { // t is<type>[][], targetType is <type>[], we need to get <type> bottomType = targetType.asArray().getBaseElementType(); if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr.array): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.array): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } } else { bottomType = targetType; if( GlueGen.debug() ) { - LOG.log(INFO, "typeToJavaType(ptr-ptr.primitive): {0}, targetType: {1}, bottomType: {2}", new Object[]{cType.getDebugString(), targetType, bottomType}); + LOG.log(INFO, cType.getASTLocusTag(), "typeToJavaType(ptr-ptr.primitive): {0}, targetType: {1}, bottomType: {2}", + cType.getDebugString(), targetType, bottomType); } } @@ -2110,16 +2138,17 @@ public class JavaEmitter implements GlueEmitter { case 2: return javaType(ArrayTypes.shortBufferArrayClass); case 4: return javaType(ArrayTypes.intBufferArrayClass); case 8: return javaType(ArrayTypes.longBufferArrayClass); - default: throw new RuntimeException("Unknown two-dimensional integer array type of element size " + - bottomType.getSize(curMachDesc) + " and name " + bottomType.getName()+", "+bottomType.getDebugString()); + default: throw new GlueGenException("Unknown two-dimensional integer array type of element size " + + bottomType.getSize(curMachDesc) + " and name " + bottomType.getName()+", "+bottomType.getDebugString(), + bottomType.getASTLocusTag()); } } else if (bottomType.isFloat()) { return javaType(ArrayTypes.floatBufferArrayClass); } else if (bottomType.isDouble()) { return javaType(ArrayTypes.doubleBufferArrayClass); } else { - throw new RuntimeException("Unexpected primitive type " + bottomType.getDebugString() + - " in two-dimensional array"); + throw new GlueGenException("Unexpected primitive type " + bottomType.getDebugString() + + " in two-dimensional array", bottomType.getASTLocusTag()); } } else if (bottomType.isVoid()) { return javaType(ArrayTypes.bufferArrayClass); @@ -2128,32 +2157,38 @@ public class JavaEmitter implements GlueEmitter { // Array of pointers; convert as array of StructAccessors return JavaType.createForCArray(bottomType); } else { - throw new RuntimeException( + throw new GlueGenException( "Could not convert C type \"" + cType.getDebugString() + "\" " + "to appropriate Java type; need to add more support for " + "depth=2 pointer/array types [debug info: targetType=\"" + - targetType + "\"]"); + targetType + "\"]", cType.getASTLocusTag()); } } else { // can't handle this type of pointer/array argument - throw new RuntimeException( + throw new GlueGenException( "Could not convert C pointer/array \"" + cType.getDebugString() + "\" to " + "appropriate Java type; types with pointer/array depth " + "greater than 2 are not yet supported [debug info: " + "pointerDepth=" + cType.pointerDepth() + " arrayDimension=" + - cType.arrayDimension() + " targetType=\"" + targetType + "\"]"); + cType.arrayDimension() + " targetType=\"" + targetType + "\"]", + cType.getASTLocusTag()); } - } else if(cType.isCompound() ) { // FIXME: Compound and Compound-Arrays - final String name = cType.getName(); + } else if( cType.isCompound() ) { // FIXME: Compound and Compound-Arrays + String name = cType.getName(); if (name == null) { - throw new RuntimeException("Couldn't find a proper type name for pointer type " + cType.getDebugString()); + name = cType.asCompound().getStructName(); + if (name == null) { + throw new GlueGenException("Couldn't find a proper type name for pointer type " + cType.getDebugString(), + cType.getASTLocusTag()); + } } return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { - throw new RuntimeException( + throw new GlueGenException( "Could not convert C type \"" + cType.getDebugString() + "\" (class " + - cType.getClass().getName() + ") to appropriate Java type"); + cType.getClass().getName() + ") to appropriate Java type", + cType.getASTLocusTag()); } } @@ -2189,7 +2224,7 @@ public class JavaEmitter implements GlueEmitter { } private boolean isOpaque(final Type type) { - return (cfg.typeInfo(type, typedefDictionary) != null); + return null != cfg.typeInfo(type); } private String compatiblePrimitiveJavaTypeName(final Type fieldType, @@ -2198,14 +2233,16 @@ public class JavaEmitter implements GlueEmitter { final Class<?> c = javaType.getJavaClass(); if (!isIntegerType(c)) { // FIXME - throw new RuntimeException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.): type: "+fieldType+", javaType "+javaType+", javaClass "+c); + throw new GlueGenException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.): type: "+fieldType+", javaType "+javaType+", javaClass "+c, + fieldType.getASTLocusTag()); } switch ((int) fieldType.getSize(curMachDesc)) { case 1: return "byte"; case 2: return "short"; case 4: return "int"; case 8: return "long"; - default: throw new RuntimeException("Can't handle opaque definitions if the starting type isn't compatible with integral types"); + default: throw new GlueGenException("Can't handle opaque definitions if the starting type isn't compatible with integral types", + fieldType.getASTLocusTag()); } } @@ -2229,13 +2266,16 @@ public class JavaEmitter implements GlueEmitter { } if (cfg.allStatic() || cfg.emitInterface()) { - javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java", cfg.className()); + javaFileName = jRoot + File.separator + cfg.className() + ".java"; + javaWriter = openFile(javaFileName, cfg.className()); } if (!cfg.allStatic() && cfg.emitImpl()) { - javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java", cfg.implClassName()); + javaFileName = jImplRoot + File.separator + cfg.implClassName() + ".java"; + javaImplWriter = openFile(javaFileName, cfg.implClassName()); } if (cfg.emitImpl()) { - cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c", cfg.implClassName()); + cFileName = nRoot + File.separator + cfg.implClassName() + "_JNI.c"; + cWriter = openFile(cFileName, cfg.implClassName()); } if (javaWriter != null) { @@ -2249,6 +2289,9 @@ public class JavaEmitter implements GlueEmitter { } } + /** For {@link #javaWriter} or {@link #javaImplWriter} */ + protected String javaFileName() { return javaFileName; } + protected PrintWriter javaWriter() { if (!cfg.allStatic() && !cfg.emitInterface()) { throw new InternalError("Should not call this"); @@ -2263,6 +2306,9 @@ public class JavaEmitter implements GlueEmitter { return javaImplWriter; } + /** For {@link #cImplWriter} */ + protected String cFileName() { return cFileName; } + protected PrintWriter cWriter() { if (!cfg.emitImpl()) { throw new InternalError("Should not call this"); @@ -2454,8 +2500,9 @@ public class JavaEmitter implements GlueEmitter { if (getConfig().emitImpl()) { cWriter.println("#include <assert.h>"); + cWriter.println("#include <stddef.h>"); cWriter.println(); - cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity); /* forward decl. */"); + cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity); /* forward decl. */"); cWriter.println(); } for (final String code : cfg.customCCode()) { @@ -2468,6 +2515,7 @@ public class JavaEmitter implements GlueEmitter { "static const char * clazzNameBuffers = \"com/jogamp/common/nio/Buffers\";\n"+ "static const char * clazzNameBuffersStaticNewCstrName = \"newDirectByteBuffer\";\n"+ "static const char * clazzNameBuffersStaticNewCstrSignature = \"(I)Ljava/nio/ByteBuffer;\";\n"+ + "static const char * sFatalError = \"FatalError:\";\n"+ "static jclass clazzBuffers = NULL;\n"+ "static jmethodID cstrBuffersNew = NULL;\n"+ "static jboolean _initClazzAccessDone = JNI_FALSE;\n"+ @@ -2479,13 +2527,13 @@ public class JavaEmitter implements GlueEmitter { "\n"+ " c = (*env)->FindClass(env, clazzNameBuffers);\n"+ " if(NULL==c) {\n"+ - " fprintf(stderr, \"FatalError: Can't find %s\\n\", clazzNameBuffers);\n"+ + " fprintf(stderr, \"%s Can't find %s\\n\", sFatalError, clazzNameBuffers);\n"+ " (*env)->FatalError(env, clazzNameBuffers);\n"+ " return JNI_FALSE;\n"+ " }\n"+ " clazzBuffers = (jclass)(*env)->NewGlobalRef(env, c);\n"+ " if(NULL==clazzBuffers) {\n"+ - " fprintf(stderr, \"FatalError: Can't use %s\\n\", clazzNameBuffers);\n"+ + " fprintf(stderr, \"%s Can't use %s\\n\", sFatalError, clazzNameBuffers);\n"+ " (*env)->FatalError(env, clazzNameBuffers);\n"+ " return JNI_FALSE;\n"+ " }\n"+ @@ -2493,7 +2541,7 @@ public class JavaEmitter implements GlueEmitter { " cstrBuffersNew = (*env)->GetStaticMethodID(env, clazzBuffers,\n"+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+ " if(NULL==cstrBuffersNew) {\n"+ - " fprintf(stderr, \"FatalError: can't create %s.%s %s\\n\",\n"+ + " fprintf(stderr, \"%s can't create %s.%s %s\\n\", sFatalError,\n"+ " clazzNameBuffers,\n"+ " clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+ " (*env)->FatalError(env, clazzNameBuffersStaticNewCstrName);\n"+ @@ -2503,18 +2551,35 @@ public class JavaEmitter implements GlueEmitter { " return JNI_TRUE;\n"+ "}\n"+ "\n"+ - "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity) {\n"+ + "#define JINT_MAX_VALUE ((size_t)0x7fffffffU)\n"+ + "static const char * sNewBufferImplNotCalled = \"initializeImpl() not called\";\n"+ + "static const char * sNewBufferMAX_INT = \"capacity > MAX_INT\";\n"+ + "static const char * sNewBufferNULL = \"New direct ByteBuffer is NULL\";\n"+ + "\n"+ + "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity) {\n"+ " jobject jbyteBuffer;\n"+ " void * byteBufferPtr;\n"+ "\n"+ " if( JNI_FALSE == _initClazzAccessDone ) {\n"+ - " fprintf(stderr, \"FatalError: initializeImpl() not called\\n\");\n"+ - " (*env)->FatalError(env, \"initializeImpl() not called\");\n"+ + " fprintf(stderr, \"%s %s\\n\", sFatalError, sNewBufferImplNotCalled);\n"+ + " (*env)->FatalError(env, sNewBufferImplNotCalled);\n"+ " return NULL;\n"+ " }\n"+ - " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, capacity);\n"+ - " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+ - " memcpy(byteBufferPtr, source_address, capacity);\n"+ + " if( JINT_MAX_VALUE < capacity ) {\n"+ + " fprintf(stderr, \"%s %s: %lu\\n\", sFatalError, sNewBufferMAX_INT, (unsigned long)capacity);\n"+ + " (*env)->FatalError(env, sNewBufferMAX_INT);\n"+ + " return NULL;\n"+ + " }\n"+ + " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, (jint)capacity);\n"+ + " if( NULL == jbyteBuffer ) {\n"+ + " fprintf(stderr, \"%s %s: size %lu\\n\", sFatalError, sNewBufferNULL, (unsigned long)capacity);\n"+ + " (*env)->FatalError(env, sNewBufferNULL);\n"+ + " return NULL;\n"+ + " }\n"+ + " if( 0 < capacity ) {\n"+ + " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+ + " memcpy(byteBufferPtr, source_address, capacity);\n"+ + " }\n"+ " return jbyteBuffer;\n"+ "}\n"+ "\n"; @@ -2580,36 +2645,46 @@ public class JavaEmitter implements GlueEmitter { potentially representing C pointers rather than true Java types) and must be lowered to concrete Java types before creating emitters for them. */ - private MethodBinding bindFunction(final FunctionSymbol sym, - final JavaType containingType, - final Type containingCType, - final MachineDataInfo curMachDesc) { - - final MethodBinding binding = new MethodBinding(sym, containingType, containingCType); - - binding.renameMethodName(cfg.getJavaSymbolRename(sym.getName())); - - // System.out.println("bindFunction(0) "+sym.getReturnType()); - - if (cfg.returnsString(binding.getName())) { + private MethodBinding bindFunction(FunctionSymbol sym, + final boolean forInterface, + final MachineDataInfo curMachDesc, + final JavaType containingType, final Type containingCType) { + + final String delegationImplName = null == containingType && null == containingCType ? + cfg.getDelegatedImplementation(sym) : null; + if( !forInterface && null != delegationImplName ) { + // We need to reflect the 'delegationImplName' for implementations + // to allow all subsequent type/cfg checks to hit on AliasedSymbol! + sym = FunctionSymbol.cloneWithDeepAliases(sym); + sym.addAliasedName(delegationImplName); + } + final String name = sym.getName(); + final JavaType javaReturnType; + + if (cfg.returnsString(sym)) { final PointerType prt = sym.getReturnType().asPointer(); if (prt == null || prt.getTargetType().asInt() == null || prt.getTargetType().getSize(curMachDesc) != 1) { - throw new RuntimeException( + throw new GlueGenException( "Cannot apply ReturnsString configuration directive to \"" + sym + - "\". ReturnsString requires native method to have return type \"char *\""); + "\". ReturnsString requires native method to have return type \"char *\"", + sym.getASTLocusTag()); } - binding.setJavaReturnType(javaType(java.lang.String.class)); + javaReturnType = javaType(java.lang.String.class); } else { - binding.setJavaReturnType(typeToJavaType(sym.getReturnType(), curMachDesc)); + final JavaType r = cfg.getOpaqueReturnType(sym); + if( null != r ) { + javaReturnType = r; + } else { + javaReturnType = typeToJavaType(sym.getReturnType(), curMachDesc); + } } - // System.out.println("bindFunction(1) "+binding.getJavaReturnType()); - // List of the indices of the arguments in this function that should be // converted from byte[] or short[] to String - final List<Integer> stringArgIndices = cfg.stringArguments(binding.getName()); + final List<JavaType> javaArgumentTypes = new ArrayList<JavaType>(); + final List<Integer> stringArgIndices = cfg.stringArguments(name); for (int i = 0; i < sym.getNumArguments(); i++) { final Type cArgType = sym.getArgumentType(i); @@ -2637,20 +2712,21 @@ public class JavaEmitter implements GlueEmitter { } } else { - throw new RuntimeException( + throw new GlueGenException( "Cannot apply ArgumentIsString configuration directive to " + "argument " + i + " of \"" + sym + "\": argument type is not " + - "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent"); + "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent", + sym.getASTLocusTag()); } } - binding.addJavaArgumentType(mappedType); + javaArgumentTypes.add(mappedType); //System.out.println("During binding of [" + sym + "], added mapping from C type: " + cArgType + " to Java type: " + mappedType); } - - // System.out.println("---> " + binding); - // System.out.println(" ---> " + binding.getCSymbol()); - // System.out.println("bindFunction(3) "+binding); - return binding; + final MethodBinding mb = new MethodBinding(sym, delegationImplName, + javaReturnType, javaArgumentTypes, + containingType, containingCType); + mangleBinding(mb); + return mb; } private MethodBinding lowerMethodBindingPointerTypes(final MethodBinding inputBinding, @@ -2710,7 +2786,7 @@ public class JavaEmitter implements GlueEmitter { result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass()); } } else { - throw new RuntimeException("Unknown C pointer type " + t); + throw new GlueGenException("Unknown C pointer type " + t); } } } @@ -2735,7 +2811,7 @@ public class JavaEmitter implements GlueEmitter { } else if (t.isCDoublePointerType()) { result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass()); } else { - throw new RuntimeException("Unknown C pointer type " + t); + throw new GlueGenException("Unknown C pointer type " + t, result.getCReturnType().getASTLocusTag()); } } @@ -2748,6 +2824,14 @@ public class JavaEmitter implements GlueEmitter { return result; } + /** + * Allow specializations to modify the given {@link MethodBinding} + * before {@link #expandMethodBinding(MethodBinding) expanding} and emission. + */ + protected void mangleBinding(final MethodBinding binding) { + // NOP + } + // Expands a MethodBinding containing C primitive pointer types into // multiple variants taking Java primitive arrays and NIO buffers, subject // to the per-function "NIO only" rule in the configuration file @@ -2779,10 +2863,11 @@ public class JavaEmitter implements GlueEmitter { private Type canonicalize(final Type t) { final Type res = canonMap.get(t); if (res != null) { - return res; + return res; + } else { + canonMap.put(t, t); + return t; } - canonMap.put(t, t); - return t; } /** diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 6966315..d3fca14 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -40,14 +40,17 @@ package com.jogamp.gluegen; import com.jogamp.gluegen.cgram.HeaderParser; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; import com.jogamp.gluegen.cgram.types.ArrayType; import com.jogamp.gluegen.cgram.types.EnumType; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import java.io.PrintWriter; import java.text.MessageFormat; import java.util.Iterator; import java.util.List; +import java.util.Set; /** * An emitter that emits only the interface for a Java<->C JNI binding. @@ -64,22 +67,22 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { protected final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); protected final CommentEmitter defaultInterfaceCommentEmitter = new InterfaceCommentEmitter(); + protected final boolean tagNativeBinding; + protected final boolean useNIODirectOnly; + protected final MethodBinding binding; // Exception type raised in the generated code if runtime checks fail private final String runtimeExceptionType; private final String unsupportedExceptionType; + private final boolean useNIOOnly; + private final boolean isNativeMethod; + private final boolean isUnimplemented; - protected boolean emitBody; - protected boolean eraseBufferAndArrayTypes; - protected boolean useNIOOnly; - protected boolean useNIODirectOnly; - protected boolean forImplementingMethodCall; - protected boolean forDirectBufferImplementation; - protected boolean forIndirectBufferAndArrayImplementation; - protected boolean isUnimplemented; - protected boolean tagNativeBinding; - - protected MethodBinding binding; + private boolean emitBody; + private boolean eraseBufferAndArrayTypes; + private boolean isPrivateNativeMethod; + private boolean forDirectBufferImplementation; + private boolean forIndirectBufferAndArrayImplementation; // Manually-specified prologue and epilogue code protected List<String> prologue; @@ -97,9 +100,6 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { // represent an array of compound type wrappers private static final String COMPOUND_ARRAY_SUFFIX = "_buf_array_copy"; - // Only present to provide more clear comments - private final JavaConfiguration cfg; - public JavaMethodBindingEmitter(final MethodBinding binding, final PrintWriter output, final String runtimeExceptionType, @@ -109,13 +109,13 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { final boolean eraseBufferAndArrayTypes, final boolean useNIOOnly, final boolean useNIODirectOnly, - final boolean forImplementingMethodCall, final boolean forDirectBufferImplementation, final boolean forIndirectBufferAndArrayImplementation, final boolean isUnimplemented, final boolean isInterface, - final JavaConfiguration configuration) { - super(output, isInterface); + final boolean isNativeMethod, + final boolean isPrivateNativeMethod, final JavaConfiguration configuration) { + super(output, isInterface, configuration); this.binding = binding; this.runtimeExceptionType = runtimeExceptionType; this.unsupportedExceptionType = unsupportedExceptionType; @@ -124,16 +124,17 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { this.eraseBufferAndArrayTypes = eraseBufferAndArrayTypes; this.useNIOOnly = useNIOOnly; this.useNIODirectOnly = useNIODirectOnly; - this.forImplementingMethodCall = forImplementingMethodCall; this.forDirectBufferImplementation = forDirectBufferImplementation; this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; this.isUnimplemented = isUnimplemented; - if (forImplementingMethodCall) { + this.isNativeMethod = isNativeMethod; + this.isPrivateNativeMethod = isPrivateNativeMethod; + if (isPrivateNativeMethod) { setCommentEmitter(defaultJavaCommentEmitter); } else { setCommentEmitter(defaultInterfaceCommentEmitter); } - cfg = configuration; + // !forImplementingMethodCall && !isInterface } public JavaMethodBindingEmitter(final JavaMethodBindingEmitter arg) { @@ -146,7 +147,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { eraseBufferAndArrayTypes = arg.eraseBufferAndArrayTypes; useNIOOnly = arg.useNIOOnly; useNIODirectOnly = arg.useNIODirectOnly; - forImplementingMethodCall = arg.forImplementingMethodCall; + isNativeMethod = arg.isNativeMethod; + isPrivateNativeMethod = arg.isPrivateNativeMethod; forDirectBufferImplementation = arg.forDirectBufferImplementation; forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation; isUnimplemented = arg.isUnimplemented; @@ -154,18 +156,31 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { epilogue = arg.epilogue; returnedArrayLengthExpression = arg.returnedArrayLengthExpression; returnedArrayLengthExpressionOnlyForComments = arg.returnedArrayLengthExpressionOnlyForComments; - cfg = arg.cfg; } public final MethodBinding getBinding() { return binding; } - public boolean isForImplementingMethodCall() { return forImplementingMethodCall; } + public boolean isNativeMethod() { return isNativeMethod; } + public boolean isPrivateNativeMethod() { return isPrivateNativeMethod; } public boolean isForDirectBufferImplementation() { return forDirectBufferImplementation; } public boolean isForIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } @Override - public String getName() { - return binding.getName(); + public String getInterfaceName() { + return binding.getInterfaceName(); + } + @Override + public String getImplName() { + return binding.getImplName(); + } + @Override + public String getNativeName() { + return binding.getNativeName(); + } + + @Override + public FunctionSymbol getCSymbol() { + return binding.getCSymbol(); } protected String getArgumentName(final int i) { @@ -233,8 +248,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } /** Accessor for subclasses. */ - public void setForImplementingMethodCall(final boolean impl) { - this.forImplementingMethodCall = impl; + public void setPrivateNativeMethod(final boolean v) { + this.isPrivateNativeMethod = v; } /** Accessor for subclasses. */ @@ -322,10 +337,12 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { @Override protected void emitName(final PrintWriter writer) { - if (forImplementingMethodCall) { - writer.print(getImplMethodName()); + if (isPrivateNativeMethod) { + writer.print(getNativeImplMethodName()); + } else if( isInterface()) { + writer.print(getInterfaceName()); } else { - writer.print(getName()); + writer.print(getImplName()); } } @@ -334,7 +351,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { boolean needComma = false; int numEmitted = 0; - if (forImplementingMethodCall && binding.hasContainingType()) { + if (isPrivateNativeMethod && binding.hasContainingType()) { // Always emit outgoing "this" argument writer.print("ByteBuffer "); writer.print(javaThisArgumentName()); @@ -395,8 +412,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } - protected String getImplMethodName() { - return binding.getName() + ( useNIODirectOnly ? "0" : "1" ); + protected String getNativeImplMethodName() { + return binding.getImplName() + ( useNIODirectOnly ? "0" : "1" ); } protected String byteOffsetArgName(final int i) { @@ -544,7 +561,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } protected void emitCall(final MethodBinding binding, final PrintWriter writer) { - writer.print(getImplMethodName()); + writer.print(getNativeImplMethodName()); writer.print("("); emitCallArguments(binding, writer); writer.print(");"); @@ -675,9 +692,10 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else if(type.isIntArray()) { writer.print(", Buffers.SIZEOF_INT * "); } else { - throw new RuntimeException("Unsupported type for calculating array offset argument for " + + throw new GlueGenException("Unsupported type for calculating array offset argument for " + getArgumentName(i) + - " -- error occurred while processing Java glue code for " + getName()); + " -- error occurred while processing Java glue code for " + getCSymbol().getAliasedString(), + getCSymbol().getASTLocusTag()); } writer.print(offsetArgName(i)); } @@ -688,7 +706,8 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } } else if (type.isPrimitiveArray()) { if (useNIOOnly) { - throw new RuntimeException("NIO[Direct]Only "+binding+" is set, but "+getArgumentName(i)+" is a primitive array"); + throw new GlueGenException("NIO[Direct]Only "+binding+" is set, but "+getArgumentName(i)+" is a primitive array", + getCSymbol().getASTLocusTag()); } writer.print( ", false"); } @@ -706,7 +725,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { // ByteBuffers back into the wrapper types for (int i = 0; i < binding.getNumArguments(); i++) { final JavaType javaArgType = binding.getJavaArgumentType(i); - if ( javaArgType.isArrayOfCompoundTypeWrappers() && !isBaseTypeConst(javaArgType.getElementCType()) ) { + if ( javaArgType.isArrayOfCompoundTypeWrappers() && !javaArgType.getElementCType().isBaseTypeConst() ) { final String argName = binding.getArgumentName(i); writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); writer.println(" if ((" + argName + "[_ctr] == null && " + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr] == null) ||"); @@ -743,8 +762,9 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } else if (returnType.isNIOLongBuffer()) { writer.println(" return _res.asLongBuffer();"); } else { - throw new RuntimeException("While emitting glue code for " + getName() + - ": can not legally make pointers opaque to anything but PointerBuffer or LongBuffer/long"); + throw new GlueGenException("While emitting glue code for " + getCSymbol().getAliasedString() + + ": can not legally make pointers opaque to anything but PointerBuffer or LongBuffer/long", + getCSymbol().getASTLocusTag()); } } else if (getBinding().getCReturnType().pointerDepth() == 1 && returnType.isNIOLongBuffer()) { writer.println(" return _res.asLongBuffer();"); @@ -812,6 +832,26 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { * emitter java method. */ protected class DefaultCommentEmitter implements CommentEmitter { + protected void emitAliasedDocNamesComment(final AliasedSymbol sym, final PrintWriter writer) { + writer.print(emitAliasedDocNamesComment(sym, new StringBuilder()).toString()); + } + protected StringBuilder emitAliasedDocNamesComment(final AliasedSymbol sym, final StringBuilder sb) { + final Set<String> aliases = cfg.getAliasedDocNames(sym); + if (aliases != null && aliases.size() > 0 ) { + int i=0; + sb.append("Alias for: <code>"); + for (final String alias : aliases) { + if(0 < i) { + sb.append("</code>, <code>"); + } + sb.append(alias); + i++; + } + sb.append("</code>"); + } + return sb; + } + @Override public void emit(final FunctionEmitter emitter, final PrintWriter writer) { emitBeginning(emitter, writer); @@ -826,9 +866,11 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { writer.print("Entry point to C language function: "); } protected void emitBindingCSignature(final MethodBinding binding, final PrintWriter writer) { - writer.print("<code> "); - writer.print(binding.getCSymbol().toString(tagNativeBinding)); - writer.print(" </code> "); + final FunctionSymbol funcSym = binding.getCSymbol(); + writer.print("<code>"); + writer.print(funcSym.toString(tagNativeBinding)); + writer.print("</code><br>"); + emitAliasedDocNamesComment(funcSym, writer); } protected void emitEnding(final FunctionEmitter emitter, final PrintWriter writer) { // If argument type is a named enum, then emit a comment detailing the @@ -852,7 +894,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { writer.print(" valid values are: <code>"); for (int j = 0; j < enumType.getNumEnumerates(); ++j) { if (j>0) writer.print(", "); - writer.print(enumType.getEnumName(j)); + writer.print(enumType.getEnum(j).getName()); } writer.println("</code>"); } else if (javaType.isNIOBuffer()) { diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java index 87804bd..9bcd663 100644 --- a/src/java/com/jogamp/gluegen/JavaType.java +++ b/src/java/com/jogamp/gluegen/JavaType.java @@ -63,6 +63,7 @@ public class JavaType { private final String structName; // Types we're generating glue code for (i.e., C structs) private final Type elementType; // Element type if this JavaType represents a C array private final C_PTR primitivePointerType; + private final boolean opaqued; private static JavaType nioBufferType; private static JavaType nioByteBufferType; @@ -107,12 +108,20 @@ public class JavaType { return elementType; } + /** Creates a JavaType corresponding to the given opaque Java type. This + can be used to represent arrays of primitive values or Strings; + the emitters understand how to perform proper conversion from + the corresponding C type. */ + public static JavaType createForOpaqueClass(final Class<?> clazz) { + return new JavaType(clazz, true); + } + /** Creates a JavaType corresponding to the given Java type. This can be used to represent arrays of primitive values or Strings; the emitters understand how to perform proper conversion from the corresponding C type. */ public static JavaType createForClass(final Class<?> clazz) { - return new JavaType(clazz); + return new JavaType(clazz, false); } /** Creates a JavaType corresponding to the specified C CompoundType @@ -336,6 +345,8 @@ public class JavaType { return "jobject"; } + public boolean isOpaqued() { return opaqued; } + public boolean isNIOBuffer() { return clazz != null && ( java.nio.Buffer.class.isAssignableFrom(clazz) || com.jogamp.common.nio.NativeBuffer.class.isAssignableFrom(clazz)) ; @@ -528,34 +539,39 @@ public class JavaType { append(sb, "primitivePointerType = "+primitivePointerType, prepComma); prepComma=true; } append(sb, "is[", prepComma); prepComma=false; - if( isArray() ) { - append(sb, "array", prepComma); prepComma=true; - } - if( isArrayOfCompoundTypeWrappers() ) { - append(sb, "compoundArray", prepComma); prepComma=true; - } - if( isCompoundTypeWrapper() ) { - append(sb, "compound", prepComma); prepComma=true; - } - if( isArray() ) { - append(sb, "array", prepComma); prepComma=true; - } - if( isPrimitive() ) { - append(sb, "primitive", prepComma); prepComma=true; - } - if( isPrimitiveArray() ) { - append(sb, "primitiveArray", prepComma); prepComma=true; - } - if( isNIOBuffer() ) { - append(sb, "nioBuffer", prepComma); prepComma=true; - } - if( isNIOBufferArray() ) { - append(sb, "nioBufferArray", prepComma); prepComma=true; - } - if( isCPrimitivePointerType() ) { - append(sb, "C-Primitive-Pointer", prepComma); prepComma=true; + { + if( isOpaqued() ) { + append(sb, "opaque", prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array", prepComma); prepComma=true; + } + if( isArrayOfCompoundTypeWrappers() ) { + append(sb, "compoundArray", prepComma); prepComma=true; + } + if( isCompoundTypeWrapper() ) { + append(sb, "compound", prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array", prepComma); prepComma=true; + } + if( isPrimitive() ) { + append(sb, "primitive", prepComma); prepComma=true; + } + if( isPrimitiveArray() ) { + append(sb, "primitiveArray", prepComma); prepComma=true; + } + if( isNIOBuffer() ) { + append(sb, "nioBuffer", prepComma); prepComma=true; + } + if( isNIOBufferArray() ) { + append(sb, "nioBufferArray", prepComma); prepComma=true; + } + if( isCPrimitivePointerType() ) { + append(sb, "C-Primitive-Pointer", prepComma); prepComma=true; + } } - append(sb, "descriptor '"+getDescriptor()+"'", prepComma); prepComma=true; + append(sb, "], descriptor '"+getDescriptor()+"']", prepComma); prepComma=true; return sb.toString(); } @@ -563,11 +579,12 @@ public class JavaType { * Constructs a representation for a type corresponding to the given Class * argument. */ - private JavaType(final Class<?> clazz) { + private JavaType(final Class<?> clazz, final boolean opaqued) { this.primitivePointerType = null; this.clazz = clazz; this.structName = null; this.elementType = null; + this.opaqued = opaqued; } /** Constructs a type representing a named C struct. */ @@ -576,6 +593,7 @@ public class JavaType { this.clazz = null; this.structName = structName; this.elementType = null; + this.opaqued = false; } /** Constructs a type representing a pointer to a C primitive @@ -585,6 +603,7 @@ public class JavaType { this.clazz = null; this.structName = null; this.elementType = null; + this.opaqued = false; } /** Constructs a type representing an array of C pointers. */ @@ -593,6 +612,7 @@ public class JavaType { this.clazz = null; this.structName = null; this.elementType = elementType; + this.opaqued = false; } /** clone only */ @@ -601,6 +621,7 @@ public class JavaType { this.clazz = clazz; this.structName = name; this.elementType = elementType; + this.opaqued = false; } private String arrayName(Class<?> clazz) { diff --git a/src/java/com/jogamp/gluegen/Logging.java b/src/java/com/jogamp/gluegen/Logging.java index 40eadcb..c057db4 100644 --- a/src/java/com/jogamp/gluegen/Logging.java +++ b/src/java/com/jogamp/gluegen/Logging.java @@ -31,57 +31,352 @@ */ package com.jogamp.gluegen; +import java.util.HashMap; +import java.util.Map; import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; +import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; +import jogamp.common.Debug; + import com.jogamp.common.util.PropertyAccess; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.Type; /** * - * @author Michael Bien + * @author Michael Bien, et.al. */ public class Logging { + public static final boolean DEBUG = Debug.debug("Logging"); + + /** + * An interface for {@link Logger}. + */ + public static interface LoggerIf { + /** + * See {@link Logger#info(String)} + */ + void info(final String msg); + /** + * See {@link Logger#info(String)} + */ + void info(final ASTLocusTag loc, final String msg); + + /** + * See {@link Logger#warning(String)} + */ + void warning(final String msg); + /** + * See {@link Logger#warning(String)} + */ + void warning(final ASTLocusTag loc, final String msg); + + /** + * Calls {@link #log(Level, String)} w/ {@link Level#FINE}. + */ + void debug(final String msg); + /** + * Calls {@link #log(Level, ASTLocusTag, String)} w/ {@link Level#FINE}. + */ + void debug(final ASTLocusTag loc, final String msg); + + /** + * See {@link Logger#log(Level, String)} + */ + void log(final Level level, final String msg); + /** + * See {@link Logger#log(Level, String, Object)} + */ + void log(final Level level, final String msg, final Object param); + /** + * See {@link Logger#log(Level, String, Object[])} + */ + void log(final Level level, final String msg, final Object ... params); + + /** + * See {@link Logger#log(Level, String)} + */ + void log(final Level level, final ASTLocusTag loc, final String msg); + /** + * See {@link Logger#log(Level, String, Object)} + */ + void log(final Level level, final ASTLocusTag loc, final String msg, final Object param); + /** + * See {@link Logger#log(Level, String, Object[])} + */ + void log(final Level level, final ASTLocusTag loc, final String msg, final Object ... params); + + /** + * See {@link Logger#setLevel(Level)} + */ + void setLevel(final Level newLevel) throws SecurityException; + /** + * See {@link Handler#setLevel(Level)} + */ + void setLevelOfAllHandler(final Level newLevel) throws SecurityException; + /** + * See {@link Logger#getLevel()} + */ + Level getLevel(); + /** + * See {@link Logger#isLoggable(Level)} + */ + boolean isLoggable(Level level); + /** + * See {@link Logger#getName()} + */ + String getName(); + /** + * See {@link Logger#getHandlers()} + */ + Handler[] getHandlers(); + /** + * See {@link LogRecord#getSourceClassName()} + */ + String getSourceClassName(); + } + /* pp */ static class FQNLogger implements LoggerIf { + public final Logger impl; + public final PlainLogConsoleHandler handler; + /* pp */ FQNLogger(final String fqnClassName, final String simpleClassName, final Level level) { + this.impl = Logger.getLogger(fqnClassName); + this.handler = new PlainLogConsoleHandler(new PlainLogFormatter(simpleClassName), Level.ALL); + this.impl.setUseParentHandlers(false); + this.impl.setLevel(level); + this.impl.addHandler(this.handler); + this.impl.log(Level.INFO, "Logging.new: "+impl.getName()+": level "+level+ + ": obj 0x"+Integer.toHexString(impl.hashCode())); + } + @Override + public void info(final String msg) { + impl.info(msg); + } + @Override + public void info(final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.info(msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void warning(final String msg) { + impl.warning(msg); + } + @Override + public void warning(final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.warning(msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void debug(final String msg) { + log(Level.FINE, msg); + } + @Override + public void debug(final ASTLocusTag loc, final String msg) { + log(Level.FINE, loc, msg); + } + + @Override + public void log(final Level level, final String msg) { + impl.log(level, msg); + } + @Override + public void log(final Level level, final String msg, final Object param) { + impl.log(level, msg, param); + } + @Override + public void log(final Level level, final String msg, final Object ... params) { + impl.log(level, msg, params); + } - static void init() { + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg); + } finally { + handler.plf.setASTLocusTag(null); + } + } + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg, final Object param) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg, param); + } finally { + handler.plf.setASTLocusTag(null); + } + } + @Override + public void log(final Level level, final ASTLocusTag loc, final String msg, final Object ... params) { + handler.plf.setASTLocusTag(loc); + try { + impl.log(level, msg, params); + } finally { + handler.plf.setASTLocusTag(null); + } + } + + @Override + public void setLevel(final Level newLevel) throws SecurityException { + impl.setLevel(newLevel); + } + @Override + public void setLevelOfAllHandler(final Level newLevel) throws SecurityException { + final Handler[] hs = getHandlers(); + for(final Handler h:hs) { + h.setLevel(newLevel); + } + } + @Override + public Level getLevel() { + return impl.getLevel(); + } + @Override + public boolean isLoggable(final Level level) { + return impl.isLoggable(level); + } + @Override + public String getName() { + return impl.getName(); + } + @Override + public synchronized Handler[] getHandlers() { + return impl.getHandlers(); + } + @Override + public String getSourceClassName() { + return handler.plf.simpleClassName; + } + } + static class PlainLogConsoleHandler extends ConsoleHandler { + final PlainLogFormatter plf; + PlainLogConsoleHandler(final PlainLogFormatter plf, final Level level) { + this.plf = plf; + setFormatter(plf); + setLevel(level); + } + @Override + public java.util.logging.Formatter getFormatter() { + return plf; + } + } + static class PlainLogFormatter extends Formatter { + final String simpleClassName; + ASTLocusTag astLocus; + PlainLogFormatter(final String simpleClassName) { + this.simpleClassName = simpleClassName; + } + public void setASTLocusTag(final ASTLocusTag loc) { astLocus = loc; } + @Override + public String format(final LogRecord record) { + // Replace [Type, JavaType] -> its debug string! + final Object[] params = record.getParameters(); + if( null != params ) { + for(int i=params.length-1; 0<=i; i--) { + final Object o = params[i]; + if( o instanceof Type ) { + params[i] = ((Type)o).getDebugString(); + } else if( o instanceof JavaType ) { + params[i] = ((JavaType)o).getDebugString(); + } else if( o instanceof AliasedSymbol ) { + params[i] = ((AliasedSymbol)o).getAliasedString(); + } + } + } + final StringBuilder sb = new StringBuilder(256); + if( null != astLocus ) { + astLocus.toString(sb, getCanonicalName(record.getLevel()), GlueGen.debug()).append(": "); + } + if( GlueGen.debug() ) { + sb.append(simpleClassName).append(": "); + } + sb.append(formatMessage(record)).append("\n"); + return sb.toString(); + } + } + + private final static Map<String, LoggerIf> loggers; + private final static FQNLogger rootPackageLogger; + static { + loggers = new HashMap<String, LoggerIf>(); final String packageName = Logging.class.getPackage().getName(); final String property = PropertyAccess.getProperty(packageName+".level", true); Level level; if(property != null) { level = Level.parse(property); } else { - level = Level.WARNING; + if( DEBUG || GlueGen.debug() ) { + level = Level.ALL; + } else { + level = Level.WARNING; + } } + final String simpleClassName = Logging.class.getSimpleName(); + final String fqnClassName = packageName+"."+simpleClassName; + rootPackageLogger = new FQNLogger(fqnClassName, simpleClassName, level); + loggers.put(fqnClassName, rootPackageLogger); + } - final ConsoleHandler handler = new ConsoleHandler() { - @Override - public java.util.logging.Formatter getFormatter() { - return new PlainLogFormatter(); - } - }; - handler.setFormatter(new PlainLogFormatter()); - handler.setLevel(level); - - final Logger rootPackageLogger = Logger.getLogger(packageName); - rootPackageLogger.setUseParentHandlers(false); - rootPackageLogger.setLevel(level); - rootPackageLogger.addHandler(handler); + /** provokes static initialization */ + static void init() { } + + public static String getCanonicalName(final Level level) { + if( Level.CONFIG == level ) { + return "config"; + } else if( Level.FINER == level ) { + return "verbose"; + } else if( Level.FINE == level ) { + return "debug"; + } else if( Level.INFO == level ) { + return "info"; + } else if( Level.WARNING == level ) { + return "warning"; + } else if( Level.SEVERE == level ) { + return "error"; + } else { + return level.getName().toLowerCase(); + } } - /** - * This log formatter needs usually one line per log record. - * @author Michael Bien - */ - private static class PlainLogFormatter extends Formatter { + /** Returns the <i>root package logger</i>. */ + public static LoggerIf getLogger() { + return rootPackageLogger; + } + /** Returns the demanded logger, while aligning its log-level to the root logger's level. */ + public static synchronized LoggerIf getLogger(final Class<?> clazz) { + return getLogger(clazz.getPackage().getName(), clazz.getSimpleName()); + } - @Override - public String format(final LogRecord record) { - final StringBuilder sb = new StringBuilder(128); - sb.append("[").append(record.getLevel()).append(' ').append(record.getSourceClassName()).append("]: "); - sb.append(formatMessage(record)).append("\n"); - return sb.toString(); + /** Returns the demanded logger, while aligning its log-level to the root logger's level. */ + public static synchronized LoggerIf getLogger(final String packageName, final String simpleClassName) { + final String fqnClassName = packageName+"."+simpleClassName; + LoggerIf res = loggers.get(fqnClassName); + if( null == res ) { + res = new FQNLogger(fqnClassName, simpleClassName, rootPackageLogger.getLevel()); + loggers.put(fqnClassName, res); } + return res; + } + /** Align log-level of given logger to the root logger's level. */ + public static void alignLevel(final LoggerIf l) { + alignLevel(l, rootPackageLogger.getLevel()); + } + /** Align log-level of given logger and all its handlers to the given level. */ + public static void alignLevel(final LoggerIf l, final Level level) { + l.setLevel(level); + l.setLevelOfAllHandler(level); } } diff --git a/src/java/com/jogamp/gluegen/MethodBinding.java b/src/java/com/jogamp/gluegen/MethodBinding.java index 93c55d5..95a10c6 100644 --- a/src/java/com/jogamp/gluegen/MethodBinding.java +++ b/src/java/com/jogamp/gluegen/MethodBinding.java @@ -43,8 +43,6 @@ import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; import java.util.List; /** Represents the binding of a C function to a Java method. Also used @@ -54,8 +52,10 @@ import java.util.List; public class MethodBinding { private final FunctionSymbol sym; - private String renamedMethodName; - private final HashSet<String> aliasedNames; + private final String delegationImplName; + private final JavaType containingType; + private final Type containingCType; + private String nativeName; private JavaType javaReturnType; private List<JavaType> javaArgumentTypes; private boolean computedSignatureProperties; @@ -69,8 +69,6 @@ public class MethodBinding { private boolean signatureUsesCArrays; private boolean signatureUsesJavaPrimitiveArrays; private boolean signatureRequiresStaticInitialization; - private JavaType containingType; - private Type containingCType; private int thisPointerIndex = -1; /** @@ -79,12 +77,12 @@ public class MethodBinding { * types. It's safe to modify this binding after construction. */ public MethodBinding(final MethodBinding bindingToCopy) { - this.sym = bindingToCopy.sym; - - this.renamedMethodName = bindingToCopy.renamedMethodName; - this.aliasedNames = new HashSet<String>(bindingToCopy.aliasedNames); + this.sym = bindingToCopy.sym; + this.delegationImplName = bindingToCopy.delegationImplName; this.containingType = bindingToCopy.containingType; this.containingCType = bindingToCopy.containingCType; + + this.nativeName = bindingToCopy.nativeName; this.javaReturnType = bindingToCopy.javaReturnType; this.javaArgumentTypes = ( null != bindingToCopy.javaArgumentTypes ) ? new ArrayList<JavaType>(bindingToCopy.javaArgumentTypes) : null; this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; @@ -101,19 +99,27 @@ public class MethodBinding { this.thisPointerIndex = bindingToCopy.thisPointerIndex; } - /** Constructor for calling a C function. */ - public MethodBinding(final FunctionSymbol sym) { - this.sym = sym; - this.aliasedNames = new HashSet<String>(); - } - - /** Constructor for calling a function pointer contained in a - struct. */ - public MethodBinding(final FunctionSymbol sym, final JavaType containingType, final Type containingCType) { + /** + * Constructor for calling a C function or a function pointer contained in a struct. + * <p> + * In case of the latter, a struct function pointer, + * the arguments {@code containingType} and {@code containingCType} must not be {@code null}! + * </p> + */ + public MethodBinding(final FunctionSymbol sym, + final String delegationImplName, + final JavaType javaReturnType, + final List<JavaType> javaArgumentTypes, + final JavaType containingType, + final Type containingCType) { this.sym = sym; + this.delegationImplName = delegationImplName; this.containingType = containingType; this.containingCType = containingCType; - this.aliasedNames = new HashSet<String>(); + + this.nativeName = null; + this.javaReturnType = javaReturnType; + this.javaArgumentTypes = javaArgumentTypes; } public void setJavaReturnType(final JavaType type) { @@ -149,6 +155,7 @@ public class MethodBinding { return sym.getArgumentType(i); } + /** Returns the {@link FunctionSymbol}. */ public FunctionSymbol getCSymbol() { return sym; } @@ -166,33 +173,42 @@ public class MethodBinding { return "arg" + i; } - public String getOrigName() { - return sym.getName(); - } - + /** Returns the {@link FunctionSymbol}'s current {@link FunctionSymbol#getName() aliased} API name. */ public String getName() { - // Defaults to same as C symbol unless renamed - if (renamedMethodName != null) { - return renamedMethodName; - } return sym.getName(); } - - /** Supports renaming C function in Java binding. */ - public void renameMethodName(final String name) { - if (null != name) { - renamedMethodName = name; - aliasedNames.add(sym.getName()); - } - } - - public void addAliasedName(final String name) { - aliasedNames.add(name); + /** + * The + * {@link JavaConfiguration#getDelegatedImplementation(com.jogamp.gluegen.cgram.types.AliasedSymbol) implementation delegation} + * name, or {@code null} for no delegation. + * @see #getImplName() + */ + public String getDelegationImplName() { + return delegationImplName; } - public Collection<String> getAliasedNames() { - return aliasedNames; + /** Returns the {@link FunctionSymbol}'s current {@link FunctionSymbol#getName() aliased} API name for the interface. */ + public String getInterfaceName() { + return sym.getName(); } + /** + * Returns the {@link FunctionSymbol}'s name for the implementation, + * which is the current {@link FunctionSymbol#getName() aliased} API name per default, + * or the {@link #getDelegationImplName() delegation} name. + * @see #getDelegationImplName() + */ + public String getImplName() { + return null != delegationImplName ? delegationImplName : sym.getName(); + } + /** + * Returns the {@link FunctionSymbol}'s name for the native function + * which is the {@link FunctionSymbol#getOrigName() original} C API name per default, + * but may be overridden via {@link #setNativeName(String)}. + */ + public String getNativeName() { + return null != nativeName ? nativeName : sym.getOrigName(); + } + public void setNativeName(final String s) { nativeName = s; } /** Creates a new MethodBinding replacing the specified Java argument type with a new argument type. If argumentNumber is diff --git a/src/java/com/jogamp/gluegen/ReferencedStructs.java b/src/java/com/jogamp/gluegen/ReferencedStructs.java index d06d47f..26deb3a 100644 --- a/src/java/com/jogamp/gluegen/ReferencedStructs.java +++ b/src/java/com/jogamp/gluegen/ReferencedStructs.java @@ -44,31 +44,43 @@ import com.jogamp.gluegen.cgram.types.*; public class ReferencedStructs implements TypeVisitor { - private final Set<Type> results = new HashSet<Type>(); + private final Map<String, Type> resultMap = new HashMap<String, Type>(); + private final Set<CompoundType> layoutSet = new HashSet<CompoundType>(); + private final Set<Type> skip = new HashSet<Type>(); - public void clear() { - results.clear(); - } + public void clear() { + resultMap.clear(); + } - public Iterator<Type> results() { - return results.iterator(); - } + public Iterator<Type> results() { + return resultMap.values().iterator(); + } + public Iterator<CompoundType> layouts() { + return layoutSet.iterator(); + } - @Override - public void visitType(final Type t) { - if (t.isPointer()) { - final PointerType p = t.asPointer(); - if (p.hasTypedefedName()) { - final CompoundType c = p.getTargetType().asCompound(); - if (c != null && c.getName() == null) { - // This otherwise-unnamed CompoundType is referred to by a - // PointerType that has a typedef name. Assume that it is - // referred to in the glue code and emit it. - results.add(p); + @Override + public void visitType(final Type t) { + if( skip.contains(t) ) { + return; + } + if ( t.isPointer() ) { + final PointerType p = t.asPointer(); + final CompoundType c = p.getTargetType().asCompound(); + if( p.isTypedef() && null != c ) { + // If containing pointer is typedef, use it (preferred) + skip.add(c); // earmark to skip the compound! + resultMap.put(c.getName(), p); + layoutSet.add(c); + } else { + // .. otherwise skip pointer and use followup compound + } + } else if( t.isCompound() ) { + // Use compound if not yet mapped, e.g. by typedef'ed (preferred) + if( !resultMap.containsKey(t.getName()) ) { + resultMap.put(t.getName(), t); + } + layoutSet.add(t.asCompound()); // always: could be const/volatile variants .. } - } - } else if (t.isCompound()) { - results.add(t); } - } } diff --git a/src/java/com/jogamp/gluegen/TypeConfig.java b/src/java/com/jogamp/gluegen/TypeConfig.java new file mode 100644 index 0000000..5f389f4 --- /dev/null +++ b/src/java/com/jogamp/gluegen/TypeConfig.java @@ -0,0 +1,52 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen; + +import com.jogamp.gluegen.cgram.types.SizeThunk; +import com.jogamp.gluegen.cgram.types.Type; + +/** + * Static {@link Type} config helper + * binding {@link JavaConfiguration#relaxedEqualSemanticsTest()} system wide. + */ +public class TypeConfig { + private static boolean relaxedEqualSemanticsTest = false; + + /** + * Returns whether {@link TypeConfig.SemanticEqualityOp#equalSemantics(TypeConfig.SemanticEqualityOp)} + * shall attempt to perform a relaxed semantic equality test, e.g. skip the {@code const} and {@code volatile} qualifier + * - or not. + */ + public static boolean relaxedEqualSemanticsTest() { + return relaxedEqualSemanticsTest; + } + /* pp */ static void setRelaxedEqualSemanticsTest(final boolean v) { + relaxedEqualSemanticsTest = v; + SizeThunk.setRelaxedEqualSemanticsTest(v); + } +} diff --git a/src/java/com/jogamp/gluegen/TypeInfo.java b/src/java/com/jogamp/gluegen/TypeInfo.java index d89ac79..52fdc04 100644 --- a/src/java/com/jogamp/gluegen/TypeInfo.java +++ b/src/java/com/jogamp/gluegen/TypeInfo.java @@ -66,7 +66,7 @@ public class TypeInfo { buf.append(name); buf.append(" pointerDepth "); buf.append(pointerDepth); - buf.append(" JavaType " + javaType); + buf.append(" JavaType " + javaType.getDebugString()); return buf.toString(); } } diff --git a/src/java/com/jogamp/gluegen/ant/GlueGenTask.java b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java index dd57365..2b11d3f 100644 --- a/src/java/com/jogamp/gluegen/ant/GlueGenTask.java +++ b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java @@ -73,7 +73,8 @@ import org.apache.tools.ant.util.JavaEnvUtils; emitter="[emitter class name]" config="[configuration file]" dumpCPP="[optional boolean]" - debug="[optional boolean]" /> + debug="[optional boolean]" + logLevel="[optional string]" /> * </pre> * * @author Rob Grzywinski <a href="mailto:[email protected]">[email protected]</a> @@ -101,6 +102,11 @@ public class GlueGenTask extends Task private boolean debug=false; /** + * <p>The optional logLevel.</p> + */ + private String logLevel = null; + + /** * <p>The optional dumpCPP flag.</p> */ private boolean dumpCPP=false; @@ -182,6 +188,15 @@ public class GlueGenTask extends Task } /** + * <p>Set the logLevel (optional). This is called by ANT.</p> + */ + public void setLogLevel(final String logLevel) + { + log( ("Setting logLevel: " + logLevel), Project.MSG_VERBOSE); + this.logLevel=logLevel; + } + + /** * <p>Set the dumpCPP flag (optional). This is called by ANT.</p> */ public void setDumpCPP(final boolean dumpCPP) @@ -456,6 +471,12 @@ public void setIncludeRefid(final Reference reference) { gluegenCommandline.createArgument().setValue("--debug"); } + // add the logLevel if enabled + if(null != logLevel) { + gluegenCommandline.createArgument().setValue("--logLevel"); + gluegenCommandline.createArgument().setValue(logLevel); + } + // add the debug flag if enabled if(dumpCPP) { gluegenCommandline.createArgument().setValue("--dumpCPP"); diff --git a/src/java/com/jogamp/gluegen/cgram/Define.java b/src/java/com/jogamp/gluegen/cgram/Define.java index 797cf6f..23caabd 100644 --- a/src/java/com/jogamp/gluegen/cgram/Define.java +++ b/src/java/com/jogamp/gluegen/cgram/Define.java @@ -39,18 +39,32 @@ package com.jogamp.gluegen.cgram; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; + /** Represents a #define of a literal to a value (a number represented in string form.) */ -public class Define { +public class Define implements ASTLocusTagProvider { private final String name; private final String value; + private final ASTLocusTag astLocus; public Define(final String name, final String value) { this.name = name; this.value = value; + this.astLocus = null; + } + + public Define(final String name, final String value, final ASTLocusTag astLocus) { + this.name = name; + this.value = value; + this.astLocus = astLocus; } public String getName() { return name; } public String getValue() { return value; } + + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } } diff --git a/src/java/com/jogamp/gluegen/cgram/TNode.java b/src/java/com/jogamp/gluegen/cgram/TNode.java index a564c54..5a36945 100644 --- a/src/java/com/jogamp/gluegen/cgram/TNode.java +++ b/src/java/com/jogamp/gluegen/cgram/TNode.java @@ -3,10 +3,15 @@ package com.jogamp.gluegen.cgram; import antlr.collections.AST; import antlr.CommonAST; import antlr.Token; + import java.lang.reflect.*; import java.util.Hashtable; import java.util.Enumeration; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.GlueGen; + /** Class TNode is an implementation of the AST interface and adds many useful features: @@ -29,7 +34,8 @@ import java.util.Enumeration; */ -public class TNode extends CommonAST { +@SuppressWarnings("serial") +public class TNode extends CommonAST implements ASTLocusTagProvider { protected int ttype; protected String text; protected int lineNum = 0; @@ -40,7 +46,22 @@ public class TNode extends CommonAST { protected Hashtable<String, Object> attributes = null; static String tokenVocabulary; - + /** + * {@inheritDoc} + * <p> + * If <i>source</i> is not available, + * implementation returns {@code null}. + * </p> + */ + @Override + public ASTLocusTag getASTLocusTag() { + final Object s = getAttribute("source"); + if( null != s ) { + return new ASTLocusTag(s, getLineNum(), -1, getText()); + } else { + return null; + } + } /** Set the token vocabulary to a tokentypes class @@ -159,15 +180,87 @@ public void initialize(final AST tr) { text = text_; } - /** Returns the text for this node and all children */ - public String getAllChildrenText() { + static class DebugASTVisitor { + protected int level; + private String tabs(final StringBuilder sb) { + sb.setLength(0); + for (int i = 0; i < level; i++) { + sb.append(" "); + } + return sb.toString(); + } + DebugASTVisitor(final int level) { + this.level = level; + } + void visit(final AST node) { + final StringBuilder sb = new StringBuilder(); + AST node2; + for (node2 = node; node2 != null; node2 = node2.getNextSibling()) { + if (node2.getText() == null) { + System.err.printf("%03d: %snil [%d]%n", level, tabs(sb), node2.getType()); + } else { + System.err.printf("%03d: %s%s [%d]%n", level, tabs(sb), node2.getText(), node2.getType()); + } + if (node2.getFirstChild() != null) { + level++; + visit(node2.getFirstChild()); + level--; + } + } + } + } + + /** + * Returns the text for this node, its children and siblings. + * <p> + * Implementation converts the AST LISP notation to serialized form. + * </p> + */ + public String getAllChildrenText(final String name) { + if( GlueGen.debug() ) { + System.err.println("TNode.XXX: "+name); + new DebugASTVisitor(1).visit(getFirstChild()); + } final StringBuilder buf = new StringBuilder(); - buf.append(getText()); - for (TNode node = (TNode) getFirstChild(); node != null; node = (TNode) node.getNextSibling()) { - buf.append(node.getText()); + final TNode down = (TNode) this.getFirstChild(); + if( null == down ) { + buf.append(this.getText()); + } else { + getAllChildrenText(buf, this, down); } return buf.toString(); } + private static void getAllChildrenText(final StringBuilder buf, + final TNode upNode, TNode thisNode) { + boolean first = true; + while( null != thisNode ) { + final boolean isClosing = HeaderParserTokenTypes.RPAREN == thisNode.getType(); + final boolean isGroupStart = HeaderParserTokenTypes.NExpressionGroup == thisNode.getType(); + + final TNode nextNode = (TNode) thisNode.getNextSibling(); + final TNode downNode = (TNode) thisNode.getFirstChild(); + if( !isClosing && + ( null == downNode && null == nextNode || // unary + !first // binary + ) + ) { + buf.append(" ").append(upNode.getText()); + } + if( null != downNode ) { + if( !isGroupStart ) { + buf.append(" ("); + } + getAllChildrenText(buf, thisNode, downNode); + if( !isGroupStart ) { + buf.append(" )"); + } + } else if( !isClosing ) { + buf.append(" ").append(thisNode.getText()); + } + thisNode = nextNode; + first = false; + } + } /** return the last child of this node, or null if there is none */ public TNode getLastChild() { diff --git a/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java b/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java new file mode 100644 index 0000000..869c658 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/AliasedSymbol.java @@ -0,0 +1,185 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.HashSet; +import java.util.Set; + +/** + * Supports symbol aliasing, i.e. renaming, + * while preserving all its original names, i.e. aliases. + */ +public interface AliasedSymbol { + /** + * Rename this symbol with the given {@code newName} if not equal {@link #getName() current-name}. + * <p> + * Before renaming the {@link #getName() current-name} will be added + * to the list of {@link #getAliasedNames() aliases}. + * while the given {@code newName} will be removed. + * </p> + * <p> + * Operation will be ignored if {@code newName} is {@code null}. + * </p> + * @param newName the new {@link #getName() current-name}, maybe {@code null} + */ + void rename(final String newName); + /** + * Add the given {@code origName} to the list of {@link #getAliasedNames() aliases} + * if not equal {@link #getName() current-name}. + * <p> + * Operation will be ignored if {@code newName} is {@code null}. + * </p> + * @param origName the new alias to be added, maybe {@code null} + */ + void addAliasedName(final String origName); + /** + * + * Returns {@code true} if this symbol has aliases, i.e. either being {@link #rename(String) renamed} + * or {@link #addAliasedName(String) aliases-added}. + * <p> + * Otherwise {@code false} is being returned. + * </p> + */ + boolean hasAliases(); + /** + * Return all aliases for this symbol, i.e. original names, for this symbol. + * <p> + * Inclusive {@link #getOrigName() original-name}, if {@link #rename(String) renamed}, + * </p> + * <p> + * Exclusive {@link #getName() current-name}. + * </p> + * <p> + * May return {@code null} or a zero sized {@link Set} for no aliases. + * </p> + */ + Set<String> getAliasedNames(); + /** + * Return the original-name as set at creation. + */ + String getOrigName(); + /** + * Return the current-name, which is the last {@link #rename(String) renamed-name} if issued, + * or the {@link #getOrigName() original-name}. + */ + String getName(); + /** + * Return this object's {@link #toString()} wrapped w/ the {@link #getName() current-name} + * and all {@link #getAliasedNames() aliases}. + */ + String getAliasedString(); + + public static class AliasedSymbolImpl implements AliasedSymbol { + private final String origName; + private final HashSet<String> aliasedNames; + private String name; + + public AliasedSymbolImpl(final String origName) { + if( null == origName ) { + throw new IllegalArgumentException("Null origName not allowed"); + } + this.origName = origName; + this.aliasedNames=new HashSet<String>(); + this.name = origName; + } + public AliasedSymbolImpl(final AliasedSymbolImpl o) { + this.origName = o.origName; + this.aliasedNames = new HashSet<String>(o.aliasedNames); + this.name = o.name; + } + @Override + public void rename(final String newName) { + if( null != newName && !name.equals(newName) ) { + aliasedNames.add(name); + aliasedNames.remove(newName); + name = newName; + } + } + @Override + public void addAliasedName(final String origName) { + if( null != origName && !name.equals(origName) ) { + aliasedNames.add(origName); + } + } + @Override + public boolean hasAliases() { + return aliasedNames.size() > 0; + } + @Override + public Set<String> getAliasedNames() { + return aliasedNames; + } + @Override + public String getOrigName() { + return origName; + } + @Override + public String getName() { + return name; + } + @Override + public String getAliasedString() { + return "["+name+", aliases "+aliasedNames.toString()+", "+toString()+"]"; + } + } + public static class NoneAliasedSymbol implements AliasedSymbol { + private final String name; + + public NoneAliasedSymbol(final String origName) { + this.name = origName; + } + @Override + public void rename(final String newName) { + throw new UnsupportedOperationException(); + } + @Override + public void addAliasedName(final String origName) { + throw new UnsupportedOperationException(); + } + @Override + public boolean hasAliases() { + return false; + } + @Override + public Set<String> getAliasedNames() { + return null; + } + @Override + public String getOrigName() { + return name; + } + @Override + public String getName() { + return name; + } + @Override + public String getAliasedString() { + return toString(); + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java index d867b40..ada34f7 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java @@ -40,6 +40,8 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents an array type. This differs from a pointer type in C syntax by the use of "[]" rather than "*". The length may or may not be known; if the length is unknown then a negative number @@ -48,50 +50,79 @@ package com.jogamp.gluegen.cgram.types; public class ArrayType extends MemoryLayoutType implements Cloneable { private final Type elementType; private final int length; - private String computedName; - public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, final int cvAttributes) { - super(elementType.getName() + " *", sizeInBytes, cvAttributes); + public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, + final int cvAttributes) { + this(elementType, sizeInBytes, length, cvAttributes, null); + } + public ArrayType(final Type elementType, final SizeThunk sizeInBytes, final int length, + final int cvAttributes, final ASTLocusTag astLocus) { + super(elementType.getName() + " *", sizeInBytes, cvAttributes, astLocus); this.elementType = elementType; this.length = length; } + private ArrayType(final ArrayType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + elementType = o.elementType; + length = o.length; + } @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || (!(arg instanceof ArrayType))) { - return false; - } + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new ArrayType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = elementType.hashCode(); + return ((hash << 5) - hash) + length; + } + + @Override + protected boolean equalsImpl(final Type arg) { final ArrayType t = (ArrayType) arg; - return (super.equals(arg) && elementType.equals(t.elementType) && (length == t.length)); + return elementType.equals(t.elementType) && + length == t.length; } @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + final int hash = elementType.hashCodeSemantics(); + return ((hash << 5) - hash) + length; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final ArrayType t = (ArrayType) arg; + return elementType.equalSemantics(t.elementType) && + length == t.length; + } + + @Override + public boolean isAnon() { return elementType.isAnon(); } + + @Override public String getName(final boolean includeCVAttrs) { - // Lazy computation of name due to lazy setting of compound type - // names during parsing - // Note: don't think cvAttributes can be set for array types (unlike pointer types) - if (computedName == null) { - computedName = (elementType.getName() + " *").intern(); - } - return computedName; + return elementType.getName() + " *"; } @Override - public ArrayType asArray() { return this; } + public final ArrayType asArray() { return this; } public Type getElementType() { return elementType; } public int getLength() { return length; } public boolean hasLength() { return length >= 0; } @Override - public Type getBaseElementType() { - ArrayType t = this; - while (t.getElementType().isArray()) { - t = t.getElementType().asArray(); - } - return t.getElementType(); - // return elementType.getBaseElementType(); + public final Type getBaseElementType() { + return elementType.getBaseElementType(); + } + + @Override + public final int arrayDimension() { + return 1 + elementType.arrayDimension(); } /** Recompute the size of this array if necessary. This needs to be @@ -114,7 +145,7 @@ public class ArrayType extends MemoryLayoutType implements Cloneable { if(elementType.isConst()) { buf.append("const "); } - buf.append(elementType.getName()); + buf.append(elementType.getCName()); if (variableName != null) { buf.append(" "); buf.append(variableName); @@ -130,9 +161,4 @@ public class ArrayType extends MemoryLayoutType implements Cloneable { super.visit(arg); elementType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new ArrayType(elementType, getSize(), length, cvAttributes); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/BitType.java b/src/java/com/jogamp/gluegen/cgram/types/BitType.java index 2644551..834ff95 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/BitType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/BitType.java @@ -40,6 +40,8 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a bitfield in a struct. */ public class BitType extends IntType implements Cloneable { @@ -47,22 +49,60 @@ public class BitType extends IntType implements Cloneable { private final int sizeInBits; private final int offset; - public BitType(final IntType underlyingType, final int sizeInBits, final int lsbOffset, final int cvAttributes) { - super(underlyingType.getName(), underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); + public BitType(final IntType underlyingType, final int sizeInBits, final int lsbOffset, + final int cvAttributes, final ASTLocusTag astLocus) { + super(underlyingType.getName(), underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes, astLocus); this.underlyingType = underlyingType; this.sizeInBits = sizeInBits; this.offset = lsbOffset; } + private BitType(final BitType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + underlyingType = o.underlyingType; + sizeInBits = o.sizeInBits; + offset = o.offset; + } + + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new BitType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCode(); + hash = ((hash << 5) - hash) + sizeInBits; + return ((hash << 5) - hash) + offset; + } + @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || (!(arg instanceof BitType))) { - return false; - } - final BitType t = (BitType) arg; - return (super.equals(arg) && underlyingType.equals(t.underlyingType) && - (sizeInBits == t.sizeInBits) && (offset == t.offset)); + protected boolean equalsImpl(final Type arg) { + final BitType t = (BitType) arg; + return super.equalsImpl(arg) && + underlyingType.equals(t.underlyingType) && + sizeInBits == t.sizeInBits && + offset == t.offset; + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeSemanticsImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCodeSemantics(); + hash = ((hash << 5) - hash) + sizeInBits; + return ((hash << 5) - hash) + offset; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final BitType t = (BitType) arg; + return super.equalSemanticsImpl(arg) && + underlyingType.equalSemantics(t.underlyingType) && + sizeInBits == t.sizeInBits && + offset == t.offset; } @Override @@ -84,9 +124,4 @@ public class BitType extends IntType implements Cloneable { super.visit(arg); underlyingType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new BitType(underlyingType, sizeInBits, offset, cvAttributes); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java index 9716f54..56bcdda 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java @@ -42,73 +42,110 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.ASTLocusTag; + /** Models all compound types, i.e., those containing fields: structs and unions. The boolean type accessors indicate how the type is really defined. */ -public abstract class CompoundType extends MemoryLayoutType implements Cloneable { +public abstract class CompoundType extends MemoryLayoutType implements Cloneable, AliasedSymbol { // The name "foo" in the construct "struct foo { ... }"; - private String structName; + private final String structName; private ArrayList<Field> fields; private boolean visiting; private boolean bodyParsed; - private boolean computedHashcode; - private int hashcode; - CompoundType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super(name, size, cvAttributes); - this.structName = structName; + @Override + public void rename(final String newName) { + throw new UnsupportedOperationException(); } - - public static CompoundType create(final String name, final SizeThunk size, final CompoundTypeKind kind, final int cvAttributes) { + @Override + public void addAliasedName(final String origName) { + throw new UnsupportedOperationException(); + } + @Override + public boolean hasAliases() { + return false; + } + @Override + public Set<String> getAliasedNames() { + return null; + } + @Override + public String getAliasedString() { + return toString(); + } + @Override + public String getOrigName() { + return getName(); + } + /** + * @param structName struct name of this CompoundType, i.e. the "foo" in the + construct {@code struct foo { int a, ... };} or {@code struct foo;} <i>even</i> for anonymous structs. + * @param size + * @param kind + * @param cvAttributes + * @return + */ + public static CompoundType create(final String structName, final SizeThunk size, + final CompoundTypeKind kind, final int cvAttributes, + final ASTLocusTag astLocus) + { + final CompoundType res; switch (kind) { case STRUCT: - return new StructType(name, size, cvAttributes); + res = new StructType(null, size, cvAttributes, structName, astLocus); + break; case UNION: - return new UnionType(name, size, cvAttributes); + res = new UnionType(null, size, cvAttributes, structName, astLocus); + break; default: throw new RuntimeException("OO relation "+kind+" / Compount not yet supported"); } + return res; } - @Override - public Object clone() { - final CompoundType n = (CompoundType) super.clone(); - if(null!=this.fields) { - n.fields = new ArrayList<Field>(this.fields); + CompoundType(final String name, final SizeThunk size, final int cvAttributes, + final String structName, final ASTLocusTag astLocus) { + super(null == name ? structName : name, size, cvAttributes, astLocus); + this.structName = structName; + } + + CompoundType(final CompoundType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + this.structName = o.structName; + if(null != o.fields) { + fields = new ArrayList<Field>(o.fields); } - return n; + bodyParsed = o.bodyParsed; } @Override - public int hashCode() { - if (computedHashcode) { - return hashcode; - } + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = 31 + ( null != structName ? structName.hashCode() : 0 ); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(fields); + } - if (structName != null) { - hashcode = structName.hashCode(); - } else if (getName() != null) { - hashcode = getName().hashCode(); - } else { - hashcode = 0; - } + @Override + protected boolean equalsImpl(final Type arg) { + final CompoundType ct = (CompoundType) arg; + return ( (structName == null ? ct.structName == null : structName.equals(ct.structName)) || + (structName != null && structName.equals(ct.structName)) + ) && + TypeComparator.listsEqual(fields, ct.fields); + } - computedHashcode = true; - return hashcode; + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + return TypeComparator.listsHashCodeSemantics(fields); } @Override - public boolean equals(final Object arg) { - if (arg == this) return true; - if (arg == null || !(arg instanceof CompoundType)) { - return false; - } - final CompoundType t = (CompoundType) arg; - return super.equals(arg) && - ((structName == null ? t.structName == null : structName.equals(t.structName)) || - (structName != null && structName.equals(t.structName))) && - listsEqual(fields, t.fields); + protected boolean equalSemanticsImpl(final Type arg) { + final CompoundType ct = (CompoundType) arg; + return TypeComparator.listsEqualSemantics(fields, ct.fields); } /** Returns the struct name of this CompoundType, i.e. the "foo" in @@ -117,22 +154,20 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable return structName; } - /** Sets the struct name of this CompoundType, i.e. the "foo" in the - construct "struct foo { ... };". */ - public void setStructName(final String structName) { - this.structName = structName; - } - @Override - public void setSize(final SizeThunk size) { - super.setSize(size); - } + public CompoundType asCompound() { return this; } @Override - public CompoundType asCompound() { return this; } + public String getCName(final boolean includeCVAttrs) { + if( isTypedef() ) { + return getName(includeCVAttrs); + } else { + return (isStruct() ? "struct " : "union ")+getName(includeCVAttrs); + } + } ArrayList<Field> getFields() { return fields; } - void setFields(final ArrayList<Field> fields) { this.fields = fields; } + void setFields(final ArrayList<Field> fields) { this.fields = fields; clearCache(); } /** Returns the number of fields in this type. */ public int getNumFields() { @@ -147,17 +182,24 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable /** Adds a field to this type. */ public void addField(final Field f) { if (bodyParsed) { - throw new RuntimeException("Body of this CompoundType has already been parsed; should not be adding more fields"); + throw new IllegalStateException("Body of this CompoundType has been already closed"); } if (fields == null) { fields = new ArrayList<Field>(); } fields.add(f); + clearCache(); } - /** Indicates to this CompoundType that its body has been parsed and - that no more {@link #addField} operations will be made. */ - public void setBodyParsed() { + /** + * Indicates to this CompoundType that its body has been parsed and + * that no more {@link #addField} operations will be made. + * @throws IllegalStateException If called twice. + */ + public void setBodyParsed() throws IllegalStateException { + if (bodyParsed) { + throw new IllegalStateException("Body of this CompoundType has been already closed"); + } bodyParsed = true; } @@ -169,8 +211,9 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable @Override public String toString() { final String cvAttributesString = getCVAttributesString(); - if (getName() != null) { - return cvAttributesString + getName(); + final String cname = getCName(); + if ( null != cname ) { + return cvAttributesString + cname; } else if (getStructName() != null) { return cvAttributesString + "struct " + getStructName(); } else { @@ -188,12 +231,12 @@ public abstract class CompoundType extends MemoryLayoutType implements Cloneable super.visit(arg); final int n = getNumFields(); for (int i = 0; i < n; i++) { - final Field f = getField(i); - f.getType().visit(arg); + getField(i).getType().visit(arg); } } finally { visiting = false; } + return; } public String getStructString() { diff --git a/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java index de42522..133a322 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java @@ -39,22 +39,22 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a double-word floating-point type (C type "double".) */ public class DoubleType extends PrimitiveType implements Cloneable { - public DoubleType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + public DoubleType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + private DoubleType(final DoubleType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof DoubleType))) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new DoubleType(this, cvAttributes, astLocus); } @Override @@ -63,7 +63,22 @@ public class DoubleType extends PrimitiveType implements Cloneable { } @Override - Type newCVVariant(final int cvAttributes) { - return new DoubleType(getName(), getSize(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java index 0b1193b..7c2fa73 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java @@ -42,73 +42,132 @@ package com.jogamp.gluegen.cgram.types; import java.util.ArrayList; import java.util.NoSuchElementException; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ConstantDefinition; +import com.jogamp.gluegen.ConstantDefinition.CNumber; +import com.jogamp.gluegen.GlueGenException; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + /** Describes enumerated types. Enumerations are like ints except that they have a set of named values. */ public class EnumType extends IntType implements Cloneable { - private IntType underlyingType; + public static class Enumerator implements TypeComparator.SemanticEqualityOp { + private final String name; + private final String expr; + private final CNumber number; - private static class Enum { + public Enumerator(final String name, final long value) { + this.name = name; + this.number = new CNumber(false, false, value); + this.expr = this.number.toJavaString(); + } + public Enumerator(final String name, final CNumber number) { + this.name = name; + this.number = number; + this.expr = this.number.toJavaString(); + } + public Enumerator(final String name, final String value) { + this.name = name; + this.expr = value; + this.number = ConstantDefinition.decodeIntegerNumber(value); + } - String name; - long value; + public String getName() { return name; } + public String getExpr() { return expr; } + public CNumber getNumber() { return number; } + public boolean hasNumber() { return null != number; } - Enum(final String name, final long value) { - this.name = name; - this.value = value; + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + final int hash = name.hashCode(); + return ((hash << 5) - hash) + expr.hashCode(); } - String getName() { - return name; + @Override + public boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof Enumerator) ) { + return false; + } + final Enumerator t = (Enumerator) arg; + return name.equals(t.name) && + expr.equals(t.expr); } - long getValue() { - return value; + @Override + public int hashCodeSemantics() { + return hashCode(); } + + @Override + public boolean equalSemantics(final SemanticEqualityOp arg) { + return equals(arg); + } + + @Override + public String toString() { return "["+name+" = ["+expr+", "+number+"]"; } } - private ArrayList<Enum> enums; + private final IntType underlyingType; + private ArrayList<Enumerator> enums; public EnumType(final String name) { super(name, SizeThunk.LONG, false, CVAttributes.CONST); this.underlyingType = new IntType(name, SizeThunk.LONG, false, CVAttributes.CONST); } - public EnumType(final String name, final SizeThunk enumSizeInBytes) { - super(name, enumSizeInBytes, false, CVAttributes.CONST); - this.underlyingType = new IntType(name, enumSizeInBytes, false, CVAttributes.CONST); + public EnumType(final String name, final SizeThunk enumSizeInBytes, final ASTLocusTag astLocus) { + super(name, enumSizeInBytes, false, CVAttributes.CONST, astLocus); + this.underlyingType = new IntType(name, enumSizeInBytes, false, CVAttributes.CONST, astLocus); } - protected EnumType(final String name, final IntType underlyingType, final int cvAttributes) { - super(name, underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); - this.underlyingType = underlyingType; + private EnumType(final EnumType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + underlyingType = o.underlyingType; + if(null != o.enums) { + enums = new ArrayList<Enumerator>(o.enums); + } } @Override - public Object clone() { - final EnumType n = (EnumType) super.clone(); - if(null!=this.underlyingType) { - n.underlyingType = (IntType) this.underlyingType.clone(); - } - if(null!=this.enums) { - n.enums = new ArrayList<Enum>(this.enums); - } - return n; + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new EnumType(this, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof EnumType))) { - return false; - } + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCode(); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(enums); + } + + @Override + protected boolean equalsImpl(final Type arg) { + final EnumType t = (EnumType) arg; + return super.equalsImpl(arg) && + underlyingType.equals(t.underlyingType) && + TypeComparator.listsEqual(enums, t.enums); + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = super.hashCodeSemanticsImpl(); + hash = ((hash << 5) - hash) + underlyingType.hashCodeSemantics(); + return ((hash << 5) - hash) + TypeComparator.listsHashCodeSemantics(enums); + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { final EnumType t = (EnumType) arg; - return (super.equals(arg) - && underlyingType.equals(t.underlyingType) - && listsEqual(enums, t.enums)); + return super.equalSemanticsImpl(arg) && + underlyingType.equalSemantics(t.underlyingType) && + TypeComparator.listsEqualSemantics(enums, t.enums); } @Override @@ -116,11 +175,14 @@ public class EnumType extends IntType implements Cloneable { return this; } - public void addEnum(final String name, final long val) { + public Type getUnderlyingType() { return this.underlyingType; } + + public void addEnum(final String name, final Enumerator newEnum) { if (enums == null) { - enums = new ArrayList<Enum>(); + enums = new ArrayList<Enumerator>(); } - enums.add(new Enum(name, val)); + enums.add(newEnum); + clearCache(); } /** Number of enumerates defined in this enum. */ @@ -128,22 +190,17 @@ public class EnumType extends IntType implements Cloneable { return enums.size(); } - /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) name */ - public String getEnumName(final int i) { - return (enums.get(i)).getName(); + /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) {@link Enumerator} */ + public Enumerator getEnum(final int i) { + return enums.get(i); } - /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) value */ - public long getEnumValue(final int i) { - return (enums.get(i)).getValue(); - } - - /** Fetch the value of the enumerate with the given name. */ - public long getEnumValue(final String name) { + /** Fetch the enumerate with the given name. */ + public Enumerator getEnum(final String name) { for (int i = 0; i < enums.size(); ++i) { - final Enum n = (enums.get(i)); + final Enumerator n = (enums.get(i)); if (n.getName().equals(name)) { - return n.getValue(); + return n; } } throw new NoSuchElementException( @@ -166,25 +223,30 @@ public class EnumType extends IntType implements Cloneable { */ public boolean removeEnumerate(final String name) { for (int i = 0; i < enums.size(); ++i) { - final Enum e = enums.get(i); + final Enumerator e = enums.get(i); if (e.getName().equals(name)) { enums.remove(e); + clearCache(); return true; } } return false; } + public StringBuilder appendEnums(final StringBuilder sb, final boolean cr) { + for(int i=0; i<enums.size(); i++) { + sb.append(enums.get(i)).append(", "); + if( cr ) { + sb.append(String.format("%n")); + } + } + sb.append("}"); + return sb; + } + @Override public void visit(final TypeVisitor arg) { super.visit(arg); underlyingType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - final EnumType t = new EnumType(getName(), underlyingType, cvAttributes); - t.enums = enums; - return t; - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/Field.java b/src/java/com/jogamp/gluegen/cgram/types/Field.java index 858d81a..a8fc599 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/Field.java +++ b/src/java/com/jogamp/gluegen/cgram/types/Field.java @@ -40,10 +40,11 @@ package com.jogamp.gluegen.cgram.types; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Represents a field in a struct or union. */ -public class Field { +public class Field implements SemanticEqualityOp { private final String name; private final Type type; private SizeThunk offset; @@ -56,21 +57,41 @@ public class Field { @Override public int hashCode() { - return name.hashCode(); + // 31 * x == (x << 5) - x + final int hash = 31 + ( null != name ? name.hashCode() : 0 ); + return ((hash << 5) - hash) + type.hashCode(); } @Override public boolean equals(final Object arg) { - if (arg == null || (!(arg instanceof Field))) { + if ( !(arg instanceof Field) ) { return false; } final Field f = (Field) arg; // Note: don't know how to examine offset any more since it's // implemented in terms of code and they're not canonicalized - return (((name != null && name.equals(f.name)) || - (name == null && f.name == null)) && - type.equals(f.type)); + return ( ( name != null && name.equals(f.name) ) || + ( name == null && f.name == null ) + ) && + type.equals(f.type); + } + + @Override + public int hashCodeSemantics() { + return type.hashCodeSemantics(); + } + + @Override + public boolean equalSemantics(final SemanticEqualityOp arg) { + if ( !(arg instanceof Field) ) { + return false; + } + + final Field f = (Field) arg; + // Note: don't know how to examine offset any more since it's + // implemented in terms of code and they're not canonicalized + return type.equalSemantics(f.type); } /** Name of this field in the containing data structure. */ diff --git a/src/java/com/jogamp/gluegen/cgram/types/FloatType.java b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java index d8b0b13..2e7a2cf 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FloatType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java @@ -40,29 +40,44 @@ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + /** Represents a single-word floating-point type (C type "float".) */ public class FloatType extends PrimitiveType implements Cloneable { - public FloatType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + public FloatType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + private FloatType(final FloatType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof FloatType))) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new FloatType(this, cvAttributes, astLocus); } @Override public FloatType asFloat() { return this; } @Override - Type newCVVariant(final int cvAttributes) { - return new FloatType(getName(), getSize(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java index d41f2fd..91a0a5a 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java @@ -38,6 +38,14 @@ */ package com.jogamp.gluegen.cgram.types; +import java.util.List; + +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl; +import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; + /** * Describes a function symbol, which includes the name and @@ -51,20 +59,37 @@ package com.jogamp.gluegen.cgram.types; * Deep comparison can be performed via {@link #isCompletelyEqual(Object o)}; * </p> **/ -public class FunctionSymbol { +public class FunctionSymbol extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider { - private final String name; private final FunctionType type; + private final ASTLocusTag astLocus; public FunctionSymbol(final String name, final FunctionType type) { - this.name = name; + super(name); + this.type = type; + this.astLocus = null; + } + + public FunctionSymbol(final String name, final FunctionType type, final ASTLocusTag locus) { + super(name); this.type = type; + this.astLocus = locus; } - public String getName() { - return name; + /** Shallow'ish copy, only aliased names are re-created. */ + public static FunctionSymbol cloneWithDeepAliases(final FunctionSymbol o) { + return new FunctionSymbol(o); + } + /** Warning: Shallow'ish copy, only aliased names are re-created. */ + private FunctionSymbol(final FunctionSymbol o) { + super(o); + this.type = o.type; + this.astLocus = o.astLocus; } + @Override + public ASTLocusTag getASTLocusTag() { return astLocus; } + /** Returns the type of this function. Do not add arguments to it directly; use addArgument instead. */ public FunctionType getType() { @@ -99,7 +124,7 @@ public class FunctionSymbol { @Override public String toString() { - return getType().toString(getName()); + return getType().toString(getName(), false); } /** Helper routine for emitting native javadoc tags */ @@ -109,10 +134,10 @@ public class FunctionSymbol { @Override public int hashCode() { - if (name == null) { + if (getName() == null) { return 0; } - return name.hashCode(); + return getName().hashCode(); } @Override @@ -120,25 +145,54 @@ public class FunctionSymbol { if (arg == this) { return true; } - - if (arg == null || (!(arg instanceof FunctionSymbol))) { + if ( !(arg instanceof FunctionSymbol) ) { return false; } - final FunctionSymbol other = (FunctionSymbol) arg; - if (getName() == null && other.getName() != null) { return false; } - return getName().equals(other.getName()); } + @Override + public int hashCodeSemantics() { + return type.hashCodeSemantics(); + } + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } + if ( !(arg instanceof FunctionSymbol) ) { + return false; + } + final FunctionSymbol other = (FunctionSymbol) arg; + return type.equalSemantics(other.type); + } + + + public static boolean containsExactly(final List<FunctionSymbol> l, final FunctionSymbol s) { + return exactIndexOf(l, s) >= 0; + } + + public static int exactIndexOf(final List<FunctionSymbol> l, final FunctionSymbol s) { + final int size = l.size(); + for (int i = 0; i < size; i++) { + final FunctionSymbol e = l.get(i); + if( null == s && null == e || + s.equals( e ) && s.type.equals(e.type) ) { + return i; + } + } + return -1; + } + /** * Compares the function type as well, since {@link #equals(Object)} * and {@link #hashCode()} won't. */ - public boolean isCompletelyEqual(final Object arg) { + public boolean exactlyEqual(final Object arg) { if( !this.equals(arg) ) { return false; } diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java index 4b39a34..2b9dec7 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java @@ -41,6 +41,8 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.ASTLocusTag; + /** Describes a function type, used to model both function declarations and (via PointerType) function pointers. */ public class FunctionType extends Type implements Cloneable { @@ -49,35 +51,63 @@ public class FunctionType extends Type implements Cloneable { private ArrayList<Type> argumentTypes; private ArrayList<String> argumentNames; - public FunctionType(final String name, final SizeThunk size, final Type returnType, final int cvAttributes) { - super(name, size, cvAttributes); + public FunctionType(final String name, final SizeThunk size, final Type returnType, + final int cvAttributes) { + this(name, size, returnType, cvAttributes, null); + } + public FunctionType(final String name, final SizeThunk size, final Type returnType, + final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); this.returnType = returnType; } - @Override - public Object clone() { - final FunctionType n = (FunctionType) super.clone(); - if(null!=this.argumentTypes) { - n.argumentTypes = new ArrayList<Type>(this.argumentTypes); + private FunctionType(final FunctionType o, final ASTLocusTag astLocus) { + super(o, o.getCVAttributes(), astLocus); + returnType = o.returnType; + if(null != o.argumentTypes) { + argumentTypes = new ArrayList<Type>(o.argumentTypes); } - if(null!=this.argumentNames) { - n.argumentNames = new ArrayList<String>(this.argumentNames); + if(null != o.argumentNames) { + argumentNames = new ArrayList<String>(o.argumentNames); } - return n; } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof FunctionType))) { - return false; + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + if( newCVVariant ) { + // Functions don't have const/volatile attributes + return this; + } else { + return new FunctionType(this, astLocus); } + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = returnType.hashCode(); + return ((hash << 5) - hash) + TypeComparator.listsHashCode(argumentTypes); + } + + @Override + protected boolean equalsImpl(final Type arg) { final FunctionType t = (FunctionType) arg; - return (super.equals(arg) - && returnType.equals(t.returnType) - && listsEqual(argumentTypes, t.argumentTypes)); + return returnType.equals(t.returnType) && + TypeComparator.listsEqual(argumentTypes, t.argumentTypes); + } + + @Override + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + final int hash = returnType.hashCodeSemantics(); + return ((hash << 5) - hash) + TypeComparator.listsHashCodeSemantics(argumentTypes); + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final FunctionType t = (FunctionType) arg; + return returnType.equalSemantics(t.returnType) && + TypeComparator.listsEqualSemantics(argumentTypes, t.argumentTypes); } @Override @@ -115,28 +145,27 @@ public class FunctionType extends Type implements Cloneable { } argumentTypes.add(argumentType); argumentNames.add(argumentName); + clearCache(); } public void setArgumentName(final int i, final String name) { argumentNames.set(i, name); + clearCache(); } @Override public String toString() { - return toString(null); - } - - public String toString(final String functionName) { - return toString(functionName, false); + return toString(null, false); } public String toString(final String functionName, final boolean emitNativeTag) { return toString(functionName, null, emitNativeTag, false); } - String toString(final String functionName, final String callingConvention, final boolean emitNativeTag, final boolean isPointer) { + String toString(final String functionName, final String callingConvention, + final boolean emitNativeTag, final boolean isPointer) { final StringBuilder res = new StringBuilder(); - res.append(getReturnType()); + res.append(getReturnType().getCName(true)); res.append(" "); if (isPointer) { res.append("("); @@ -169,7 +198,7 @@ public class FunctionType extends Type implements Cloneable { } else if (t.isArray()) { res.append(t.asArray().toString(getArgumentName(i))); } else { - res.append(t); + res.append(t.getCName(true)); final String argumentName = getArgumentName(i); if (argumentName != null) { res.append(" "); @@ -193,10 +222,4 @@ public class FunctionType extends Type implements Cloneable { getArgumentType(i).visit(arg); } } - - @Override - Type newCVVariant(final int cvAttributes) { - // Functions don't have const/volatile attributes - return this; - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/IntType.java b/src/java/com/jogamp/gluegen/cgram/types/IntType.java index 3f8dddc..2433fc6 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/IntType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/IntType.java @@ -39,37 +39,95 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class IntType extends PrimitiveType implements Cloneable { private final boolean unsigned; - private boolean typedefedUnsigned; + private boolean typedefUnsigned; public IntType(final String name, final SizeThunk size, final boolean unsigned, final int cvAttributes) { - this(name, size, unsigned, cvAttributes, false); + this(name, size, unsigned, cvAttributes, null); } - public IntType(final String name, final SizeThunk size, final boolean unsigned, final int cvAttributes, final boolean typedefedUnsigned) { - super(name, size, cvAttributes); + public IntType(final String name, final SizeThunk size, + final boolean unsigned, final int cvAttributes, + final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); this.unsigned = unsigned; - this.typedefedUnsigned = typedefedUnsigned; + this.typedefUnsigned = false; } - @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof IntType))) { - return false; + /** + * Only for HeaderParser! + * + * @param name the name + * @param size the size + * @param unsigned true if this instance is unsigned, not the <i>typedef</i>! + * @param cvAttributes the cvAttributes for this instance, not for the <i>typedef</i>! + * @param isTypedef true if this instance is a <i>typedef</i> variant + * @param typedefUnsigned true if the <i>typedef</i> itself is unsigned + * @param astLocus the location in source code + */ + public IntType(final String name, final SizeThunk size, + final boolean unsigned, final int cvAttributes, + final boolean isTypedef, final boolean typedefUnsigned, + final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + this.unsigned = unsigned; + if( isTypedef ) { + // the 'cvAttributes' are intended for this instance, not the 'typedef cvAttributes'! + setTypedef(0); + this.typedefUnsigned = typedefUnsigned; + } else { + this.typedefUnsigned = false; } + } + + IntType(final IntType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + this.unsigned = o.unsigned; + this.typedefUnsigned = o.typedefUnsigned; + } + + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new IntType(this, cvAttributes, astLocus); + } + + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 1; + hash = ((hash << 5) - hash) + ( unsigned ? 1 : 0 ); + return ((hash << 5) - hash) + ( typedefUnsigned ? 1 : 0 ); + } + + @Override + protected boolean equalsImpl(final Type arg) { final IntType t = (IntType) arg; - return (super.equals(arg) && (unsigned == t.unsigned)); + return unsigned == t.unsigned && + typedefUnsigned == t.typedefUnsigned; } @Override - public void setName(final String name) { - super.setName(name); - typedefedUnsigned = unsigned; + protected int hashCodeSemanticsImpl() { + // 31 * x == (x << 5) - x + int hash = 1; + if( !relaxedEqSem ) { + hash = ((hash << 5) - hash) + ( unsigned ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( typedefUnsigned ? 1 : 0 ); + } + return hash; + } + + @Override + protected boolean equalSemanticsImpl(final Type arg) { + final IntType t = (IntType) arg; + return relaxedEqSem || + ( unsigned == t.unsigned && + typedefUnsigned == t.typedefUnsigned + ); } @Override @@ -82,18 +140,27 @@ public class IntType extends PrimitiveType implements Cloneable { return unsigned; } - /** Indicates whether this type is an unsigned primitive type, as opposed to a typedef type that's unsigned. */ - public boolean isPrimitiveUnsigned() { - return unsigned && !typedefedUnsigned; + @Override + public String getCName(final boolean includeCVAttrs) { + if ( !unsigned || typedefUnsigned ) { + return super.getCName(includeCVAttrs); + } else { + return "unsigned "+super.getCName(includeCVAttrs); + } } @Override public String toString() { - return getCVAttributesString() + ((isUnsigned() & (!typedefedUnsigned)) ? "unsigned " : "") + getName(); + return getCVAttributesString() + ( unsigned && !typedefUnsigned ? "unsigned " : "") + getCName(); } @Override - Type newCVVariant(final int cvAttributes) { - return new IntType(getName(), getSize(), isUnsigned(), cvAttributes, typedefedUnsigned); + public boolean setTypedefName(final String name) { + if( super.setTypedefName(name) ) { + typedefUnsigned = unsigned; + return true; + } else { + return false; + } } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java b/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java index 25d2d1d..8b06a7e 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/MemoryLayoutType.java @@ -27,15 +27,23 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public abstract class MemoryLayoutType extends Type { private boolean isLayouted; - protected MemoryLayoutType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + protected MemoryLayoutType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); isLayouted = false; } + MemoryLayoutType(final MemoryLayoutType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + isLayouted = o.isLayouted; + } public boolean isLayouted() { return isLayouted; } - public void setLayouted() { isLayouted = true; } + public void setLayouted() { + isLayouted = true; + } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/PointerType.java b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java index d1dfb17..5707b5c 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/PointerType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java @@ -39,111 +39,124 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class PointerType extends Type implements Cloneable { private final Type targetType; - private String computedName; - private boolean hasTypedefedName; public PointerType(final SizeThunk size, final Type targetType, final int cvAttributes) { + this(size, targetType, cvAttributes, null); + } + public PointerType(final SizeThunk size, final Type targetType, final int cvAttributes, final ASTLocusTag astLocus) { // can pass null for the final name parameter because the PointerType's getName() // completely replaces superclass behavior - this(size, targetType, cvAttributes, false, null); + super(targetType.getName() + " *", size, cvAttributes, astLocus); + this.targetType = targetType; } - private PointerType(final SizeThunk size, final Type targetType, final int cvAttributes, final boolean hasTypedefedName, final String typedefedName) { - super(targetType.getName() + " *", size, cvAttributes); - this.hasTypedefedName = false; - this.targetType = targetType; - if (hasTypedefedName) { - setName(typedefedName); - } + private PointerType(final PointerType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); + targetType = o.targetType; } @Override - public int hashCode() { - return targetType.hashCode(); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new PointerType(this, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == this) { - return true; - } - if (arg == null || (!(arg instanceof PointerType))) { - return false; - } + protected int hashCodeImpl() { + return targetType.hashCode(); + } + + @Override + protected boolean equalsImpl(final Type arg) { final PointerType t = (PointerType) arg; - // Note we ignore the name of this type (which might be a typedef - // name) for comparison purposes because this is what allows - // e.g. a newly-fabricated type "PIXELFORMATDESCRIPTOR *" to be - // canonicalized to e.g. "LPPIXELFORMATDESCRIPTOR" - return ((getSize() == t.getSize()) - && (getCVAttributes() == t.getCVAttributes()) - && targetType.equals(t.targetType)); + return targetType.equals(t.targetType); + } + + @Override + protected int hashCodeSemanticsImpl() { + return targetType.hashCodeSemantics(); } @Override - public void setName(final String name) { - super.setName(name); - hasTypedefedName = true; + protected boolean equalSemanticsImpl(final Type arg) { + final PointerType pt = (PointerType) arg; + return targetType.equalSemantics(pt.targetType); + } + + @Override + public boolean isAnon() { + if ( isTypedef() ) { + return super.isAnon(); + } else { + return targetType.isAnon(); + } } @Override public String getName(final boolean includeCVAttrs) { - if (hasTypedefedName) { + if ( isTypedef() ) { return super.getName(includeCVAttrs); + } else if (!includeCVAttrs) { + return targetType.getName(includeCVAttrs) + " *"; } else { - // Lazy computation of name due to lazy setting of compound type - // names during parsing - if (computedName == null) { - computedName = (targetType.getName(includeCVAttrs) + " *").intern(); - } - if (!includeCVAttrs) { - return computedName; - } return targetType.getName(includeCVAttrs) + " * " + getCVAttributesString(); } } - public boolean hasTypedefedName() { - return hasTypedefedName; + @Override + public String getCName(final boolean includeCVAttrs) { + if ( isTypedef() ) { + return super.getCName(includeCVAttrs); + } else if (!includeCVAttrs) { + return targetType.getCName(includeCVAttrs) + " *"; + } else { + return targetType.getCName(includeCVAttrs) + " * " + getCVAttributesString(); + } } @Override - public PointerType asPointer() { + public final PointerType asPointer() { return this; } - public Type getTargetType() { + @Override + public final Type getTargetType() { return targetType; } @Override - public Type getBaseElementType() { - /** - if(targetType.isPointer()) { - return ((PointerType)targetType).getBaseElementType(); - } else { - return targetType; - } */ + public final Type getBaseElementType() { return targetType.getBaseElementType(); } @Override - public boolean isFunctionPointer() { + public final boolean isFunctionPointer() { return targetType.isFunction(); } @Override + public final int pointerDepth() { + return 1 + targetType.pointerDepth(); + } + + @Override public String toString() { - if (hasTypedefedName) { - return super.getName(true); + if ( isTypedef() ) { + return super.getCName(true); + } else { + return toStringInt(); + } + } + private String toStringInt() { + if (!targetType.isFunction()) { + return targetType.getCName(true) + " * " + getCVAttributesString(); } else { - if (!targetType.isFunction()) { - return targetType.toString() + " * " + getCVAttributesString(); - } - return toString(null, null); // this is a pointer to an unnamed function + // return toString(null, null); // this is a pointer to an unnamed function + return ((FunctionType) targetType).toString(null /* functionName */, null /* callingConvention */, false, true); } } @@ -162,9 +175,4 @@ public class PointerType extends Type implements Cloneable { super.visit(arg); targetType.visit(arg); } - - @Override - Type newCVVariant(final int cvAttributes) { - return new PointerType(getSize(), targetType, cvAttributes, hasTypedefedName, (hasTypedefedName ? getName() : null)); - } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java b/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java index 8a86337..76f3ff1 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java @@ -39,10 +39,16 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public abstract class PrimitiveType extends Type implements Cloneable { - protected PrimitiveType(final String name, final SizeThunk size, final int cvAttributes) { - super(name, size, cvAttributes); + protected PrimitiveType(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, size, cvAttributes, astLocus); + } + + PrimitiveType(final PrimitiveType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override diff --git a/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java index 9843d6b..7a9c62a 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java +++ b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java @@ -41,17 +41,25 @@ package com.jogamp.gluegen.cgram.types; import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Provides a level of indirection between the definition of a type's size and the absolute value of this size. Necessary when generating glue code for two different CPU architectures (e.g., 32-bit and 64-bit) from the same internal representation of the various types involved. */ -public abstract class SizeThunk implements Cloneable { +public abstract class SizeThunk implements Cloneable, SemanticEqualityOp { + /* pp */ static boolean relaxedEqSem = false; private final boolean fixedNativeSize; + public static void setRelaxedEqualSemanticsTest(final boolean v) { + relaxedEqSem = v; + } + // Private constructor because there are only a few of these - private SizeThunk(final boolean fixedNativeSize) { this.fixedNativeSize = fixedNativeSize; } + private SizeThunk(final boolean fixedNativeSize) { + this.fixedNativeSize = fixedNativeSize; + } @Override public Object clone() { @@ -67,6 +75,55 @@ public abstract class SizeThunk implements Cloneable { public abstract long computeSize(MachineDataInfo machDesc); public abstract long computeAlignment(MachineDataInfo machDesc); + @Override + public final int hashCode() { + final int hash = 0x02DEAD6F; // magic hash start + return ((hash << 5) - hash) + hashCodeImpl(); + } + /* pp */ abstract int hashCodeImpl(); + + @Override + public final boolean equals(final Object arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof SizeThunk) ) { + return false; + } else { + final SizeThunk t = (SizeThunk) arg; + return hashCodeImpl() == t.hashCodeImpl(); + } + } + + @Override + public final int hashCodeSemantics() { + final int hash = 0x01DEAD5F; // magic hash start + return ((hash << 5) - hash) + hashCodeSemanticsImpl(); + } + /* pp */ abstract int hashCodeSemanticsImpl(); + + @Override + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof SizeThunk) ) { + return false; + } else { + final SizeThunk t = (SizeThunk) arg; + return hashCodeSemanticsImpl() == t.hashCodeSemanticsImpl(); + } + } + + static final int magic_int08 = 0x00000010; + static final int magic_int16 = 0x00000012; + static final int magic_int32 = 0x00000014; + static final int magic_intxx = 0x00000016; + static final int magic_long64 = 0x00000020; + static final int magic_longxx = 0x00000022; + static final int magic_float32 = 0x00000030; + static final int magic_float64 = 0x00000032; + static final int magic_aptr64 = 0x00000040; + static final int magic_ops = 0x00010000; + public static final SizeThunk INT8 = new SizeThunk(true) { @Override public long computeSize(final MachineDataInfo machDesc) { @@ -76,6 +133,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int8AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 1; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_int08; } }; public static final SizeThunk INT16 = new SizeThunk(true) { @@ -87,6 +148,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int16AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 2; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_int16; } }; public static final SizeThunk INT32 = new SizeThunk(true) { @@ -98,6 +163,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int32AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 3; } + @Override + protected int hashCodeSemanticsImpl() { return magic_int32; } }; public static final SizeThunk INTxx = new SizeThunk(false) { @@ -109,6 +178,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.intAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 4; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_int32 : magic_intxx; } }; public static final SizeThunk LONG = new SizeThunk(false) { @@ -120,6 +193,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.longAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 5; } + @Override + protected int hashCodeSemanticsImpl() { return relaxedEqSem ? magic_long64 : magic_longxx; } }; public static final SizeThunk INT64 = new SizeThunk(true) { @@ -131,6 +208,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.int64AlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 6; } + @Override + protected int hashCodeSemanticsImpl() { return magic_long64; } }; public static final SizeThunk FLOAT = new SizeThunk(true) { @@ -142,6 +223,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.floatAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 7; } + @Override + protected int hashCodeSemanticsImpl() { return magic_float32; } }; public static final SizeThunk DOUBLE = new SizeThunk(true) { @@ -153,6 +238,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.doubleAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 8; } + @Override + protected int hashCodeSemanticsImpl() { return magic_float64; } }; public static final SizeThunk POINTER = new SizeThunk(false) { @@ -164,6 +253,10 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return machDesc.pointerAlignmentInBytes(); } + @Override + protected int hashCodeImpl() { return 9; } + @Override + protected int hashCodeSemanticsImpl() { return magic_aptr64; } }; // Factory methods for performing certain limited kinds of @@ -181,6 +274,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 10; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 1; } }; } @@ -197,6 +299,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 11; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 2; } }; } @@ -239,6 +350,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = alignmentThunk.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 12; + hash = ((hash << 5) - hash) + ( null != offsetThunk ? offsetThunk.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != alignmentThunk ? alignmentThunk.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 3; } }; } @@ -255,6 +375,15 @@ public abstract class SizeThunk implements Cloneable { final long thunk2A = thunk2.computeAlignment(machDesc); return ( thunk1A > thunk2A ) ? thunk1A : thunk2A ; } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + 13; + hash = ((hash << 5) - hash) + ( null != thunk1 ? thunk1.hashCode() : 0 ); + return ((hash << 5) - hash) + ( null != thunk2 ? thunk2.hashCode() : 0 ); + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 4; } }; } @@ -268,6 +397,14 @@ public abstract class SizeThunk implements Cloneable { public long computeAlignment(final MachineDataInfo machDesc) { return 1; // no alignment for constants } + @Override + protected int hashCodeImpl() { + // 31 * x == (x << 5) - x + final int hash = 31 + 14; + return ((hash << 5) - hash) + constant; + } + @Override + protected int hashCodeSemanticsImpl() { return magic_ops + 5; } }; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/StructType.java b/src/java/com/jogamp/gluegen/cgram/types/StructType.java index 27099e9..fa78006 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/StructType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/StructType.java @@ -27,34 +27,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class StructType extends CompoundType { - public StructType(final String name, final SizeThunk size, final int cvAttributes) { - this(name, size, cvAttributes, null); + StructType(final String name, final SizeThunk size, final int cvAttributes, final String structName, final ASTLocusTag astLocus) { + super (name, size, cvAttributes, structName, astLocus); } - StructType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super (name, size, cvAttributes, structName); + private StructType(final StructType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == null || !(arg instanceof StructType)) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new StructType(this, cvAttributes, astLocus); } @Override public final boolean isStruct() { return true; } @Override public final boolean isUnion() { return false; } - - @Override - Type newCVVariant(final int cvAttributes) { - final StructType t = new StructType(getName(), getSize(), cvAttributes, getStructName()); - t.setFields(getFields()); - return t; - } - } diff --git a/src/java/com/jogamp/gluegen/cgram/types/Type.java b/src/java/com/jogamp/gluegen/cgram/types/Type.java index 28ba6b4..04c46af 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/Type.java +++ b/src/java/com/jogamp/gluegen/cgram/types/Type.java @@ -40,46 +40,110 @@ package com.jogamp.gluegen.cgram.types; -import java.util.List; - import com.jogamp.common.os.MachineDataInfo; +import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.GlueGen; +import com.jogamp.gluegen.TypeConfig; +import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** Models a C type. Primitive types include int, float, and double. All types have an associated name. Structs and unions are modeled as "compound" types -- composed of fields of primitive or other types. */ -public abstract class Type implements Cloneable { - +public abstract class Type implements SemanticEqualityOp, ASTLocusTagProvider { + public final boolean relaxedEqSem; + private final int cvAttributes; + final ASTLocusTag astLocus; private String name; private SizeThunk size; - private final int cvAttributes; - private int typedefedCVAttributes; - private boolean hasTypedefName; - - protected Type(final String name, final SizeThunk size, final int cvAttributes) { - setName(name); + private int typedefCVAttributes; + private boolean isTypedef; + private boolean hasCachedHash; + private int cachedHash; + private boolean hasCachedSemanticHash; + private int cachedSemanticHash; + + protected Type(final String name, final SizeThunk size, final int cvAttributes, final ASTLocusTag astLocus) { + setName(name); // -> clearCache() + this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest(); + this.cvAttributes = cvAttributes; + this.astLocus = astLocus; this.size = size; + this.typedefCVAttributes = 0; + this.isTypedef = false; + } + Type(final Type o, final int cvAttributes, final ASTLocusTag astLocus) { + this.relaxedEqSem = o.relaxedEqSem; this.cvAttributes = cvAttributes; - hasTypedefName = false; + this.astLocus = astLocus; + this.name = o.name; + this.size = o.size; + this.typedefCVAttributes = o.typedefCVAttributes; + this.isTypedef = o.isTypedef; + clearCache(); } - @Override - public Object clone() { - try { - return super.clone(); - } catch (final CloneNotSupportedException ex) { - throw new InternalError(); + protected final void clearCache() { + hasCachedHash = false; + cachedHash = 0; + hasCachedSemanticHash = false; + cachedSemanticHash = 0; + } + + /** + * Return a variant of this type matching the given const/volatile + * attributes. May return this object if the attributes match. + */ + public final Type newCVVariant(final int cvAttributes) { + if (this.cvAttributes == cvAttributes) { + return this; + } else { + return newVariantImpl(true, cvAttributes, astLocus); } } + /** + * Clones this instance using a new {@link ASTLocusTag}. + */ + public Type clone(final ASTLocusTag newLoc) { + return newVariantImpl(true, cvAttributes, newLoc); + } + + /** + * Create a new variant of this type matching the given parameter + * <p> + * Implementation <i>must</i> use {@link Type}'s copy-ctor: {@link #Type(Type, int, ASTLocusTag)}! + * </p> + * @param newCVVariant true if new variant is intended to have new <i>cvAttributes</i> + * @param cvAttributes the <i>cvAttributes</i> to be used + * @param astLocus the {@link ASTLocusTag} to be used + */ + abstract Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus); + + @Override + public final ASTLocusTag getASTLocusTag() { return astLocus; } + + public boolean isAnon() { return null == name; } + /** Returns the name of this type. The returned string is suitable - for use as a type specifier. Does not include any const/volatile + for use as a type specifier for native C. Does not include any const/volatile + attributes. */ + public final String getCName() { return getCName(false); } + + /** Returns the name of this type, optionally including + const/volatile attributes. The returned string is suitable for + use as a type specifier for native C. */ + public String getCName(final boolean includeCVAttrs) { return getName(includeCVAttrs); } + + /** Returns the name of this type. The returned string is suitable + for use as a type specifier for Java. Does not include any const/volatile attributes. */ public final String getName() { return getName(false); } /** Returns the name of this type, optionally including const/volatile attributes. The returned string is suitable for - use as a type specifier. */ + use as a type specifier for Java. */ public String getName(final boolean includeCVAttrs) { if (!includeCVAttrs) { return name; @@ -87,26 +151,53 @@ public abstract class Type implements Cloneable { return getCVAttributesString() + name; } - private void append(final StringBuilder sb, final String val, final boolean prepComma) { + /** + * Returns a string representation of this type. + * The returned string is suitable for use as a type specifier for native C. + * It does contain an expanded description of structs/unions, + * hence may not be suitable for type declarations. + */ + @Override + public String toString() { + return getCName(true); + } + + + private static StringBuilder append(final StringBuilder sb, final String val, final boolean prepComma) { if( prepComma ) { sb.append(", "); } sb.append(val); + return sb; } // For debugging - public String getDebugString() { + public final String getDebugString() { final StringBuilder sb = new StringBuilder(); boolean prepComma = false; sb.append("CType["); + sb.append("(").append(getClass().getSimpleName()).append(") "); + if( isTypedef() ) { + sb.append("typedef "); + } if( null != name ) { - append(sb, "'"+name+"'", prepComma); prepComma=true; + sb.append("'").append(name).append("'"); } else { - append(sb, "ANON", prepComma); prepComma=true; + sb.append("ANON"); + } + final Type targetType = getTargetType(); + if( null != targetType && this != targetType ) { + sb.append(" -> "); + if (!targetType.isFunction()) { + sb.append("(" + targetType.toString() + ") * " + getCVAttributesString()); + } else { + sb.append(((FunctionType) targetType).toString(null /* functionName */, null /* callingConvention */, false, true)); + } } - if( hasTypedefName() ) { - sb.append(" (typedef)"); + if( GlueGen.debug() ) { + sb.append(", o=0x"+Integer.toHexString(objHash())); } - append(sb, "size ", prepComma); prepComma=true; + sb.append(", size "); + prepComma=true; if( null != size ) { final long mdSize; { @@ -121,67 +212,135 @@ public abstract class Type implements Cloneable { sb.append(" ZERO"); } append(sb, "[", prepComma); prepComma=false; - if( isConst() ) { - append(sb, "const ", false); - } - if( isVolatile() ) { - append(sb, "volatile ", false); - } - if( isPointer() ) { - append(sb, "pointer*"+pointerDepth(), prepComma); prepComma=true; - } - if( isArray() ) { - append(sb, "array*"+arrayDimension(), prepComma); prepComma=true; - } - if( isBit() ) { - append(sb, "bit", prepComma); prepComma=true; - } - if( isCompound() ) { - sb.append("struct{").append(asCompound().getNumFields()); - append(sb, "}", prepComma); prepComma=true; - } - if( isDouble() ) { - append(sb, "double", prepComma); prepComma=true; - } - if( isEnum() ) { - append(sb, "enum", prepComma); prepComma=true; - } - if( isFloat() ) { - append(sb, "float", prepComma); prepComma=true; - } - if( isFunction() ) { - append(sb, "function", prepComma); prepComma=true; - } - if( isFunctionPointer() ) { - append(sb, "funcPointer", prepComma); prepComma=true; - } - if( isInt() ) { - append(sb, "int", prepComma); prepComma=true; - } - if( isVoid() ) { - append(sb, "void", prepComma); prepComma=true; + { + append(sb, "const[", prepComma); prepComma=false; + { + if( isConstTypedef() ) { + append(sb, "type ", prepComma); prepComma=true; + } + if( isConstRaw() ) { + append(sb, "inst -> ", prepComma); prepComma=false; + } + if( isConst() ) { + append(sb, "true]", prepComma); + } else { + append(sb, "false]", prepComma); + } + prepComma=true; + } + if( isVolatile() ) { + append(sb, "volatile ", prepComma); prepComma=true; + } + if( isPointer() ) { + append(sb, "pointer*"+pointerDepth(), prepComma); prepComma=true; + } + if( isArray() ) { + append(sb, "array*"+arrayDimension(), prepComma); prepComma=true; + } + if( isBit() ) { + append(sb, "bit", prepComma); prepComma=true; + } + if( isCompound() ) { + append(sb, "struct{", prepComma).append(asCompound().getStructName()).append(": ").append(asCompound().getNumFields()); + append(sb, "}", prepComma); prepComma=true; + } + if( isDouble() ) { + append(sb, "double", prepComma); prepComma=true; + } + if( isEnum() ) { + final EnumType eT = asEnum(); + append(sb, "enum ", prepComma).append(" [").append(eT.getUnderlyingType()).append("] {").append(eT.getNumEnumerates()).append(": "); + eT.appendEnums(sb, false); + prepComma=true; + } + if( isFloat() ) { + append(sb, "float", prepComma); prepComma=true; + } + if( isFunction() ) { + append(sb, "function", prepComma); prepComma=true; + } + if( isFunctionPointer() ) { + append(sb, "funcPointer", prepComma); prepComma=true; + } + if( isInt() ) { + append(sb, "int", prepComma); prepComma=true; + } + if( isVoid() ) { + append(sb, "void", prepComma); prepComma=true; + } + sb.append("]"); } - sb.append("]]"); + sb.append("]"); return sb.toString(); } + private final int objHash() { return super.hashCode(); } + - /** Set the name of this type; used for handling typedefs. */ - public void setName(final String name) { - if (name == null) { + /** + * Returns {@code true} if given {@code name} is not {@code null} + * and has a length > 0. In this case this instance's names will + * be set to the internalized version. + * <p> + * Otherwise method returns {@code false} + * and this instance's name will be set to {@code null}. + * </p> + * <p> + * Method issues {@link #clearCache()}, to force re-evaluation + * of hashes. + * </p> + */ + private final boolean setName(final String name) { + clearCache(); + if( null == name || 0 == name.length() ) { this.name = name; + return false; } else { this.name = name.intern(); + return true; + } + } + + /** + * Set the typedef name of this type and renders this type a typedef, + * if given {@code name} has a length. + * <p> + * Method issues {@link #clearCache()}, to force re-evaluation + * of hashes. + * </p> + */ + public boolean setTypedefName(final String name) { + if( setName(name) ) { + // Capture the const/volatile attributes at the time of typedef so + // we don't redundantly repeat them in the CV attributes string + typedefCVAttributes = cvAttributes; + isTypedef = true; + return true; + } else { + return false; } - // Capture the const/volatile attributes at the time of typedef so - // we don't redundantly repeat them in the CV attributes string - typedefedCVAttributes = cvAttributes; - hasTypedefName = true; + } + final void setTypedef(final int typedefedCVAttributes) { + this.name = this.name.intern(); // just make sure .. + this.typedefCVAttributes = typedefedCVAttributes; + this.isTypedef = true; + clearCache(); + } + final int getTypedefCVAttributes() { + return typedefCVAttributes; + } + + /** + * Indicates whether this type is a typedef type, + * i.e. declared via {@link #setTypedefName(String)}. + */ + public final boolean isTypedef() { + return isTypedef; } /** SizeThunk which computes size of this type in bytes. */ - public SizeThunk getSize() { return size; } + public final SizeThunk getSize() { return size; } /** Size of this type in bytes according to the given MachineDataInfo. */ - public long getSize(final MachineDataInfo machDesc) { + public final long getSize(final MachineDataInfo machDesc) { final SizeThunk thunk = getSize(); if (thunk == null) { throw new RuntimeException("No size set for type \"" + getName() + "\""); @@ -189,7 +348,10 @@ public abstract class Type implements Cloneable { return thunk.computeSize(machDesc); } /** Set the size of this type; only available for CompoundTypes. */ - void setSize(final SizeThunk size) { this.size = size; } + final void setSize(final SizeThunk size) { + this.size = size; + clearCache(); + } /** Casts this to a BitType or returns null if not a BitType. */ public BitType asBit() { return null; } @@ -213,82 +375,153 @@ public abstract class Type implements Cloneable { public VoidType asVoid() { return null; } /** Indicates whether this is a BitType. */ - public boolean isBit() { return (asBit() != null); } + public final boolean isBit() { return (asBit() != null); } /** Indicates whether this is an IntType. */ - public boolean isInt() { return (asInt() != null); } + public final boolean isInt() { return (asInt() != null); } /** Indicates whether this is an EnumType. */ - public boolean isEnum() { return (asEnum() != null); } + public final boolean isEnum() { return (asEnum() != null); } /** Indicates whether this is a FloatType. */ - public boolean isFloat() { return (asFloat() != null); } + public final boolean isFloat() { return (asFloat() != null); } /** Indicates whether this is a DoubleType. */ - public boolean isDouble() { return (asDouble() != null); } + public final boolean isDouble() { return (asDouble() != null); } /** Indicates whether this is a PointerType. */ - public boolean isPointer() { return (asPointer() != null); } + public final boolean isPointer() { return (asPointer() != null); } /** Indicates whether this is an ArrayType. */ - public boolean isArray() { return (asArray() != null); } + public final boolean isArray() { return (asArray() != null); } /** Indicates whether this is a CompoundType. */ - public boolean isCompound() { return (asCompound() != null); } + public final boolean isCompound() { return (asCompound() != null); } /** Indicates whether this is a FunctionType. */ - public boolean isFunction() { return (asFunction() != null); } + public final boolean isFunction() { return (asFunction() != null); } /** Indicates whether this is a VoidType. */ - public boolean isVoid() { return (asVoid() != null); } + public final boolean isVoid() { return (asVoid() != null); } - /** Indicates whether this type is const. */ - public boolean isConst() { return (((cvAttributes & ~typedefedCVAttributes) & CVAttributes.CONST) != 0); } /** Indicates whether this type is volatile. */ - public boolean isVolatile() { return (((cvAttributes & ~typedefedCVAttributes) & CVAttributes.VOLATILE) != 0); } + public final boolean isVolatile() { return 0 != ( ( cvAttributes & ~typedefCVAttributes ) & CVAttributes.VOLATILE ); } + /** Indicates whether this type is const. */ + public final boolean isConst() { return 0 != ( ( cvAttributes & ~typedefCVAttributes ) & CVAttributes.CONST ); } + + private final boolean isConstTypedef() { return 0 != ( typedefCVAttributes & CVAttributes.CONST ); } + private final boolean isConstRaw() { return 0 != ( cvAttributes & CVAttributes.CONST ); } /** Indicates whether this type is a primitive type. */ - public boolean isPrimitive(){ return false; } + public boolean isPrimitive(){ return false; } /** Convenience routine indicating whether this Type is a pointer to a function. */ public boolean isFunctionPointer() { - return (isPointer() && asPointer().getTargetType().isFunction()); + return false; + } + + /** + * Checks the base type of pointer-to-pointer, pointer, array or plain for const-ness. + * <p> + * Note: Intermediate 'const' qualifier are not considered, e.g. const pointer. + * </p> + */ + public final boolean isBaseTypeConst() { + return getBaseElementType().isConst(); } /** Hashcode for Types. */ @Override - public int hashCode() { - if (name == null) { - return 0; - } - - if (cvAttributes != 0) { - final String nameWithAttribs = name + cvAttributes; - return nameWithAttribs.hashCode(); + public final int hashCode() { + if( !hasCachedHash ) { + // 31 * x == (x << 5) - x + int hash = 31 + ( isTypedef ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( null != size ? size.hashCode() : 0 ); + hash = ((hash << 5) - hash) + cvAttributes; + hash = ((hash << 5) - hash) + typedefCVAttributes; + hash = ((hash << 5) - hash) + ( null != name ? name.hashCode() : 0 ); + if( !isTypedef ) { + hash = ((hash << 5) - hash) + hashCodeImpl(); + } + cachedHash = hash; + hasCachedHash = true; } - return name.hashCode(); + return cachedHash; } + protected abstract int hashCodeImpl(); /** - * Equality test for Types. + * Equality test for Types inclusive its given {@link #getName() name}. */ @Override - public boolean equals(final Object arg) { + public final boolean equals(final Object arg) { if (arg == this) { - return true; + return true; + } else if ( !getClass().isInstance(arg) ) { // implies null == arg || !(arg instanceof Type) + return false; + } else { + final Type t = (Type)arg; + if( isTypedef == t.isTypedef && + ( ( null != size && size.equals(t.size) ) || + ( null == size && null == t.size ) + ) && + cvAttributes == t.cvAttributes && + typedefCVAttributes == t.typedefCVAttributes && + ( null == name ? null == t.name : name.equals(t.name) ) + ) + { + if( !isTypedef ) { + return equalsImpl(t); + } else { + return true; + } + } else { + return false; + } } + } + protected abstract boolean equalsImpl(final Type t); - if ( !(arg instanceof Type) ) { - return false; + @Override + public final int hashCodeSemantics() { + if( !hasCachedSemanticHash ) { + // 31 * x == (x << 5) - x + int hash = 31 + ( null != size ? size.hashCodeSemantics() : 0 ); + if( !relaxedEqSem ) { + hash = ((hash << 5) - hash) + cvAttributes; + hash = ((hash << 5) - hash) + typedefCVAttributes; + } + hash = ((hash << 5) - hash) + hashCodeSemanticsImpl(); + cachedSemanticHash = hash; + hasCachedSemanticHash = true; } - - final Type t = (Type)arg; - return size == t.size && cvAttributes == t.cvAttributes && - ( null == name ? null == t.name : name.equals(t.name) ) ; + return cachedSemanticHash; } + protected abstract int hashCodeSemanticsImpl(); - /** Returns a string representation of this type. This string is not - necessarily suitable for use as a type specifier; for example, - it will contain an expanded description of structs/unions. */ @Override - public String toString() { - return getName(true); + public final boolean equalSemantics(final SemanticEqualityOp arg) { + if (arg == this) { + return true; + } else if ( !(arg instanceof Type) || + !getClass().isInstance(arg) ) { // implies null == arg + return false; + } else { + final Type t = (Type) arg; + if( ( ( null != size && size.equalSemantics(t.size) ) || + ( null == size && null == t.size ) + ) && + ( relaxedEqSem || + ( cvAttributes == t.cvAttributes && + typedefCVAttributes == t.typedefCVAttributes + ) + ) + ) + { + return equalSemanticsImpl(t); + } else { + return false; + } + } } + protected abstract boolean equalSemanticsImpl(final Type t); - /** Visit this type and all of the component types of this one; for - example, the return type and argument types of a FunctionType. */ + /** + * Traverse this {@link Type} and all of its component types; for + * example, the return type and argument types of a FunctionType. + */ public void visit(final TypeVisitor visitor) { visitor.visitType(this); } @@ -306,45 +539,18 @@ public abstract class Type implements Cloneable { return ""; } - /** Return a variant of this type matching the given const/volatile - attributes. May return this object if the attributes match. */ - public final Type getCVVariant(final int cvAttributes) { - if (this.cvAttributes == cvAttributes) { - return this; - } - return newCVVariant(cvAttributes); - } - - /** Create a new variant of this type matching the given - const/volatile attributes. */ - abstract Type newCVVariant(int cvAttributes); - - /** Indicates whether setName() has been called on this type, - indicating that it already has a typedef name. */ - public boolean hasTypedefName() { - return hasTypedefName; - } - /** Helper method for determining how many pointer indirections this type represents (i.e., "void **" returns 2). Returns 0 if this type is not a pointer type. */ public int pointerDepth() { - final PointerType pt = asPointer(); - if (pt == null) { - return 0; - } - return 1 + pt.getTargetType().pointerDepth(); + return 0; } /** Helper method for determining how many array dimentions this type represents (i.e., "char[][]" returns 2). Returns 0 if this type is not an array type. */ public int arrayDimension() { - final ArrayType arrayType = asArray(); - if (arrayType == null) { - return 0; - } - return 1 + arrayType.getElementType().arrayDimension(); + return 0; } /** @@ -358,8 +564,10 @@ public abstract class Type implements Cloneable { return this; } - /** Helper routine for list equality comparison */ - static <C> boolean listsEqual(final List<C> a, final List<C> b) { - return ((a == null && b == null) || (a != null && b != null && a.equals(b))); + /** + * Helper method to returns the target type of this type, in case another type is being referenced. + */ + public Type getTargetType() { + return this; } } diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java b/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java new file mode 100644 index 0000000..850d953 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeComparator.java @@ -0,0 +1,143 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.List; + +public class TypeComparator { + /** + * Supports semantic equality and hash functions for types. + */ + public static interface SemanticEqualityOp { + /** + * Semantic hashcode for Types exclusive its given {@link #getName() name}. + * @see #equalSemantics(SemanticEqualityOp) + */ + int hashCodeSemantics(); + + /** + * Semantic equality test for Types exclusive its given {@link #getName() name}. + * @see #hashCodeSemantics() + */ + boolean equalSemantics(final SemanticEqualityOp arg); + } + /** + * Supports common interface for {@link SemanticEqualityOp} and {@link AliasedSymbol}. + */ + public static interface AliasedSemanticSymbol extends AliasedSymbol, SemanticEqualityOp { }; + + /** Helper routine for list equality comparison*/ + static <C> boolean listsEqual(final List<C> a, final List<C> b) { + if( a == null ) { + if( null != b ) { + return false; + } else { + return true; // elements equal, i.e. both null + } + } + if( b != null && a.size() == b.size() ) { + final int count = a.size(); + for(int i=0; i<count; i++) { + final C ac = a.get(i); + final C bc = b.get(i); + if( null == ac ) { + if( null != bc ) { + return false; + } else { + continue; // elements equal, i.e. both null + } + } + if( !ac.equals(bc) ) { + return false; + } + } + return true; + } + return false; + } + + /** Helper routine for list hashCode */ + static <C extends SemanticEqualityOp> int listsHashCode(final List<C> a) { + if( a == null ) { + return 0; + } else { + final int count = a.size(); + int hash = 31; + for(int i=0; i<count; i++) { + final C ac = a.get(i); + hash = ((hash << 5) - hash) + ( null != ac ? ac.hashCode() : 0 ); + } + return hash; + } + } + + /** Helper routine for list semantic equality comparison*/ + static <C extends SemanticEqualityOp> boolean listsEqualSemantics(final List<C> a, final List<C> b) { + if( a == null ) { + if( null != b ) { + return false; + } else { + return true; // elements equal, i.e. both null + } + } + if( b != null && a.size() == b.size() ) { + final int count = a.size(); + for(int i=0; i<count; i++) { + final C ac = a.get(i); + final C bc = b.get(i); + if( null == ac ) { + if( null != bc ) { + return false; + } else { + continue; // elements equal, i.e. both null + } + } + if( !ac.equalSemantics(bc) ) { + return false; + } + } + return true; + } + return false; + } + + /** Helper routine for list hashCode */ + static <C extends SemanticEqualityOp> int listsHashCodeSemantics(final List<C> a) { + if( a == null ) { + return 0; + } else { + final int count = a.size(); + int hash = 31; + for(int i=0; i<count; i++) { + final C ac = a.get(i); + hash = ((hash << 5) - hash) + ( null != ac ? ac.hashCodeSemantics() : 0 ); + } + return hash; + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java index cd03388..c1cfcdf 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java @@ -41,6 +41,9 @@ package com.jogamp.gluegen.cgram.types; import java.util.*; +import com.jogamp.gluegen.GlueGen; +import com.jogamp.gluegen.JavaConfiguration; + /** Utility class for recording names of typedefs and structs. */ @@ -63,6 +66,38 @@ public class TypeDictionary { return map.get(name); } + public List<Type> getEqualSemantics(final Type s, final JavaConfiguration cfg, final boolean skipOpaque) { + final List<Type> res = new ArrayList<Type>(); + if( !skipOpaque || null == cfg.typeInfo(s) ) { + final Set<Map.Entry<String, Type>> entries = entrySet(); + for(final Iterator<Map.Entry<String, Type>> iter = entries.iterator(); iter.hasNext(); ) { + final Map.Entry<String, Type> entry = iter.next(); + final Type t = entry.getValue(); + if( s.equalSemantics(t) ) { + if( !skipOpaque || null == cfg.typeInfo(t) ) { + if( GlueGen.debug() ) { + System.err.println(" tls["+res.size()+"]: -> "+entry.getKey()+" -> "+t.getDebugString()); + } + res.add(t); + } + } + } + } + return res; + } + public Type getEqualSemantics1(final Type s, final JavaConfiguration cfg, final boolean skipOpaque) { + final List<Type> tls = getEqualSemantics(s, cfg, skipOpaque); + if( tls.size() > 0 ) { + final Type res = tls.get(0); + if( GlueGen.debug() ) { + System.err.println(" tls.0: "+res.getDebugString()); + } + return res; + } else { + return null; + } + } + //this method is broken /** * Get the names that correspond to the given type. There will be more than diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java index 89c014b..ed5cfa9 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java @@ -39,6 +39,12 @@ package com.jogamp.gluegen.cgram.types; +/** + * A visitor for {@link Type}'s visitor model. + */ public interface TypeVisitor { + /** + * Visiting the given {@link Type}. + */ public void visitType(Type t); } diff --git a/src/java/com/jogamp/gluegen/cgram/types/UnionType.java b/src/java/com/jogamp/gluegen/cgram/types/UnionType.java index 99d2fed..8c6d9dd 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/UnionType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/UnionType.java @@ -27,34 +27,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class UnionType extends CompoundType { - public UnionType(final String name, final SizeThunk size, final int cvAttributes) { - this(name, size, cvAttributes, null); + UnionType(final String name, final SizeThunk size, final int cvAttributes, final String structName, final ASTLocusTag astLocus) { + super (name, size, cvAttributes, structName, astLocus); } - UnionType(final String name, final SizeThunk size, final int cvAttributes, final String structName) { - super (name, size, cvAttributes, structName); + private UnionType(final UnionType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } @Override - public boolean equals(final Object arg) { - if (arg == null || !(arg instanceof UnionType)) { - return false; - } - return super.equals(arg); + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new UnionType(this, cvAttributes, astLocus); } @Override public final boolean isStruct() { return false; } @Override public final boolean isUnion() { return true; } - - @Override - Type newCVVariant(final int cvAttributes) { - final UnionType t = new UnionType(getName(), getSize(), cvAttributes, getStructName()); - t.setFields(getFields()); - return t; - } - } diff --git a/src/java/com/jogamp/gluegen/cgram/types/VoidType.java b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java index 2e1f069..bf51523 100644 --- a/src/java/com/jogamp/gluegen/cgram/types/VoidType.java +++ b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java @@ -39,14 +39,25 @@ */ package com.jogamp.gluegen.cgram.types; +import com.jogamp.gluegen.ASTLocusTag; + public class VoidType extends Type implements Cloneable { - public VoidType(final int cvAttributes) { - this("void", cvAttributes); + public VoidType(final int cvAttributes, final ASTLocusTag astLocus) { + this("void", cvAttributes, astLocus); + } + + private VoidType(final String name, final int cvAttributes, final ASTLocusTag astLocus) { + super(name, null, cvAttributes, astLocus); + } + + private VoidType(final VoidType o, final int cvAttributes, final ASTLocusTag astLocus) { + super(o, cvAttributes, astLocus); } - private VoidType(final String name, final int cvAttributes) { - super(name, null, cvAttributes); + @Override + Type newVariantImpl(final boolean newCVVariant, final int cvAttributes, final ASTLocusTag astLocus) { + return new VoidType(this, cvAttributes, astLocus); } @Override @@ -55,7 +66,22 @@ public class VoidType extends Type implements Cloneable { } @Override - Type newCVVariant(final int cvAttributes) { - return new VoidType(getName(), cvAttributes); + protected int hashCodeImpl() { + return 0; + } + + @Override + protected boolean equalsImpl(final Type t) { + return true; + } + + @Override + protected int hashCodeSemanticsImpl() { + return 0; + } + + @Override + protected boolean equalSemanticsImpl(final Type t) { + return true; } } diff --git a/src/java/com/jogamp/gluegen/pcpp/PCPP.java b/src/java/com/jogamp/gluegen/pcpp/PCPP.java index aca7b14..c766634 100644 --- a/src/java/com/jogamp/gluegen/pcpp/PCPP.java +++ b/src/java/com/jogamp/gluegen/pcpp/PCPP.java @@ -56,16 +56,24 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; +import java.util.logging.Level; + +import com.jogamp.gluegen.ASTLocusTag; +import com.jogamp.gluegen.ConstantDefinition; +import com.jogamp.gluegen.GenericCPP; +import com.jogamp.gluegen.GlueGenException; +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; + import static java.util.logging.Level.*; /** A minimal pseudo-C-preprocessor designed in particular to preserve #define statements defining constants so they can be observed by a glue code generator. */ -public class PCPP { +public class PCPP implements GenericCPP { - private static final Logger LOG = Logger.getLogger(PCPP.class.getPackage().getName()); + private final LoggerIf LOG; /** Map containing the results of #define statements. We must evaluate certain very simple definitions (to properly handle @@ -86,13 +94,15 @@ public class PCPP { private final boolean enableCopyOutput2Stderr; public PCPP(final List<String> includePaths, final boolean debug, final boolean copyOutput2Stderr) { + LOG = Logging.getLogger(PCPP.class.getPackage().getName(), PCPP.class.getSimpleName()); this.includePaths = includePaths; setOut(System.out); enableDebugPrint = debug; enableCopyOutput2Stderr = copyOutput2Stderr; } - public void run(final Reader reader, final String filename) throws IOException { + @Override + public void run(final Reader reader, final String filename) throws GlueGenException { StreamTokenizer tok = null; BufferedReader bufReader = null; if (reader instanceof BufferedReader) { @@ -108,13 +118,29 @@ public class PCPP { final ParseState oldState = state; state = curState; lineDirective(); - parse(); + try { + parse(); + } catch (final Exception e) { + final StringBuilder buf = new StringBuilder("Preprocessor failed"); + LOG.log(Level.SEVERE, buf.toString(), e); + if( e instanceof GlueGenException ) { + throw (GlueGenException)e; + } else { + throw new GlueGenException("Preprocessor failed", + new ASTLocusTag(filename(), lineNumber(), -1, null), e); + } + } state = oldState; if (state != null) { lineDirective(); } } + @Override + public List<ConstantDefinition> getConstantDefinitions() throws GlueGenException { + return new ArrayList<ConstantDefinition>(); // NOP + } + private void initTokenizer(final StreamTokenizer tok) { tok.resetSyntax(); tok.wordChars('a', 'z'); @@ -131,6 +157,7 @@ public class PCPP { tok.slashStarComments(true); } + @Override public String findFile(final String filename) { final String sep = File.separator; for (final String inclPath : includePaths) { @@ -143,10 +170,12 @@ public class PCPP { return null; } + @Override public OutputStream out() { return out; } + @Override public void setOut(final OutputStream out) { this.out = out; writer = new PrintWriter(out); @@ -375,7 +404,7 @@ public class PCPP { } } - if(isIdentifier(value)) { + if(ConstantDefinition.isIdentifier(value)) { newS +=" "; } @@ -459,28 +488,30 @@ public class PCPP { if (enabled()) { final String oldDef = defineMap.remove(name); if (oldDef == null) { - LOG.log(WARNING, "ignoring redundant \"#undef {0}\", at \"{1}\" line {2}: \"{3}\" was not previously defined", - new Object[]{name, filename(), lineNumber(), name}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, name), + "ignoring redundant \"#undef {0}\" - was not previously defined", + name); } else { // System.err.println("UNDEFINED: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); } nonConstantDefines.remove(name); } else { - LOG.log(WARNING, "FAILED TO UNDEFINE: ''{0}'' (line {1} file {2})", new Object[]{name, lineNumber(), filename()}); + LOG.log(INFO, new ASTLocusTag(filename(), lineNumber(), -1, name), + "DISABLED UNDEFINE: ''{0}''", name); } } private void handleWarning() throws IOException { final String msg = nextWordOrString(); if (enabled()) { - LOG.log(WARNING, "#warning {0} at \"{1}\" line \"{2}\"", new Object[]{msg, filename(), lineNumber()}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), msg); } } - private void handleError() throws IOException { + private void handleError() throws IOException, GlueGenException { final String msg = nextWordOrString(); if (enabled()) { - throw new RuntimeException("#error "+msg+" at \""+filename()+"\" line "+lineNumber()); + throw new GlueGenException(msg, new ASTLocusTag(filename(), lineNumber(), -1, null)); } } @@ -520,6 +551,7 @@ public class PCPP { addDefine(name, macroDefinition, values); } + @Override public void addDefine(final String name, final String value) { final List<String> values = new ArrayList<String>(); values.add(value); @@ -541,7 +573,8 @@ public class PCPP { final String value = ""; final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"\"", new Object[]{name, oldDef}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"\"", name, oldDef); } // We don't want to emit the define, because it would serve no purpose // and cause GlueGen errors (confuse the GnuCParser) @@ -551,12 +584,13 @@ public class PCPP { // See whether the value is a constant final String value = values.get(0); - if (isConstant(value)) { + if (ConstantDefinition.isNumber(value)) { // Value is numeric constant like "#define FOO 5". // Put it in the #define map final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, value}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, value); } debugPrint(true, "DEFINE " + name + " ["+oldDef+" ] -> "+value + " CONST"); //System.err.println("//---DEFINED: " + name + " to \"" + value + "\""); @@ -606,7 +640,8 @@ public class PCPP { final Macro macro = new Macro(params, values); final Macro oldDef = macroMap.put(name, macro); if (oldDef != null) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, macro}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, macro); } emitDefine = false; @@ -618,7 +653,7 @@ public class PCPP { boolean containsIdentifier = false; for (final String value : values) { - if(isIdentifier(value)) { + if(ConstantDefinition.isIdentifier(value)) { containsIdentifier = true; break; } @@ -657,7 +692,8 @@ public class PCPP { final String oldDef = defineMap.put(name, value); if (oldDef != null && !oldDef.equals(value)) { - LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, value}); + LOG.log(WARNING, new ASTLocusTag(filename(), lineNumber(), -1, null), + "\"{0}\" redefined from \"{1}\" to \"{2}\"", name, oldDef, value); } debugPrint(true, "DEFINE " + name + " ["+oldDef+" ] -> "+value + " CONST"); // System.err.println("#define " + name +" "+value + " CONST EXPRESSION"); @@ -681,68 +717,6 @@ public class PCPP { //System.err.println("OUT HANDLE_DEFINE: " + name); } - private boolean isIdentifier(final String value) { - - boolean identifier = false; - - final char[] chars = value.toCharArray(); - - for (int i = 0; i < chars.length; i++) { - final char c = chars[i]; - if (i == 0) { - if (Character.isJavaIdentifierStart(c)) { - identifier = true; - } - } else { - if (!Character.isJavaIdentifierPart(c)) { - identifier = false; - break; - } - } - } - return identifier; - } - - private boolean isConstant(final String s) { - if (s.startsWith("0x") || s.startsWith("0X")) { - return checkHex(s); - } else { - return checkDecimal(s); - } - } - - private boolean checkHex(final String s) { - char c='\0'; - int i; - for (i = 2; i < s.length(); i++) { - c = s.charAt(i); - if (!((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'))) { - break; - } - } - if(i==s.length()) { - return true; - } else if(i==s.length()-1) { - // Const qualifier .. - return c == 'l' || c == 'L' || - c == 'f' || c == 'F' || - c == 'u' || c == 'U' ; - } - return false; - } - - private boolean checkDecimal(final String s) { - try { - Float.valueOf(s); - } catch (final NumberFormatException e) { - // not parsable as a number - return false; - } - return true; - } - private String resolveDefine(final String word, final boolean returnNullIfNotFound) { String lastWord = defineMap.get(word); if (lastWord == null) { @@ -920,6 +894,27 @@ public class PCPP { ifValue = false; } break; + case '*': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; + case '+': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; + case '-': + { + // NOTE: we don't handle expressions like this properly + final boolean rhs = handleIfRecursive(false); + ifValue = false; + } + break; case '=': { // NOTE: we don't handle expressions like this properly @@ -1008,7 +1003,8 @@ public class PCPP { buf.append(curTokenAsString()); } if (t == StreamTokenizer.TT_EOF) { - LOG.warning("unexpected EOF while processing #include directive"); + LOG.warning(new ASTLocusTag(filename(), lineNumber(), -1, null), + "unexpected EOF while processing #include directive"); } filename = buf.toString(); } diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java index 5c059c9..37a39e1 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java @@ -42,30 +42,35 @@ package com.jogamp.gluegen.procaddress; import com.jogamp.gluegen.CMethodBindingEmitter; import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.JavaType; + import java.io.*; + import com.jogamp.gluegen.cgram.types.*; public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { private boolean callThroughProcAddress; - private boolean needsLocalTypedef; + private boolean hasProcAddrTypedef; private String localTypedefCallingConvention; private static final String procAddressJavaTypeName = JavaType.createForClass(Long.TYPE).jniTypeName(); private ProcAddressEmitter emitter; - public ProcAddressCMethodBindingEmitter(final CMethodBindingEmitter methodToWrap, final boolean callThroughProcAddress, - final boolean needsLocalTypedef, final String localTypedefCallingConvention, final ProcAddressEmitter emitter) { + public ProcAddressCMethodBindingEmitter(final CMethodBindingEmitter methodToWrap, + final boolean callThroughProcAddress, + final boolean hasProcAddrTypedef, + final String localTypedefCallingConvention, + final ProcAddressEmitter emitter) { super( new MethodBinding(methodToWrap.getBinding()) { @Override - public String getName() { + public String getImplName() { if (callThroughProcAddress) { - return ProcAddressEmitter.WRAP_PREFIX + super.getName(); + return ProcAddressEmitter.WRAP_PREFIX + super.getImplName(); } else { - return super.getName(); + return super.getImplName(); } } }, @@ -76,9 +81,9 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { methodToWrap.getIsJavaMethodStatic(), true, methodToWrap.forIndirectBufferAndArrayImplementation(), - methodToWrap.getMachineDataInfo() + methodToWrap.getMachineDataInfo(), + emitter.getConfiguration() ); - if (methodToWrap.getReturnValueCapacityExpression() != null) { setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); } @@ -91,7 +96,7 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { setCommentEmitter(defaultCommentEmitter); this.callThroughProcAddress = callThroughProcAddress; - this.needsLocalTypedef = needsLocalTypedef; + this.hasProcAddrTypedef = hasProcAddrTypedef; this.localTypedefCallingConvention = localTypedefCallingConvention; this.emitter = emitter; } @@ -116,28 +121,31 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { if (callThroughProcAddress) { // create variable for the function pointer with the right type, and set // it to the value of the passed-in proc address - final FunctionSymbol cSym = getBinding().getCSymbol(); - String funcPointerTypedefName = - emitter.getFunctionPointerTypedefName(cSym); - - if (needsLocalTypedef) { - // We (probably) didn't get a typedef for this function - // pointer type in the header file; the user requested that we - // forcibly generate one. Here we force the emission of one. - final PointerType funcPtrType = new PointerType(null, cSym.getType(), 0); - // Just for safety, emit this name slightly differently than - // the mangling would otherwise produce - funcPointerTypedefName = "_local_" + funcPointerTypedefName; - - writer.print(" typedef "); - writer.print(funcPtrType.toString(funcPointerTypedefName, localTypedefCallingConvention)); - writer.println(";"); + final FunctionSymbol cSym = binding.getCSymbol(); + + // Always emit the local typedef, based on our parsing results. + // In case we do have the public typedef from the original header, + // we use it for the local var and assign our proc-handle to it, + // cast to the local typedef. + // This allows the native C compiler to validate our types! + final String funcPointerTypedefBaseName = emitter.getFunctionPointerTypedefName(cSym); + final String funcPointerTypedefLocalName = "_local_" + funcPointerTypedefBaseName; + final String funcPointerTypedefName; + if (hasProcAddrTypedef) { + funcPointerTypedefName = funcPointerTypedefBaseName; + } else { + funcPointerTypedefName = funcPointerTypedefLocalName; } + final PointerType funcPtrType = new PointerType(null, cSym.getType(), 0); + + writer.print(" typedef "); + writer.print(funcPtrType.toString(funcPointerTypedefLocalName, localTypedefCallingConvention)); + writer.println(";"); writer.print(" "); - writer.print(funcPointerTypedefName); + writer.print(funcPointerTypedefName); // Uses public typedef if available! writer.print(" ptr_"); - writer.print(cSym.getName()); + writer.print(getNativeName()); writer.println(";"); } @@ -150,18 +158,25 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { if (callThroughProcAddress) { // set the function pointer to the value of the passed-in procAddress - final FunctionSymbol cSym = getBinding().getCSymbol(); - String funcPointerTypedefName = emitter.getFunctionPointerTypedefName(cSym); - if (needsLocalTypedef) { - funcPointerTypedefName = "_local_" + funcPointerTypedefName; + // See above notes in emitBodyVariableDeclarations(..)! + final String funcPointerTypedefBaseName = emitter.getFunctionPointerTypedefName(binding.getCSymbol()); + final String funcPointerTypedefLocalName = "_local_" + funcPointerTypedefBaseName; + final String funcPointerTypedefName; + if (hasProcAddrTypedef) { + funcPointerTypedefName = funcPointerTypedefBaseName; + } else { + funcPointerTypedefName = funcPointerTypedefLocalName; } - final String ptrVarName = "ptr_" + cSym.getName(); + final String ptrVarName = "ptr_" + getNativeName(); + if (hasProcAddrTypedef) { + writer.println(" // implicit type validation of "+funcPointerTypedefLocalName+" -> "+funcPointerTypedefName); + } writer.print(" "); writer.print(ptrVarName); writer.print(" = ("); - writer.print(funcPointerTypedefName); + writer.print(funcPointerTypedefLocalName); writer.println(") (intptr_t) procAddress;"); writer.println(" assert(" + ptrVarName + " != NULL);"); @@ -181,7 +196,12 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { final Type cReturnType = binding.getCReturnType(); if (!cReturnType.isVoid()) { - writer.print("_res = "); + // Note we respect const/volatile in the function return type. + // However, we cannot have it 'const' for our local variable. + // See return type in CMethodBindingEmitter.emitBodyVariableDeclarations(..)! + writer.print("_res = ("); + writer.print(cReturnType.getCName(false)); + writer.print(") "); } final MethodBinding mBinding = getBinding(); if (mBinding.hasContainingType()) { @@ -192,7 +212,7 @@ public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { // call throught the run-time function pointer writer.print("(* ptr_"); - writer.print(mBinding.getCSymbol().getName()); + writer.print(getNativeName()); writer.print(") "); writer.print("("); emitBodyPassCArguments(writer); diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java index 0c5692b..36d433a 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java @@ -38,7 +38,12 @@ */ package com.jogamp.gluegen.procaddress; +import static java.util.logging.Level.INFO; + import com.jogamp.gluegen.JavaConfiguration; +import com.jogamp.gluegen.cgram.types.AliasedSymbol; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; + import java.io.*; import java.text.*; import java.util.*; @@ -269,8 +274,15 @@ public class ProcAddressConfiguration extends JavaConfiguration { return tableClassName; } - public boolean skipProcAddressGen(final String name) { - return skipProcAddressGen.contains(name); + public boolean skipProcAddressGen(final FunctionSymbol symbol) { + if ( skipProcAddressGen.contains( symbol.getName() ) || + oneInSet(skipProcAddressGen, symbol.getAliasedNames()) + ) + { + LOG.log(INFO, symbol.getASTLocusTag(), "Skip ProcAddress: {0}", symbol); + return true; + } + return false; } public boolean isForceProcAddressGen4All() { @@ -298,9 +310,25 @@ public class ProcAddressConfiguration extends JavaConfiguration { return procAddressNameConverter.convert(funcName); } - public boolean forceProcAddressGen(final String funcName) { - return forceProcAddressGen4All || forceProcAddressGenSet.contains(funcName); + public boolean forceProcAddressGen(final FunctionSymbol symbol) { + if( forceProcAddressGen4All ) { + if(!forceProcAddressGen4AllOnce) { + forceProcAddressGen4AllOnce = true; + LOG.log(INFO, symbol.getASTLocusTag(), "Force ALL ProcAddress"); + } + return true; + } + + if ( forceProcAddressGenSet.contains( symbol.getName() ) || + oneInSet(forceProcAddressGenSet, symbol.getAliasedNames()) + ) + { + LOG.log(INFO, symbol.getASTLocusTag(), "Force ProcAddress: {0}", symbol); + return true; + } + return false; } + private static boolean forceProcAddressGen4AllOnce = false; public void addForceProcAddressGen(final String funcName) { forceProcAddressGen.add(funcName); @@ -311,11 +339,15 @@ public class ProcAddressConfiguration extends JavaConfiguration { localProcAddressCallingConventionMap.put(funcName, callingConvention); } - public String getLocalProcAddressCallingConvention(final String funcName) { - if (isLocalProcAddressCallingConvention4All()) { + public String getLocalProcAddressCallingConvention(final FunctionSymbol symbol) { + if ( isLocalProcAddressCallingConvention4All() ) { return getLocalProcAddressCallingConvention4All(); } - return localProcAddressCallingConventionMap.get(funcName); + final String res = localProcAddressCallingConventionMap.get(symbol.getName()); + if( null != res ) { + return res; + } + return oneInMap(localProcAddressCallingConventionMap, symbol.getAliasedNames()); } public boolean isLocalProcAddressCallingConvention4All() { diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java index 4145cc4..ec29b08 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java @@ -47,6 +47,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Level; import com.jogamp.gluegen.CMethodBindingEmitter; import com.jogamp.gluegen.CodeGenUtils; @@ -54,7 +55,6 @@ import com.jogamp.gluegen.FunctionEmitter; import com.jogamp.gluegen.JavaConfiguration; import com.jogamp.gluegen.JavaEmitter; import com.jogamp.gluegen.JavaMethodBindingEmitter; -import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import com.jogamp.gluegen.cgram.types.TypeDictionary; @@ -114,40 +114,45 @@ public class ProcAddressEmitter extends JavaEmitter { } @Override - protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - return generateMethodBindingEmittersImpl(methodBindingSet, sym); + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final FunctionSymbol sym) throws Exception { + return generateMethodBindingEmittersImpl(sym); } protected boolean needsModifiedEmitters(final FunctionSymbol sym) { - if (!needsProcAddressWrapper(sym) || getConfig().isUnimplemented(getAliasedSymName(sym))) { + if ( !callThroughProcAddress(sym) || getConfig().isUnimplemented(sym) ) { return false; + } else { + return true; } - - return true; } - private List<? extends FunctionEmitter> generateMethodBindingEmittersImpl(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { - final List<? extends FunctionEmitter> defaultEmitters = super.generateMethodBindingEmitters(methodBindingSet, sym); + private List<? extends FunctionEmitter> generateMethodBindingEmittersImpl(final FunctionSymbol sym) throws Exception { + final List<? extends FunctionEmitter> defaultEmitters = super.generateMethodBindingEmitters(sym); // if the superclass didn't generate any bindings for the symbol, let's // honor that (for example, the superclass might have caught an Ignore // direction that matched the symbol's name). if (defaultEmitters.isEmpty()) { + LOG.log(Level.INFO, sym.getASTLocusTag(), "genModProcAddrEmitter: SKIP, empty binding set: {0}", sym); return defaultEmitters; } - // Don't do anything special if this symbol doesn't require - // modifications - if (!needsModifiedEmitters(sym)) { + final boolean callThroughProcAddress = callThroughProcAddress(sym); + final boolean isUnimplemented = getConfig().isUnimplemented(sym); + + // Don't do anything special if this symbol doesn't require modifications + if( !callThroughProcAddress || isUnimplemented ) { + LOG.log(Level.INFO, sym.getASTLocusTag(), "genModProcAddrEmitter: SKIP, not needed: callThrough {0}, isUnimplemented {1}: {2}", + callThroughProcAddress, isUnimplemented, sym); return defaultEmitters; } final ArrayList<FunctionEmitter> modifiedEmitters = new ArrayList<FunctionEmitter>(defaultEmitters.size()); - if (needsProcAddressWrapper(sym)) { + if ( callThroughProcAddress ) { if (getProcAddressConfig().emitProcAddressTable()) { // emit an entry in the GL proc address table for this method. - emitProcAddressTableEntryForString(getAliasedSymName(sym)); + emitProcAddressTableEntryForString(sym.getName()); } } for (final FunctionEmitter emitter : defaultEmitters) { @@ -172,7 +177,7 @@ public class ProcAddressEmitter extends JavaEmitter { * whether or not the typedef is actually defined. */ protected String getFunctionPointerTypedefName(final FunctionSymbol sym) { - return getProcAddressConfig().convertToFunctionPointerName(sym.getName()); + return getProcAddressConfig().convertToFunctionPointerName(sym.getOrigName()); } //---------------------------------------------------------------------- @@ -194,20 +199,14 @@ public class ProcAddressEmitter extends JavaEmitter { protected void generateModifiedEmitters(final JavaMethodBindingEmitter baseJavaEmitter, final List<FunctionEmitter> emitters) { // See whether we need a proc address entry for this one - final boolean callThroughProcAddress = needsProcAddressWrapper(baseJavaEmitter.getBinding().getCSymbol()); + final boolean callThroughProcAddress = callThroughProcAddress(baseJavaEmitter.getBinding().getCSymbol()); // If this emitter doesn't have a body (i.e., is a direct native // call with no intervening argument processing), we need to force - // it to emit a body, and produce another one to act as the entry - // point - // FIXME: the negative test against the PRIVATE modifier is a - // nasty hack to prevent the ProcAddressJavaMethodBindingEmitter - // from incorrectly introducing method bodies to the private - // native implementing methods; want this to work at least for - // public and package-private methods + // it to emit a body, and produce another one to act as the entry point final boolean needsJavaWrapper = baseJavaEmitter.signatureOnly() && - !baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.PRIVATE) && - baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.NATIVE) && + baseJavaEmitter.isNativeMethod() && + !baseJavaEmitter.isPrivateNativeMethod() && callThroughProcAddress; @@ -215,7 +214,7 @@ public class ProcAddressEmitter extends JavaEmitter { final ProcAddressJavaMethodBindingEmitter emitter = new ProcAddressJavaMethodBindingEmitter(baseJavaEmitter, callThroughProcAddress, getProcAddressConfig().getProcAddressTableExpr(), - baseJavaEmitter.isForImplementingMethodCall(), + baseJavaEmitter.isPrivateNativeMethod(), this); if( needsJavaWrapper ) { emitter.setEmitBody(true); @@ -232,7 +231,7 @@ public class ProcAddressEmitter extends JavaEmitter { getProcAddressConfig().getProcAddressTableExpr(), true, this); - emitter.setForImplementingMethodCall(true); + emitter.setPrivateNativeMethod(true); fixSecurityModifiers(emitter); emitters.add(emitter); } @@ -243,13 +242,13 @@ public class ProcAddressEmitter extends JavaEmitter { final FunctionSymbol cSymbol = baseCEmitter.getBinding().getCSymbol(); // See whether we need a proc address entry for this one - final boolean callThroughProcAddress = needsProcAddressWrapper(cSymbol); - final boolean forceProcAddress = getProcAddressConfig().forceProcAddressGen(cSymbol.getName()); + final boolean hasProcAddrTypedef = hasFunctionPointerTypedef(cSymbol); + final boolean callThroughProcAddress = hasProcAddrTypedef || callThroughProcAddress(cSymbol); + final String localProcCallingConvention = getProcAddressConfig().getLocalProcAddressCallingConvention(cSymbol); + + LOG.log(Level.INFO, cSymbol.getASTLocusTag(), "genModProcAddrEmitter: callThrough {0}, hasTypedef {1}, localCallConv {2}: {3}", + callThroughProcAddress, hasProcAddrTypedef, localProcCallingConvention, cSymbol); - String forcedCallingConvention = null; - if (forceProcAddress) { - forcedCallingConvention = getProcAddressConfig().getLocalProcAddressCallingConvention(cSymbol.getName()); - } // Note that we don't care much about the naming of the C argument // variables so to keep things simple we ignore the buffer object // property for the binding @@ -258,7 +257,7 @@ public class ProcAddressEmitter extends JavaEmitter { // extra final argument, which is the address (the OpenGL procedure // address) of the function it needs to call final ProcAddressCMethodBindingEmitter res = new ProcAddressCMethodBindingEmitter( - baseCEmitter, callThroughProcAddress, forceProcAddress, forcedCallingConvention, this); + baseCEmitter, callThroughProcAddress, hasProcAddrTypedef, localProcCallingConvention, this); final MessageFormat exp = baseCEmitter.getReturnValueCapacityExpression(); if (exp != null) { @@ -267,34 +266,30 @@ public class ProcAddressEmitter extends JavaEmitter { emitters.add(res); } - private String getAliasedSymName(final FunctionSymbol sym) { - String symName = getConfig().getJavaSymbolRename(sym.getName()); - if (null == symName) { - symName = sym.getName(); + protected boolean callThroughProcAddress(final FunctionSymbol sym) { + final ProcAddressConfiguration cfg = getProcAddressConfig(); + boolean res = false; + int mode = 0; + if (cfg.forceProcAddressGen(sym)) { + res = true; + mode = 1; + } else { + if (cfg.skipProcAddressGen(sym)) { + res = false; + mode = 2; + } else { + res = hasFunctionPointerTypedef(sym); + mode = 3; + } } - return symName; + LOG.log(Level.INFO, sym.getASTLocusTag(), "callThroughProcAddress: {0} [m {1}]: {2}", res, mode, sym); + return res; } - - protected boolean needsProcAddressWrapper(final FunctionSymbol sym) { - final String symName = getAliasedSymName(sym); - - final ProcAddressConfiguration config = getProcAddressConfig(); - - // We should only generate code to call through a function pointer - // if the symbol has an associated function pointer typedef. + protected boolean hasFunctionPointerTypedef(final FunctionSymbol sym) { final String funcPointerTypedefName = getFunctionPointerTypedefName(sym); - boolean shouldWrap = typedefDictionary.containsKey(funcPointerTypedefName); - //System.err.println(funcPointerTypedefName + " defined: " + shouldWrap); - - if (config.skipProcAddressGen(symName)) { - shouldWrap = false; - } - - if (config.forceProcAddressGen(symName)) { - shouldWrap = true; - } - - return shouldWrap; + final boolean res = typedefDictionary.containsKey(funcPointerTypedefName); + LOG.log(Level.INFO, sym.getASTLocusTag(), "hasFunctionPointerTypedef: {0}: {1}", res, sym); + return res; } protected void beginProcAddressTable() throws Exception { diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java index a70c18d..5298a8d 100644 --- a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java @@ -41,6 +41,7 @@ package com.jogamp.gluegen.procaddress; import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.FunctionEmitter; import com.jogamp.gluegen.JavaMethodBindingEmitter; + import java.io.*; /** A specialization of JavaMethodBindingEmitter with knowledge of how @@ -76,12 +77,12 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte public ProcAddressJavaMethodBindingEmitter(final ProcAddressJavaMethodBindingEmitter methodToWrap) { this(methodToWrap, methodToWrap.callThroughProcAddress, methodToWrap.getProcAddressTableExpr, - methodToWrap.changeNameAndArguments, methodToWrap.emitter); + methodToWrap.changeNameAndArguments, methodToWrap.emitter); } @Override - public String getName() { - final String res = super.getName(); + public String getImplName() { + final String res = super.getImplName(); if (changeNameAndArguments) { return ProcAddressEmitter.WRAP_PREFIX + res; } @@ -106,8 +107,8 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte } @Override - protected String getImplMethodName() { - final String name = super.getImplMethodName(); + protected String getNativeImplMethodName() { + final String name = super.getNativeImplMethodName(); if (callThroughProcAddress) { return ProcAddressEmitter.WRAP_PREFIX + name; } @@ -119,7 +120,7 @@ public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitte super.emitPreCallSetup(binding, writer); if (callThroughProcAddress) { - final String procAddressVariable = ProcAddressEmitter.PROCADDRESS_VAR_PREFIX + binding.getName(); + final String procAddressVariable = ProcAddressEmitter.PROCADDRESS_VAR_PREFIX + binding.getNativeName(); writer.println(" final long __addr_ = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (__addr_ == 0) {"); writer.format(" throw new %s(String.format(\"Method \\\"%%s\\\" not available\", \"%s\"));%n", diff --git a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java index c4dedb7..7ccfd1b 100644 --- a/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java +++ b/src/java/com/jogamp/gluegen/structgen/CStructAnnotationProcessor.java @@ -131,7 +131,11 @@ public class CStructAnnotationProcessor extends AbstractProcessor { if( f.exists() ) { return f; } - } catch (final IOException e) { if(DEBUG) { System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage()); /* e.printStackTrace(); */ } } + } catch (final IOException e) { + if(DEBUG) { + System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage()); /* e.printStackTrace(); */ + } + } return null; } @@ -263,6 +267,9 @@ public class CStructAnnotationProcessor extends AbstractProcessor { } catch (final FileNotFoundException ex) { throw new RuntimeException("input file not found", ex); } + if( DEBUG ) { + GlueGen.setDebug(true); + } new GlueGen().run(reader, filename, AnnotationProcessorJavaStructEmitter.class, includePaths, cfgFiles, outputPath1, false /* copyCPPOutput2Stderr */); diff --git a/src/java/jogamp/android/launcher/MainLauncher.java b/src/java/jogamp/android/launcher/MainLauncher.java index 0dc6b4a..e0eff7d 100644 --- a/src/java/jogamp/android/launcher/MainLauncher.java +++ b/src/java/jogamp/android/launcher/MainLauncher.java @@ -33,6 +33,8 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import com.jogamp.common.util.InterruptSource; + import android.app.Activity; import android.net.Uri; import android.os.Bundle; @@ -117,17 +119,17 @@ public class MainLauncher extends Activity { public void onResume() { Log.d(TAG, "onResume - S - "+Thread.currentThread().getName()); super.onResume(); - final Thread mainThread = new Thread("Main") { + final Thread mainThread = new InterruptSource.Thread(null, null, "Main") { public void run() { try { - Log.d(TAG, "onResume - main.0 - "+Thread.currentThread().getName()); + Log.d(TAG, "onResume - main.0 - "+java.lang.Thread.currentThread().getName()); mainClazzMain.invoke(null, new Object[] { mainClassArgs } ); } catch (final InvocationTargetException ite) { ite.getTargetException().printStackTrace(); } catch (final Throwable t) { t.printStackTrace(); } - Log.d(TAG, "onResume - main.X -> finish() - "+Thread.currentThread().getName()); + Log.d(TAG, "onResume - main.X -> finish() - "+java.lang.Thread.currentThread().getName()); finish(); } }; mainThread.start(); diff --git a/src/java/jogamp/common/os/PlatformPropsImpl.java b/src/java/jogamp/common/os/PlatformPropsImpl.java index 2d8bdec..fdd6b7f 100644 --- a/src/java/jogamp/common/os/PlatformPropsImpl.java +++ b/src/java/jogamp/common/os/PlatformPropsImpl.java @@ -63,6 +63,10 @@ public abstract class PlatformPropsImpl { public static final VersionNumber Version16; /** Version 1.7. As a JVM version, it enables certain JVM 1.7 features. */ public static final VersionNumber Version17; + /** Version 1.8. As a JVM version, it enables certain JVM 1.8 features. */ + public static final VersionNumber Version18; + /** Version 1.9. As a JVM version, it enables certain JVM 1.9 features. */ + public static final VersionNumber Version19; public static final String OS; public static final String OS_lower; @@ -101,6 +105,8 @@ public abstract class PlatformPropsImpl { static { Version16 = new VersionNumber(1, 6, 0); Version17 = new VersionNumber(1, 7, 0); + Version18 = new VersionNumber(1, 8, 0); + Version19 = new VersionNumber(1, 9, 0); // We don't seem to need an AccessController.doPrivileged() block // here as these system properties are visible even to unsigned Applets. diff --git a/src/java/jogamp/common/util/Int32ArrayBitfield.java b/src/java/jogamp/common/util/Int32ArrayBitfield.java new file mode 100644 index 0000000..5bc95eb --- /dev/null +++ b/src/java/jogamp/common/util/Int32ArrayBitfield.java @@ -0,0 +1,207 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple bitfield interface for efficient storage access in O(1). + * <p> + * Implementation uses a 32bit integer array for storage. + * </p> + */ +public class Int32ArrayBitfield implements Bitfield { + private static final int UNIT_SHIFT = 5; + private final int[] storage; + private final int bitSize; + + /** + * @param storageBitSize + */ + public Int32ArrayBitfield(final int storageBitSize) { + final int units = Math.max(1, ( storageBitSize + 31 ) >>> UNIT_SHIFT); + this.storage = new int[units]; // initialized w/ default '0' + this.bitSize = units << UNIT_SHIFT; + } + + @Override + public int size() { + return bitSize; + } + + @Override + public final void clearField(final boolean bit) { + final int v; + if( bit ) { + v = Bitfield.UNSIGNED_INT_MAX_VALUE; + } else { + v = 0; + } + for(int i=storage.length-1; i>=0; i--) { + storage[i] = v; + } + } + + private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException { + if( 0 > bitnum || bitnum >= size ) { + throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum); + } + } + + @Override + public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(bitSize-length+1, lowBitnum); + final int u = lowBitnum >>> UNIT_SHIFT; + final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + return m & storage[u]; + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + final int d = m & ( storage[u] >>> lowBitnum ); + final int l2 = length - l; // length of last chunk < 32 + if( l2 > 0 ) { + final int m2 = ( 1 << l2 ) - 1; // mask of last chunk + return d | ( ( m2 & storage[u+1] ) << l ); + } else { + return d; + } + } + } + @Override + public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(bitSize-length+1, lowBitnum); + final int u = lowBitnum >>> UNIT_SHIFT; + final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + storage[u] = ( ( ~m ) & storage[u] ) // keep non-written storage bits + | ( m & data ); // overwrite storage w/ used data bits + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + storage[u] = ( ( ~( m << lowBitnum ) ) & storage[u] ) // keep non-written storage bits + | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits + final int l2 = length - l; // length of last chunk < 32 + if( l2 > 0 ) { + final int m2 = ( 1 << l2 ) - 1; // mask of last chunk + storage[u+1] = ( ( ~m2 ) & storage[u+1] ) // keep non-written storage bits + | ( m2 & ( data >>> l ) ); // overwrite storage w/ used data bits + } + } + } + @Override + public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException { + final int data = get32(srcBitnum, length); + put32(dstBitnum, length, data); + return data; + } + + @Override + public final boolean get(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + return 0 != ( storage[u] & ( 1 << b ) ) ; + } + + @Override + public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + final boolean prev = 0 != ( storage[u] & m ) ; + if( prev != bit ) { + if( bit ) { + storage[u] |= m; + } else { + storage[u] &= ~m; + } + } + return prev; + } + @Override + public final void set(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + storage[u] |= m; + } + @Override + public final void clear(final int bitnum) throws IndexOutOfBoundsException { + check(bitSize, bitnum); + final int u = bitnum >>> UNIT_SHIFT; + final int b = bitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + storage[u] &= ~m; + } + @Override + public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + check(bitSize, srcBitnum); + check(bitSize, dstBitnum); + final boolean bit; + // get + { + final int u = srcBitnum >>> UNIT_SHIFT; + final int b = srcBitnum - ( u << UNIT_SHIFT ); + bit = 0 != ( storage[u] & ( 1 << b ) ) ; + } + // put + final int u = dstBitnum >>> UNIT_SHIFT; + final int b = dstBitnum - ( u << UNIT_SHIFT ); + final int m = 1 << b; + if( bit ) { + storage[u] |= m; + } else { + storage[u] &= ~m; + } + return bit; + } + + @Override + public int bitCount() { + int c = 0; + for(int i = storage.length-1; i>=0; i--) { + c += Bitfield.Util.bitCount(storage[i]); + } + return c; + } +} diff --git a/src/java/jogamp/common/util/Int32Bitfield.java b/src/java/jogamp/common/util/Int32Bitfield.java new file mode 100644 index 0000000..7b55a59 --- /dev/null +++ b/src/java/jogamp/common/util/Int32Bitfield.java @@ -0,0 +1,163 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple bitfield interface for efficient storage access in O(1). + * <p> + * Implementation uses one 32bit integer field for storage. + * </p> + */ +public class Int32Bitfield implements Bitfield { + /** Unit size in bits, here 32 bits for one int unit. */ + private static final int UNIT_SIZE = 32; + private int storage; + + public Int32Bitfield() { + this.storage = 0; + } + + @Override + public int size() { + return UNIT_SIZE; + } + + @Override + public final void clearField(final boolean bit) { + if( bit ) { + storage = Bitfield.UNSIGNED_INT_MAX_VALUE; + } else { + storage = 0; + } + } + + private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException { + if( 0 > bitnum || bitnum >= size ) { + throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum); + } + } + + @Override + public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(UNIT_SIZE-length+1, lowBitnum); + final int left = 32 - lowBitnum; // remaining bits of first chunk + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + return m & storage; + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + return m & ( storage >>> lowBitnum ); + } + } + @Override + public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + if( 0 > length || length > 32 ) { + throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length); + } + check(UNIT_SIZE-length+1, lowBitnum); + final int left = 32 - lowBitnum; // remaining bits of first chunk storage + if( 32 == left ) { + // fast path + final int m = Util.getBitMask(length); // mask of chunk + storage = ( ( ~m ) & storage ) // keep non-written storage bits + | ( m & data ); // overwrite storage w/ used data bits + } else { + // slow path + final int l = Math.min(length, left); // length of first chunk < 32 + final int m = ( 1 << l ) - 1; // mask of first chunk + storage = ( ( ~( m << lowBitnum ) ) & storage ) // keep non-written storage bits + | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits + } + } + @Override + public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException { + final int data = get32(srcBitnum, length); + put32(dstBitnum, length, data); + return data; + } + + @Override + public final boolean get(final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + return 0 != ( storage & ( 1 << bitnum ) ) ; + } + @Override + public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + final boolean prev = 0 != ( storage & m ) ; + if( prev != bit ) { + if( bit ) { + storage |= m; + } else { + storage &= ~m; + } + } + return prev; + } + @Override + public final void set(final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + storage |= m; + } + @Override + public final void clear (final int bitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, bitnum); + final int m = 1 << bitnum; + storage &= ~m; + } + @Override + public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + check(UNIT_SIZE, srcBitnum); + check(UNIT_SIZE, dstBitnum); + // get + final boolean bit = 0 != ( storage & ( 1 << srcBitnum ) ) ; + // put + final int m = 1 << dstBitnum; + if( bit ) { + storage |= m; + } else { + storage &= ~m; + } + return bit; + } + + @Override + public int bitCount() { + return Bitfield.Util.bitCount(storage); + } +} diff --git a/src/java/jogamp/common/util/SyncedBitfield.java b/src/java/jogamp/common/util/SyncedBitfield.java new file mode 100644 index 0000000..49c27b0 --- /dev/null +++ b/src/java/jogamp/common/util/SyncedBitfield.java @@ -0,0 +1,96 @@ +/** + * Copyright 2015 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.util; + +import com.jogamp.common.util.Bitfield; + +/** + * Simple synchronized {@link Bitfield} by wrapping an existing {@link Bitfield}. + */ +public class SyncedBitfield implements Bitfield { + private final Bitfield impl; + + public SyncedBitfield(final Bitfield impl) { + this.impl = impl; + } + + @Override + public final synchronized int size() { + return impl.size(); + } + + @Override + public final synchronized void clearField(final boolean bit) { + impl.clearField(bit); + } + + @Override + public final synchronized int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException { + return impl.get32(lowBitnum, length); + } + + @Override + public final synchronized void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException { + impl.put32(lowBitnum, length, data); + } + + @Override + public final synchronized int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException { + return impl.copy32(srcLowBitnum, dstLowBitnum, length); + } + + @Override + public final synchronized boolean get(final int bitnum) throws IndexOutOfBoundsException { + return impl.get(bitnum); + } + + @Override + public final synchronized boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException { + return impl.put(bitnum, bit); + } + + @Override + public final synchronized void set(final int bitnum) throws IndexOutOfBoundsException { + impl.set(bitnum); + } + + @Override + public final synchronized void clear(final int bitnum) throws IndexOutOfBoundsException { + impl.clear(bitnum); + } + + @Override + public final synchronized boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException { + return impl.copy(srcBitnum, dstBitnum); + } + + @Override + public final synchronized int bitCount() { + return impl.bitCount(); + } +}
\ No newline at end of file diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java index c930dff..1286924 100644 --- a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java +++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.AbstractOwnableSynchronizer; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.RecursiveLock; /** @@ -197,7 +198,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock { } catch (final InterruptedException e) { if( !wCur.signaledByUnlock ) { sync.queue.remove(wCur); // O(n) - throw e; // propagate interruption not send by unlock + throw SourcedInterruptedException.wrap(e); // propagate interruption not send by unlock } else if( cur != sync.getOwner() ) { // Issued by unlock, but still locked by other thread // @@ -215,6 +216,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock { } // else: Issued by unlock, owning lock .. expected! } } while ( cur != sync.getOwner() && 0 < timeout ) ; + Thread.interrupted(); // clear slipped interrupt if( 0 >= timeout && cur != sync.getOwner() ) { // timed out diff --git a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java index 77f73d8..fc5f739 100644 --- a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java +++ b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java @@ -42,6 +42,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish threadNum = 0; threads = null; holdCountAdditionOwner = 0; + waitingOrigOwner = null; } @Override public final void incrHoldCount(final Thread t) { @@ -64,6 +65,12 @@ public class RecursiveThreadGroupLockImpl01Unfairish public final boolean isOriginalOwner(final Thread t) { return super.isOwner(t); } + public final void setWaitingOrigOwner(final Thread origOwner) { + waitingOrigOwner = origOwner; + } + public final Thread getWaitingOrigOwner() { + return waitingOrigOwner; + } @Override public final boolean isOwner(final Thread t) { if(getExclusiveOwnerThread()==t) { @@ -133,6 +140,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish private int holdCountAdditionOwner; private Thread[] threads; private int threadNum; + private Thread waitingOrigOwner; } public RecursiveThreadGroupLockImpl01Unfairish() { @@ -157,10 +165,10 @@ public class RecursiveThreadGroupLockImpl01Unfairish final Thread cur = Thread.currentThread(); final ThreadGroupSync tgSync = (ThreadGroupSync)sync; if(!tgSync.isOriginalOwner(cur)) { - throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur); + throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur+": "+toString()); } if(tgSync.isOriginalOwner(t)) { - throw new IllegalArgumentException("Passed thread is original owner: "+t); + throw new IllegalArgumentException("Passed thread is original owner: "+t+", "+toString()); } tgSync.addOwner(t); } @@ -179,19 +187,25 @@ public class RecursiveThreadGroupLockImpl01Unfairish // original locking owner thread if( tgSync.getHoldCount() - tgSync.getAdditionalOwnerHoldCount() == 1 ) { // release orig. lock - while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) { - try { - sync.wait(); - } catch (final InterruptedException e) { - // regular wake up! + tgSync.setWaitingOrigOwner(cur); + try { + while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) { + try { + sync.wait(); + } catch (final InterruptedException e) { + // regular wake up! + } } + } finally { + tgSync.setWaitingOrigOwner(null); + Thread.interrupted(); // clear slipped interrupt } tgSync.removeAllOwners(); } } else if( tgSync.getAdditionalOwnerHoldCount() == 1 ) { - // last additional owner thread wakes up original owner - final Thread originalOwner = tgSync.getOwner(); - if(originalOwner.getState() == Thread.State.WAITING) { + // last additional owner thread wakes up original owner if waiting in unlock(..) + final Thread originalOwner = tgSync.getWaitingOrigOwner(); + if( null != originalOwner ) { originalOwner.interrupt(); } } diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java index 44a5d28..9fe7966 100644 --- a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java +++ b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java @@ -32,6 +32,8 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; + +import com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.locks.SingletonInstance; public class SingletonInstanceFileLock extends SingletonInstance { @@ -76,7 +78,7 @@ public class SingletonInstanceFileLock extends SingletonInstance { private void setupFileCleanup() { file.deleteOnExit(); - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { @Override public void run() { if(isLocked()) { diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java index b1b42c3..6219b5c 100644 --- a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java +++ b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java @@ -33,10 +33,16 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.SingletonInstance; public class SingletonInstanceServerSocket extends SingletonInstance { + private static int serverInstanceCount = 0; private final Server singletonServer; private final String fullName; @@ -71,7 +77,7 @@ public class SingletonInstanceServerSocket extends SingletonInstance { fullName = ilh.toString()+":"+portNumber; singletonServer = new Server(ilh, portNumber); - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { @Override public void run() { singletonServer.kill(); @@ -139,38 +145,58 @@ public class SingletonInstanceServerSocket extends SingletonInstance { public final boolean start() { if(alive) return true; + final String sname; + synchronized (Server.class) { + serverInstanceCount++; + sname = "SingletonServerSocket"+serverInstanceCount+"-"+fullName; + } synchronized (syncOnStartStop) { - serverThread = new Thread(this); + shallQuit = false; + serverThread = new InterruptSource.Thread(null, this, sname); serverThread.setDaemon(true); // be a daemon, don't keep the JVM running serverThread.start(); try { - syncOnStartStop.wait(); + while( !alive && !shallQuit ) { + syncOnStartStop.wait(); + } } catch (final InterruptedException ie) { - ie.printStackTrace(); + final InterruptedException ie2 = SourcedInterruptedException.wrap(ie); + shutdown(false); + throw new InterruptedRuntimeException(ie2); } } final boolean ok = isBound(); if(!ok) { - shutdown(); + shutdown(true); } return ok; } public final boolean shutdown() { + return shutdown(true); + } + private final boolean shutdown(final boolean wait) { if(!alive) return true; - synchronized (syncOnStartStop) { - shallQuit = true; - connect(); - try { - syncOnStartStop.wait(); - } catch (final InterruptedException ie) { - ie.printStackTrace(); + try { + synchronized (syncOnStartStop) { + shallQuit = true; + connect(); + if( wait ) { + try { + while( alive ) { + syncOnStartStop.wait(); + } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + } + } + } finally { + if(alive) { + System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); + kill(); } - } - if(alive) { - System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); - kill(); } return true; } @@ -185,7 +211,8 @@ public class SingletonInstanceServerSocket extends SingletonInstance { System.err.println(infoPrefix()+" XXX "+getName()+" - Kill @ JVM Shutdown"); } alive = false; - if(null != serverThread) { + shallQuit = false; + if(null != serverThread && serverThread.isAlive() ) { try { serverThread.stop(); } catch(final Throwable t) { } @@ -214,47 +241,49 @@ public class SingletonInstanceServerSocket extends SingletonInstance { @Override public void run() { - { - final Thread currentThread = Thread.currentThread(); - currentThread.setName(currentThread.getName() + " - SISock: "+getName()); - if(DEBUG) { - System.err.println(currentThread.getName()+" - started"); - } + if(DEBUG) { + System.err.println(infoPrefix()+" III - Start"); } - alive = false; - synchronized (syncOnStartStop) { - try { - serverSocket = new ServerSocket(portNumber, 1, localInetAddress); - serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed - alive = true; - } catch (final IOException e) { - System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); - shallQuit = true; - } finally { - syncOnStartStop.notifyAll(); + try { + synchronized (syncOnStartStop) { + try { + serverSocket = new ServerSocket(portNumber, 1, localInetAddress); + serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed + alive = true; + } catch (final IOException e) { + System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); + shallQuit = true; + } finally { + syncOnStartStop.notifyAll(); + } } - } - while (!shallQuit) { - try { - final Socket clientSocket = serverSocket.accept(); - clientSocket.close(); - } catch (final IOException ioe) { - System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + while (!shallQuit) { + try { + final Socket clientSocket = serverSocket.accept(); + clientSocket.close(); + } catch (final IOException ioe) { + System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + } } - } - - synchronized (syncOnStartStop) { - try { + } catch(final ThreadDeath td) { + if( DEBUG ) { + ExceptionUtils.dumpThrowable("", td); + } + } finally { + synchronized (syncOnStartStop) { + if(DEBUG) { + System.err.println(infoPrefix()+" III - Stopping: alive "+alive+", shallQuit "+shallQuit+", hasSocket "+(null!=serverSocket)); + } if(null != serverSocket) { - serverSocket.close(); + try { + serverSocket.close(); + } catch (final IOException e) { + System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); + } } - } catch (final IOException e) { - System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); - } finally { serverSocket = null; alive = false; - shallQuit = false; syncOnStartStop.notifyAll(); } } |