diff options
author | Sven Gothel <[email protected]> | 2010-11-06 23:13:39 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2010-11-06 23:13:39 +0100 |
commit | 6f2d046c8d532db94f6af5003e341104d5bf4aff (patch) | |
tree | 723c31b8f9c1097ae48486acbf68e4e06fab2517 /src/java/com/jogamp/gluegen | |
parent | ec6d61f4597af32c22319c4bda3c9dd9ab80bf25 (diff) |
Renamed com.sun.gluegen -> com.jogamp.gluegen
Diffstat (limited to 'src/java/com/jogamp/gluegen')
66 files changed, 22684 insertions, 0 deletions
diff --git a/src/java/com/jogamp/gluegen/ArrayTypes.java b/src/java/com/jogamp/gluegen/ArrayTypes.java new file mode 100644 index 0000000..78122f1 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ArrayTypes.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.nio.*; + +/** + * Convenience class containing the Class objects corresponding to arrays of + * various types (e.g., {@link #booleanArrayClass} is the Class of Java type + * "boolean[]"). + */ +public class ArrayTypes { + /** Class for Java type boolean[] */ + public static final Class<?> booleanArrayClass; + /** Class for Java type byte[] */ + public static final Class<?> byteArrayClass; + /** Class for Java type char[] */ + public static final Class<?> charArrayClass; + /** Class for Java type short[] */ + public static final Class<?> shortArrayClass; + /** Class for Java type int[] */ + public static final Class<?> intArrayClass; + /** Class for Java type long[] */ + public static final Class<?> longArrayClass; + /** Class for Java type float[] */ + public static final Class<?> floatArrayClass; + /** Class for Java type double[] */ + public static final Class<?> doubleArrayClass; + /** Class for Java type String[] */ + public static final Class<?> stringArrayClass; + + // Classes for two-dimensional arrays. + // + // GlueGen converts C types like int** into Java arrays of direct + // buffers of the appropriate type (like IntBuffer[]). If the tool + // supported conversions like byte[][] -> char**, it would + // effectively be necessary to copy all of the data from the Java + // heap to the C heap during each call. The reason for this is that + // if we wanted to use GetPrimitiveArrayCritical to lock down the + // storage for each individual array element, we would need to fetch + // each element of the two-dimensional Java array into temporary + // storage before making the first GetPrimitiveArrayCritical call, + // since one can not call GetObjectArrayElement inside a Get / + // ReleasePrimitiveArrayCritical pair. This means that we would need + // two top-level pieces of temporary storage for the two-dimensional + // array as well as two loops to set up the contents, which would be + // too complicated. + // + // The one concession we make is converting String[] -> char**. The + // JVM takes care of the C heap allocation for GetStringUTFChars and + // ReleaseStringUTFChars, and this conversion is important for + // certain OpenGL operations. + + /** Class for Java type Buffer[] */ + public static final Class<?> bufferArrayClass; + /** Class for Java type ByteBuffer[] */ + public static final Class<?> byteBufferArrayClass; + /** Class for Java type ShortBuffer[] */ + public static final Class<?> shortBufferArrayClass; + /** Class for Java type IntBuffer[] */ + public static final Class<?> intBufferArrayClass; + /** Class for Java type LongBuffer[] */ + public static final Class<?> longBufferArrayClass; + /** Class for Java type FloatBuffer[] */ + public static final Class<?> floatBufferArrayClass; + /** Class for Java type DoubleBuffer[] */ + public static final Class<?> doubleBufferArrayClass; + + static { + booleanArrayClass = new boolean[0].getClass(); + byteArrayClass = new byte [0].getClass(); + charArrayClass = new char [0].getClass(); + shortArrayClass = new short [0].getClass(); + intArrayClass = new int [0].getClass(); + longArrayClass = new long [0].getClass(); + floatArrayClass = new float [0].getClass(); + doubleArrayClass = new double [0].getClass(); + stringArrayClass = new String [0].getClass(); + + bufferArrayClass = new Buffer [0].getClass(); + byteBufferArrayClass = new ByteBuffer [0].getClass(); + shortBufferArrayClass = new ShortBuffer [0].getClass(); + intBufferArrayClass = new IntBuffer [0].getClass(); + longBufferArrayClass = new LongBuffer [0].getClass(); + floatBufferArrayClass = new FloatBuffer [0].getClass(); + doubleBufferArrayClass = new DoubleBuffer[0].getClass(); + } +} diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java new file mode 100644 index 0000000..ba2eb17 --- /dev/null +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -0,0 +1,1505 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.util.*; +import java.io.*; +import java.text.MessageFormat; + +import com.jogamp.gluegen.cgram.types.*; +import java.util.logging.Logger; + +import static java.util.logging.Level.*; + +/** 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 MethodBinding binding; + + /** Name of the package in which the corresponding Java method resides.*/ + private String packageName; + + /** Name of the class in which the corresponding Java method resides.*/ + private String className; + + /** + * Whether or not the Java<->C JNI binding for this emitter's MethodBinding + * is overloaded. + */ + private boolean isOverloadedBinding; + + /** + * Whether or not the Java-side of the Java<->C JNI binding for this + * emitter's MethodBinding is static. + */ + private boolean isJavaMethodStatic; + + // Flags which change various aspects of glue code generation + protected boolean forImplementingMethodCall; + protected boolean forIndirectBufferAndArrayImplementation; + + /** + * Optional List of Strings containing temporary C variables to declare. + */ + private List<String> temporaryCVariableDeclarations; + + /** + * Optional List of Strings containing assignments to temporary C variables + * to make after the call is completed. + */ + private List<String> temporaryCVariableAssignments; + + /** + * Capacity of the return value in the event that it is encapsulated in a + * java.nio.Buffer. Is ignored if binding.getJavaReturnType().isNIOBuffer() + * == false; + */ + private MessageFormat returnValueCapacityExpression = null; + + /** + * Length of the returned array. Is ignored if + * binding.getJavaReturnType().isArray() is false. + */ + private MessageFormat returnValueLengthExpression = null; + + protected static final String STRING_CHARS_PREFIX = "_strchars_"; + + // We need this in order to compute sizes of certain types + protected MachineDescription machDesc; + + /** + * Constructs an emitter for the specified binding, and sets a default + * comment emitter that will emit the signature of the C function that is + * being bound. + */ + public CMethodBindingEmitter(MethodBinding binding, + PrintWriter output, + String javaPackageName, + String javaClassName, + boolean isOverloadedBinding, + boolean isJavaMethodStatic, + boolean forImplementingMethodCall, + boolean forIndirectBufferAndArrayImplementation, + MachineDescription machDesc) + { + super(output, false); + + assert(binding != null); + assert(javaClassName != null); + assert(javaPackageName != null); + + this.binding = binding; + this.packageName = javaPackageName; + this.className = javaClassName; + this.isOverloadedBinding = isOverloadedBinding; + this.isJavaMethodStatic = isJavaMethodStatic; + + this.forImplementingMethodCall = forImplementingMethodCall; + this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; + this.machDesc = machDesc; + + setCommentEmitter(defaultCommentEmitter); + } + + public final MethodBinding getBinding() { return binding; } + + public String getName() { + return binding.getName(); + } + + /** + * Get the expression for the capacity of the returned java.nio.Buffer. + */ + public final MessageFormat getReturnValueCapacityExpression() { + return returnValueCapacityExpression; + } + + /** + * If this function returns a void* encapsulated in a + * java.nio.Buffer (or compound type wrapper), sets the expression + * for the capacity of the returned Buffer. + * + * @param expression a MessageFormat which, when applied to an array + * of type String[] that contains each of the arguments names of the + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the capacity of the java.nio.Buffer + * returned from this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false and + * binding.getJavaReturnType().isCompoundTypeWrapper() == false + * </code> + */ + public final void setReturnValueCapacityExpression(MessageFormat expression) { + returnValueCapacityExpression = expression; + + if (!binding.getJavaReturnType().isNIOBuffer() && + !binding.getJavaReturnType().isCompoundTypeWrapper()) { + throw new IllegalArgumentException( + "Cannot specify return value capacity for a method that does not " + + "return java.nio.Buffer or a compound type wrapper: \"" + binding + "\""); + } + } + + /** + * Get the expression for the length of the returned array + */ + public final MessageFormat getReturnValueLengthExpression() { + return returnValueLengthExpression; + } + + /** + * If this function returns an array, sets the expression for the + * length of the returned array. + * + * @param expression a MessageFormat which, when applied to an array + * of type String[] that contains each of the arguments names of the + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the length of the array returned from + * this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false + * </code> + */ + public final void setReturnValueLengthExpression(MessageFormat expression) { + returnValueLengthExpression = expression; + + if (!binding.getJavaReturnType().isArray() && + !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers()) { + throw new IllegalArgumentException( + "Cannot specify return value length for a method that does not " + + "return an array: \"" + binding + "\""); + } + } + + /** + * Returns the List of Strings containing declarations for temporary + * C variables to be assigned to after the underlying function call. + */ + public final List<String> getTemporaryCVariableDeclarations() { + return temporaryCVariableDeclarations; + } + + /** + * Sets up a List of Strings containing declarations for temporary C + * variables to be assigned to after the underlying function call. A + * null argument indicates that no manual declarations are to be made. + */ + public final void setTemporaryCVariableDeclarations(List<String> arg) { + temporaryCVariableDeclarations = arg; + } + + /** + * Returns the List of Strings containing assignments for temporary + * C variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be + * made. + */ + public final List<String> getTemporaryCVariableAssignments() { + return temporaryCVariableAssignments; + } + + /** + * Sets up a List of Strings containing assignments for temporary C + * variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be made. + */ + public final void setTemporaryCVariableAssignments(List<String> arg) { + temporaryCVariableAssignments = arg; + } + + /** + * Get the name of the class in which the corresponding Java method + * resides. + */ + public String getJavaPackageName() { return packageName; } + + /** + * Get the name of the package in which the corresponding Java method + * resides. + */ + public String getJavaClassName() { return className; } + + /** + * Is the Java<->C JNI binding for this emitter's MethodBinding one of + * several overloaded methods with the same name? + */ + public final boolean getIsOverloadedBinding() { return isOverloadedBinding; } + + /** + * Is the Java side of the Java<->C JNI binding for this emitter's + * MethodBinding a static method?. + */ + public final boolean getIsJavaMethodStatic() { return isJavaMethodStatic; } + + /** + * Is this CMethodBindingEmitter implementing the case of an + * indirect buffer or array being passed down to C code? + */ + public final boolean forIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } + + /** + * Used for certain internal type size computations + */ + public final MachineDescription getMachineDescription() { return machDesc; } + + + protected void emitReturnType(PrintWriter writer) { + writer.print("JNIEXPORT "); + writer.print(binding.getJavaReturnType().jniTypeName()); + writer.print(" JNICALL"); + } + + protected void emitName(PrintWriter writer) { + writer.println(); // start name on new line + writer.print("Java_"); + writer.print(jniMangle(getJavaPackageName())); + writer.print("_"); + writer.print(jniMangle(getJavaClassName())); + writer.print("_"); + if (isOverloadedBinding) { + writer.print(jniMangle(binding)); + //System.err.println("OVERLOADED MANGLING FOR " + getName() + + // " = " + jniMangle(binding)); + } else { + writer.print(jniMangle(getName())); + //System.err.println(" NORMAL MANGLING FOR " + binding.getName() + + // " = " + jniMangle(getName())); + } + } + + protected String getImplSuffix() { + if (forImplementingMethodCall) { + if (forIndirectBufferAndArrayImplementation) { + return "1"; + } else { + return "0"; + } + } + return ""; + } + + protected int emitArguments(PrintWriter writer) { + writer.print("JNIEnv *env, "); + int numEmitted = 1; // initially just the JNIEnv + if (isJavaMethodStatic && !binding.hasContainingType()) { + writer.print("jclass"); + } else { + writer.print("jobject"); + } + writer.print(" _unused"); + ++numEmitted; + + if (binding.hasContainingType()) { + // "this" argument always comes down in argument 0 as direct buffer + writer.print(", jobject " + JavaMethodBindingEmitter.javaThisArgumentName()); + } + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + // Handle case where only param is void + if (javaArgType.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(binding.getNumArguments() == 1); + continue; + } + if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + writer.print(", "); + writer.print(javaArgType.jniTypeName()); + writer.print(" "); + writer.print(binding.getArgumentName(i)); + ++numEmitted; + + if (javaArgType.isPrimitiveArray() || + javaArgType.isNIOBuffer()) { + writer.print(", jint " + byteOffsetArgName(i)); + if(forIndirectBufferAndArrayImplementation) { + writer.print(", jboolean " + isNIOArgName(i)); + } + } else if (javaArgType.isNIOBufferArray()) { + writer.print(", jintArray " + + byteOffsetArrayArgName(i)); + } + } + return numEmitted; + } + + + protected void emitBody(PrintWriter writer) { + writer.println(" {"); +// writer.println("printf(\" - - - - "+ getName() + getImplSuffix() +" - - - -\\n\");"); + emitBodyVariableDeclarations(writer); + emitBodyUserVariableDeclarations(writer); + emitBodyVariablePreCallSetup(writer); + emitBodyCallCFunction(writer); + emitBodyUserVariableAssignments(writer); + emitBodyVariablePostCallCleanup(writer); + emitBodyReturnResult(writer); + writer.println("}"); + writer.println(); + } + + protected void emitBodyVariableDeclarations(PrintWriter writer) { + // Emit declarations for all pointer and String conversion variables + if (binding.hasContainingType()) { + emitPointerDeclaration(writer, + binding.getContainingType(), + binding.getContainingCType(), + CMethodBindingEmitter.cThisArgumentName(), + null); + } + + boolean emittedDataCopyTemps = false; + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + + if (type.isArray() || type.isNIOBuffer() || type.isCompoundTypeWrapper() || type.isArrayOfCompoundTypeWrappers()) { + String javaArgName = binding.getArgumentName(i); + String convName = pointerConversionArgumentName(javaArgName); + // handle array/buffer argument types + boolean needsDataCopy = + emitPointerDeclaration(writer, + type, + binding.getCArgumentType(i), + convName, javaArgName); + if (needsDataCopy && !emittedDataCopyTemps) { + // emit loop counter and array length variables used during data + // copy + writer.println(" jobject _tmpObj;"); + writer.println(" int _copyIndex;"); + writer.println(" jsize _tmpArrayLen;"); + + // Pointer to the data in the Buffer, taking the offset into account + writer.println(" int * _offsetHandle = NULL;"); + + emittedDataCopyTemps = true; + } + } else if (type.isString()) { + Type cType = binding.getCArgumentType(i); + if (isUTF8Type(cType)) { + writer.print(" const char* "); + } else { + writer.print(" jchar* "); + } + writer.print(STRING_CHARS_PREFIX); + writer.print(binding.getArgumentName(i)); + writer.println(" = NULL;"); + } + + } + + // Emit declaration for return value if necessary + Type cReturnType = binding.getCReturnType(); + + JavaType javaReturnType = binding.getJavaReturnType(); + String capitalizedComponentType = null; + if (!cReturnType.isVoid()) { + writer.print(" "); + // Note we must respect const/volatile for return argument + writer.print(binding.getCSymbol().getReturnType().getName(true)); + writer.println(" _res;"); + if (javaReturnType.isNIOByteBufferArray() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + writer.print(" int "); + writer.print(arrayIdx); + writer.println(";"); + writer.print(" jobjectArray "); + writer.print(arrayRes); + writer.println(";"); + } else if (javaReturnType.isArray()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + + Class<?> componentType = javaReturnType.getJavaClass().getComponentType(); + if (componentType.isArray()) { + throw new RuntimeException("Multi-dimensional arrays not supported yet"); + } + + String javaTypeName = componentType.getName(); + capitalizedComponentType = + "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); + String javaArrayTypeName = "j" + javaTypeName + "Array"; + writer.print(" "); + writer.print(javaArrayTypeName); + writer.print(" "); + writer.print(arrayRes); + writer.println(";"); + } + } + } + + /** Emits the user-defined C variable declarations from the + TemporaryCVariableDeclarations directive in the .cfg file. */ + protected void emitBodyUserVariableDeclarations(PrintWriter writer) { + if (temporaryCVariableDeclarations != null) { + for (String val : temporaryCVariableDeclarations) { + writer.print(" "); + writer.println(val); + } + } + } + + /** Checks a type to see whether it is for a UTF-8 pointer type + (i.e., "const char *", "const char **"). False implies that this + type is for a Unicode pointer type ("jchar *", "jchar **"). */ + protected boolean isUTF8Type(Type type) { + int i = 0; + // Try to dereference the type at most two levels + while (!type.isInt() && !type.isVoid() && (i < 2)) { + PointerType pt = type.asPointer(); + if (pt != null) { + type = pt.getTargetType(); + } else { + ArrayType arrt = type.asArray(); + if (arrt == null) { + throw new IllegalArgumentException("Type " + type + " should have been a pointer or array type"); + } + type = arrt.getElementType(); + } + } + if (type.isVoid()) { + // Assume UTF-8 since UTF-16 is rare + return true; + } + if (!type.isInt()) { + throw new IllegalArgumentException("Type " + type + " should have been a one- or two-dimensional integer pointer or array type"); + } + if (type.getSize(machDesc) != 1 && type.getSize(machDesc) != 2) { + throw new IllegalArgumentException("Type " + type + " should have been a one- or two-dimensional pointer to char or short"); + } + return (type.getSize(machDesc) == 1); + } + + /** Checks a type (expected to be pointer-to-pointer) for const-ness */ + protected boolean isConstPtrPtr(Type type) { + if (type.pointerDepth() != 2) { + return false; + } + if (type.asPointer().getTargetType().asPointer().getTargetType().isConst()) { + return true; + } + return false; + } + + /** + * Code to init the variables that were declared in + * emitBodyVariableDeclarations(), PRIOR TO calling the actual C + * function. + */ + protected void emitBodyVariablePreCallSetup(PrintWriter writer) { + + // Convert all Buffers to pointers first so we don't have to + // call ReleasePrimitiveArrayCritical for any arrays if any + // incoming buffers aren't direct + if (binding.hasContainingType()) { + emitPointerConversion(writer, binding, + binding.getContainingType(), + binding.getContainingCType(), + JavaMethodBindingEmitter.javaThisArgumentName(), + CMethodBindingEmitter.cThisArgumentName(), + null); + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + + if (type.isCompoundTypeWrapper() || + (type.isNIOBuffer() && !forIndirectBufferAndArrayImplementation)) { + String javaArgName = binding.getArgumentName(i); + emitPointerConversion(writer, binding, type, + binding.getCArgumentType(i), javaArgName, + pointerConversionArgumentName(javaArgName), + byteOffsetArgName(i)); + } + } + + // Convert all arrays to pointers, and get UTF-8 versions of jstring args + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + + if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + String javaArgName = binding.getArgumentName(i); + + if (javaArgType.isArray() || + (javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation) || + javaArgType.isArrayOfCompoundTypeWrappers()) { + boolean needsDataCopy = javaArgTypeNeedsDataCopy(javaArgType); + + writer.println(" if ( NULL != " + javaArgName + " ) {"); + + Type cArgType = binding.getCArgumentType(i); + String cArgTypeName = cArgType.getName(); + + String convName = pointerConversionArgumentName(javaArgName); + + if (!needsDataCopy) { + writer.print(" "); + writer.print(convName); + writer.print(" = ("); + if (javaArgType.isStringArray()) { + // java-side type is String[] + cArgTypeName = "jstring *"; + } + writer.print(cArgTypeName); + writer.print(") (((char*) ( JNI_TRUE == " + isNIOArgName(i) + " ? "); + writer.print(" (*env)->GetDirectBufferAddress(env, " + javaArgName + ") : "); + writer.print(" (*env)->GetPrimitiveArrayCritical(env, " + javaArgName + ", NULL) ) ) + "); + writer.println(byteOffsetArgName(i) + ");"); + } else { + // Handle the case where the array elements are of a type that needs a + // data copy operation to convert from the java memory model to the C + // memory model (e.g., int[][], String[], etc) + // + // FIXME: should factor out this whole block of code into a separate + // method for clarity and maintenance purposes + // + // Note that we properly handle only the case of an array of + // compound type wrappers in emitBodyVariablePostCallCleanup below + if (!isConstPtrPtr(cArgType) && + !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."); + } + + writer.println(); + writer.println(" /* Copy contents of " + javaArgName + " into " + convName + "_copy */"); + + // get length of array being copied + String arrayLenName = "_tmpArrayLen"; + writer.print(" "); + writer.print(arrayLenName); + writer.print(" = (*env)->GetArrayLength(env, "); + writer.print(javaArgName); + writer.println(");"); + + // allocate an array to hold each element + if (cArgType.pointerDepth() != 2) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; copying only supported for types of the form " + + "ptr-to-ptr-to-type."); + } + PointerType cArgPtrType = cArgType.asPointer(); + if (cArgPtrType == null) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; currently only pointer types supported."); + } + PointerType cArgElementType = cArgPtrType.getTargetType().asPointer(); + emitMalloc( + writer, + convName+"_copy", + cArgElementType.getName(), + isConstPtrPtr(cArgPtrType), + arrayLenName, + "Could not allocate buffer for copying data in argument \\\""+javaArgName+"\\\""); + + // Get the handle for the byte offset array sent down for Buffers + // FIXME: not 100% sure this is correct with respect to the + // JNI spec because it may be illegal to call + // GetObjectArrayElement while in a critical section. May + // need to do another loop and add in the offsets. + if (javaArgType.isNIOBufferArray()) { + writer.println + (" _offsetHandle = (int *) (*env)->GetPrimitiveArrayCritical(env, " + + byteOffsetArrayArgName(i) + + ", NULL);"); + } + + // process each element in the array + writer.println(" for (_copyIndex = 0; _copyIndex < "+arrayLenName+"; ++_copyIndex) {"); + + // get each array element + writer.println(" /* get each element of the array argument \"" + javaArgName + "\" */"); + writer.print(" _tmpObj = (*env)->GetObjectArrayElement(env, "); + writer.print(javaArgName); + writer.println(", _copyIndex);"); + + if (javaArgType.isStringArray()) { + writer.print(" "); + emitGetStringChars(writer, + "(jstring) _tmpObj", + convName+"_copy[_copyIndex]", + isUTF8Type(cArgType), + true); + } else if (javaArgType.isNIOBufferArray()) { + /* We always assume an integer "byte offset" argument follows any Buffer + in the method binding. */ + emitGetDirectBufferAddress(writer, + "_tmpObj", + cArgElementType.getName(), + convName + "_copy[_copyIndex]", + "_offsetHandle[_copyIndex]", + true); + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + // These come down in similar fashion to an array of NIO + // Buffers only we do not pass down any integer byte + // offset argument + emitGetDirectBufferAddress(writer, + "_tmpObj", + cArgElementType.getName(), + convName + "_copy[_copyIndex]", + null, + true); + } else { + // Question: do we always need to copy the sub-arrays, or just + // GetPrimitiveArrayCritical on each jobjectarray element and + // assign it to the appropriate elements at pointer depth 1? + // Probably depends on const-ness of the argument. + // Malloc enough space to hold a copy of each sub-array + writer.print(" "); + emitMalloc( + writer, + convName+"_copy[_copyIndex]", + cArgElementType.getTargetType().getName(), // assumes cArgPtrType is ptr-to-ptr-to-primitive !! + isConstPtrPtr(cArgPtrType), + "(*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"); + } + + } + writer.println(" }"); + + if (javaArgType.isNIOBufferArray()) { + writer.println + (" (*env)->ReleasePrimitiveArrayCritical(env, " + + byteOffsetArrayArgName(i) + + ", _offsetHandle, JNI_ABORT);"); + } + + writer.println(); + } // end of data copy + + writer.println(" }"); + + } else if (javaArgType.isString()) { + emitGetStringChars(writer, javaArgName, + STRING_CHARS_PREFIX + javaArgName, + isUTF8Type(binding.getCArgumentType(i)), + false); + } + } + } + + + /** + * Code to clean up any variables that were declared in + * emitBodyVariableDeclarations(), AFTER calling the actual C function. + */ + protected void emitBodyVariablePostCallCleanup(PrintWriter writer) { + + // Release primitive arrays and temporary UTF8 strings if necessary + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + if (javaArgType.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + + Type cArgType = binding.getCArgumentType(i); + String javaArgName = binding.getArgumentName(i); + + if (javaArgType.isArray() || + (javaArgType.isNIOBuffer() && forIndirectBufferAndArrayImplementation) || + javaArgType.isArrayOfCompoundTypeWrappers()) { + boolean needsDataCopy = javaArgTypeNeedsDataCopy(javaArgType); + + String convName = pointerConversionArgumentName(javaArgName); + + if (!needsDataCopy) { + writer.println(" if ( NULL != " + javaArgName + " && JNI_FALSE == " + isNIOArgName(i) + " ) {"); + + // Release array + writer.print(" (*env)->ReleasePrimitiveArrayCritical(env, " + javaArgName + ", " + convName + ", 0);"); + } else { + writer.println(" if ( NULL != " + javaArgName + " ) {"); + + // clean up the case where the array elements are of a type that needed + // a data copy operation to convert from the java memory model to the + // C memory model (e.g., int[][], String[], etc) + // + // FIXME: should factor out this whole block of code into a separate + // method for clarity and maintenance purposes + if (!isConstPtrPtr(cArgType)) { + // FIXME: handle any cleanup from treatment of non-const args, + // assuming they were treated differently in + // emitBodyVariablePreCallSetup() (see the similar section in that + // method for details). + if (javaArgType.isArrayOfCompoundTypeWrappers()) { + // This is the only form of cleanup we handle right now + writer.println(" _tmpArrayLen = (*env)->GetArrayLength(env, " + javaArgName + ");"); + writer.println(" for (_copyIndex = 0; _copyIndex < _tmpArrayLen; ++_copyIndex) {"); + writer.println(" _tmpObj = (*env)->GetObjectArrayElement(env, " + javaArgName + ", _copyIndex);"); + // We only skip the copy back in limited situations + String copyName = pointerConversionArgumentName(javaArgName) + "_copy"; + writer.println(" if ((" + copyName + "[_copyIndex] == NULL && _tmpObj == NULL) ||"); + writer.println(" (" + copyName + "[_copyIndex] != NULL && _tmpObj != NULL &&"); + writer.println(" (*env)->GetDirectBufferAddress(env, _tmpObj) == " + copyName + "[_copyIndex])) {"); + writer.println(" /* No copy back needed */"); + writer.println(" } else {"); + writer.println(" if (" + copyName + "[_copyIndex] == NULL) {"); + writer.println(" (*env)->SetObjectArrayElement(env, " + javaArgName + ", _copyIndex, NULL);"); + writer.println(" } else {"); + writer.println(" _tmpObj = (*env)->NewDirectByteBuffer(env, " + copyName + "[_copyIndex], sizeof(" + cArgType.getName() + "));"); + writer.println(" (*env)->SetObjectArrayElement(env, " + javaArgName + ", _copyIndex, _tmpObj);"); + writer.println(" }"); + writer.println(" }"); + 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."); + } + } + + writer.println(" /* Clean up " + convName + "_copy */"); + + // Only need to perform cleanup for individual array + // elements if they are not direct buffers + if (!javaArgType.isNIOBufferArray() && + !javaArgType.isArrayOfCompoundTypeWrappers()) { + // Re-fetch length of array that was copied + String arrayLenName = "_tmpArrayLen"; + writer.print(" "); + writer.print(arrayLenName); + writer.print(" = (*env)->GetArrayLength(env, "); + writer.print(javaArgName); + writer.println(");"); + + // free each element + PointerType cArgPtrType = cArgType.asPointer(); + if (cArgPtrType == null) { + throw new RuntimeException( + "Could not copy data for type \"" + cArgType + + "\"; currently only pointer types supported."); + } + + // process each element in the array + writer.println(" for (_copyIndex = 0; _copyIndex < " + arrayLenName +"; ++_copyIndex) {"); + + // get each array element + writer.println(" /* free each element of " +convName +"_copy */"); + writer.print(" _tmpObj = (*env)->GetObjectArrayElement(env, "); + writer.print(javaArgName); + writer.println(", _copyIndex);"); + + if (javaArgType.isStringArray()) { + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print("(jstring) _tmpObj"); + writer.print(", "); + 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"); + } + writer.println(" }"); + } + + // free the main array + writer.print(" free((void*) "); + writer.print(convName+"_copy"); + writer.println(");"); + } // end of cleaning up copied data + + writer.println(" }"); + + } else if (javaArgType.isString()) { + writer.println(" if ( NULL != " + javaArgName + " ) {"); + + if (isUTF8Type(cArgType)) { + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print(javaArgName); + writer.print(", " + STRING_CHARS_PREFIX); + writer.print(javaArgName); + writer.println(");"); + } else { + writer.println(" free((void*) " + STRING_CHARS_PREFIX + javaArgName + ");"); + } + + writer.println(" }"); + } + } + } + + /** Returns the number of arguments passed so calling code knows + whether to print a comma */ + protected int emitBodyPassCArguments(PrintWriter writer) { + for (int i = 0; i < binding.getNumArguments(); i++) { + if (i != 0) { + writer.print(", "); + } + JavaType javaArgType = binding.getJavaArgumentType(i); + // Handle case where only param is void. + if (javaArgType.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(binding.getNumArguments() == 1); + continue; + } + + if (javaArgType.isJNIEnv()) { + writer.print("env"); + } else if (binding.isArgumentThisPointer(i)) { + writer.print(CMethodBindingEmitter.cThisArgumentName()); + } else { + writer.print("("); + Type cArgType = binding.getCSymbol().getArgumentType(i); + if (isConstPtrPtr(cArgType)) { + writer.print("const "); + } + writer.print(cArgType.getName()); + writer.print(") "); + if (binding.getCArgumentType(i).isPointer() && javaArgType.isPrimitive()) { + writer.print("(intptr_t) "); + } + if (javaArgType.isArray() || javaArgType.isNIOBuffer() || + javaArgType.isCompoundTypeWrapper() || javaArgType.isArrayOfCompoundTypeWrappers()) { + writer.print(pointerConversionArgumentName(binding.getArgumentName(i))); + if (javaArgTypeNeedsDataCopy(javaArgType)) { + writer.print("_copy"); + } + } else { + if (javaArgType.isString()) { writer.print(STRING_CHARS_PREFIX); } + writer.print(binding.getArgumentName(i)); + } + } + } + return binding.getNumArguments(); + } + + protected void emitBodyCallCFunction(PrintWriter writer) { + + // Make the call to the actual C function + writer.print(" "); + + // WARNING: this code assumes that the return type has already been + // typedef-resolved. + Type cReturnType = binding.getCReturnType(); + + if (!cReturnType.isVoid()) { + writer.print("_res = "); + } + if (binding.hasContainingType()) { + // Call through function pointer + writer.print(CMethodBindingEmitter.cThisArgumentName() + "->"); + } + writer.print(binding.getCSymbol().getName()); + writer.print("("); + emitBodyPassCArguments(writer); + writer.println(");"); + } + + /** Emits the user-defined C variable assignments from the + TemporaryCVariableAssignments directive in the .cfg file. */ + protected void emitBodyUserVariableAssignments(PrintWriter writer) { + if (temporaryCVariableAssignments != null) { + for (String val : temporaryCVariableAssignments) { + writer.print(" "); + writer.println(val); + } + } + } + + protected void emitBodyReturnResult(PrintWriter writer) + { + // WARNING: this code assumes that the return type has already been + // typedef-resolved. + Type cReturnType = binding.getCReturnType(); + + // Return result if necessary + if (!cReturnType.isVoid()) { + JavaType javaReturnType = binding.getJavaReturnType(); + if (javaReturnType.isPrimitive()) { + writer.print(" return "); + if (cReturnType.isPointer()) { + // Pointer being converted to int or long: cast this result + // (through intptr_t to avoid compiler warnings with gcc) + writer.print("(" + javaReturnType.jniTypeName() + ") (intptr_t) "); + } + writer.println("_res;"); + } else if (javaReturnType.isNIOBuffer() || + javaReturnType.isCompoundTypeWrapper()) { + writer.println(" if (NULL == _res) return NULL;"); + writer.print(" return (*env)->NewDirectByteBuffer(env, _res, "); + // See whether capacity has been specified + if (returnValueCapacityExpression != null) { + writer.print( + 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)" + ); + } + } + 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); + /** + throw new RuntimeException( + "No capacity specified for java.nio.Buffer return " + + "value for function \"" + binding + "\";" + + " C return type is " + cReturnType.getName() + ": " + binding); */ + } + writer.println(");"); + } else if (javaReturnType.isString()) { + writer.println(" if (NULL == _res) return NULL;"); + writer.println(" return (*env)->NewStringUTF(env, _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); + } + writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNameArray()) + ";"); + writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); + writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); + Type retType = binding.getCSymbol().getReturnType(); + Type pointerType; + if (retType.isPointer()) { + pointerType = retType.asPointer().getTargetType(); + } else { + pointerType = retType.asArray().getElementType(); + } + Type baseType = pointerType.asPointer().getTargetType(); + writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + + ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], sizeof(" + pointerType.getName() + ")));"); + writer.println(" }"); + writer.println(" return " + arrayRes + ";"); + } else if (javaReturnType.isArray()) { + // FIXME: must have user provide length of array in .cfg file + // by providing a constant value, input parameter, or + // expression which computes the array size (already present + // as ReturnValueCapacity, not yet implemented / tested here) + + throw new RuntimeException( + "Could not emit native code for function \"" + binding + + "\": array return values for non-char types not implemented yet"); + + // FIXME: This is approximately what will be required here + // + //writer.print(" "); + //writer.print(arrayRes); + //writer.print(" = (*env)->New"); + //writer.print(capitalizedComponentType); + //writer.print("Array(env, "); + //writer.print(arrayResLength); + //writer.println(");"); + //writer.print(" (*env)->Set"); + //writer.print(capitalizedComponentType); + //writer.print("ArrayRegion(env, "); + //writer.print(arrayRes); + //writer.print(", 0, "); + //writer.print(arrayResLength); + //writer.println(", _res);"); + //writer.print(" return "); + //writer.print(arrayRes); + //writer.println(";"); + } else { + System.err.print("Unhandled return type: "); + javaReturnType.dump(); + throw new RuntimeException("Unhandled return type"); + } + } + } + + protected static String cThisArgumentName() { + return "this0"; + } + + // Mangle a class, package or function name + protected String jniMangle(String name) { + return name.replaceAll("_", "_1").replace('.', '_'); + } + + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(jniMangle(getName())); + buf.append(getImplSuffix()); + buf.append("__"); + if (binding.hasContainingType()) { + // "this" argument always comes down in argument 0 as direct buffer + jniMangle(java.nio.ByteBuffer.class, buf, true); + } + for (int i = 0; i < binding.getNumArguments(); i++) { + if (binding.isArgumentThisPointer(i)) { + continue; + } + JavaType type = binding.getJavaArgumentType(i); + if (type.isVoid()) { + // 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() + "\""); + } + } else { + Class<?> c = type.getJavaClass(); + if (c != null) { + jniMangle(c, buf, false); + // If Buffer offset arguments were added, we need to mangle the JNI for the + // extra arguments + if (type.isNIOBuffer()) { + jniMangle(Integer.TYPE, buf, false); + if(forIndirectBufferAndArrayImplementation) { + jniMangle(Boolean.TYPE, buf, false); + } + } else if (type.isNIOBufferArray()) { + int[] intArrayType = new int[0]; + c = intArrayType.getClass(); + jniMangle(c , buf, true); + } + if (type.isPrimitiveArray()) { + jniMangle(Integer.TYPE, buf, false); + } + } else if (type.isCompoundTypeWrapper()) { + // Mangle wrappers for C structs as ByteBuffer + jniMangle(java.nio.ByteBuffer.class, buf, true); + } else if (type.isArrayOfCompoundTypeWrappers()) { + // Mangle arrays of C structs as ByteBuffer[] + java.nio.ByteBuffer[] tmp = new java.nio.ByteBuffer[0]; + jniMangle(tmp.getClass(), buf, true); + } else if (type.isJNIEnv()) { + // 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()); + } + } + } + + return buf.toString(); + } + + protected void jniMangle(Class<?> c, StringBuffer res, boolean syntheticArgument) { + if (c.isPrimitive()) { + if (c == Boolean.TYPE) res.append("Z"); + else if (c == Byte.TYPE) res.append("B"); + else if (c == Character.TYPE) res.append("C"); + else if (c == Short.TYPE) res.append("S"); + else if (c == Integer.TYPE) res.append("I"); + else if (c == Long.TYPE) res.append("J"); + else if (c == Float.TYPE) res.append("F"); + else if (c == Double.TYPE) res.append("D"); + else throw new RuntimeException("Illegal primitive type \"" + c.getName() + "\""); + } else { + // Arrays and NIO Buffers are always passed down as java.lang.Object. + // The only arrays that show up as true arrays in the signature + // are the synthetic byte offset arrays created when passing + // down arrays of direct Buffers. Compound type wrappers are + // passed down as ByteBuffers (no good reason, just to avoid + // accidental conflation) so we mangle them differently. + if (syntheticArgument) { + if (c.isArray()) { + res.append("_3"); + Class<?> componentType = c.getComponentType(); + // Handle arrays of compound type wrappers differently for + // convenience of the Java-level glue code generation + jniMangle(componentType, res, + (componentType == java.nio.ByteBuffer.class)); + } else { + res.append("L"); + res.append(c.getName().replace('.', '_')); + res.append("_2"); + } + } else { + if (c.isArray()) { + res.append("_3"); + jniMangle(c.getComponentType(), res, false); + } else if (c == java.lang.String.class) { + res.append("L"); + res.append(c.getName().replace('.', '_')); + res.append("_2"); + } else { + res.append("L"); + res.append("java_lang_Object"); + res.append("_2"); + } + } + } + } + + private void emitOutOfMemoryCheck(PrintWriter writer, String varName, String errorMessage) { + writer.println(" if ( NULL == " + varName + " ) {"); + writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryError\"),"); + writer.print(" \"" + errorMessage); + writer.print(" in native dispatcher for \\\""); + writer.print(getName()); + writer.println("\\\"\");"); + writer.print(" return"); + if (!binding.getJavaReturnType().isVoid()) { + writer.print(" 0"); + } + writer.println(";"); + writer.println(" }"); + } + + private void emitMalloc(PrintWriter writer, + String targetVarName, + String elementTypeString, + boolean elementTypeIsConst, + String numElementsExpression, + String mallocFailureErrorString) { + writer.print(" "); + writer.print(targetVarName); + writer.print(" = ("); + if(elementTypeIsConst) { + writer.print("const "); + } + writer.print(elementTypeString); + writer.print(" *) malloc("); + writer.print(numElementsExpression); + writer.print(" * sizeof("); + writer.print(elementTypeString); + writer.println("));"); + // Catch memory allocation failure + emitOutOfMemoryCheck( writer, targetVarName, mallocFailureErrorString); + } + + private void emitCalloc(PrintWriter writer, + String targetVarName, + String elementTypeString, + String numElementsExpression, + String mallocFailureErrorString) { + writer.print(" "); + writer.print(targetVarName); + writer.print(" = ("); + writer.print(elementTypeString); + writer.print(" *) calloc("); + writer.print(numElementsExpression); + writer.print(", sizeof("); + writer.print(elementTypeString); + writer.println("));"); + // Catch memory allocation failure + emitOutOfMemoryCheck( writer, targetVarName, mallocFailureErrorString); + } + + private void emitGetStringChars(PrintWriter writer, + String sourceVarName, + String receivingVarName, + boolean isUTF8, + boolean emitElseClause) { + writer.println(" if ( NULL != " + sourceVarName + " ) {"); + + if (isUTF8) { + writer.print(" "); + writer.print(receivingVarName); + writer.print(" = (*env)->GetStringUTFChars(env, "); + writer.print(sourceVarName); + writer.println(", (jboolean*)NULL);"); + // Catch memory allocation failure in the event that the VM didn't pin + // the String and failed to allocate a copy + emitOutOfMemoryCheck( writer, receivingVarName, "Failed to get UTF-8 chars for argument \\\""+sourceVarName+"\\\""); + } else { + // The UTF-16 case is basically Windows specific. Unix platforms + // tend to use only the UTF-8 encoding. On Windows the problem + // is that wide character strings are expected to be null + // terminated, but the JNI GetStringChars doesn't return a + // null-terminated Unicode string. For this reason we explicitly + // calloc our buffer, including the null terminator, and use + // GetStringRegion to fetch the string's characters. + emitCalloc(writer, + receivingVarName, + "jchar", + "(*env)->GetStringLength(env, " + sourceVarName + ") + 1", + "Could not allocate temporary buffer for copying string argument \\\""+sourceVarName+"\\\""); + writer.println(" (*env)->GetStringRegion(env, " + sourceVarName + ", 0, (*env)->GetStringLength(env, " + sourceVarName + "), " + receivingVarName + ");"); + } + writer.print(" }"); + if (emitElseClause) { + writer.print(" else {"); + writer.print(" "); + writer.print(receivingVarName); + writer.println(" = NULL;"); + writer.println(" }"); + } else { + writer.println(); + } + } + + private void emitGetDirectBufferAddress(PrintWriter writer, + String sourceVarName, + String receivingVarTypeString, + String receivingVarName, + String byteOffsetVarName, + boolean emitElseClause) { + writer.println(" if ( NULL != " + sourceVarName + " ) {"); + writer.print(" "); + + writer.print(" "); + writer.print(receivingVarName); + writer.print(" = ("); + writer.print(receivingVarTypeString); + + writer.print(") (((char*) (*env)->GetDirectBufferAddress(env, "); + writer.print(sourceVarName); + writer.println(")) + " + ((byteOffsetVarName != null) ? byteOffsetVarName : "0") + ");"); + + writer.print(" }"); + if (emitElseClause) { + writer.println(" else {"); + writer.print(" "); + writer.print(receivingVarName); + writer.println(" = NULL;"); + writer.println(" }"); + } else { + writer.println(); + } + } + + // Note: if the data in the Type needs to be converted from the Java memory + // model to the C memory model prior to calling any C-side functions, then + // an extra variable named XXX_copy (where XXX is the value of the + // cVariableName argument) will be emitted and TRUE will be returned. + private boolean emitPointerDeclaration(PrintWriter writer, + JavaType javaType, + Type cType, + String cVariableName, + String javaArgumentName) { + String ptrTypeString = null; + boolean needsDataCopy = false; + + // Emit declaration for the pointer variable. + // + // Note that we don't need to obey const/volatile for outgoing arguments + // + if (javaType.isNIOBuffer()) { + ptrTypeString = cType.getName(); + } else if (javaType.isArray() || javaType.isArrayOfCompoundTypeWrappers()) { + needsDataCopy = javaArgTypeNeedsDataCopy(javaType); + if (javaType.isPrimitiveArray() || + javaType.isNIOBufferArray() || + javaType.isArrayOfCompoundTypeWrappers()) { + ptrTypeString = cType.getName(); + } else if (!javaType.isStringArray()) { + Class<?> elementType = javaType.getJavaClass().getComponentType(); + if (elementType.isArray()) { + Class<?> subElementType = elementType.getComponentType(); + if (subElementType.isPrimitive()) { + // type is pointer to pointer to primitive + ptrTypeString = cType.getName(); + } 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() + "\""); + } + } 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() + "\""); + } + } + } else { + ptrTypeString = cType.getName(); + } + + if (!needsDataCopy) { + // declare the pointer variable + writer.print(" "); + writer.print(ptrTypeString); + writer.print(" "); + writer.print(cVariableName); + writer.println(" = NULL;"); + } else { + // Declare a variable to hold a copy of the argument data in which the + // incoming data has been properly laid out in memory to match the C + // memory model + if (javaType.isStringArray()) { + String cElementTypeName = "char *"; + PointerType cPtrType = cType.asPointer(); + if (cPtrType != null) { + cElementTypeName = cPtrType.getTargetType().asPointer().getName(); + } + if (isConstPtrPtr(cType)) { + writer.print(" const "+cElementTypeName+" *"); + } else { + writer.print(" "+cElementTypeName+" *"); + } + } else { + if (isConstPtrPtr(cType)) { + writer.print(" const " + ptrTypeString); + } else { + writer.print(" " + ptrTypeString); + } + } + writer.print(" "); + writer.print(cVariableName); + writer.print("_copy = NULL; /* copy of data in "); + writer.print(javaArgumentName); + writer.println(", laid out according to C memory model */"); + } + + return needsDataCopy; + } + + private void emitPointerConversion(PrintWriter writer, + MethodBinding binding, + JavaType type, + Type cType, + String incomingArgumentName, + String cVariableName, + String byteOffsetVarName) { + // Compound type wrappers do not get byte offsets added on + if (type.isCompoundTypeWrapper()) { + byteOffsetVarName = null; + } + + emitGetDirectBufferAddress(writer, + incomingArgumentName, + cType.getName(), + cVariableName, + byteOffsetVarName, + false); + } + + protected String byteOffsetArgName(int i) { + return byteOffsetArgName(binding.getArgumentName(i)); + } + + protected String byteOffsetArgName(String s) { + return s + "_byte_offset"; + } + + protected String isNIOArgName(int i) { + return isNIOArgName(binding.getArgumentName(i)); + } + + protected String isNIOArgName(String s) { + return s + "_is_nio"; + } + + protected String byteOffsetArrayArgName(int i) { + return binding.getArgumentName(i) + "_byte_offset_array"; + } + + protected String[] argumentNameArray() { + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + if (binding.getJavaArgumentType(i).isPrimitiveArray()) { + // Add on _offset argument in comma-separated expression + argumentNames[i] = argumentNames[i] + ", " + byteOffsetArgName(i); + } + } + return argumentNames; + } + + protected String pointerConversionArgumentName(String argName) { + return "_" + argName + "_ptr"; + } + + /** + * Class that emits a generic comment for CMethodBindingEmitters; the comment + * includes the C signature of the native method that is being bound by the + * emitter java method. + */ + protected static class DefaultCommentEmitter implements CommentEmitter { + public void emit(FunctionEmitter emitter, PrintWriter writer) { + emitBeginning((CMethodBindingEmitter)emitter, writer); + emitEnding((CMethodBindingEmitter)emitter, writer); + } + protected void emitBeginning(CMethodBindingEmitter emitter, PrintWriter writer) { + writer.println(" Java->C glue code:"); + writer.print(" * Java package: "); + writer.print(emitter.getJavaPackageName()); + writer.print("."); + writer.println(emitter.getJavaClassName()); + writer.print(" * Java method: "); + MethodBinding binding = emitter.getBinding(); + writer.println(binding); + writer.println(" * C function: " + binding.getCSymbol()); + } + protected void emitEnding(CMethodBindingEmitter emitter, PrintWriter writer) { + } + } + + protected boolean javaArgTypeNeedsDataCopy(JavaType javaArgType) { + if (javaArgType.isArray()) { + return (javaArgType.isNIOBufferArray() || + javaArgType.isStringArray() || + javaArgType.getJavaClass().getComponentType().isArray()); + } + if (javaArgType.isArrayOfCompoundTypeWrappers()) { + return true; + } + return false; + } +} diff --git a/src/java/com/jogamp/gluegen/CodeGenUtils.java b/src/java/com/jogamp/gluegen/CodeGenUtils.java new file mode 100644 index 0000000..1b28eb8 --- /dev/null +++ b/src/java/com/jogamp/gluegen/CodeGenUtils.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen; + +import java.io.*; +import java.util.*; + +public class CodeGenUtils { + + /** + * Given a java package name (e.g., "java.lang"), return the package as a + * directory path (i.e., "java/lang"). + */ + public static String packageAsPath(String packageName) { + String path = packageName.replace('.', File.separatorChar); + //System.out.println("Converted package [" + packageName + "] to path [" + path +"]"); + return path; + } + + /** + * @param generator the object that is emitting the autogenerated code. If + * null, the generator will not be mentioned in the warning message. + */ + public static void emitAutogeneratedWarning(PrintWriter w, Object generator) { + w.print("/* !---- DO NOT EDIT: This file autogenerated "); + if (generator != null) { + w.print("by "); + w.print(packageAsPath(generator.getClass().getName())); + w.print(".java "); + } + w.print("on "); + w.print((new Date()).toString()); + w.println(" ----! */"); + w.println(); + } + + /** + * Emit the opening headers for one java class/interface file. + */ + public static void emitJavaHeaders(PrintWriter w, + String packageName, + String className, + boolean isClassNotInterface, + List<String> imports, + String[] accessModifiers, + String[] interfaces, + String classExtended, + EmissionCallback classDocComment) throws IOException { + w.println("package " + packageName + ";"); + w.println(); + + for (String imp : imports) { + w.print("import "); + w.print(imp); + w.println(';'); + } + + w.println(); + + if (classDocComment != null) { + classDocComment.emit(w); + } + + for (int i = 0; accessModifiers != null && i < accessModifiers.length; ++i) { + w.print(accessModifiers[i]); + w.print(' '); + } + + if (isClassNotInterface) { + w.print("class "); + w.print(className); + w.print(' '); + if (classExtended != null) { + w.print("extends "); + w.print(classExtended); + } + } else { + if (classExtended != null) { + throw new IllegalArgumentException("Autogenerated interface class " + className + " cannot extend class " + classExtended); + } + w.print("interface "); + w.print(className); + w.print(' '); + } + + for (int i = 0; interfaces != null && i < interfaces.length; ++i) { + if (i == 0) { + w.print(isClassNotInterface ? "implements " : "extends "); + } + w.print(interfaces[i]); + if (i < interfaces.length - 1) { + w.print(", "); + } + } + + w.println('{'); + } + + //----------------------------------------- + /** A class that emits source code of some time when activated. */ + public interface EmissionCallback { + + /** Emit appropriate source code through the given writer. */ + public void emit(PrintWriter output); + } +} diff --git a/src/java/com/jogamp/gluegen/CommentEmitter.java b/src/java/com/jogamp/gluegen/CommentEmitter.java new file mode 100644 index 0000000..89db474 --- /dev/null +++ b/src/java/com/jogamp/gluegen/CommentEmitter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.io.*; + +public interface CommentEmitter { + /** + * Emit the body of a comment for the specified function; do NOT emit the + * open (e.g., comment "/*") or close (e.g., "*\/") characters. + */ + public void emit(FunctionEmitter funcEmitter, PrintWriter output); +} + diff --git a/src/java/com/jogamp/gluegen/ConstantDefinition.java b/src/java/com/jogamp/gluegen/ConstantDefinition.java new file mode 100644 index 0000000..4216b52 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ConstantDefinition.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.gluegen; + +import java.util.*; + +/** Represents the definition of a constant which was provided either + via a #define statement or through an enum definition. */ +public class ConstantDefinition { + + private String origName; + private HashSet<String> aliasedNames; + private String name; + private String value; + private boolean isEnum; + private String enumName; + private Set<String> aliases; + + public ConstantDefinition(String name, + String value, + boolean isEnum, + String enumName) { + this.origName = name; + this.name = name; + this.value = value; + this.isEnum = isEnum; + this.enumName = enumName; + this.aliasedNames=new HashSet<String>(); + } + + public boolean equals(ConstantDefinition other) { + return (equals(name, other.name) && + equals(value, other.value) && + equals(enumName, other.enumName)); + } + + private boolean equals(String s1, String s2) { + if (s1 == null || s2 == null) { + if (s1 == null && s2 == null) { + return true; + } + return false; + } + + return s1.equals(s2); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + /** Supports renaming in Java binding. */ + public void rename(String name) { + if(null!=name) { + this.name = name; + aliasedNames.add(origName); + } + } + + public void addAliasedName(String name) { + aliasedNames.add(name); + } + public Collection<String> getAliasedNames() { + return aliasedNames; + } + + public String getOrigName() { + return origName; + } + + public String getName() { + return name; + } + + 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; } + + public boolean isEnum() { return isEnum; } + + public Set<String> getAliases() { + return aliases; + } + + public void addAlias(String alias) { + if (aliases == null) { + aliases = new LinkedHashSet<String>(); + } + aliases.add(alias); + } + + @Override + public String toString() { + return "ConstantDefinition [name " + name + " origName " + origName + " value " + value + + " aliasedNames " + aliasedNames + " aliases " + aliases + + " enumName " + enumName + " isEnum " + isEnum + "]"; + } + +} diff --git a/src/java/com/jogamp/gluegen/DebugEmitter.java b/src/java/com/jogamp/gluegen/DebugEmitter.java new file mode 100644 index 0000000..22cc0c5 --- /dev/null +++ b/src/java/com/jogamp/gluegen/DebugEmitter.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.util.*; + +import com.jogamp.gluegen.cgram.types.*; + +/** Debug emitter which prints the parsing results to standard output. */ + +public class DebugEmitter implements GlueEmitter { + + public void readConfigurationFile(String filename) {} + + public void setMachineDescription(MachineDescription md32, + MachineDescription md64) {} + + public void beginEmission(GlueEmitterControls controls) { + System.out.println("----- BEGIN EMISSION OF GLUE CODE -----"); + } + + public void endEmission() { + System.out.println("----- END EMISSION OF GLUE CODE -----"); + } + + public void beginDefines() {} + + public void emitDefine(ConstantDefinition def, String optionalComment) { + String name = def.getName(); + String value = def.getValue(); + System.out.println("#define " + name + " " + value + + (optionalComment != null ? ("// " + optionalComment) : "")); + } + public void endDefines() {} + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map<Type, Type> canonMap) { + Set<String> keys = typedefDictionary.keySet(); + for (String key: keys) { + Type value = typedefDictionary.get(key); + System.out.println("typedef " + value + " " + key + ";"); + } + } + + public Iterator<FunctionSymbol> emitFunctions(List<FunctionSymbol> originalCFunctions) throws Exception { + for (FunctionSymbol sym : originalCFunctions) { + emitSingleFunction(sym); + } + return originalCFunctions.iterator(); + } + public void emitSingleFunction(FunctionSymbol sym) { + System.out.println(sym); + System.out.println(" -> " + sym.toString()); + } + public void endFunctions() {} + + public void beginStructLayout() throws Exception {} + public void layoutStruct(CompoundType t) throws Exception {} + public void endStructLayout() throws Exception {} + + public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map<Type, Type> canonMap) { + } + + public void emitStruct(CompoundType t, String alternateName) { + String name = t.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + System.out.println("Referenced type \"" + name + "\""); + } + + public void endStructs() {} +} diff --git a/src/java/com/jogamp/gluegen/FunctionEmitter.java b/src/java/com/jogamp/gluegen/FunctionEmitter.java new file mode 100644 index 0000000..f9fbd21 --- /dev/null +++ b/src/java/com/jogamp/gluegen/FunctionEmitter.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.util.*; +import java.io.*; + +public abstract class FunctionEmitter { + + public static final EmissionModifier STATIC = new EmissionModifier("static"); + + private boolean isInterfaceVal; + private ArrayList<EmissionModifier> modifiers = new ArrayList<EmissionModifier>(); + private CommentEmitter commentEmitter = null; + private PrintWriter defaultOutput; + + /** + * Constructs the FunctionEmitter with a CommentEmitter that emits nothing. + */ + public FunctionEmitter(PrintWriter defaultOutput, boolean isInterface) { + assert(defaultOutput != null); + this.defaultOutput = defaultOutput; + this.isInterfaceVal = isInterface; + } + + /** + * Makes this FunctionEmitter a copy of the passed one. + */ + @SuppressWarnings("unchecked") + public FunctionEmitter(FunctionEmitter arg) { + modifiers = (ArrayList<EmissionModifier>)arg.modifiers.clone(); + commentEmitter = arg.commentEmitter; + defaultOutput = arg.defaultOutput; + isInterfaceVal = arg.isInterfaceVal; + } + + public boolean isInterface() { return isInterfaceVal; } + + public PrintWriter getDefaultOutput() { return defaultOutput; } + + public void addModifiers(Iterator<EmissionModifier> mi) { + while (mi.hasNext()) { + modifiers.add(mi.next()); + } + } + public void addModifier(EmissionModifier m) { modifiers.add(m); } + + public boolean removeModifier(EmissionModifier m) { return modifiers.remove(m); } + + public void clearModifiers() { modifiers.clear(); } + + public boolean hasModifier(EmissionModifier m) { return modifiers.contains(m); } + + public Iterator<EmissionModifier> getModifiers() { return modifiers.iterator(); } + + public abstract String getName(); + + /** + * Emit the function to the specified output (instead of the default + * output). + */ + public void emit(PrintWriter output) { + emitDocComment(output); + //output.println(" // Emitter: " + getClass().getName()); + emitSignature(output); + emitBody(output); + } + + /** + * Emit the function to the default output (the output that was passed to + * the constructor) + */ + public final void emit() { + emit(getDefaultOutput()); + } + + /** Returns, as a String, whatever {@link #emit} would output. */ + @Override + public String toString() { + StringWriter sw = new StringWriter(500); + PrintWriter w = new PrintWriter(sw); + emit(w); + return sw.toString(); + } + + /** + * Set the object that will emit the comment for this function. If the + * parameter is null, no comment will be emitted. + */ + public void setCommentEmitter(CommentEmitter cEmitter) { + commentEmitter = cEmitter; + } + + /** + * Get the comment emitter for this FunctionEmitter. The return value may be + * null, in which case no comment emitter has been set. + */ + public CommentEmitter getCommentEmitter() { return commentEmitter; } + + protected void emitDocComment(PrintWriter writer) { + + if (commentEmitter != null) { + writer.print(getBaseIndentString()); //indent + + writer.print(getCommentStartString()); + + commentEmitter.emit(this, writer); + + writer.print(getBaseIndentString()); //indent + + writer.println(getCommentEndString()); + } + } + + protected void emitSignature(PrintWriter writer) { + + writer.print(getBaseIndentString()); // indent method + + int numEmitted = emitModifiers(writer); + if (numEmitted > 0) { + writer.print(" "); + } + + emitReturnType(writer); + writer.print(" "); + + emitName(writer); + writer.print("("); + + emitArguments(writer); + writer.print(")"); + } + + protected int emitModifiers(PrintWriter writer) { + PrintWriter w = getDefaultOutput(); + int numEmitted = 0; + for (Iterator<EmissionModifier> it = getModifiers(); it.hasNext(); ) { + writer.print(it.next()); + ++numEmitted; + if (it.hasNext()) { + writer.print(" "); + } + } + return numEmitted; + } + + protected String getBaseIndentString() { return ""; } + + protected String getCommentStartString() { return "/* "; } + protected String getCommentEndString() { return " */"; } + + protected abstract void emitReturnType(PrintWriter writer); + protected abstract void emitName(PrintWriter writer); + /** Returns the number of arguments emitted. */ + protected abstract int emitArguments(PrintWriter writer); + protected abstract void emitBody(PrintWriter writer); + + public static class EmissionModifier { + + @Override + public final String toString() { return emittedForm; } + + private String emittedForm; + + @Override + public int hashCode() { + return emittedForm.hashCode(); + } + + @Override + public boolean equals(Object arg) { + if (arg == null || (!(arg instanceof EmissionModifier))) { + return false; + } + + return emittedForm.equals(((EmissionModifier) arg).emittedForm); + } + + protected EmissionModifier(String emittedForm) { this.emittedForm = emittedForm; } + } +} + diff --git a/src/java/com/jogamp/gluegen/GlueEmitter.java b/src/java/com/jogamp/gluegen/GlueEmitter.java new file mode 100644 index 0000000..5f627f9 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GlueEmitter.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.util.*; +import com.jogamp.gluegen.cgram.types.*; + +/** Specifies the interface by which GlueGen requests glue code to be + generated. Can be replaced to generate glue code for other + languages and foreign function interfaces. */ + +public interface GlueEmitter { + + public void readConfigurationFile(String filename) throws Exception; + + /** Sets the description of the underlying hardware. "md32" + specifies the description of a 32-bit version of the underlying + CPU architecture. "md64" specifies the description of a 64-bit + version of the underlying CPU architecture. At least one must be + specified. When both are specified, the bulk of the glue code is + generated using the 32-bit machine description, but structs are + laid out twice and the base class delegates between the 32-bit + and 64-bit implementation at run time. This allows Java code + which can access both 32-bit and 64-bit versions of the data + structures to be included in the same jar file. <P> + + It is up to the end user to provide the appropriate opaque + definitions to ensure that types of varying size (longs and + pointers in particular) are exposed to Java in such a way that + changing the machine description does not cause different shared + glue code to be generated for the 32- and 64-bit ports. + */ + public void setMachineDescription(MachineDescription md32, + MachineDescription md64); + + /** + * Begin the emission of glue code. This might include opening files, + * emitting class headers, etc. + */ + public void beginEmission(GlueEmitterControls controls) throws Exception; + + /** + * Finish the emission of glue code. This might include closing files, + * closing open class definitions, etc. + */ + public void endEmission() throws Exception; + + public void beginDefines() throws Exception; + /** + * @param optionalComment If optionalComment is non-null, the emitter can + * emit that string as a comment providing extra information about the + * define. + */ + public void emitDefine(ConstantDefinition def, String optionalComment) throws Exception; + public void endDefines() throws Exception; + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map<Type, Type> canonMap) throws Exception; + + /** Emit glue code for the list of FunctionSymbols. */ + public Iterator<FunctionSymbol> emitFunctions(List<FunctionSymbol> cFunctions) throws Exception; + public void endFunctions() throws Exception; + + /** Begins the process of computing field offsets and type sizes for + the structs to be emitted. */ + public void beginStructLayout() throws Exception; + /** Lays out one struct which will be emitted later. */ + public void layoutStruct(CompoundType t) throws Exception; + /** Finishes the struct layout process. */ + public void endStructLayout() throws Exception; + + public void beginStructs(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map<Type, Type> canonMap) throws Exception; + /** Emit glue code for the given CompoundType. alternateName 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; + public void endStructs() throws Exception; +} diff --git a/src/java/com/jogamp/gluegen/GlueEmitterControls.java b/src/java/com/jogamp/gluegen/GlueEmitterControls.java new file mode 100644 index 0000000..07b370d --- /dev/null +++ b/src/java/com/jogamp/gluegen/GlueEmitterControls.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +/** Specifies the interface by which a GlueEmitter can request + additional information from the glue generator. */ + +public interface GlueEmitterControls { + /** Requests emission of an accessor for a struct that will not be + referenced by any functions or other structs. */ + public void forceStructEmission(String typedefName); + + /** Finds the full path name of the specified header file based on + the include directories specified on the command line. */ + public String findHeaderFile(String headerFileName); + + /** Runs the given filter on the #defines, enum definitions and + function symbols that this controller has parsed. It is valid to + call this method as soon as {@link GlueEmitter#beginEmission} + has been called on the GlueEmitter, and it is recommended to + call it from that method call. Calling it during glue code + emission may cause problems. */ + public void runSymbolFilter(SymbolFilter filter); +} diff --git a/src/java/com/jogamp/gluegen/GlueGen.java b/src/java/com/jogamp/gluegen/GlueGen.java new file mode 100644 index 0000000..901dd86 --- /dev/null +++ b/src/java/com/jogamp/gluegen/GlueGen.java @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen; + +import java.io.*; +import java.util.*; + +import antlr.*; +import com.jogamp.gluegen.cgram.*; +import com.jogamp.gluegen.cgram.types.*; +import com.jogamp.gluegen.pcpp.*; + +import static java.lang.System.*; + +/** + * Glue code generator for C functions and data structures.<br> + * <br> + * Gluegen has build-in types (terminal symbols) for:<br> + * <br> + * <table border="1"> + * <tr><th>type</th> <th>java</th> <th>native-x32</th><th>native-x64</th><th>type</th> <th>signed</th></tr> + * <tr><th>__int32</th> <th>32bit</th><th>32bit</th> <th>32bit</th> <th>integer</th><th>signed or unsigned</th></tr> + * <tr><th>int32_t</th> <th>32bit</th><th>32bit</th> <th>32bit</th> <th>integer</th><th>signed</th></tr> + * <tr><th>uint32_t</th> <th>32bit</th><th>32bit</th> <th>32bit</th> <th>integer</th><th>unsigned</th></tr> + * <tr><th>__int64</th> <th>64bit</th><th>64bit</th> <th>64bit</th> <th>integer</th><th>signed or unsigned</th></tr> + * <tr><th>int64_t</th> <th>64bit</th><th>64bit</th> <th>64bit</th> <th>integer</th><th>signed</th></tr> + * <tr><th>uint64_t</th> <th>64bit</th><th>64bit</th> <th>64bit</th> <th>integer</th><th>unsigned</th></tr> + * <tr><th>ptrdiff_t</th> <th>64bit</th><th>32bit</th> <th>64bit</th> <th>integer</th><th>signed</th></tr> + * <tr><th>size_t</th> <th>64bit</th><th>32bit</th> <th>64bit</th> <th>integer</th><th>unsigned</th></tr> + * </table> + */ +public class GlueGen implements GlueEmitterControls { + + static{ + Logging.init(); + } + + private List<String> forcedStructNames = new ArrayList<String>(); + private PCPP preprocessor; + + // State for SymbolFilters + private List<ConstantDefinition> constants; + private List<FunctionSymbol> functions; + + public void forceStructEmission(String typedefName) { + forcedStructNames.add(typedefName); + } + + public String findHeaderFile(String headerFileName) { + return preprocessor.findFile(headerFileName); + } + + public void runSymbolFilter(SymbolFilter filter) { + filter.filterSymbols(constants, functions); + List<ConstantDefinition> newConstants = filter.getConstants(); + List<FunctionSymbol> newFunctions = filter.getFunctions(); + if (newConstants != null) { + constants = newConstants; + } + if (newFunctions != null) { + functions = newFunctions; + } + } + + + @SuppressWarnings("unchecked") + public void run(final Reader reader, final String filename, Class<?> emitterClass, List<String> includePaths, List<String> cfgFiles, String outputRootDir, boolean debug) { + + try { + final PipedInputStream ppIn = new PipedInputStream(); + final PipedOutputStream ppOut = new PipedOutputStream(ppIn); + + preprocessor = new PCPP(includePaths, debug); + preprocessor.setOut(ppOut); + + new Thread("PCPP") { + + @Override + public void run() { + try { + preprocessor.run(reader, filename); + ppOut.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }.start(); + + DataInputStream dis = new DataInputStream(ppIn); + GnuCLexer lexer = new GnuCLexer(dis); + lexer.setTokenObjectClass(CToken.class.getName()); + lexer.initialize(); + // Parse the input expression. + GnuCParser parser = new GnuCParser(lexer); + + // set AST node type to TNode or get nasty cast class errors + parser.setASTNodeClass(TNode.class.getName()); + TNode.setTokenVocabulary(GNUCTokenTypes.class.getName()); + + // invoke parser + try { + parser.translationUnit(); + } catch (RecognitionException e) { + throw new RuntimeException("Fatal IO error", e); + } catch (TokenStreamException e) { + throw new RuntimeException("Fatal IO error", e); + } + + HeaderParser headerParser = new HeaderParser(); + headerParser.setDebug(debug); + TypeDictionary td = new TypeDictionary(); + headerParser.setTypedefDictionary(td); + TypeDictionary sd = new TypeDictionary(); + headerParser.setStructDictionary(sd); + // set AST node type to TNode or get nasty cast class errors + headerParser.setASTNodeClass(TNode.class.getName()); + // walk that tree + headerParser.translationUnit(parser.getAST()); + + /** + // For debugging: Dump type dictionary and struct dictionary to System.err + if(debug) { + td.dumpDictionary(err, "All Types"); + sd.dumpDictionary(err, "All Structs"); + } */ + + // At this point we have all of the pieces we need in order to + // 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 (Exception e) { + throw new RuntimeException("Exception occurred while instantiating emitter class.", e); + } + } + + for (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 + JavaEmitter jemit = (JavaEmitter) emit; + if (null != jemit.getConfig()) { + jemit.getConfig().setOutputRootDir(outputRootDir); + } + } + } + + // Provide MachineDescriptions to emitter + MachineDescription md32 = new MachineDescription32Bit(); + MachineDescription md64 = new MachineDescription64Bit(); + emit.setMachineDescription(md32, md64); + + // 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>(); + for (Object elem : headerParser.getEnums()) { + EnumType enumeration = (EnumType) elem; + String enumName = enumeration.getName(); + if (enumName.equals("<anonymous>")) { + enumName = null; + } + // iterate over all values in the enumeration + for (int i = 0; i < enumeration.getNumEnumerates(); ++i) { + String enumElementName = enumeration.getEnumName(i); + String value = String.valueOf(enumeration.getEnumValue(i)); + constants.add(new ConstantDefinition(enumElementName, value, true, enumName)); + } + } + for (Object elem : lexer.getDefines()) { + Define def = (Define) elem; + constants.add(new ConstantDefinition(def.getName(), def.getValue(), false, null)); + } + + functions = headerParser.getParsedFunctions(); + + // begin emission of glue code + emit.beginEmission(this); + + emit.beginDefines(); + Set<String> emittedDefines = new HashSet<String>(100); + // emit java equivalent of enum { ... } statements + for (ConstantDefinition def : constants) { + if (!emittedDefines.contains(def.getName())) { + emittedDefines.add(def.getName()); + String comment = null; + Set<String> aliases = def.getAliases(); + if (aliases != null) { + comment = "Alias for: <code>"; + for (String alias : aliases) { + comment += " " + alias; + } + comment += "</code>"; + } + if (def.getEnumName() != null) { + String enumName = "Defined as part of enum type \"" + def.getEnumName() + "\""; + if (comment == null) { + comment = enumName; + } else { + comment += "<br>\n" + enumName; + } + } + emit.emitDefine(def, comment); + } + } + emit.endDefines(); + + // Iterate through the functions finding structs that are referenced in + // the function signatures; these will be remembered for later emission + ReferencedStructs referencedStructs = new ReferencedStructs(); + for (FunctionSymbol sym : functions) { + // 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). + sym.getType().visit(referencedStructs); + } + + // Normally only referenced types will be emitted. The user can force a + // type to be emitted via a .cfg file directive. Those directives are + // processed here. + for (String name : forcedStructNames) { + Type type = td.get(name); + if (type == null) { + err.println("WARNING: during forced struct emission: struct \"" + name + "\" not found"); + } else if (!type.isCompound()) { + err.println("WARNING: during forced struct emission: type \"" + name + "\" was not a struct"); + } else { + type.visit(referencedStructs); + } + } + + // Lay out structs + emit.beginStructLayout(); + for (Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { + Type t = iter.next(); + if (t.isCompound()) { + emit.layoutStruct(t.asCompound()); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + emit.layoutStruct(c); + } + } + emit.endStructLayout(); + + // Emit structs + emit.beginStructs(td, sd, headerParser.getCanonMap()); + for (Iterator<Type> iter = referencedStructs.results(); iter.hasNext();) { + Type t = iter.next(); + if (t.isCompound()) { + emit.emitStruct(t.asCompound(), null); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p.getName()); + } + } + emit.endStructs(); + + // emit java and C code to interface with the native functions + emit.beginFunctions(td, sd, headerParser.getCanonMap()); + emit.emitFunctions(functions); + emit.endFunctions(); + + // end emission of glue code + emit.endEmission(); + + } catch (Exception e) { + throw new RuntimeException("Exception occurred while generating glue code.", e); + } + } + + public static void main(String... args) { + + if (args.length == 0) { + usage(); + } + + Reader reader = null; + String filename = null; + String emitterFQN = null; + String outputRootDir = null; + List<String> cfgFiles = new ArrayList<String>(); + boolean debug = false; + + List<String> includePaths = new ArrayList<String>(); + for (int i = 0; i < args.length; i++) { + if (i < args.length - 1) { + String arg = args[i]; + if (arg.startsWith("-I")) { + String[] paths = arg.substring(2).split(getProperty("path.separator")); + includePaths.addAll(Arrays.asList(paths)); + } else if (arg.startsWith("-O")) { + outputRootDir = arg.substring(2); + } else if (arg.startsWith("-E")) { + emitterFQN = arg.substring(2); + } else if (arg.startsWith("-C")) { + cfgFiles.add(arg.substring(2)); + } else if (arg.equals("--debug")) { + debug=true; + } else { + usage(); + } + } else { + String arg = args[i]; + if (arg.equals("-")) { + reader = new InputStreamReader(in); + filename = "standard input"; + } else { + if (arg.startsWith("-")) { + usage(); + } + filename = arg; + try { + reader = new BufferedReader(new FileReader(filename)); + } catch (FileNotFoundException ex) { + throw new RuntimeException("input file not found", ex); + } + } + } + } + + try { + Class<?> emitterClass = emitterFQN == null ? null : Class.forName(emitterFQN); + new GlueGen().run(reader, filename, emitterClass, includePaths, cfgFiles, outputRootDir, debug); + } catch (ClassNotFoundException ex) { + throw new RuntimeException("specified emitter class was not in the classpath", ex); + } + + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + private static void usage() { + out.println("Usage: java GlueGen [-I...] [-Eemitter_class_name] [-Ccfg_file_name...] <filename | ->"); + out.println(); + out.println("Runs C header parser on input file or standard input, first"); + out.println("passing input through minimal pseudo-C-preprocessor. Use -I"); + out.println("command-line arguments to specify the search path for #includes."); + out.println("Emitter class name can be specified with -E option: i.e.,"); + out.println("-Ecom.jogamp.gluegen.JavaEmitter (the default). Use"); + out.println("-Ecom.jogamp.gluegen.DebugEmitter to print recognized entities"); + out.println("(#define directives to constant numbers, typedefs, and function"); + out.println("declarations) to standard output. Emitter-specific configuration"); + out.println("file or files can be specified with -C option; e.g,"); + out.println("-Cjava-emitter.cfg."); + out.println(" --debug enables debug mode"); + exit(1); + } +} diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java new file mode 100644 index 0000000..5323c77 --- /dev/null +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -0,0 +1,1594 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import com.jogamp.gluegen.JavaEmitter.EmissionStyle; +import com.jogamp.gluegen.JavaEmitter.MethodAccess; +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 static java.util.logging.Level.*; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; +import static com.jogamp.gluegen.JavaEmitter.EmissionStyle.*; + +/** Parses and provides access to the contents of .cfg files for the + JavaEmitter. */ + +public class JavaConfiguration { + + 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()); + + /** + * Root directory for the hierarchy of generated java classes. Default is + * working directory. + */ + private String javaOutputDir = "."; + + /** + * Top output root directory for all generated files. Default is null, ie not to use it. + */ + private String outputRootDir = null; + + /** + * Directory into which generated native JNI code will be written. Default + * is current working directory. + */ + private String nativeOutputDir = "."; + + /** + * If true, then each native *.c and *.h file will be generated in the + * directory nativeOutputDir/packageAsPath(packageName). Default is false. + */ + private boolean nativeOutputUsesJavaHierarchy; + + /** + * If true, then the comment of a native method binding will include a @native tag + * to allow taglets to augment the javadoc with additional information regarding + * the mapped C function. Defaults to false. + */ + private boolean tagNativeBinding; + + /** + * Style of code emission. Can emit everything into one class + * (AllStatic), separate interface and implementing classes + * (InterfaceAndImpl), only the interface (InterfaceOnly), or only + * the implementation (ImplOnly). + */ + private EmissionStyle emissionStyle = AllStatic; + + /** + * List of imports to emit at the head of the output files. + */ + private List<String> imports = new ArrayList<String>(); + + /** + * The package in which the generated glue code expects to find its + * run-time helper classes (Buffers, Platform, + * StructAccessor). Defaults to "com.jogamp.gluegen.runtime". + */ + private String gluegenRuntimePackage = "com.jogamp.gluegen.runtime"; + + /** + * The kind of exception raised by the generated code if run-time + * checks fail. Defaults to RuntimeException. + */ + private String runtimeExceptionType = "RuntimeException"; + private String unsupportedExceptionType = "UnsupportedOperationException"; + + private Map<String, MethodAccess> accessControl = new HashMap<String, MethodAccess>(); + private Map<String, TypeInfo> typeInfoMap = new HashMap<String, TypeInfo>(); + private Set<String> returnsString = new HashSet<String>(); + private Map<String, String> returnedArrayLengths = new HashMap<String, String>(); + + /** + * Key is function that has some byte[] or short[] arguments that should be + * converted to String args; value is List of Integer argument indices + */ + private Map<String, List<Integer>> argumentsAreString = new HashMap<String, List<Integer>>(); + private Set<String> extendedIntfSymbolsIgnore = new HashSet<String>(); + private Set<String> extendedIntfSymbolsOnly = new HashSet<String>(); + private Set<Pattern> ignores = new HashSet<Pattern>(); + private Map<String, Pattern> ignoreMap = new HashMap<String, Pattern>(); + private Set<Pattern> ignoreNots = new HashSet<Pattern>(); + private Set<Pattern> unignores = new HashSet<Pattern>(); + private Set<Pattern> unimplemented = new HashSet<Pattern>(); + private boolean forceNioOnly4All = false; + private Set<String> nioOnly = new HashSet<String>(); + private boolean forceNioDirectOnly4All = false; + private Set<String> nioDirectOnly = new HashSet<String>(); + private Set<String> manuallyImplement = new HashSet<String>(); + private Map<String, List<String>> customJavaCode = new HashMap<String, List<String>>(); + private Map<String, List<String>> classJavadoc = new HashMap<String, List<String>>(); + private Map<String, String> structPackages = new HashMap<String, String>(); + private List<String> customCCode = new ArrayList<String>(); + private List<String> forcedStructs = new ArrayList<String>(); + private Map<String, String> returnValueCapacities = new HashMap<String, String>(); + private Map<String, String> returnValueLengths = new HashMap<String, String>(); + private Map<String, List<String>> temporaryCVariableDeclarations = new HashMap<String, List<String>>(); + private Map<String, List<String>> temporaryCVariableAssignments = new HashMap<String, List<String>>(); + private Map<String, List<String>> extendedInterfaces = new HashMap<String, List<String>>(); + private Map<String, List<String>> implementedInterfaces = new HashMap<String, List<String>>(); + private Map<String, String> parentClass = new HashMap<String, String>(); + private Map<String, String> javaTypeRenames = new HashMap<String, String>(); + private Map<String, String> javaSymbolRenames = new HashMap<String, String>(); + private Map<String, List<String>> javaPrologues = new HashMap<String, List<String>>(); + private Map<String, List<String>> javaEpilogues = new HashMap<String, List<String>>(); + + /** Reads the configuration file. + @param filename path to file that should be read + */ + public final void read(String filename) throws IOException { + read(filename, null); + } + + /** Reads the specified file, treating each line as if it started with the + specified string. + @param filename path to file that should be read + @param linePrefix if not null, treat each line read as if it were + prefixed with the specified string. + */ + protected final void read(String filename, String linePrefix) throws IOException { + File file = new File(filename); + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + } + catch (FileNotFoundException fnfe) { + throw new RuntimeException("Could not read file \"" + file + "\"", fnfe); + } + int lineNo = 0; + String line = null; + boolean hasPrefix = linePrefix != null && linePrefix.length() > 0; + try { + ++nestedReads; + while ((line = reader.readLine()) != null) { + ++lineNo; + if (hasPrefix) { + line = linePrefix + " " + line; + } + + if (line.trim().startsWith("#")) { + // comment line + continue; + } + + StringTokenizer tok = new StringTokenizer(line); + if (tok.hasMoreTokens()) { + // always reset delimiters in case of CustomJavaCode, etc. + String cmd = tok.nextToken(" \t\n\r\f"); + + dispatch(cmd, tok, file, filename, lineNo); + } + } + reader.close(); + } finally { + --nestedReads; + } + + if (nestedReads == 0) { + if (allStatic() && implClassName != null) { + throw new IllegalStateException("Error in configuration file \"" + filename + "\": Cannot use " + + "directive \"ImplJavaClass\" in conjunction with " + + "\"Style AllStatic\""); + } + + if (className == null && (emissionStyle() != ImplOnly)) { +// throw new RuntimeException("Output class name was not specified in configuration file \"" + filename + "\""); + } + if (packageName == null && (emissionStyle() != ImplOnly)) { + throw new RuntimeException("Output package name was not specified in configuration file \"" + filename + "\""); + } + + if (allStatic()) { + implClassName = className; + // If we're using the "Style AllStatic" directive, then the + // implPackageName is the same as the regular package name + implPackageName = packageName; + } else { + if (implClassName == null) { + // implClassName defaults to "<className>Impl" if ImplJavaClass + // directive is not used + if (className == null) { + throw new RuntimeException("If ImplJavaClass is not specified, must specify JavaClass"); + } + implClassName = className + "Impl"; + } + if (implPackageName == null) { + // implPackageName defaults to "<packageName>.impl" if ImplPackage + // directive is not used + if (packageName == null) { + throw new RuntimeException("If ImplPackageName is not specified, must specify PackageName"); + } + implPackageName = packageName + ".impl"; + } + } + } + } + + public void setOutputRootDir(String s) { outputRootDir=s; } + + /** Returns the package name parsed from the configuration file. */ + public String packageName() { + return packageName; + } + + /** Returns the implementation package name parsed from the configuration file. */ + public String implPackageName() { + return implPackageName; + } + + /** Returns the class name parsed from the configuration file. */ + public String className() { + return className; + } + + /** Returns the implementation class name parsed from the configuration file. */ + public String implClassName() { + return implClassName; + } + + public boolean structsOnly() { + return className == null && implClassName == null; + } + + /** Returns the Java code output directory parsed from the configuration file. */ + public String javaOutputDir() { + return (null != outputRootDir) ? (outputRootDir + "/" + javaOutputDir) : javaOutputDir; + } + + /** Returns the native code output directory parsed from the configuration file. */ + public String nativeOutputDir() { + return (null != outputRootDir) ? (outputRootDir + "/" + nativeOutputDir) : nativeOutputDir; + } + + /** Returns whether the native code directory structure mirrors the Java hierarchy. */ + public boolean nativeOutputUsesJavaHierarchy() { + return nativeOutputUsesJavaHierarchy; + } + + /** Returns whether the comment of a native method binding should include a @native tag. */ + public boolean tagNativeBinding() { + return tagNativeBinding; + } + + /** Returns the code emission style (constants in JavaEmitter) parsed from the configuration file. */ + public EmissionStyle emissionStyle() { + return emissionStyle; + } + + /** Returns the access control for the emitted Java method. Returns one of JavaEmitter.ACC_PUBLIC, JavaEmitter.ACC_PROTECTED, JavaEmitter.ACC_PRIVATE, or JavaEmitter.ACC_PACKAGE_PRIVATE. */ + public MethodAccess accessControl(String methodName) { + MethodAccess ret = accessControl.get(methodName); + if (ret != null) { + return ret; + } + // 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, + StructAccessor). Defaults to "com.jogamp.gluegen.runtime". */ + public String gluegenRuntimePackage() { + return gluegenRuntimePackage; + } + + /** Returns the kind of exception to raise if run-time checks fail in the generated code. */ + public String runtimeExceptionType() { + return runtimeExceptionType; + } + + /** Returns the kind of exception to raise if run-time checks fail in the generated code. */ + public String unsupportedExceptionType() { + return unsupportedExceptionType; + } + + /** Returns the list of imports that should be emitted at the top of each .java file. */ + public List<String> imports() { + return imports; + } + + private static final boolean DEBUG_TYPE_INFO = false; + /** 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, TypeDictionary typedefDictionary) { + // 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); + int pointerDepth = type.pointerDepth(); + for (int i = 0; i <= pointerDepth; i++) { + String name = type.getName(); + if (DEBUG_TYPE_INFO) { + System.err.println(" Type = " + type); + System.err.println(" Name = " + name); + } + if (name != null) { + TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); + if (info != null) { + 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); + } + } + + if (type.isCompound()) { + // Try struct name as well + name = type.asCompound().getStructName(); + if (name != null) { + TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); + if (info != null) { + 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 + Set<Entry<String, Type>> entrySet = typedefDictionary.entrySet(); + for (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); + } + TypeInfo info = closestTypeInfo(name, i + type.pointerDepth()); + if (info != null) { + 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); + } + } + } + + if (type.isPointer()) { + type = type.asPointer().getTargetType(); + } + } + + return null; + } + + // Helper functions for above + private TypeInfo closestTypeInfo(String name, int pointerDepth) { + TypeInfo info = typeInfoMap.get(name); + TypeInfo closest = null; + while (info != null) { + if (DEBUG_TYPE_INFO) + System.err.println(" Checking TypeInfo for " + name + " at pointerDepth " + pointerDepth); + if (info.pointerDepth() <= pointerDepth && (closest == null || info.pointerDepth() > closest.pointerDepth())) { + if (DEBUG_TYPE_INFO) + System.err.println(" Accepted"); + closest = info; + } + info = info.next(); + } + return closest; + } + + // Promotes a TypeInfo to a higher pointer type (if necessary) + private TypeInfo promoteTypeInfo(TypeInfo info, int numPointersStripped) { + int diff = numPointersStripped - info.pointerDepth(); + if (diff == 0) { + return info; + } + + if (diff < 0) { + throw new RuntimeException("TypeInfo for " + info.name() + " and pointerDepth " + + info.pointerDepth() + " should not have matched for depth " + + numPointersStripped); + } + + Class<?> c = info.javaType().getJavaClass(); + int pd = info.pointerDepth(); + + // Handle single-pointer stripping for types compatible with C + // integral and floating-point types specially so we end up + // generating NIO variants for these + if (diff == 1) { + JavaType jt = null; + if (c == Boolean.TYPE) jt = JavaType.createForCCharPointer(); + else if (c == Byte.TYPE) jt = JavaType.createForCCharPointer(); + else if (c == Short.TYPE) jt = JavaType.createForCShortPointer(); + else if (c == Integer.TYPE) jt = JavaType.createForCInt32Pointer(); + else if (c == Long.TYPE) jt = JavaType.createForCInt64Pointer(); + else if (c == Float.TYPE) jt = JavaType.createForCFloatPointer(); + else if (c == Double.TYPE) jt = JavaType.createForCDoublePointer(); + + if (jt != null) + return new TypeInfo(info.name(), pd + numPointersStripped, jt); + } + + while (diff > 0) { + c = Array.newInstance(c, 0).getClass(); + --diff; + } + + return new TypeInfo(info.name(), + numPointersStripped, + JavaType.createForClass(c)); + } + + /** 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(String functionName) { + return returnsString.contains(functionName); + } + + /** Provides a Java MessageFormat expression indicating the number + of elements in the returned array from the specified function + name. Indicates that the given return value, which must be a + pointer to a CompoundType, is actually an array of the + CompoundType rather than a pointer to a single object. */ + public String returnedArrayLength(String functionName) { + return returnedArrayLengths.get(functionName); + } + + /** Returns a list of <code>Integer</code>s which are the indices of <code>const char*</code> + arguments that should be converted to <code>String</code>s. Returns null if there are no + such hints for the given function name. */ + + public List<Integer> stringArguments(String functionName) { + return argumentsAreString.get(functionName); + } + + public boolean isForceNioOnly4All() { return forceNioOnly4All; } + + public void addNioOnly(String fname ) { + nioOnly.add(fname); + } + public boolean nioOnly(String functionName) { + return forceNioOnly4All || nioOnly.contains(functionName); + } + + public boolean isForceNioDirectOnly4All() { return forceNioDirectOnly4All; } + + public void addNioDirectOnly(String fname ) { + nioDirectOnly.add(fname); + } + /** Returns true if the given function should only create a java.nio + variant, and no array variants, for <code>void*</code> and other + C primitive pointers. */ + public boolean nioDirectOnly(String functionName) { + return forceNioDirectOnly4All || nioDirectOnly.contains(functionName); + } + + /** Returns true if the glue code for the given function will be + manually implemented by the end user. */ + public boolean manuallyImplement(String functionName) { + return manuallyImplement.contains(functionName); + } + + /** Returns a list of Strings containing user-implemented code for + the given Java type name (not fully-qualified, only the class + name); returns either null or an empty list if there is no + custom code for the class. */ + public List<String> customJavaCodeForClass(String className) { + List<String> res = customJavaCode.get(className); + if (res == null) { + res = new ArrayList<String>(); + customJavaCode.put(className, res); + } + return res; + } + + /** Returns a list of Strings containing Javadoc documentation for + the given Java type name (not fully-qualified, only the class + name); returns either null or an empty list if there is no + Javadoc documentation for the class. */ + public List<String> javadocForClass(String className) { + List<String> res = classJavadoc.get(className); + if (res == null) { + res = new ArrayList<String>(); + classJavadoc.put(className, res); + } + return res; + } + + /** Returns the package into which to place the glue code for + accessing the specified struct. Defaults to emitting into the + regular package (i.e., the result of {@link #packageName}). */ + public String packageForStruct(String structName) { + String res = structPackages.get(structName); + if (res == null) { + res = packageName; + } + return res; + } + + /** Returns, as a List of Strings, the custom C code to be emitted + along with the glue code for the main class. */ + public List<String> customCCode() { + return customCCode; + } + + /** Returns, as a List of Strings, the structs for which glue code + emission should be forced. */ + public List<String> forcedStructs() { + return forcedStructs; + } + + /** Returns a MessageFormat string of the C expression calculating + the capacity of the java.nio.ByteBuffer being returned from a + native method, or null if no expression has been specified. */ + public String returnValueCapacity(String functionName) { + return returnValueCapacities.get(functionName); + } + + /** Returns a MessageFormat string of the C expression calculating + the length of the array being returned from a native method, or + null if no expression has been specified. */ + public String returnValueLength(String functionName) { + return returnValueLengths.get(functionName); + } + + /** Returns a List of Strings of expressions declaring temporary C + variables in the glue code for the specified function. */ + public List<String> temporaryCVariableDeclarations(String functionName) { + return temporaryCVariableDeclarations.get(functionName); + } + + /** Returns a List of Strings of expressions containing assignments + to temporary C variables in the glue code for the specified + function. */ + public List<String> temporaryCVariableAssignments(String functionName) { + return temporaryCVariableAssignments.get(functionName); + } + + /** Returns a List of Strings indicating the interfaces the passed + interface should declare it extends. May return null or a list + of zero length if there are none. */ + public List<String> extendedInterfaces(String interfaceName) { + List<String> res = extendedInterfaces.get(interfaceName); + if (res == null) { + res = new ArrayList<String>(); + extendedInterfaces.put(interfaceName, res); + } + return res; + } + + /** Returns a List of Strings indicating the interfaces the passed + class should declare it implements. May return null or a list + of zero length if there are none. */ + public List<String> implementedInterfaces(String className) { + List<String> res = implementedInterfaces.get(className); + if (res == null) { + res = new ArrayList<String>(); + implementedInterfaces.put(className, res); + } + return res; + } + + /** Returns a List of Strings indicating the interfaces the passed + class should declare it implements. May return null or a list + of zero length if there are none. */ + public String extendedParentClass(String className) { + return parentClass.get(className); + } + + public static final boolean DEBUG_IGNORES = false; + public static boolean dumpedIgnores = false; + + public void dumpIgnoresOnce() { + if(!dumpedIgnores) { + dumpedIgnores = true; + dumpIgnores(); + } + } + + public void dumpIgnores() { + System.err.println("Extended Intf: "); + for (String str : extendedIntfSymbolsIgnore) { + System.err.println("\t"+str); + } + System.err.println("Ignores (All): "); + for (Pattern pattern : ignores) { + System.err.println("\t"+pattern); + } + } + + public void dumpRenames() { + System.err.println("Symbol Renames: "); + for (String key : javaSymbolRenames.keySet()) { + System.err.println("\t"+key+" -> "+javaSymbolRenames.get(key)); + } + } + + /** Returns true if this #define, function, struct, or field within + a struct should be ignored during glue code generation. */ + public boolean shouldIgnoreInInterface(String symbol) { + if(DEBUG_IGNORES) { + dumpIgnoresOnce(); + } + // Simple case; the entire symbol (orig or renamed) is in the interface ignore table + if (extendedIntfSymbolsIgnore.contains(symbol) || + extendedIntfSymbolsIgnore.contains(getJavaSymbolRename(symbol))) { + if(DEBUG_IGNORES) { + System.err.println("Ignore Intf: "+symbol); + } + return true; + } + return shouldIgnoreInImpl_Int(symbol); + } + + public boolean shouldIgnoreInImpl(String symbol) { + return shouldIgnoreInImpl_Int(symbol); + } + + private boolean shouldIgnoreInImpl_Int(String symbol) { + + if(DEBUG_IGNORES) { + dumpIgnoresOnce(); + } + + if (!extendedIntfSymbolsOnly.isEmpty()) { + if(!extendedIntfSymbolsOnly.contains(symbol) && + !extendedIntfSymbolsOnly.contains(getJavaSymbolRename(symbol))) { + if(DEBUG_IGNORES) { + System.err.println("Ignore Impl !extended: " + symbol); + } + return true; + } + } + + // Simple case; the entire symbol is in the ignore table. + if (ignores.contains(symbol)) { + if(DEBUG_IGNORES) { + System.err.println("Ignore Impl ignores: "+symbol); + } + return true; + } + + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (Pattern regexp : ignores) { + Matcher matcher = regexp.matcher(symbol); + if (matcher.matches()) { + if(DEBUG_IGNORES) { + System.err.println("Ignore Impl RegEx: "+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 (Pattern regexp : ignoreNots) { + 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; + } + + boolean unignoreFound = false; + for (Pattern unignoreRegexp : unignores) { + Matcher unignoreMatcher = unignoreRegexp.matcher(symbol); + if (unignoreMatcher.matches()) { + unignoreFound = true; + break; + } + } + + if (!unignoreFound) + if(DEBUG_IGNORES) { + System.err.println("Ignore Impl !unignore: "+symbol); + } + return true; + } + } + } + + 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(String symbol) { + + // Simple case; the entire symbol is in the ignore table. + if (unimplemented.contains(symbol)) { + return true; + } + + // Ok, the slow case. We need to check the entire table, in case the table + // contains an regular expression that matches the symbol. + for (Pattern regexp : unimplemented) { + Matcher matcher = regexp.matcher(symbol); + if (matcher.matches()) { + return true; + } + } + + return false; + } + + /** Returns a replacement name for this type, which should be the + name of a Java wrapper class for a C struct, or the name + unchanged if no RenameJavaType directive was specified for this + type. */ + public String renameJavaType(String javaTypeName) { + String rename = javaTypeRenames.get(javaTypeName); + if (rename != null) { + return rename; + } + return javaTypeName; + } + + /** Returns a replacement name for this function or definition which + should be used as the Java name for the bound method or + constant. If a function, it still calls the originally-named C + function under the hood. Returns null if this symbol has not + been explicitly renamed. */ + public String getJavaSymbolRename(String symbolName) { + return javaSymbolRenames.get(symbolName); + } + + /** Programmatically adds a rename directive for the given symbol. */ + public void addJavaSymbolRename(String origName, String newName) { + javaSymbolRenames.put(origName, newName); + } + + /** Returns true if the emission style is AllStatic. */ + public boolean allStatic() { + return emissionStyle == AllStatic; + } + + /** Returns true if an interface should be emitted during glue code generation. */ + public boolean emitInterface() { + return emissionStyle() == InterfaceAndImpl || emissionStyle() == InterfaceOnly; + } + + /** Returns true if an implementing class should be emitted during glue code generation. */ + public boolean emitImpl() { + return emissionStyle() == AllStatic || emissionStyle() == InterfaceAndImpl || emissionStyle() == ImplOnly; + } + + /** Returns a list of Strings which should be emitted as a prologue + to the body for the Java-side glue code for the given method. + Returns null if no prologue was specified. */ + public List<String> javaPrologueForMethod(MethodBinding binding, + boolean forImplementingMethodCall, + boolean eraseBufferAndArrayTypes) { + List<String> res = javaPrologues.get(binding.getName()); + if (res == null) { + // Try again with method name and descriptor + res = javaPrologues.get(binding.getName() + binding.getDescriptor(forImplementingMethodCall, eraseBufferAndArrayTypes)); + } + return res; + } + + /** Returns a list of Strings which should be emitted as an epilogue + to the body for the Java-side glue code for the given method. + Returns null if no epilogue was specified. */ + public List<String> javaEpilogueForMethod(MethodBinding binding, + boolean forImplementingMethodCall, + boolean eraseBufferAndArrayTypes) { + List<String> res = javaEpilogues.get(binding.getName()); + if (res == null) { + // Try again with method name and descriptor + res = javaEpilogues.get(binding.getName() + binding.getDescriptor(forImplementingMethodCall, eraseBufferAndArrayTypes)); + } + return res; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { + //System.err.println("read cmd = [" + cmd + "]"); + if (cmd.equalsIgnoreCase("Package")) { + packageName = readString("package", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("GlueGenRuntimePackage")) { + gluegenRuntimePackage = readString("GlueGenRuntimePackage", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ImplPackage")) { + implPackageName = readString("ImplPackage", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("JavaClass")) { + className = readString("JavaClass", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ImplJavaClass")) { + implClassName = readString("ImplJavaClass", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("JavaOutputDir")) { + javaOutputDir = readString("JavaOutputDir", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("NativeOutputDir")) { + nativeOutputDir = readString("NativeOutputDir", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("HierarchicalNativeOutput")) { + String tmp = readString("HierarchicalNativeOutput", tok, filename, lineNo); + nativeOutputUsesJavaHierarchy = Boolean.valueOf(tmp).booleanValue(); + } else if (cmd.equalsIgnoreCase("TagNativeBinding")) { + tagNativeBinding = readBoolean("TagNativeBinding", tok, filename, lineNo).booleanValue(); + } else if (cmd.equalsIgnoreCase("Style")) { + try{ + emissionStyle = EmissionStyle.valueOf(readString("Style", tok, filename, lineNo)); + }catch(IllegalArgumentException ex) { + LOG.log(WARNING, "Error parsing \"style\" command at line {0} in file \"{1}\"", new Object[]{lineNo, filename}); + } + } else if (cmd.equalsIgnoreCase("AccessControl")) { + readAccessControl(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("Import")) { + imports.add(readString("Import", tok, filename, lineNo)); + } else if (cmd.equalsIgnoreCase("Opaque")) { + readOpaque(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ReturnsString")) { + readReturnsString(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ReturnedArrayLength")) { + readReturnedArrayLength(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnedArrayLength changes them. + } else if (cmd.equalsIgnoreCase("ArgumentIsString")) { + readArgumentIsString(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsIgnore")) { + readExtendedInterfaceSymbols(tok, filename, lineNo, false); + } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsOnly")) { + readExtendedInterfaceSymbols(tok, filename, lineNo, true); + } else if (cmd.equalsIgnoreCase("Ignore")) { + readIgnore(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("Unignore")) { + readUnignore(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("IgnoreNot")) { + readIgnoreNot(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("Unimplemented")) { + readUnimplemented(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("IgnoreField")) { + readIgnoreField(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ManuallyImplement")) { + readManuallyImplement(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("CustomJavaCode")) { + readCustomJavaCode(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because readCustomJavaCode changes them. + } else if (cmd.equalsIgnoreCase("CustomCCode")) { + readCustomCCode(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because readCustomCCode changes them. + } else if (cmd.equalsIgnoreCase("ClassJavadoc")) { + readClassJavadoc(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because readClassJavadoc changes them. + } else if (cmd.equalsIgnoreCase("NioOnly")) { + String funcName = readString("NioOnly", tok, filename, lineNo); + if(funcName.equals("__ALL__")) { + forceNioOnly4All=true; + } else { + addNioOnly( funcName ); + } + } else if (cmd.equalsIgnoreCase("NioDirectOnly")) { + String funcName = readString("NioDirectOnly", tok, filename, lineNo); + if(funcName.equals("__ALL__")) { + forceNioDirectOnly4All=true; + } else { + addNioDirectOnly( funcName ); + } + } else if (cmd.equalsIgnoreCase("EmitStruct")) { + forcedStructs.add(readString("EmitStruct", tok, filename, lineNo)); + } else if (cmd.equalsIgnoreCase("StructPackage")) { + readStructPackage(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("TemporaryCVariableDeclaration")) { + readTemporaryCVariableDeclaration(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because TemporaryCVariableDeclaration changes them. + } else if (cmd.equalsIgnoreCase("TemporaryCVariableAssignment")) { + readTemporaryCVariableAssignment(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because TemporaryCVariableAssignment changes them. + } else if (cmd.equalsIgnoreCase("ReturnValueCapacity")) { + readReturnValueCapacity(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnValueCapacity changes them. + } else if (cmd.equalsIgnoreCase("ReturnValueLength")) { + readReturnValueLength(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnValueLength changes them. + } else if (cmd.equalsIgnoreCase("Include")) { + doInclude(tok, file, filename, lineNo); + } else if (cmd.equalsIgnoreCase("IncludeAs")) { + doIncludeAs(tok, file, filename, lineNo); + } else if (cmd.equalsIgnoreCase("Extends")) { + readExtend(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("Implements")) { + readImplements(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ParentClass")) { + readParentClass(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("RenameJavaType")) { + readRenameJavaType(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("RenameJavaSymbol") || + // Backward compatibility + cmd.equalsIgnoreCase("RenameJavaMethod")) { + readRenameJavaSymbol(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("RuntimeExceptionType")) { + runtimeExceptionType = readString("RuntimeExceptionType", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("UnsupportedExceptionType")) { + unsupportedExceptionType = readString("UnsupportedExceptionType", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("JavaPrologue")) { + readJavaPrologueOrEpilogue(tok, filename, lineNo, true); + // Warning: make sure delimiters are reset at the top of this loop + // because readJavaPrologueOrEpilogue changes them. + } else if (cmd.equalsIgnoreCase("JavaEpilogue")) { + readJavaPrologueOrEpilogue(tok, filename, lineNo, false); + // Warning: make sure delimiters are reset at the top of this loop + // because readJavaPrologueOrEpilogue changes them. + } else if (cmd.equalsIgnoreCase("RangeCheck")) { + readRangeCheck(tok, filename, lineNo, false); + // Warning: make sure delimiters are reset at the top of this loop + // because RangeCheck changes them. + } else if (cmd.equalsIgnoreCase("RangeCheckBytes")) { + readRangeCheck(tok, filename, lineNo, true); + // Warning: make sure delimiters are reset at the top of this loop + // because RangeCheckBytes changes them. + } else { + throw new RuntimeException("Unknown command \"" + cmd + + "\" in command file " + filename + + " at line number " + lineNo); + } + } + + protected String readString(String cmd, StringTokenizer tok, String filename, int lineNo) { + try { + return tok.nextToken(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"" + cmd + "\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected Boolean readBoolean(String cmd, StringTokenizer tok, String filename, int lineNo) { + try { + return Boolean.valueOf(tok.nextToken()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"" + cmd + "\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected boolean value", e); + } + } + + protected Class<?> stringToPrimitiveType(String type) throws ClassNotFoundException { + if (type.equals("boolean")) return Boolean.TYPE; + if (type.equals("byte")) return Byte.TYPE; + if (type.equals("char")) return Character.TYPE; + if (type.equals("short")) return Short.TYPE; + if (type.equals("int")) return Integer.TYPE; + if (type.equals("long")) return Long.TYPE; + if (type.equals("float")) return Float.TYPE; + if (type.equals("double")) return Double.TYPE; + throw new RuntimeException("Only primitive types are supported here"); + } + + protected void readAccessControl(StringTokenizer tok, String filename, int lineNo) { + try { + String methodName = tok.nextToken(); + String style = tok.nextToken(); + MethodAccess access = MethodAccess.valueOf(style.toUpperCase()); + accessControl.put(methodName, access); + } catch (Exception e) { + throw new RuntimeException("Error parsing \"AccessControl\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readOpaque(StringTokenizer tok, String filename, int lineNo) { + try { + JavaType javaType = JavaType.createForClass(stringToPrimitiveType(tok.nextToken())); + String cType = null; + while (tok.hasMoreTokens()) { + if (cType == null) { + cType = tok.nextToken(); + } else { + cType = cType + " " + tok.nextToken(); + } + } + if (cType == null) { + throw new RuntimeException("No C type for \"Opaque\" command at line " + lineNo + + " in file \"" + filename + "\""); + } + TypeInfo info = parseTypeInfo(cType, javaType); + addTypeInfo(info); + } catch (Exception e) { + throw new RuntimeException("Error parsing \"Opaque\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readReturnsString(StringTokenizer tok, String filename, int lineNo) { + try { + String name = tok.nextToken(); + returnsString.add(name); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnsString\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readReturnedArrayLength(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnedArrayLengths.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnedArrayLength\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + @SuppressWarnings("unchecked") + protected void readExtendedInterfaceSymbols(StringTokenizer tok, String filename, int lineNo, boolean onlyList) { + File javaFile; + BufferedReader javaReader; + try { + javaFile = new File(tok.nextToken()); + javaReader = new BufferedReader(new FileReader(javaFile)); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + + JavaLexer lexer = new JavaLexer(javaReader); + lexer.setFilename(javaFile.getName()); + + JavaParser parser = new JavaParser(lexer); + parser.setFilename(javaFile.getName()); + + try { + parser.compilationUnit(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + if(onlyList) { + extendedIntfSymbolsOnly.addAll(parser.getParsedEnumNames()); + extendedIntfSymbolsOnly.addAll(parser.getParsedFunctionNames()); + } else { + extendedIntfSymbolsIgnore.addAll(parser.getParsedEnumNames()); + extendedIntfSymbolsIgnore.addAll(parser.getParsedFunctionNames()); + } + } + + protected void readIgnore(StringTokenizer tok, String filename, int lineNo) { + try { + String regex = tok.nextToken(); + Pattern pattern = Pattern.compile(regex); + ignores.add(pattern); + ignoreMap.put(regex, pattern); + //System.err.println("IGNORING " + regex + " / " + ignores.get(regex)); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Ignore\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readUnignore(StringTokenizer tok, String filename, int lineNo) { + try { + String regex = tok.nextToken(); + Pattern pattern = ignoreMap.get(regex); + ignoreMap.remove(regex); + ignores.remove(pattern); + + // If the pattern wasn't registered before, then make sure we have a + // valid pattern instance to put into the unignores set. + if(pattern == null) + pattern = Pattern.compile(regex); + unignores.add(pattern); + + //System.err.println("UN-IGNORING " + regex + " / " + ignores.get(regex)); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Unignore\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readIgnoreNot(StringTokenizer tok, String filename, int lineNo) { + try { + String regex = tok.nextToken(); + ignoreNots.add(Pattern.compile(regex)); + //System.err.println("IGNORING NEGATION OF " + regex + " / " + ignores.get(regex)); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"IgnoreNot\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readUnimplemented(StringTokenizer tok, String filename, int lineNo) { + try { + String regex = tok.nextToken(); + unimplemented.add(Pattern.compile(regex)); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Unimplemented\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readIgnoreField(StringTokenizer tok, String filename, int lineNo) { + try { + String containingStruct = tok.nextToken(); + String name = tok.nextToken(); + ignores.add(Pattern.compile(containingStruct + " " + name)); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"IgnoreField\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readManuallyImplement(StringTokenizer tok, String filename, int lineNo) { + try { + String name = tok.nextToken(); + manuallyImplement.add(name); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ManuallyImplement\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readCustomJavaCode(StringTokenizer tok, String filename, int lineNo) { + try { + String tokenClassName = tok.nextToken(); + try { + String restOfLine = tok.nextToken("\n\r\f"); + addCustomJavaCode(tokenClassName, restOfLine); + } catch (NoSuchElementException e) { + addCustomJavaCode(tokenClassName, ""); + } + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"CustomJavaCode\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void addCustomJavaCode(String className, String code) { + List<String> codeList = customJavaCodeForClass(className); + codeList.add(code); + } + + protected void readCustomCCode(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + customCCode.add(restOfLine); + } catch (NoSuchElementException e) { + customCCode.add(""); + } + } + + protected void readClassJavadoc(StringTokenizer tok, String filename, int lineNo) { + try { + String tokenClassName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + addClassJavadoc(tokenClassName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ClassJavadoc\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void addClassJavadoc(String className, String code) { + List<String> codeList = javadocForClass(className); + codeList.add(code); + } + + /** + * When const char* arguments in the C function prototypes are encountered, + * the emitter will normally convert them to <code>byte[]</code> + * arguments. This directive lets you specify which of those arguments + * should be converted to <code>String</code> arguments instead of <code> + * byte[] </code>. <p> + * + * For example, given the C prototype: + * <pre> + * void FuncName(const char* ugh, int bar, const char *foo, const char* goop); + * </pre> + * + * The emitter will normally emit: + * <pre> + * public abstract void FuncName(byte[] ugh, int bar, byte[] foo, byte[] goop); + * </pre> + * + * However, if you supplied the following directive: + * + * <pre> + * ArgumentIsString FuncName 0 2 + * </pre> + * + * The emitter will instead emit: + * <pre> + * public abstract void FuncName(String ugh, int bar, String foo, byte[] goop); + * </pre> + * + */ + protected void readArgumentIsString(StringTokenizer tok, String filename, int lineNo) { + try { + String methodName = tok.nextToken(); + ArrayList<Integer> argIndices = new ArrayList<Integer>(2); + while (tok.hasMoreTokens()) { + Integer idx = Integer.valueOf(tok.nextToken()); + argIndices.add(idx); + } + + if (argIndices.size() > 0) { + argumentsAreString.put(methodName, argIndices); + } else { + throw new RuntimeException("ERROR: Error parsing \"ArgumentIsString\" command at line " + lineNo + + " in file \"" + filename + "\": directive requires specification of at least 1 index"); + } + } catch (NoSuchElementException e) { + throw new RuntimeException( + "Error parsing \"ArgumentIsString\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readStructPackage(StringTokenizer tok, String filename, int lineNo) { + try { + String struct = tok.nextToken(); + String pkg = tok.nextToken(); + structPackages.put(struct, pkg); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"StructPackage\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readReturnValueCapacity(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnValueCapacities.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnValueCapacity\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readReturnValueLength(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnValueLengths.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnValueLength\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readTemporaryCVariableDeclaration(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + List<String> list = temporaryCVariableDeclarations.get(functionName); + if (list == null) { + list = new ArrayList<String>(); + temporaryCVariableDeclarations.put(functionName, list); + } + list.add(restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"TemporaryCVariableDeclaration\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readTemporaryCVariableAssignment(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + List<String> list = temporaryCVariableAssignments.get(functionName); + if (list == null) { + list = new ArrayList<String>(); + temporaryCVariableAssignments.put(functionName, list); + } + list.add(restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"TemporaryCVariableAssignment\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void doInclude(StringTokenizer tok, File file, String filename, int lineNo) throws IOException { + try { + String includedFilename = tok.nextToken(); + File includedFile = new File(includedFilename); + if (!includedFile.isAbsolute()) { + includedFile = new File(file.getParentFile(), includedFilename); + } + read(includedFile.getAbsolutePath()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Include\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void doIncludeAs(StringTokenizer tok, File file, String filename, int lineNo) throws IOException { + try { + StringBuilder linePrefix = new StringBuilder(128); + while (tok.countTokens() > 1) + { + linePrefix.append(tok.nextToken()); + linePrefix.append(" "); + } + // last token is filename + String includedFilename = tok.nextToken(); + File includedFile = new File(includedFilename); + if (!includedFile.isAbsolute()) { + includedFile = new File(file.getParentFile(), includedFilename); + } + read(includedFile.getAbsolutePath(), linePrefix.toString()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"IncludeAs\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readExtend(StringTokenizer tok, String filename, int lineNo) { + try { + String interfaceName = tok.nextToken(); + List<String> intfs = extendedInterfaces(interfaceName); + intfs.add(tok.nextToken()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Extends\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected void readImplements(StringTokenizer tok, String filename, int lineNo) { + try { + String tokenClassName = tok.nextToken(); + List<String> intfs = implementedInterfaces(tokenClassName); + intfs.add(tok.nextToken()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"Implements\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected void readParentClass(StringTokenizer tok, String filename, int lineNo) { + try { + String tokenClassName = tok.nextToken(); + parentClass.put(tokenClassName, tok.nextToken()); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ParentClass\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected void readRenameJavaType(StringTokenizer tok, String filename, int lineNo) { + try { + String fromName = tok.nextToken(); + String toName = tok.nextToken(); + javaTypeRenames.put(fromName, toName); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"RenameJavaType\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected void readRenameJavaSymbol(StringTokenizer tok, String filename, int lineNo) { + try { + String fromName = tok.nextToken(); + String toName = tok.nextToken(); + javaSymbolRenames.put(fromName, toName); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"RenameJavaSymbol\" command at line " + lineNo + + " in file \"" + filename + "\": missing expected parameter", e); + } + } + + protected void readJavaPrologueOrEpilogue(StringTokenizer tok, String filename, int lineNo, boolean prologue) { + try { + String methodName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + if (startsWithDescriptor(restOfLine)) { + // Assume it starts with signature for disambiguation + int spaceIdx = restOfLine.indexOf(' '); + if (spaceIdx > 0) { + String descriptor = restOfLine.substring(0, spaceIdx); + restOfLine = restOfLine.substring(spaceIdx + 1, restOfLine.length()); + methodName = methodName + descriptor; + } + } + addJavaPrologueOrEpilogue(methodName, restOfLine, prologue); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"" + + (prologue ? "JavaPrologue" : "JavaEpilogue") + + "\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void addJavaPrologueOrEpilogue(String methodName, String code, boolean prologue) { + Map<String, List<String>> codes = (prologue ? javaPrologues : javaEpilogues); + List<String> data = codes.get(methodName); + if (data == null) { + data = new ArrayList<String>(); + codes.put(methodName, data); + } + data.add(code); + } + + protected void readRangeCheck(StringTokenizer tok, String filename, int lineNo, boolean inBytes) { + try { + String functionName = tok.nextToken(); + int argNum = Integer.parseInt(tok.nextToken()); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + // Construct a JavaPrologue for this + addJavaPrologueOrEpilogue(functionName, + "Buffers.rangeCheck" + + (inBytes ? "Bytes" : "") + + "({" + argNum + "}, " + restOfLine + ");", + true); + } catch (Exception e) { + throw new RuntimeException("Error parsing \"RangeCheck" + (inBytes ? "Bytes" : "") + "\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected static TypeInfo parseTypeInfo(String cType, JavaType javaType) { + String typeName = null; + int pointerDepth = 0; + int idx = 0; + while (idx < cType.length() && + (cType.charAt(idx) != ' ') && + (cType.charAt(idx) != '*')) { + ++idx; + } + typeName = cType.substring(0, idx); + // Count pointer depth + while (idx < cType.length()) { + if (cType.charAt(idx) == '*') { + ++pointerDepth; + } + ++idx; + } + return new TypeInfo(typeName, pointerDepth, javaType); + } + + protected void addTypeInfo(TypeInfo info) { + TypeInfo tmp = typeInfoMap.get(info.name()); + if (tmp == null) { + typeInfoMap.put(info.name(), info); + return; + } + while (tmp.next() != null) { + tmp = tmp.next(); + } + tmp.setNext(info); + } + + private static int nextIndexAfterType(String s, int idx) { + int len = s.length(); + while (idx < len) { + char c = s.charAt(idx); + + if (Character.isJavaIdentifierStart(c) || + Character.isJavaIdentifierPart(c) || + (c == '/')) { + idx++; + } else if (c == ';') { + return (idx + 1); + } else { + return -1; + } + } + return -1; + } + + private static int nextIndexAfterDescriptor(String s, int idx) { + char c = s.charAt(idx); + switch (c) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 'V': return (1 + idx); + case 'L': return nextIndexAfterType(s, idx + 1); + case ')': return idx; + default: break; + } + return -1; + } + + protected static boolean startsWithDescriptor(String s) { + // Try to see whether the String s starts with a valid Java + // descriptor. + + int idx = 0; + int len = s.length(); + while ((idx < len) && s.charAt(idx) == ' ') { + ++idx; + } + + if (idx >= len) return false; + if (s.charAt(idx++) != '(') return false; + while (idx < len) { + int nextIdx = nextIndexAfterDescriptor(s, idx); + if (nextIdx < 0) { + return false; + } + if (nextIdx == idx) { + // ')' + break; + } + idx = nextIdx; + } + int nextIdx = nextIndexAfterDescriptor(s, idx + 1); + if (nextIdx < 0) { + return false; + } + return true; + } +} diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java new file mode 100644 index 0000000..250da79 --- /dev/null +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -0,0 +1,1950 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.DynamicLookupHelper; +import java.io.*; +import java.util.*; +import java.text.MessageFormat; + +import com.jogamp.gluegen.cgram.types.*; +import java.nio.Buffer; +import java.util.logging.Logger; + +import static java.util.logging.Level.*; +import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*; + +// PROBLEMS: +// - what if something returns 'const int *'? Could we +// return an IntBuffer that has read-only behavior? Or do we copy the array +// (but we don't know its size!). What do we do if it returns a non-const +// int*? Should the user be allowed to write back to the returned pointer? +// +// - Non-const array types must be properly released with JNI_COMMIT +// in order to see side effects if the array was copied. + + +public class JavaEmitter implements GlueEmitter { + + private StructLayout layout; + private TypeDictionary typedefDictionary; + private TypeDictionary structDictionary; + private Map<Type, Type> canonMap; + protected JavaConfiguration cfg; + + /** + * Style of code emission. Can emit everything into one class + * (AllStatic), separate interface and implementing classes + * (InterfaceAndImpl), only the interface (InterfaceOnly), or only + * the implementation (ImplOnly). + */ + public enum EmissionStyle {AllStatic, InterfaceAndImpl, InterfaceOnly, ImplOnly}; + + /** + * Access control for emitted Java methods. + */ + public enum MethodAccess {PUBLIC, PROTECTED, PRIVATE, PACKAGE_PRIVATE, PUBLIC_ABSTRACT} + + private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything + private PrintWriter javaImplWriter; // Only used in non-AllStatic modes for impl class + private PrintWriter cWriter; + private MachineDescription machDesc32; + private MachineDescription machDesc64; + + protected final static Logger LOG = Logger.getLogger(JavaEmitter.class.getPackage().getName()); + + public void readConfigurationFile(String filename) throws Exception { + cfg = createConfig(); + cfg.read(filename); + } + + public void setMachineDescription(MachineDescription md32, MachineDescription md64) { + + if ((md32 == null) && (md64 == null)) { + throw new RuntimeException("Must specify at least one MachineDescription"); + } + + machDesc32 = md32; + machDesc64 = md64; + } + + class ConstantRenamer implements SymbolFilter { + + private List<ConstantDefinition> constants; + + public void filterSymbols(List<ConstantDefinition> constants, List<FunctionSymbol> functions) { + this.constants = constants; + doWork(); + } + + public List<ConstantDefinition> getConstants() { + return constants; + } + + public List<FunctionSymbol> getFunctions() { + return null; + } + + private void doWork() { + List<ConstantDefinition> newConstants = new ArrayList<ConstantDefinition>(); + JavaConfiguration cfg = getConfig(); + for (ConstantDefinition def : constants) { + def.rename(cfg.getJavaSymbolRename(def.getName())); + newConstants.add(def); + } + constants = newConstants; + } + } + + public void beginEmission(GlueEmitterControls controls) throws IOException { + + // Request emission of any structs requested + for (String structs : cfg.forcedStructs()) { + controls.forceStructEmission(structs); + } + + if (!cfg.structsOnly()) { + try { + openWriters(); + } catch (Exception e) { + throw new RuntimeException("Unable to open files for writing", e); + } + emitAllFileHeaders(); + + // Handle renaming of constants + controls.runSymbolFilter(new ConstantRenamer()); + } + } + + public void endEmission() { + if (!cfg.structsOnly()) { + emitAllFileFooters(); + + try { + closeWriters(); + } catch (Exception e) { + throw new RuntimeException("Unable to close open files", e); + } + } + } + + public void beginDefines() throws Exception { + if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + javaWriter().println(); + } + } + + protected static int getJavaRadix(String name, 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 longVal = Long.parseLong(parseValue, radix); + return radix; + } catch (NumberFormatException e) { + try { + // see if it's a double or float + double dVal = Double.parseDouble(value); + return 10; + } catch (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(String name, String value) { + + // "calculates" the result type of a simple expression + // example: (2+3)-(2.0f-3.0) -> Double + // example: (1 << 2) -> Integer + + Scanner scanner = new Scanner(value).useDelimiter("[+-/*/></(/)]"); + + Object resultType = null; + + while (scanner.hasNext()) { + + String t = scanner.next().trim(); + + if(0<t.length()) { + 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(String name, 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; + } + if(lastChar == 'u' || lastChar == 'U') { + parseValue = parseValue.substring(0, parseValue.length()-1); + } + + //System.err.println("parsing " + value + " as long w/ radix " + radix); + 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 (NumberFormatException e) { + try { + // see if it's a double or float + double dVal = Double.parseDouble(value); + 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 (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(String name, String value) { + Object oval = getJavaValue(name, value); + return getJavaType(name, oval); + } + + protected static String getJavaType(String name, 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"); + } + + public void emitDefine(ConstantDefinition def, String optionalComment) throws Exception { + + if (cfg.allStatic() || cfg.emitInterface()) { + // 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 + // emitted as the numeric value of the original definition. If we decide + // emit them as references we'll also have to emit them in the correct + // order. It's probably not an issue right now because the emitter + // currently only emits only numeric defines -- if it handled #define'd + // objects it would make a bigger difference. + + String name = def.getName(); + String value = def.getValue(); + + if (!cfg.shouldIgnoreInInterface(name)) { + String type = getJavaType(name, value); + if (optionalComment != null && optionalComment.length() != 0) { + javaWriter().println(" /** " + optionalComment + " */"); + } + 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().println(" public static final " + type + " " + name + " = " + value + suffix + ";"); + } + } + } + + public void endDefines() throws Exception { + } + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map<Type, Type> canonMap) throws Exception { + + this.typedefDictionary = typedefDictionary; + this.structDictionary = structDictionary; + this.canonMap = canonMap; + + if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) { + javaWriter().println(); + } + } + + public Iterator<FunctionSymbol> emitFunctions(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 + Set<FunctionSymbol> funcsToBindSet = new HashSet<FunctionSymbol>(100); + for (FunctionSymbol cFunc : originalCFunctions) { + if (!funcsToBindSet.contains(cFunc)) { + funcsToBindSet.add(cFunc); + } + } + + // validateFunctionsToBind(funcsToBindSet); + + ArrayList<FunctionSymbol> funcsToBind = new ArrayList<FunctionSymbol>(funcsToBindSet); + // sort functions to make them easier to find in native code + Collections.sort(funcsToBind, new Comparator<FunctionSymbol>() { + public int compare(FunctionSymbol o1, FunctionSymbol o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + + // Bind all the C funcs to Java methods + HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>(); + ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size()); + for (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 (FunctionEmitter emitter : methodBindingEmitters) { + try { + if (!emitter.isInterface() || !cfg.shouldIgnoreInInterface(emitter.getName())) { + emitter.emit(); + emitter.getDefaultOutput().println(); // put newline after method body + } + } catch (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(); + } + + /** + * Create the object that will read and store configuration information for + * this JavaEmitter. + */ + protected JavaConfiguration createConfig() { + return new JavaConfiguration(); + } + + /** + * Get the configuration information for this JavaEmitter. + */ + protected JavaConfiguration getConfig() { + return cfg; + } + + /** + * Generates the public emitters for this MethodBinding which will + * produce either simply signatures (for the interface class, if + * any) or function definitions with or without a body (depending on + * whether or not the implementing function can go directly to + * native code because it doesn't need any processing of the + * outgoing arguments). + */ + protected void generatePublicEmitters(MethodBinding binding, + List<FunctionEmitter> allEmitters, + boolean signatureOnly) { + PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter()); + + if (cfg.manuallyImplement(binding.getName()) && !signatureOnly) { + // We only generate signatures for manually-implemented methods; + // user provides the implementation + return; + } + + MethodAccess accessControl = cfg.accessControl(binding.getName()); + // We should not emit anything except public APIs into interfaces + if (signatureOnly && (accessControl != PUBLIC)) { + return; + } + + // 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 + boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); + List<String> prologue = cfg.javaPrologueForMethod(binding, false, false); + List<String> epilogue = cfg.javaEpilogueForMethod(binding, false, false); + boolean needsBody = (isUnimplemented || + (binding.needsNIOWrappingOrUnwrapping() || + binding.signatureUsesJavaPrimitiveArrays()) || + (prologue != null) || + (epilogue != null)); + + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + cfg.unsupportedExceptionType(), + !signatureOnly && needsBody, + cfg.tagNativeBinding(), + false, + cfg.nioDirectOnly(binding.getName()), + false, + false, + false, + isUnimplemented, + signatureOnly, + cfg); + switch (accessControl) { + case PUBLIC: emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); break; + case PROTECTED: emitter.addModifier(JavaMethodBindingEmitter.PROTECTED); break; + case PRIVATE: emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); break; + default: break; // package-private adds no modifiers + } + if (cfg.allStatic()) { + emitter.addModifier(JavaMethodBindingEmitter.STATIC); + } + if (!isUnimplemented && !needsBody && !signatureOnly) { + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + } + emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + emitter.setPrologue(prologue); + emitter.setEpilogue(epilogue); + allEmitters.add(emitter); + } + + /** + * Generates the private emitters for this MethodBinding. On the + * Java side these will simply produce signatures for native + * methods. On the C side these will create the emitters which will + * write the JNI code to interface to the functions. We need to be + * careful to make the signatures all match up and not produce too + * many emitters which would lead to compilation errors from + * creating duplicated methods / functions. + */ + protected void generatePrivateEmitters(MethodBinding binding, + List<FunctionEmitter> allEmitters) { + if (cfg.manuallyImplement(binding.getName())) { + // Don't produce emitters for the implementation class + return; + } + + boolean hasPrologueOrEpilogue = + ((cfg.javaPrologueForMethod(binding, false, false) != null) || + (cfg.javaEpilogueForMethod(binding, false, false) != null)); + + // If we already generated a public native entry point for this + // method, don't emit another one + if (!cfg.isUnimplemented(binding.getName()) && + (binding.needsNIOWrappingOrUnwrapping() || + binding.signatureUsesJavaPrimitiveArrays() || + hasPrologueOrEpilogue)) { + PrintWriter writer = (cfg.allStatic() ? javaWriter() : javaImplWriter()); + + // If the binding uses primitive arrays, we are going to emit + // the private native entry point for it along with the version + // taking only NIO buffers + if (!binding.signatureUsesJavaPrimitiveArrays()) { + // (Always) emit the entry point taking only direct buffers + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + cfg.unsupportedExceptionType(), + false, + cfg.tagNativeBinding(), + true, + cfg.nioDirectOnly(binding.getName()), + true, + true, + false, + false, + false, + cfg); + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + if (cfg.allStatic()) { + emitter.addModifier(JavaMethodBindingEmitter.STATIC); + } + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(emitter); + } + } + + // Now generate the C emitter(s). We need to produce one for every + // Java native entry point (public or private). The only + // situations where we don't produce one are (a) when the method + // is unimplemented, and (b) when the signature contains primitive + // arrays, since the latter is handled by the method binding + // variant taking only NIO Buffers. + if (!cfg.isUnimplemented(binding.getName()) && + !binding.signatureUsesJavaPrimitiveArrays()) { + CMethodBindingEmitter cEmitter; + // Generate a binding without mixed access (NIO-direct, -indirect, array) + cEmitter = + new CMethodBindingEmitter(binding, + cWriter(), + cfg.implPackageName(), + cfg.implClassName(), + true, // NOTE: we always disambiguate with a suffix now, so this is optional + cfg.allStatic(), + (binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue), + !cfg.nioDirectOnly(binding.getName()), + machDesc64); + prepCEmitter(binding, cEmitter); + allEmitters.add(cEmitter); + } + } + + protected void prepCEmitter(MethodBinding binding, CMethodBindingEmitter cEmitter) + { + // See whether we need an expression to help calculate the + // length of any return type + JavaType javaReturnType = binding.getJavaReturnType(); + if (javaReturnType.isNIOBuffer() || + javaReturnType.isCompoundTypeWrapper()) { + // See whether capacity has been specified + String capacity = cfg.returnValueCapacity(binding.getName()); + if (capacity != null) { + cEmitter.setReturnValueCapacityExpression( new MessageFormat(capacity) ); + } + } else if (javaReturnType.isArray() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + // NOTE: adding a check here because the CMethodBindingEmitter + // also doesn't yet handle returning scalar arrays. In order + // to implement this, return the type as a Buffer instead + // (i.e., IntBuffer, FloatBuffer) and add code as necessary. + if (javaReturnType.isPrimitiveArray()) { + throw new RuntimeException("Primitive array return types not yet supported"); + } + + // See whether length has been specified + String len = cfg.returnValueLength(binding.getName()); + if (len != null) { + cEmitter.setReturnValueLengthExpression( new MessageFormat(len) ); + } + } + cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); + cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); + } + + /** + * Generate all appropriate Java bindings for the specified C function + * symbols. + */ + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception { + + ArrayList<FunctionEmitter> allEmitters = new ArrayList<FunctionEmitter>(); + + try { + // Get Java binding for the function + MethodBinding mb = bindFunction(sym, null, null, machDesc64); + + // JavaTypes representing C pointers in the initial + // MethodBinding have not been lowered yet to concrete types + List<MethodBinding> bindings = expandMethodBinding(mb); + + for (MethodBinding binding : bindings) { + + if(!methodBindingSet.add(binding)) { + // skip .. already exisiting binding .. + continue; + } + + if (cfg.allStatic() && binding.hasContainingType()) { + // This should not currently happen since structs are emitted using a different mechanism + throw new IllegalArgumentException("Cannot create binding in AllStatic mode because method has containing type: \"" + + binding + "\""); + } + + // The structure of the generated glue code looks something like this: + // Simple method (no arrays, void pointers, etc.): + // Interface class: + // public void fooMethod(); + // Implementation class: + // public native void fooMethod(); + // + // Method taking void* argument: + // Interface class: + // public void fooMethod(Buffer arg); + // Implementation class: + // public void fooMethod(Buffer arg) { + // ... bounds checks, etc. ... + // + // boolean arg_direct = arg != null && Buffers.isDirect(arg); + // + // fooMethod0(arg_direct?arg:Buffers.getArray(arg), + // arg_direct?Buffers.getDirectBufferByteOffset(arg):Buffers.getIndirectBufferByteOffset(arg), + // arg_direct, + // ... ); + // } + // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...); + // + // Method taking primitive array argument: + // Interface class: + // public void fooMethod(int[] arg, int arg_offset); + // public void fooMethod(IntBuffer arg); + // Implementing class: + // public void fooMethod(int[] arg, int arg_offset) { + // ... range checks, etc. ... + // fooMethod1(arg, SIZEOF_INT * arg_offset); + // } + // public void fooMethod(IntBuffer arg) { + // ... bounds checks, etc. ... + // + // boolean arg_direct = BufferFactory.isDirect(arg); + // + // fooMethod1(arg_direct?arg:BufferFactory.getArray(arg), + // arg_direct?BufferFactory.getDirectBufferByteOffset(arg):BufferFactory.getIndirectBufferByteOffset(arg), + // arg_direct, + // ... ); + // } + // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...); + // + // Note in particular that the public entry point taking an + // array is merely a special case of the indirect buffer case. + + if (cfg.emitInterface()) { + generatePublicEmitters(binding, allEmitters, true); + } + if (cfg.emitImpl()) { + generatePublicEmitters(binding, allEmitters, false); + generatePrivateEmitters(binding, allEmitters); + } + } // end iteration over expanded bindings + } catch (Exception e) { + throw new RuntimeException("Error while generating bindings for \"" + sym + "\"", e); + } + + return allEmitters; + } + + + public void endFunctions() throws Exception { + if (!cfg.structsOnly()) { + if (cfg.allStatic() || cfg.emitInterface()) { + emitCustomJavaCode(javaWriter(), cfg.className()); + } + if (!cfg.allStatic() && cfg.emitImpl()) { + emitCustomJavaCode(javaImplWriter(), cfg.implClassName()); + } + } + } + + public void beginStructLayout() throws Exception {} + public void layoutStruct(CompoundType t) throws Exception { + getLayout().layout(t); + } + public void endStructLayout() throws Exception {} + + public void beginStructs(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map<Type, Type> canonMap) throws Exception { + this.typedefDictionary = typedefDictionary; + this.structDictionary = structDictionary; + this.canonMap = canonMap; + } + + public void emitStruct(CompoundType structType, String alternateName) throws Exception { + // Emit abstract base class delegating to 32-bit or 64-bit implementations + emitStructImpl(structType, alternateName, machDesc32, machDesc64, true, false); + // Emit concrete implementing class for each variant + emitStructImpl(structType, alternateName, machDesc32, machDesc64, false, true); + emitStructImpl(structType, alternateName, machDesc32, machDesc64, false, false); + } + + public void emitStructImpl(CompoundType structType, + String alternateName, + MachineDescription md32, + MachineDescription md64, + boolean doBaseClass, + boolean do32Bit) throws Exception { + String name = structType.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + if (name == null) { + LOG.log(WARNING, "skipping emission of unnamed struct \"{0}\"", structType); + return; + } + + if (cfg.shouldIgnoreInInterface(name)) { + return; + } + + Type containingCType = canonicalize(new PointerType(SizeThunk.POINTER, structType, 0)); + JavaType containingType = typeToJavaType(containingCType, false, null); + if (!containingType.isCompoundTypeWrapper()) { + return; + } + String containingTypeName = containingType.getName(); + + if ((md32 == null) || (md64 == null)) { + throw new RuntimeException("Must supply both 32- and 64-bit MachineDescriptions to emitStructImpl"); + } + String suffix = ""; + + // The "external" MachineDescription is the one used to determine + // the sizes of the primitive types seen in the public API. For + // example, if a C long is an element of a struct, it is the size + // of a Java int on a 32-bit machine but the size of a Java long + // on a 64-bit machine. To support both of these sizes with the + // same API, the abstract base class must take and return a Java + // long from the setter and getter for this field. However the + // implementation on a 32-bit platform must downcast this to an + // int and set only an int's worth of data in the struct. The + // "internal" MachineDescription is the one used to determine how + // much data to set in or get from the struct and exactly from + // where it comes. + // + // Note that the 64-bit MachineDescription is always used as the + // external MachineDescription. + + MachineDescription extMachDesc = md64; + MachineDescription intMachDesc = null; + + if (!doBaseClass) { + if (do32Bit) { + intMachDesc = md32; + suffix = "32"; + } else { + intMachDesc = md64; + suffix = "64"; + } + } + + boolean needsNativeCode = false; + // Native code for calls through function pointers gets emitted + // into the abstract base class; Java code which accesses fields + // gets emitted into the concrete classes + if (doBaseClass) { + for (int i = 0; i < structType.getNumFields(); i++) { + if (structType.getField(i).getType().isFunctionPointer()) { + needsNativeCode = true; + break; + } + } + } + + String structClassPkg = cfg.packageForStruct(name); + PrintWriter writer = null; + PrintWriter newWriter = null; + try { + writer = openFile( + cfg.javaOutputDir() + File.separator + + CodeGenUtils.packageAsPath(structClassPkg) + + File.separator + containingTypeName + suffix + ".java"); + CodeGenUtils.emitAutogeneratedWarning(writer, this); + if (needsNativeCode) { + String nRoot = cfg.nativeOutputDir(); + if (cfg.nativeOutputUsesJavaHierarchy()) { + nRoot += File.separator + CodeGenUtils.packageAsPath(cfg.packageName()); + } + newWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c"); + CodeGenUtils.emitAutogeneratedWarning(newWriter, this); + emitCHeader(newWriter, containingTypeName); + } + } catch(Exception e) { + throw new RuntimeException("Unable to open files for emission of struct class", e); + } + + writer.println(); + writer.println("package " + structClassPkg + ";"); + writer.println(); + writer.println("import java.nio.*;"); + writer.println(); + + writer.println("import " + cfg.gluegenRuntimePackage() + ".*;"); + writer.println("import " + DynamicLookupHelper.class.getPackage().getName() + ".*;"); + writer.println("import " + Buffers.class.getPackage().getName() + ".*;"); + writer.println(); + List<String> imports = cfg.imports(); + for (String str : imports) { + writer.print("import "); + writer.print(str); + writer.println(";"); + } + writer.println(); + List<String> javadoc = cfg.javadocForClass(containingTypeName); + for (String doc : javadoc) { + writer.println(doc); + } + writer.print((doBaseClass ? "public " : "") + (doBaseClass ? "abstract " : "") + "class " + containingTypeName + suffix + " "); + if (!doBaseClass) { + writer.print("extends " + containingTypeName + " "); + } + boolean firstIteration = true; + List<String> userSpecifiedInterfaces = cfg.implementedInterfaces(containingTypeName); + for (String userInterface : userSpecifiedInterfaces) { + if (firstIteration) { + writer.print("implements "); + } + firstIteration = false; + writer.print(userInterface); + writer.print(" "); + } + writer.println("{"); + writer.println(); + if (doBaseClass) { + writer.println(" StructAccessor accessor;"); + writer.println(); + } + + writer.println(" public static int size() {"); + if (doBaseClass) { + writer.println(" if (Platform.is32Bit()) {"); + writer.println(" return " + containingTypeName + "32" + ".size();"); + writer.println(" } else {"); + writer.println(" return " + containingTypeName + "64" + ".size();"); + writer.println(" }"); + } else { + writer.println(" return " + structType.getSize(intMachDesc) + ";"); + } + writer.println(" }"); + writer.println(); + if (doBaseClass) { + writer.println(" public static " + containingTypeName + " create() {"); + writer.println(" return create(Buffers.newDirectByteBuffer(size()));"); + writer.println(" }"); + writer.println(); + writer.println(" public static " + containingTypeName + " create(java.nio.ByteBuffer buf) {"); + writer.println(" if (Platform.is32Bit()) {"); + writer.println(" return new " + containingTypeName + "32(buf);"); + writer.println(" } else {"); + writer.println(" return new " + containingTypeName + "64(buf);"); + writer.println(" }"); + writer.println(" }"); + writer.println(); + writer.println(" " + containingTypeName + "(java.nio.ByteBuffer buf) {"); + writer.println(" accessor = new StructAccessor(buf);"); + writer.println(" }"); + writer.println(); + writer.println(" public java.nio.ByteBuffer getBuffer() {"); + writer.println(" return accessor.getBuffer();"); + writer.println(" }"); + } else { + writer.println(" " + containingTypeName + suffix + "(java.nio.ByteBuffer buf) {"); + writer.println(" super(buf);"); + writer.println(" }"); + writer.println(); + } + for (int i = 0; i < structType.getNumFields(); i++) { + + Field field = structType.getField(i); + Type fieldType = field.getType(); + + if (!cfg.shouldIgnoreInInterface(name + " " + field.getName())) { + + String renamed = cfg.getJavaSymbolRename(field.getName()); + String fieldName = renamed==null ? field.getName() : renamed; + + if (fieldType.isFunctionPointer()) { + + if (doBaseClass) { + try { + // Emit method call and associated native code + FunctionType funcType = fieldType.asPointer().getTargetType().asFunction(); + FunctionSymbol funcSym = new FunctionSymbol(fieldName, funcType); + MethodBinding binding = bindFunction(funcSym, containingType, containingCType, machDesc64); + binding.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis + writer.println(); + + // Emit public Java entry point for calling this function pointer + JavaMethodBindingEmitter emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + cfg.unsupportedExceptionType(), + true, + cfg.tagNativeBinding(), + false, + true, // FIXME: should unify this with the general emission code + false, + false, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false, + cfg); + emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); + emitter.emit(); + + // Emit private native Java entry point for calling this function pointer + emitter = + new JavaMethodBindingEmitter(binding, + writer, + cfg.runtimeExceptionType(), + cfg.unsupportedExceptionType(), + false, + cfg.tagNativeBinding(), + true, + true, // FIXME: should unify this with the general emission code + true, + true, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false, // FIXME: should unify this with the general emission code + false, + cfg); + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + emitter.addModifier(JavaMethodBindingEmitter.NATIVE); + emitter.emit(); + + // Emit (private) C entry point for calling this function pointer + CMethodBindingEmitter cEmitter = + new CMethodBindingEmitter(binding, + newWriter, + structClassPkg, + containingTypeName, + true, // FIXME: this is optional at this point + false, + true, + false, // FIXME: should unify this with the general emission code + machDesc64); + prepCEmitter(binding, cEmitter); + cEmitter.emit(); + } catch (Exception e) { + System.err.println("While processing field " + field + " of type " + name + ":"); + throw(e); + } + } + } else if (fieldType.isCompound()) { + // 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 \"" + name + "\")"); + } + + writer.println(); + generateGetterSignature(writer, doBaseClass, fieldType.getName(), capitalizeString(fieldName)); + if (doBaseClass) { + writer.println(";"); + } else { + writer.println(" {"); + writer.println(" return " + fieldType.getName() + ".create(accessor.slice(" + + field.getOffset(intMachDesc) + ", " + fieldType.getSize(intMachDesc) + "));"); + writer.println(" }"); + } + + } else if (fieldType.isArray()) { + + Type baseElementType = field.getType().asArray().getBaseElementType(); + + if(!baseElementType.isPrimitive()) + break; + + String paramType = typeToJavaType(baseElementType, false, extMachDesc).getName(); + String capitalized = capitalizeString(fieldName); + + int slot = -1; + if(!doBaseClass) { + slot = slot(fieldType, (int) field.getOffset(intMachDesc), intMachDesc); + } + + // Setter + writer.println(); + generateSetterSignature(writer, doBaseClass, containingTypeName, capitalized, paramType+"[]"); + if (doBaseClass) { + writer.println(";"); + } else { + writer.println(" {"); + writer.print (" accessor.set" + capitalizeString(paramType) + "sAt(" + slot + ", "); + writer.println("val);"); + writer.println(" return this;"); + writer.println(" }"); + } + writer.println(); + // Getter + generateGetterSignature(writer, doBaseClass, paramType+"[]", capitalized); + if (doBaseClass) { + writer.println(";"); + } else { + writer.println(" {"); + writer.print (" return "); + writer.println("accessor.get" + capitalizeString(paramType) + "sAt(" + slot + ", new " +paramType+"["+fieldType.asArray().getLength()+"]);"); + writer.println(" }"); + } + + } else { + JavaType internalJavaType = null; + JavaType externalJavaType = null; + + try { + externalJavaType = typeToJavaType(fieldType, false, extMachDesc); + if (!doBaseClass) { + internalJavaType = typeToJavaType(fieldType, false, intMachDesc); + } + } catch (Exception e) { + System.err.println("Error occurred while creating accessor for field \"" + + field.getName() + "\" in type \"" + name + "\""); + throw(e); + } + if (externalJavaType.isPrimitive()) { + // Primitive type + String externalJavaTypeName = null; + String internalJavaTypeName = null; + externalJavaTypeName = externalJavaType.getName(); + if (!doBaseClass) { + internalJavaTypeName = internalJavaType.getName(); + } + if (isOpaque(fieldType)) { + externalJavaTypeName = compatiblePrimitiveJavaTypeName(fieldType, externalJavaType, extMachDesc); + if (!doBaseClass) { + internalJavaTypeName = compatiblePrimitiveJavaTypeName(fieldType, internalJavaType, intMachDesc); + } + } + String capitalized = null; + if (!doBaseClass) { + capitalized = capitalizeString(internalJavaTypeName); + } + int slot = -1; + if (!doBaseClass) { + slot = slot(fieldType, (int) field.getOffset(intMachDesc), intMachDesc); + } + writer.println(); + String capitalizedFieldName = capitalizeString(fieldName); + // Setter + generateSetterSignature(writer, doBaseClass, containingTypeName, capitalizedFieldName, externalJavaTypeName); + if (doBaseClass) { + writer.println(";"); + } else { + writer.println(" {"); + writer.print (" accessor.set" + capitalized + "At(" + slot + ", "); + if (!externalJavaTypeName.equals(internalJavaTypeName)) { + writer.print("(" + internalJavaTypeName + ") "); + } + writer.println("val);"); + writer.println(" return this;"); + writer.println(" }"); + } + writer.println(); + // Getter + generateGetterSignature(writer, doBaseClass, externalJavaTypeName, capitalizedFieldName); + if (doBaseClass) { + writer.println(";"); + } else { + writer.println(" {"); + writer.print (" return "); + if (!externalJavaTypeName.equals(internalJavaTypeName)) { + writer.print("(" + externalJavaTypeName + ") "); + } + writer.println("accessor.get" + capitalized + "At(" + slot + ");"); + writer.println(" }"); + } + } else { + // FIXME + LOG.log(WARNING, "Complicated fields (field \"{0}\" of type \"{1}\") not implemented yet", new Object[]{field, name}); + // throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t + + // "\") not implemented yet"); + } + } + } + } + if (doBaseClass) { + emitCustomJavaCode(writer, containingTypeName); + } + writer.println("}"); + writer.flush(); + writer.close(); + if (needsNativeCode) { + newWriter.flush(); + newWriter.close(); + } + } + public void endStructs() throws Exception {} + + public static int addStrings2Buffer(StringBuilder buf, String sep, String first, Collection<String> col) { + int num = 0; + if(null==buf) { + buf = new StringBuilder(); + } + + Iterator<String> iter = col.iterator(); + if(null!=first) { + buf.append(first); + if( iter.hasNext() ) { + buf.append(sep); + } + num++; + } + while( iter.hasNext() ) { + buf.append(iter.next()); + if( iter.hasNext() ) { + buf.append(sep); + } + num++; + } + return num; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void generateGetterSignature(PrintWriter writer, boolean baseClass, String returnTypeName, String capitalizedFieldName) { + writer.print(" public " + (baseClass ? "abstract " : "") + returnTypeName + " get" + capitalizedFieldName + "()"); + } + + private void generateSetterSignature(PrintWriter writer, boolean baseClass, String returnTypeName, String capitalizedFieldName, String paramTypeName) { + writer.print(" public " + (baseClass ? "abstract " : "") + returnTypeName + " set" + capitalizedFieldName + "(" + paramTypeName + " val)"); + } + + private JavaType typeToJavaType(Type cType, boolean outgoingArgument, MachineDescription curMachDesc) { + // Recognize JNIEnv* case up front + PointerType opt = cType.asPointer(); + if ((opt != null) && + (opt.getTargetType().getName() != null) && + (opt.getTargetType().getName().equals("JNIEnv"))) { + return JavaType.createForJNIEnv(); + } + Type t = cType; + + // Opaque specifications override automatic conversions + // in case the identity is being used .. not if ptr-ptr + TypeInfo info = cfg.typeInfo(t, typedefDictionary); + if (info != null) { + boolean isPointerPointer = false; + if (t.pointerDepth() > 0 || t.arrayDimension() > 0) { + Type targetType; // target type + if (t.isPointer()) { + // t is <type>*, we need to get <type> + targetType = t.asPointer().getTargetType(); + } else { + // t is <type>[], we need to get <type> + targetType = t.asArray().getElementType(); + } + if (t.pointerDepth() == 2 || t.arrayDimension() == 2) { + // Get the target type of the target type (targetType was computer earlier + // as to be a pointer to the target type, so now we need to get its + // target type) + if (targetType.isPointer()) { + isPointerPointer = true; + + // t is<type>**, targetType is <type>*, we need to get <type> + Type bottomType = targetType.asPointer().getTargetType(); + LOG.log(INFO, "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", new Object[]{t, targetType, bottomType}); + } + } + } + if(!isPointerPointer) { + return info.javaType(); + } + } + + if (t.isInt() || t.isEnum()) { + switch ((int) t.getSize(curMachDesc)) { + case 1: return javaType(Byte.TYPE); + 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 " + + t.getSize(curMachDesc) + " and name " + t.getName()); + } + } else if (t.isFloat()) { + return javaType(Float.TYPE); + } else if (t.isDouble()) { + return javaType(Double.TYPE); + } else if (t.isVoid()) { + return javaType(Void.TYPE); + } else { + if (t.pointerDepth() > 0 || t.arrayDimension() > 0) { + Type targetType; // target type + if (t.isPointer()) { + // t is <type>*, we need to get <type> + targetType = t.asPointer().getTargetType(); + } else { + // t is <type>[], we need to get <type> + targetType = t.asArray().getElementType(); + } + + // Handle Types of form pointer-to-type or array-of-type, like + // char* or int[]; these are expanded out into Java primitive + // arrays, NIO buffers, or both in expandMethodBinding + if (t.pointerDepth() == 1 || t.arrayDimension() == 1) { + if (targetType.isVoid()) { + return JavaType.createForVoidPointer(); + } else if (targetType.isInt()) { + // size_t and intptr_t is always a PointerBuffer since size is arch dependent + if ("size_t".equals(targetType.getName()) || "intptr_t".equals(targetType.getName())) { + return JavaType.forNIOPointerBufferClass(); + } + switch ((int) targetType.getSize(curMachDesc)) { + case 1: return JavaType.createForCCharPointer(); + 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 " + + t.getSize(curMachDesc) + " and name " + t.getName()); + } + } else if (targetType.isFloat()) { + return JavaType.createForCFloatPointer(); + } else if (targetType.isDouble()) { + return JavaType.createForCDoublePointer(); + } else if (targetType.isCompound()) { + if (t.isArray()) { + throw new RuntimeException("Arrays of compound types not handled yet"); + } + // Special cases for known JNI types (in particular for converting jawt.h) + if (t.getName() != null && + t.getName().equals("jobject")) { + return javaType(java.lang.Object.class); + } + + String name = targetType.getName(); + if (name == null) { + // Try containing pointer type for any typedefs + name = t.getName(); + if (name == null) { + throw new RuntimeException("Couldn't find a proper type name for pointer type " + t); + } + } + + return JavaType.createForCStruct(cfg.renameJavaType(name)); + } else { + throw new RuntimeException("Don't know how to convert pointer/array type \"" + + t + "\""); + } + } + // Handle Types of form pointer-to-pointer-to-type or + // array-of-arrays-of-type, like char** or int[][] + else if (t.pointerDepth() == 2 || t.arrayDimension() == 2) { + // Get the target type of the target type (targetType was computer earlier + // as to be a pointer to the target type, so now we need to get its + // target type) + Type bottomType; + if (targetType.isPointer()) { + // t is<type>**, targetType is <type>*, we need to get <type> + bottomType = targetType.asPointer().getTargetType(); + return JavaType.forNIOPointerBufferClass(); + } else { + // t is<type>[][], targetType is <type>[], we need to get <type> + bottomType = targetType.asArray().getElementType(); + LOG.log(WARNING, "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2} -> Unhandled!", new Object[]{t, targetType, bottomType}); + } + + // Warning: The below code is not backed up by an implementation, + // the only working variant is a ptr-ptr type which results in a PointerBuffer. + // + if (bottomType.isPrimitive()) { + if (bottomType.isInt()) { + switch ((int) bottomType.getSize(curMachDesc)) { + case 1: return javaType(ArrayTypes.byteBufferArrayClass); + 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()); + } + } else if (bottomType.isFloat()) { + return javaType(ArrayTypes.floatBufferArrayClass); + } else if (bottomType.isDouble()) { + return javaType(ArrayTypes.doubleBufferArrayClass); + } else { + throw new RuntimeException("Unexpected primitive type " + bottomType.getName() + + " in two-dimensional array"); + } + } else if (bottomType.isVoid()) { + return javaType(ArrayTypes.bufferArrayClass); + } else if (targetType.isPointer() && (targetType.pointerDepth() == 1) && + targetType.asPointer().getTargetType().isCompound()) { + // Array of pointers; convert as array of StructAccessors + return JavaType.createForCArray(bottomType); + } else { + throw new RuntimeException( + "Could not convert C type \"" + t + "\" " + + "to appropriate Java type; need to add more support for " + + "depth=2 pointer/array types [debug info: targetType=\"" + + targetType + "\"]"); + } + } else { + // can't handle this type of pointer/array argument + throw new RuntimeException( + "Could not convert C pointer/array \"" + t + "\" to " + + "appropriate Java type; types with pointer/array depth " + + "greater than 2 are not yet supported [debug info: " + + "pointerDepth=" + t.pointerDepth() + " arrayDimension=" + + t.arrayDimension() + " targetType=\"" + targetType + "\"]"); + } + + } else { + throw new RuntimeException( + "Could not convert C type \"" + t + "\" (class " + + t.getClass().getName() + ") to appropriate Java type"); + } + } + } + + private static boolean isIntegerType(Class<?> c) { + return ((c == Byte.TYPE) || + (c == Short.TYPE) || + (c == Character.TYPE) || + (c == Integer.TYPE) || + (c == Long.TYPE)); + } + + private int slot(Type t, int byteOffset, MachineDescription curMachDesc) { + if (t.isInt()) { + switch ((int) t.getSize(curMachDesc)) { + case 1: + case 2: + case 4: + case 8: return byteOffset / (int) t.getSize(curMachDesc); + default: throw new RuntimeException("Illegal type"); + } + } else if (t.isFloat()) { + return byteOffset / 4; + } else if (t.isDouble()) { + return byteOffset / 8; + } else if (t.isPointer()) { + return byteOffset / curMachDesc.pointerSizeInBytes(); + } else if (t.isArray()) { + return slot(t.asArray().getBaseElementType(), byteOffset, curMachDesc); + } else { + throw new RuntimeException("Illegal type " + t); + } + } + + private StructLayout getLayout() { + if (layout == null) { + layout = StructLayout.createForCurrentPlatform(); + } + return layout; + } + + protected PrintWriter openFile(String filename) throws IOException { + //System.out.println("Trying to open: " + filename); + File file = new File(filename); + String parentDir = file.getParent(); + if (parentDir != null) + { + File pDirFile = new File(parentDir); + pDirFile.mkdirs(); + } + return new PrintWriter(new BufferedWriter(new FileWriter(file))); + } + + private boolean isOpaque(Type type) { + return (cfg.typeInfo(type, typedefDictionary) != null); + } + + private String compatiblePrimitiveJavaTypeName(Type fieldType, + JavaType javaType, + MachineDescription curMachDesc) { + 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.)"); + } + 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"); + } + } + + private void openWriters() throws IOException { + String jRoot = null; + if (cfg.allStatic() || cfg.emitInterface()) { + jRoot = cfg.javaOutputDir() + File.separator + + CodeGenUtils.packageAsPath(cfg.packageName()); + } + String jImplRoot = null; + if (!cfg.allStatic()) { + jImplRoot = + cfg.javaOutputDir() + File.separator + + CodeGenUtils.packageAsPath(cfg.implPackageName()); + } + String nRoot = cfg.nativeOutputDir(); + if (cfg.nativeOutputUsesJavaHierarchy()) + { + nRoot += + File.separator + CodeGenUtils.packageAsPath(cfg.packageName()); + } + + if (cfg.allStatic() || cfg.emitInterface()) { + javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java"); + } + if (!cfg.allStatic() && cfg.emitImpl()) { + javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java"); + } + if (cfg.emitImpl()) { + cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c"); + } + + if (javaWriter != null) { + CodeGenUtils.emitAutogeneratedWarning(javaWriter, this); + } + if (javaImplWriter != null) { + CodeGenUtils.emitAutogeneratedWarning(javaImplWriter, this); + } + if (cWriter != null) { + CodeGenUtils.emitAutogeneratedWarning(cWriter, this); + } + } + + protected PrintWriter javaWriter() { + if (!cfg.allStatic() && !cfg.emitInterface()) { + throw new InternalError("Should not call this"); + } + return javaWriter; + } + + protected PrintWriter javaImplWriter() { + if (cfg.allStatic() || !cfg.emitImpl()) { + throw new InternalError("Should not call this"); + } + return javaImplWriter; + } + + protected PrintWriter cWriter() { + if (!cfg.emitImpl()) { + throw new InternalError("Should not call this"); + } + return cWriter; + } + + private void closeWriter(PrintWriter writer) throws IOException { + writer.flush(); + writer.close(); + } + + private void closeWriters() throws IOException { + if (javaWriter != null) { + closeWriter(javaWriter); + } + if (javaImplWriter != null) { + closeWriter(javaImplWriter); + } + if (cWriter != null) { + closeWriter(cWriter); + } + javaWriter = null; + javaImplWriter = null; + cWriter = null; + } + + /** + * Returns the value that was specified by the configuration directive + * "JavaOutputDir", or the default if none was specified. + */ + protected String getJavaOutputDir() { + return cfg.javaOutputDir(); + } + + /** + * Returns the value that was specified by the configuration directive + * "Package", or the default if none was specified. + */ + protected String getJavaPackageName() { + return cfg.packageName(); + } + + /** + * Returns the value that was specified by the configuration directive + * "ImplPackage", or the default if none was specified. + */ + protected String getImplPackageName() { + return cfg.implPackageName(); + } + + /** + * Emit all the strings specified in the "CustomJavaCode" parameters of + * the configuration file. + */ + protected void emitCustomJavaCode(PrintWriter writer, String className) throws Exception { + List<String> code = cfg.customJavaCodeForClass(className); + if (code.isEmpty()) + return; + + writer.println(); + writer.println(" // --- Begin CustomJavaCode .cfg declarations"); + for (String line : code) { + writer.println(line); + } + writer.println(" // ---- End CustomJavaCode .cfg declarations"); + } + + /** + * Write out any header information for the output files (class declaration + * and opening brace, import statements, etc). + */ + protected void emitAllFileHeaders() throws IOException { + try { + List<String> imports = new ArrayList<String>(cfg.imports()); + imports.add(cfg.gluegenRuntimePackage()+".*"); + imports.add(DynamicLookupHelper.class.getPackage().getName()+".*"); + imports.add(Buffers.class.getPackage().getName()+".*"); + imports.add(Buffer.class.getPackage().getName()+".*"); + + if (cfg.allStatic() || cfg.emitInterface()) { + + String[] interfaces; + List<String> userSpecifiedInterfaces = null; + if (cfg.emitInterface()) { + userSpecifiedInterfaces = cfg.extendedInterfaces(cfg.className()); + } else { + userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.className()); + } + interfaces = new String[userSpecifiedInterfaces.size()]; + userSpecifiedInterfaces.toArray(interfaces); + + final List<String> intfDocs = cfg.javadocForClass(cfg.className()); + CodeGenUtils.EmissionCallback docEmitter = + new CodeGenUtils.EmissionCallback() { + public void emit(PrintWriter w) { + for (Iterator iter = intfDocs.iterator(); iter.hasNext(); ) { + w.println((String) iter.next()); + } + } + }; + + String[] accessModifiers = null; + if(cfg.accessControl(cfg.className()) == PUBLIC_ABSTRACT) { + accessModifiers = new String[] { "public", "abstract" }; + } else { + accessModifiers = new String[] { "public" }; + } + + CodeGenUtils.emitJavaHeaders( + javaWriter, + cfg.packageName(), + cfg.className(), + cfg.allStatic() ? true : false, + imports, + accessModifiers, + interfaces, + cfg.extendedParentClass(cfg.className()), + docEmitter); + } + + if (!cfg.allStatic() && cfg.emitImpl()) { + final List<String> implDocs = cfg.javadocForClass(cfg.implClassName()); + CodeGenUtils.EmissionCallback docEmitter = + new CodeGenUtils.EmissionCallback() { + public void emit(PrintWriter w) { + for (Iterator iter = implDocs.iterator(); iter.hasNext(); ) { + w.println((String) iter.next()); + } + } + }; + + String[] interfaces; + List<String> userSpecifiedInterfaces = null; + userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.implClassName()); + int additionalNum = 0; + if (cfg.className() != null) { + additionalNum = 1; + } + interfaces = new String[additionalNum + userSpecifiedInterfaces.size()]; + userSpecifiedInterfaces.toArray(interfaces); + if (additionalNum == 1) { + interfaces[userSpecifiedInterfaces.size()] = cfg.className(); + } + + String[] accessModifiers = null; + if(cfg.accessControl(cfg.implClassName()) == PUBLIC_ABSTRACT) { + accessModifiers = new String[] { "public", "abstract" }; + } else { + accessModifiers = new String[] { "public" }; + } + + CodeGenUtils.emitJavaHeaders( + javaImplWriter, + cfg.implPackageName(), + cfg.implClassName(), + true, + imports, + accessModifiers, + interfaces, + cfg.extendedParentClass(cfg.implClassName()), + docEmitter); + } + + if (cfg.emitImpl()) { + emitCHeader(cWriter(), cfg.implClassName()); + } + } catch (Exception e) { + throw new RuntimeException( + "Error emitting all file headers: cfg.allStatic()=" + cfg.allStatic() + + " cfg.emitImpl()=" + cfg.emitImpl() + " cfg.emitInterface()=" + cfg.emitInterface(), + e); + } + + } + + protected void emitCHeader(PrintWriter cWriter, String className) { + cWriter.println("#include <jni.h>"); + cWriter.println("#include <stdlib.h>"); + cWriter.println(); + + if (getConfig().emitImpl()) { + cWriter.println("#include <assert.h>"); + cWriter.println(); + } + + for (String code : cfg.customCCode()) { + cWriter.println(code); + } + cWriter.println(); + } + + /** + * Write out any footer information for the output files (closing brace of + * class definition, etc). + */ + protected void emitAllFileFooters(){ + if (cfg.allStatic() || cfg.emitInterface()) { + javaWriter().println(); + javaWriter().println("} // end of class " + cfg.className()); + } + if (!cfg.allStatic() && cfg.emitImpl()) { + javaImplWriter().println(); + javaImplWriter().println("} // end of class " + cfg.implClassName()); + } + } + + private JavaType javaType(Class<?> c) { + return JavaType.createForClass(c); + } + + /** Maps the C types in the specified function to Java types through + the MethodBinding interface. Note that the JavaTypes in the + returned MethodBinding are "intermediate" JavaTypes (some + 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(FunctionSymbol sym, + JavaType containingType, + Type containingCType, + MachineDescription curMachDesc) { + + 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())) { + PointerType prt = sym.getReturnType().asPointer(); + if (prt == null || + prt.getTargetType().asInt() == null || + prt.getTargetType().getSize(curMachDesc) != 1) { + throw new RuntimeException( + "Cannot apply ReturnsString configuration directive to \"" + sym + + "\". ReturnsString requires native method to have return type \"char *\""); + } + binding.setJavaReturnType(javaType(java.lang.String.class)); + } else { + binding.setJavaReturnType(typeToJavaType(sym.getReturnType(), false, 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 + List<Integer> stringArgIndices = cfg.stringArguments(binding.getName()); + + for (int i = 0; i < sym.getNumArguments(); i++) { + Type cArgType = sym.getArgumentType(i); + JavaType mappedType = typeToJavaType(cArgType, true, curMachDesc); + // System.out.println("C arg type -> \"" + cArgType + "\"" ); + // System.out.println(" Java -> \"" + mappedType + "\"" ); + + // Take into account any ArgumentIsString configuration directives that apply + if (stringArgIndices != null && stringArgIndices.contains(i)) { + // System.out.println("Forcing conversion of " + binding.getName() + " arg #" + i + " from byte[] to String "); + if (mappedType.isCVoidPointerType() || + mappedType.isCCharPointerType() || + mappedType.isCShortPointerType() || + mappedType.isNIOPointerBuffer() || + (mappedType.isArray() && + (mappedType.getJavaClass() == ArrayTypes.byteBufferArrayClass) || + (mappedType.getJavaClass() == ArrayTypes.shortBufferArrayClass))) { + // convert mapped type from: + // void*, byte[], and short[] to String + // ByteBuffer[] and ShortBuffer[] to String[] + if (mappedType.isArray() || mappedType.isNIOPointerBuffer()) { + mappedType = javaType(ArrayTypes.stringArrayClass); + } else { + mappedType = javaType(String.class); + } + } + else { + throw new RuntimeException( + "Cannot apply ArgumentIsString configuration directive to " + + "argument " + i + " of \"" + sym + "\": argument type is not " + + "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent"); + } + } + binding.addJavaArgumentType(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; + } + + private MethodBinding lowerMethodBindingPointerTypes(MethodBinding inputBinding, + boolean convertToArrays, + boolean[] canProduceArrayVariant) { + MethodBinding result = inputBinding; + boolean arrayPossible = false; + + // System.out.println("lowerMethodBindingPointerTypes(0): "+result); + + for (int i = 0; i < inputBinding.getNumArguments(); i++) { + JavaType t = inputBinding.getJavaArgumentType(i); + if (t.isCPrimitivePointerType()) { + if (t.isCVoidPointerType()) { + // These are always bound to java.nio.Buffer + result = result.replaceJavaArgumentType(i, JavaType.forNIOBufferClass()); + } else if (t.isCCharPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.byteArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOByteBufferClass()); + } + } else if (t.isCShortPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.shortArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOShortBufferClass()); + } + } else if (t.isCInt32PointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.intArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOIntBufferClass()); + } + } else if (t.isCInt64PointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.longArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOInt64BufferClass()); + } + } else if (t.isCFloatPointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.floatArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIOFloatBufferClass()); + } + } else if (t.isCDoublePointerType()) { + arrayPossible = true; + if (convertToArrays) { + result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.doubleArrayClass)); + } else { + result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass()); + } + } else { + throw new RuntimeException("Unknown C pointer type " + t); + } + } + } + + // System.out.println("lowerMethodBindingPointerTypes(1): "+result); + + // Always return primitive pointer types as NIO buffers + JavaType t = result.getJavaReturnType(); + if (t.isCPrimitivePointerType()) { + if (t.isCVoidPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass()); + } else if (t.isCCharPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass()); + } else if (t.isCShortPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOShortBufferClass()); + } else if (t.isCInt32PointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOIntBufferClass()); + } else if (t.isCInt64PointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOInt64BufferClass()); + } else if (t.isCFloatPointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIOFloatBufferClass()); + } else if (t.isCDoublePointerType()) { + result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass()); + } else { + throw new RuntimeException("Unknown C pointer type " + t); + } + } + + // System.out.println("lowerMethodBindingPointerTypes(2): "+result); + + if (canProduceArrayVariant != null) { + canProduceArrayVariant[0] = arrayPossible; + } + + return result; + } + + // 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 + protected List<MethodBinding> expandMethodBinding(MethodBinding binding) { + + List<MethodBinding> result = new ArrayList<MethodBinding>(); + // Indicates whether it is possible to produce an array variant + // Prevents e.g. char* -> String conversions from emitting two entry points + boolean[] canProduceArrayVariant = new boolean[1]; + + if (binding.signatureUsesCPrimitivePointers() || + binding.signatureUsesCVoidPointers() || + binding.signatureUsesCArrays()) { + + result.add(lowerMethodBindingPointerTypes(binding, false, canProduceArrayVariant)); + + // FIXME: should add new configuration flag for this + if (canProduceArrayVariant[0] && (binding.signatureUsesCPrimitivePointers() || binding.signatureUsesCArrays()) && + !cfg.nioDirectOnly(binding.getName()) && !cfg.nioOnly(binding.getName())) { + result.add(lowerMethodBindingPointerTypes(binding, true, null)); + } + } else { + result.add(binding); + } + + return result; + } + + private String resultName() { + return "_res"; + } + + private Type canonicalize(Type t) { + Type res = canonMap.get(t); + if (res != null) { + return res; + } + canonMap.put(t, t); + return t; + } + + /** + * Converts first letter to upper case. + */ + private final String capitalizeString(String string) { + return Character.toUpperCase(string.charAt(0)) + string.substring(1); + } + +} diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java new file mode 100644 index 0000000..3bf3dc1 --- /dev/null +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.io.*; +import java.util.*; +import java.text.MessageFormat; + +import com.jogamp.gluegen.cgram.types.*; +import com.jogamp.gluegen.cgram.*; + +/** + * An emitter that emits only the interface for a Java<->C JNI binding. + */ +public class JavaMethodBindingEmitter extends FunctionEmitter { + + public static final EmissionModifier PUBLIC = new EmissionModifier("public"); + public static final EmissionModifier PROTECTED = new EmissionModifier("protected"); + public static final EmissionModifier PRIVATE = new EmissionModifier("private"); + public static final EmissionModifier ABSTRACT = new EmissionModifier("abstract"); + public static final EmissionModifier FINAL = new EmissionModifier("final"); + public static final EmissionModifier NATIVE = new EmissionModifier("native"); + public static final EmissionModifier SYNCHRONIZED = new EmissionModifier("synchronized"); + + protected final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); + protected final CommentEmitter defaultInterfaceCommentEmitter = new InterfaceCommentEmitter(); + + // Exception type raised in the generated code if runtime checks fail + private String runtimeExceptionType; + private String unsupportedExceptionType; + + protected boolean emitBody; + protected boolean eraseBufferAndArrayTypes; + protected boolean directNIOOnly; + protected boolean forImplementingMethodCall; + protected boolean forDirectBufferImplementation; + protected boolean forIndirectBufferAndArrayImplementation; + protected boolean isUnimplemented; + protected boolean tagNativeBinding; + + protected MethodBinding binding; + + // Manually-specified prologue and epilogue code + protected List<String> prologue; + protected List<String> epilogue; + + // A non-null value indicates that rather than returning a compound + // type accessor we are returning an array of such accessors; this + // expression is a MessageFormat string taking the names of the + // incoming Java arguments as parameters and computing as an int the + // number of elements of the returned array. + private String returnedArrayLengthExpression; + + // A suffix used to create a temporary outgoing array of Buffers to + // 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 JavaConfiguration cfg; + + public JavaMethodBindingEmitter(MethodBinding binding, + PrintWriter output, + String runtimeExceptionType, + String unsupportedExceptionType, + boolean emitBody, + boolean tagNativeBinding, + boolean eraseBufferAndArrayTypes, + boolean directNIOOnly, + boolean forImplementingMethodCall, + boolean forDirectBufferImplementation, + boolean forIndirectBufferAndArrayImplementation, + boolean isUnimplemented, + boolean isInterface, + JavaConfiguration configuration) { + super(output, isInterface); + this.binding = binding; + this.runtimeExceptionType = runtimeExceptionType; + this.unsupportedExceptionType = unsupportedExceptionType; + this.emitBody = emitBody; + this.tagNativeBinding = tagNativeBinding; + this.eraseBufferAndArrayTypes = eraseBufferAndArrayTypes; + this.directNIOOnly = directNIOOnly; + this.forImplementingMethodCall = forImplementingMethodCall; + this.forDirectBufferImplementation = forDirectBufferImplementation; + this.forIndirectBufferAndArrayImplementation = forIndirectBufferAndArrayImplementation; + this.isUnimplemented = isUnimplemented; + if (forImplementingMethodCall) { + setCommentEmitter(defaultJavaCommentEmitter); + } else { + setCommentEmitter(defaultInterfaceCommentEmitter); + } + cfg = configuration; + } + + public JavaMethodBindingEmitter(JavaMethodBindingEmitter arg) { + super(arg); + binding = arg.binding; + runtimeExceptionType = arg.runtimeExceptionType; + unsupportedExceptionType = arg.unsupportedExceptionType; + emitBody = arg.emitBody; + tagNativeBinding = arg.tagNativeBinding; + eraseBufferAndArrayTypes = arg.eraseBufferAndArrayTypes; + directNIOOnly = arg.directNIOOnly; + forImplementingMethodCall = arg.forImplementingMethodCall; + forDirectBufferImplementation = arg.forDirectBufferImplementation; + forIndirectBufferAndArrayImplementation = arg.forIndirectBufferAndArrayImplementation; + isUnimplemented = arg.isUnimplemented; + prologue = arg.prologue; + epilogue = arg.epilogue; + returnedArrayLengthExpression = arg.returnedArrayLengthExpression; + cfg = arg.cfg; + } + + public final MethodBinding getBinding() { return binding; } + + public boolean isForImplementingMethodCall() { return forImplementingMethodCall; } + public boolean isForDirectBufferImplementation() { return forDirectBufferImplementation; } + public boolean isForIndirectBufferAndArrayImplementation() { return forIndirectBufferAndArrayImplementation; } + + public String getName() { + return binding.getName(); + } + + protected String getArgumentName(int i) { + return binding.getArgumentName(i); + } + + /** The type of exception (must subclass + <code>java.lang.RuntimeException</code>) raised if runtime + checks fail in the generated code. */ + public String getRuntimeExceptionType() { + return runtimeExceptionType; + } + + public String getUnsupportedExceptionType() { + return unsupportedExceptionType; + } + + /** If the underlying function returns an array (currently only + arrays of compound types are supported) as opposed to a pointer + to an object, this method should be called to provide a + MessageFormat string containing an expression that computes the + number of elements of the returned array. The parameters to the + MessageFormat expression are the names of the incoming Java + arguments. */ + public void setReturnedArrayLengthExpression(String expr) { + returnedArrayLengthExpression = expr; + } + + /** Sets the manually-generated prologue code for this emitter. */ + public void setPrologue(List<String> prologue) { + this.prologue = prologue; + } + + /** Sets the manually-generated epilogue code for this emitter. */ + public void setEpilogue(List<String> epilogue) { + this.epilogue = epilogue; + } + + /** Indicates whether this emitter will print only a signature, or + whether it will emit Java code for the body of the method as + well. */ + public boolean signatureOnly() { + return !emitBody; + } + + /** Accessor for subclasses. */ + public void setEmitBody(boolean emitBody) { + this.emitBody = emitBody; + } + + /** Accessor for subclasses. */ + public void setEraseBufferAndArrayTypes(boolean erase) { + this.eraseBufferAndArrayTypes = erase; + } + + /** Accessor for subclasses. */ + public void setForImplementingMethodCall(boolean impl) { + this.forImplementingMethodCall = impl; + } + + /** Accessor for subclasses. */ + public void setForDirectBufferImplementation(boolean direct) { + this.forDirectBufferImplementation = direct; + } + + /** Accessor for subclasses. */ + public void setForIndirectBufferAndArrayImplementation(boolean indirect) { + this.forIndirectBufferAndArrayImplementation = indirect; + } + + protected void emitReturnType(PrintWriter writer) { + writer.print(getReturnTypeString(false)); + } + + protected String erasedTypeString(JavaType type, boolean skipBuffers) { + if (eraseBufferAndArrayTypes) { + if (type.isNIOBuffer()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Object"; + } + if (!type.isNIOByteBuffer()) { + // Return buffer requiring change of view from ByteBuffer to e.g. LongBuffer + return "ByteBuffer"; + } + } else if (type.isPrimitiveArray()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Object"; + } + } else if (type.isNIOBufferArray()) { + // Arrays of direct Buffers sent down as Object[] + // (Note we don't yet support returning void**) + return "Object[]"; + } else if (type.isCompoundTypeWrapper()) { + // Compound type wrappers are unwrapped to ByteBuffer + return "ByteBuffer"; + } else if (type.isArrayOfCompoundTypeWrappers()) { + if (skipBuffers) { + return "ByteBuffer"; + } else { + // In the case where this is called with a false skipBuffers + // argument we want to erase the array of compound type + // wrappers to ByteBuffer[] + return "ByteBuffer[]"; + } + } + } + String name = type.getName(); + int index = name.lastIndexOf('.')+1; // always >= 0 + name = name.substring(index); + + if (type.isArrayOfCompoundTypeWrappers()) { + // We don't want to bake the array specification into the type name + return name + "[]"; + } + return name; + } + + protected String getReturnTypeString(boolean skipArray) { + // The first arm of the "if" clause is used by the glue code + // generation for arrays of compound type wrappers + if (skipArray || + // The following arm is used by most other kinds of return types + (getReturnedArrayLengthExpression() == null && + !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers()) || + // The following arm is used specifically to get the splitting up + // of one returned ByteBuffer into an array of compound type + // wrappers to work (e.g., XGetVisualInfo) + (eraseBufferAndArrayTypes && + binding.getJavaReturnType().isCompoundTypeWrapper() && + (getReturnedArrayLengthExpression() != null))) { + return erasedTypeString(binding.getJavaReturnType(), true); + } + return erasedTypeString(binding.getJavaReturnType(), true) + "[]"; + } + + protected void emitName(PrintWriter writer) { + if (forImplementingMethodCall) { + writer.print(getImplMethodName()); + } else { + writer.print(getName()); + } + } + + protected int emitArguments(PrintWriter writer) { + boolean needComma = false; + int numEmitted = 0; + + if (forImplementingMethodCall && binding.hasContainingType()) { + // Always emit outgoing "this" argument + writer.print("ByteBuffer "); + writer.print(javaThisArgumentName()); + ++numEmitted; + needComma = true; + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + if (binding.getNumArguments() != 1) { + throw new InternalError( + "\"void\" argument type found in " + + "multi-argument function \"" + binding + "\""); + } + continue; + } + + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (needComma) { + writer.print(", "); + } + + writer.print(erasedTypeString(type, false)); + writer.print(" "); + writer.print(getArgumentName(i)); + + ++numEmitted; + needComma = true; + + // Add Buffer and array index offset arguments after each associated argument + if (forDirectBufferImplementation || forIndirectBufferAndArrayImplementation) { + if (type.isNIOBuffer()) { + writer.print(", int " + byteOffsetArgName(i)); + if(!directNIOOnly) { + writer.print(", boolean " + isNIOArgName(i)); + } + } else if (type.isNIOBufferArray()) { + writer.print(", int[] " + byteOffsetArrayArgName(i)); + } + } + + // Add offset argument after each primitive array + if (type.isPrimitiveArray()) { + if(directNIOOnly) { + throw new RuntimeException("NIODirectOnly "+binding+" is set, but "+getArgumentName(i)+" is a primitive array"); + } + writer.print(", int " + offsetArgName(i)); + } + } + return numEmitted; + } + + + protected String getImplMethodName() { + return binding.getName() + ( directNIOOnly ? "0" : "1" ); + } + + protected String byteOffsetArgName(int i) { + return byteOffsetArgName(getArgumentName(i)); + } + + protected String byteOffsetArgName(String s) { + return s + "_byte_offset"; + } + + protected String isNIOArgName(int i) { + return isNIOArgName(binding.getArgumentName(i)); + } + + protected String isNIOArgName(String s) { + return s + "_is_direct"; + } + + protected String byteOffsetArrayArgName(int i) { + return getArgumentName(i) + "_byte_offset_array"; + } + + protected String offsetArgName(int i) { + return getArgumentName(i) + "_offset"; + } + + protected void emitBody(PrintWriter writer) { + if (!emitBody) { + writer.println(';'); + } else { + MethodBinding mBinding = getBinding(); + writer.println(" {"); + writer.println(); + if (isUnimplemented) { + writer.println(" throw new " + getUnsupportedExceptionType() + "(\"Unimplemented\");"); + } else { + emitPrologueOrEpilogue(prologue, writer); + emitPreCallSetup(mBinding, writer); + //emitReturnVariableSetup(binding, writer); + emitReturnVariableSetupAndCall(mBinding, writer); + } + writer.println(" }"); + } + } + + protected void emitPrologueOrEpilogue(List<String> code, PrintWriter writer) { + if (code != null) { + String[] argumentNames = argumentNameArray(); + for (String str : code) { + try { + MessageFormat fmt = new MessageFormat(str); + writer.println(" " + fmt.format(argumentNames)); + } catch (IllegalArgumentException e) { + // (Poorly) handle case where prologue / epilogue contains blocks of code with braces + writer.println(" " + str); + } + } + } + } + + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + emitArrayLengthAndNIOBufferChecks(binding, writer); + emitCompoundArrayCopies(binding, writer); + } + + protected void emitArrayLengthAndNIOBufferChecks(MethodBinding binding, PrintWriter writer) { + + // Check lengths of any incoming arrays if necessary + for (int i = 0; i < binding.getNumArguments(); i++) { + Type type = binding.getCArgumentType(i); + if (type.isArray()) { + ArrayType arrayType = type.asArray(); + writer.println(" if (" + getArgumentName(i) + ".length < " + + arrayType.getLength() + ")"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Length of array \\\"" + getArgumentName(i) + + "\\\" was less than the required " + arrayType.getLength() + "\");"); + // FIXME: What is this ??? Until resolved - throw an exception ! + throw new RuntimeException("????? "+binding+": binding.getCArgumentType("+i+").isArray(): "+type); + } else { + JavaType javaType = binding.getJavaArgumentType(i); + if (javaType.isNIOBuffer()) { + if (directNIOOnly) { + writer.println(" if (!Buffers.isDirect(" + getArgumentName(i) + "))"); + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Argument \\\"" + + getArgumentName(i) + "\\\" was not a direct buffer\");"); + } else { + writer.print(" boolean " + isNIOArgName(i) + " = "); + writer.println(getArgumentName(i) + " != null && Buffers.isDirect(" + getArgumentName(i) + ");"); + } + } else if (javaType.isNIOBufferArray()) { + // All buffers passed down in an array of NIO buffers must be direct + String argName = getArgumentName(i); + String arrayName = byteOffsetArrayArgName(i); + writer.println(" int[] " + arrayName + " = new int[" + argName + ".length];"); + // Check direct buffer properties of all buffers within + writer.println(" if (" + argName + " != null) {"); + writer.println(" for (int _ctr = 0; _ctr < " + argName + ".length; _ctr++) {"); + writer.println(" if (!Buffers.isDirect(" + argName + "[_ctr])) {"); + writer.println(" throw new " + getRuntimeExceptionType() + + "(\"Element \" + _ctr + \" of argument \\\"" + + getArgumentName(i) + "\\\" was not a direct buffer\");"); + writer.println(" }"); + // get the Buffer Array offset values and save them into another array to send down to JNI + writer.print (" " + arrayName + "[_ctr] = Buffers.getDirectBufferByteOffset("); + writer.println(argName + "[_ctr]);"); + writer.println(" }"); + writer.println(" }"); + } else if (javaType.isPrimitiveArray()) { + String argName = getArgumentName(i); + String offsetArg = offsetArgName(i); + writer.println(" if(" + argName + " != null && " + argName + ".length <= " + offsetArg + ")"); + writer.print (" throw new " + getRuntimeExceptionType()); + writer.println("(\"array offset argument \\\"" + offsetArg + "\\\" (\" + " + offsetArg + + " + \") equals or exceeds array length (\" + " + argName + ".length + \")\");"); + } + } + } + } + + protected void emitCompoundArrayCopies(MethodBinding binding, PrintWriter writer) { + // If the method binding uses outgoing arrays of compound type + // wrappers, we need to generate a temporary copy of this array + // into a ByteBuffer[] for processing by the native code + if (binding.signatureUsesArraysOfCompoundTypeWrappers()) { + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaType = binding.getJavaArgumentType(i); + if (javaType.isArrayOfCompoundTypeWrappers()) { + String argName = getArgumentName(i); + String tempArrayName = argName + COMPOUND_ARRAY_SUFFIX; + writer.println(" ByteBuffer[] " + tempArrayName + " = new ByteBuffer[" + argName + ".length];"); + writer.println(" for (int _ctr = 0; _ctr < + " + argName + ".length; _ctr++) {"); + writer.println(" " + javaType.getName() + " _tmp = " + argName + "[_ctr];"); + writer.println(" " + tempArrayName + "[_ctr] = ((_tmp == null) ? null : _tmp.getBuffer());"); + writer.println(" }"); + } + } + } + } + + protected void emitCall(MethodBinding binding, PrintWriter writer) { + writer.print(getImplMethodName()); + writer.print("("); + emitCallArguments(binding, writer); + writer.print(");"); + } + + + protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { + writer.print(" "); + JavaType returnType = binding.getJavaReturnType(); + boolean needsResultAssignment = false; + + if (!returnType.isVoid()) { + if (returnType.isCompoundTypeWrapper() || + returnType.isNIOBuffer()) { + writer.println("ByteBuffer _res;"); + needsResultAssignment = true; + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("ByteBuffer[] _res;"); + needsResultAssignment = true; + } else if (((epilogue != null) && (epilogue.size() > 0)) || + binding.signatureUsesArraysOfCompoundTypeWrappers()) { + emitReturnType(writer); + writer.println(" _res;"); + needsResultAssignment = true; + } + } + + if (needsResultAssignment) { + writer.print(" _res = "); + } else { + writer.print(" "); + if (!returnType.isVoid()) { + writer.print("return "); + } + } + + emitCall(binding, writer); + writer.println(); + + emitPostCallCleanup(binding, writer); + emitPrologueOrEpilogue(epilogue, writer); + if (needsResultAssignment) { + emitCallResultReturn(binding, writer); + } + } + + protected int emitCallArguments(MethodBinding binding, PrintWriter writer) { + boolean needComma = false; + int numArgsEmitted = 0; + + if (binding.hasContainingType()) { + // Emit this pointer + assert(binding.getContainingType().isCompoundTypeWrapper()); + writer.print("getBuffer()"); + needComma = true; + ++numArgsEmitted; + } + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(binding.getNumArguments() == 1); + continue; + } + + if (needComma) { + writer.print(", "); + } + + if (type.isCompoundTypeWrapper()) { + writer.print("(("); + } + + if (type.isNIOBuffer()) { + if(type.isNIOInt64Buffer() || type.isNIOPointerBuffer()) { + if (directNIOOnly) { + writer.print( getArgumentName(i)+ " != null ? " + getArgumentName(i) + ".getBuffer() : null"); + } else { + writer.print( isNIOArgName(i) + " ? ( " + getArgumentName(i)+ " != null ? " + getArgumentName(i) + ".getBuffer() : null )"); + writer.print( " : Buffers.getArray(" + getArgumentName(i) + ")" ); + } + } else { + if (directNIOOnly) { + writer.print( getArgumentName(i) ); + } else { + writer.print( isNIOArgName(i) + " ? " + getArgumentName(i) + " : Buffers.getArray(" + getArgumentName(i) + ")" ); + } + } + } else if (type.isArrayOfCompoundTypeWrappers()) { + writer.print(getArgumentName(i) + COMPOUND_ARRAY_SUFFIX); + } else { + writer.print(getArgumentName(i)); + } + + if (type.isCompoundTypeWrapper()) { + writer.print(" == null) ? null : "); + writer.print(getArgumentName(i)); + writer.print(".getBuffer())"); + } + + if (type.isNIOBuffer()) { + if (directNIOOnly) { + writer.print( ", Buffers.getDirectBufferByteOffset(" + getArgumentName(i) + ")"); + } else { + writer.print( ", " + isNIOArgName(i) + " ? Buffers.getDirectBufferByteOffset(" + getArgumentName(i) + ")"); + writer.print( " : Buffers.getIndirectBufferByteOffset(" + getArgumentName(i) + ")"); + } + } else if (type.isNIOBufferArray()) { + writer.print(", " + byteOffsetArrayArgName(i)); + } else if (type.isPrimitiveArray()) { + if(type.isFloatArray()) { + writer.print(", Buffers.SIZEOF_FLOAT * "); + } else if(type.isDoubleArray()) { + writer.print(", Buffers.SIZEOF_DOUBLE * "); + } else if(type.isByteArray()) { + writer.print(", "); + } else if(type.isLongArray()) { + writer.print(", Buffers.SIZEOF_LONG * "); + } else if(type.isShortArray()) { + writer.print(", Buffers.SIZEOF_SHORT * "); + } else if(type.isIntArray()) { + writer.print(", Buffers.SIZEOF_INT * "); + } else { + throw new RuntimeException("Unsupported type for calculating array offset argument for " + + getArgumentName(i) + + " -- error occurred while processing Java glue code for " + getName()); + } + writer.print(offsetArgName(i)); + } + + if (type.isNIOBuffer()) { + if (!directNIOOnly) { + writer.print( ", " + isNIOArgName(i) ); + } + } else if (type.isPrimitiveArray()) { + if (directNIOOnly) { + throw new RuntimeException("NIODirectOnly "+binding+" is set, but "+getArgumentName(i)+" is a primitive array"); + } + writer.print( ", false"); + } + + needComma = true; + ++numArgsEmitted; + } + return numArgsEmitted; + } + + protected void emitPostCallCleanup(MethodBinding binding, PrintWriter writer) { + if (binding.signatureUsesArraysOfCompoundTypeWrappers()) { + // For each such array, we need to take the ByteBuffer[] that + // came back from the C method invocation and wrap the + // ByteBuffers back into the wrapper types + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType javaArgType = binding.getJavaArgumentType(i); + if (javaArgType.isArrayOfCompoundTypeWrappers()) { + 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) ||"); + writer.println(" (" + argName + "[_ctr] != null && " + argName + "[_ctr].getBuffer() == " + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr])) {"); + writer.println(" // No copy back needed"); + writer.println(" } else {"); + writer.println(" if (" + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr] == null) {"); + writer.println(" " + argName + "[_ctr] = null;"); + writer.println(" } else {"); + writer.println(" " + argName + "[_ctr] = " + javaArgType.getName() + ".create(" + argName + COMPOUND_ARRAY_SUFFIX + "[_ctr]);"); + writer.println(" }"); + writer.println(" }"); + writer.println(" }"); + } + } + } + } + + protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { + JavaType returnType = binding.getJavaReturnType(); + + if (returnType.isCompoundTypeWrapper()) { + String fmt = getReturnedArrayLengthExpression(); + writer.println(" if (_res == null) return null;"); + if (fmt == null) { + writer.print(" return " + returnType.getName() + ".create(Buffers.nativeOrder(_res))"); + } else { + writer.println(" Buffers.nativeOrder(_res);"); + String expr = new MessageFormat(fmt).format(argumentNameArray()); + PointerType cReturnTypePointer = binding.getCReturnType().asPointer(); + CompoundType cReturnType = null; + if (cReturnTypePointer != null) { + cReturnType = cReturnTypePointer.getTargetType().asCompound(); + } + if (cReturnType == null) { + throw new RuntimeException("ReturnedArrayLength directive currently only supported for pointers to compound types " + + "(error occurred while generating Java glue code for " + getName() + ")"); + } + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[" + expr + "];"); + writer.println(" for (int _count = 0; _count < " + expr + "; _count++) {"); + // Create temporary ByteBuffer slice + // FIXME: probably need Type.getAlignedSize() for arrays of + // compound types (rounding up to machine-dependent alignment) + writer.println(" _res.position(_count * " + getReturnTypeString(true) + ".size());"); + writer.println(" _res.limit ((1 + _count) * " + getReturnTypeString(true) + ".size());"); + writer.println(" ByteBuffer _tmp = _res.slice();"); + writer.println(" Buffers.nativeOrder(_tmp);"); + writer.println(" _res.position(0);"); + writer.println(" _res.limit(_res.capacity());"); + writer.println(" _retarray[_count] = " + getReturnTypeString(true) + ".create(_tmp);"); + writer.println(" }"); + writer.print (" return _retarray"); + } + writer.println(";"); + } else if (returnType.isNIOBuffer()) { + writer.println(" if (_res == null) return null;"); + writer.println(" Buffers.nativeOrder(_res);"); + if (!returnType.isNIOByteBuffer()) { + // See whether we have to expand pointers to longs + if (getBinding().getCReturnType().pointerDepth() >= 2) { + if (returnType.isNIOPointerBuffer()) { + writer.println(" return PointerBuffer.wrap(_res);"); + } else if (returnType.isNIOInt64Buffer()) { + writer.println(" return Int64Buffer.wrap(_res);"); + } else { + throw new RuntimeException("While emitting glue code for " + getName() + + ": can not legally make pointers opaque to anything but PointerBuffer or Int64Buffer/long"); + } + } else if (getBinding().getCReturnType().pointerDepth() == 1 && + returnType.isNIOInt64Buffer()) { + writer.println(" return Int64Buffer.wrap(_res);"); + } else { + String returnTypeName = returnType.getName().substring("java.nio.".length()); + writer.println(" return _res.as" + returnTypeName + "();"); + } + } else { + writer.println(" return _res;"); + } + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println(" if (_res == null) return null;"); + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); + writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); + writer.println(" _retarray[_count] = " + getReturnTypeString(true) + ".create(_res[_count]);"); + writer.println(" }"); + writer.println(" return _retarray;"); + } else { + // Assume it's a primitive type or other type we don't have to + // do any conversion on + writer.println(" return _res;"); + } + } + + protected String[] argumentNameArray() { + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = getArgumentName(i); + if (binding.getJavaArgumentType(i).isPrimitiveArray()) { + // Add on _offset argument in comma-separated expression + argumentNames[i] = argumentNames[i] + ", " + offsetArgName(i); + } + } + return argumentNames; + } + + public static String javaThisArgumentName() { + return "jthis0"; + } + + @Override + protected String getCommentStartString() { return "/** "; } + + @Override + protected String getBaseIndentString() { return " "; } + + protected String getReturnedArrayLengthExpression() { + return returnedArrayLengthExpression; + } + + /** + * Class that emits a generic comment for JavaMethodBindingEmitters; the comment + * includes the C signature of the native method that is being bound by the + * emitter java method. + */ + protected class DefaultCommentEmitter implements CommentEmitter { + public void emit(FunctionEmitter emitter, PrintWriter writer) { + emitBeginning(emitter, writer); + emitBindingCSignature(((JavaMethodBindingEmitter)emitter).getBinding(), writer); + emitEnding(emitter, writer); + } + protected void emitBeginning(FunctionEmitter emitter, PrintWriter writer) { + writer.print("Entry point to C language function: "); + } + protected void emitBindingCSignature(MethodBinding binding, PrintWriter writer) { + writer.print("<code> "); + writer.print(binding.getCSymbol().toString(tagNativeBinding)); + writer.print(" </code> "); + } + protected void emitEnding(FunctionEmitter emitter, PrintWriter writer) { + // If argument type is a named enum, then emit a comment detailing the + // acceptable values of that enum. + // If we're emitting a direct buffer variant only, then declare + // that the NIO buffer arguments must be direct. + MethodBinding binding = ((JavaMethodBindingEmitter)emitter).getBinding(); + for (int i = 0; i < binding.getNumArguments(); i++) { + Type type = binding.getCArgumentType(i); + JavaType javaType = binding.getJavaArgumentType(i); + // don't emit param comments for anonymous enums, since we can't + // distinguish between the values found within multiple anonymous + // enums in the same C translation unit. + if (type.isEnum() && !HeaderParser.ANONYMOUS_ENUM_NAME.equals(type.getName())) { + EnumType enumType = (EnumType)type; + writer.println(); + writer.print(emitter.getBaseIndentString()); + writer.print(" "); + writer.print("@param "); + writer.print(getArgumentName(i)); + 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.println("</code>"); + } else if (directNIOOnly && javaType.isNIOBuffer()) { + writer.println(); + writer.print(emitter.getBaseIndentString()); + writer.print(" "); + writer.print("@param "); + writer.print(getArgumentName(i)); + writer.print(" a direct {@link " + javaType.getName() + "}"); + } + } + } + } + + protected class InterfaceCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { + + @Override + protected void emitBeginning(FunctionEmitter emitter, + PrintWriter writer) { + writer.print("Interface to C language function: <br> "); + } + } +} + diff --git a/src/java/com/jogamp/gluegen/JavaType.java b/src/java/com/jogamp/gluegen/JavaType.java new file mode 100644 index 0000000..9a89203 --- /dev/null +++ b/src/java/com/jogamp/gluegen/JavaType.java @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.nio.*; + +import com.jogamp.gluegen.cgram.types.*; + +/** + * Describes a java-side representation of a type that is used to represent + * the same data on both the Java-side and C-side during a JNI operation. Also + * contains some utility methods for creating common types. + */ +public class JavaType { + + /* + * Represents C arrays that will / can be represented + * with NIO buffers (resolved down to another JavaType later in processing) + */ + private enum C_PTR { + VOID, CHAR, SHORT, INT32, INT64, FLOAT, DOUBLE; + } + + private Class<?> clazz; // Primitive types and other types representable as Class objects + private String name; // Types we're generating glue code for (i.e., C structs) + private Type elementType; // Element type if this JavaType represents a C array + private C_PTR primitivePointerType; + + private static JavaType nioBufferType; + private static JavaType nioByteBufferType; + private static JavaType nioShortBufferType; + private static JavaType nioIntBufferType; + private static JavaType nioLongBufferType; + private static JavaType nioPointerBufferType; + private static JavaType nioInt64BufferType; + private static JavaType nioFloatBufferType; + private static JavaType nioDoubleBufferType; + private static JavaType nioByteBufferArrayType; + + @Override + public boolean equals(Object arg) { + if ((arg == null) || (!(arg instanceof JavaType))) { + return false; + } + JavaType t = (JavaType) arg; + return (this == t || + (t.clazz == clazz && + ((name == null ? t.name == null : name.equals(t.name)) || + ((name != null) && (t.name != null) && (name.equals(t.name)))) && + ((elementType == t.elementType) || + (elementType != null) && (t.elementType != null) && (elementType.equals(t.elementType))) && + (primitivePointerType == t.primitivePointerType))); + } + + @Override + public int hashCode() { + if (clazz == null) { + if (name == null) { + return 0; + } + return name.hashCode(); + } + return clazz.hashCode(); + } + + public JavaType getElementType() { + return new JavaType(elementType); + } + + /** 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(Class<?> clazz) { + return new JavaType(clazz); + } + + /** Creates a JavaType corresponding to the specified C CompoundType + name; for example, if "Foo" is supplied, then this JavaType + represents a "Foo *" by way of a StructAccessor. */ + public static JavaType createForCStruct(String name) { + return new JavaType(name); + } + + /** Creates a JavaType corresponding to an array of the given + element type. This is used to represent arrays of "Foo **" which + should be mapped to Foo[] in Java. */ + public static JavaType createForCArray(Type elementType) { + return new JavaType(elementType); + } + + public static JavaType createForVoidPointer() { + return new JavaType(C_PTR.VOID); + } + + public static JavaType createForCCharPointer() { + return new JavaType(C_PTR.CHAR); + } + + public static JavaType createForCShortPointer() { + return new JavaType(C_PTR.SHORT); + } + + public static JavaType createForCInt32Pointer() { + return new JavaType(C_PTR.INT32); + } + + public static JavaType createForCInt64Pointer() { + return new JavaType(C_PTR.INT64); + } + + public static JavaType createForCFloatPointer() { + return new JavaType(C_PTR.FLOAT); + } + + public static JavaType createForCDoublePointer() { + return new JavaType(C_PTR.DOUBLE); + } + + public static JavaType createForJNIEnv() { + return createForCStruct("JNIEnv"); + } + + public static JavaType forNIOBufferClass() { + if (nioBufferType == null) { + nioBufferType = createForClass(java.nio.Buffer.class); + } + return nioBufferType; + } + + public static JavaType forNIOByteBufferClass() { + if (nioByteBufferType == null) { + nioByteBufferType = createForClass(java.nio.ByteBuffer.class); + } + return nioByteBufferType; + } + + public static JavaType forNIOShortBufferClass() { + if (nioShortBufferType == null) { + nioShortBufferType = createForClass(java.nio.ShortBuffer.class); + } + return nioShortBufferType; + } + + public static JavaType forNIOIntBufferClass() { + if (nioIntBufferType == null) { + nioIntBufferType = createForClass(java.nio.IntBuffer.class); + } + return nioIntBufferType; + } + + public static JavaType forNIOLongBufferClass() { + if (nioLongBufferType == null) { + nioLongBufferType = createForClass(java.nio.LongBuffer.class); + } + return nioLongBufferType; + } + + public static JavaType forNIOInt64BufferClass() { + if(nioInt64BufferType == null) + nioInt64BufferType = createForClass(com.jogamp.common.nio.Int64Buffer.class); + return nioInt64BufferType; + } + + public static JavaType forNIOPointerBufferClass() { + if(nioPointerBufferType == null) + nioPointerBufferType = createForClass(com.jogamp.common.nio.PointerBuffer.class); + return nioPointerBufferType; + } + + public static JavaType forNIOFloatBufferClass() { + if (nioFloatBufferType == null) { + nioFloatBufferType = createForClass(java.nio.FloatBuffer.class); + } + return nioFloatBufferType; + } + + public static JavaType forNIODoubleBufferClass() { + if (nioDoubleBufferType == null) { + nioDoubleBufferType = createForClass(java.nio.DoubleBuffer.class); + } + return nioDoubleBufferType; + } + + public static JavaType forNIOByteBufferArrayClass() { + if (nioByteBufferArrayType == null) { + ByteBuffer[] tmp = new ByteBuffer[0]; + nioByteBufferArrayType = createForClass(tmp.getClass()); + } + return nioByteBufferArrayType; + } + + /** + * Returns the Java Class corresponding to this type. Returns null if this + * object corresponds to a C primitive array type. + */ + public Class<?> getJavaClass() { + return clazz; + } + + /** + * Returns the Java type name corresponding to this type. + */ + public String getName() { + if (clazz != null) { + if (clazz.isArray()) { + return arrayName(clazz); + } + return clazz.getName(); + } + if (elementType != null) { + return elementType.getName(); + } + return name; + } + + /** + * Returns the descriptor (internal type signature) corresponding to + * this type. + */ + public String getDescriptor() { + // FIXME: this is not completely accurate at this point (for + // example, it knows nothing about the packages for compound + // types) + if (clazz != null) { + return descriptor(clazz); + } + if (elementType != null) { + if(elementType.getName()==null) { + throw new RuntimeException("elementType.name is null: "+getDumpString()); + } + return "[" + descriptor(elementType.getName()); + } + return descriptor(name); + } + + /** Returns the String corresponding to the JNI type for this type, + or NULL if it can't be represented (i.e., it's a boxing class + that we need to call getBuffer() on.) */ + public String jniTypeName() { + if (isCompoundTypeWrapper()) { + // These are sent down as Buffers (e.g., jobject) + return "jobject"; + } + + if (isArrayOfCompoundTypeWrappers()) { + // These are returned as arrays of ByteBuffers (e.g., jobjectArray) + return "jobjectArray /* of ByteBuffer */"; + } + + if (clazz == null) { + return null; + } + + if (isVoid()) { + return "void"; + } + + if (isPrimitive()) { + return "j" + clazz.getName(); + } + + if (isPrimitiveArray() || isNIOBuffer()) { + // We now pass primitive arrays and buffers uniformly down to native code as java.lang.Object. + return "jobject"; + } + + if (isArray()) { + if (isStringArray()) { + return "jobjectArray /*elements are String*/"; + } + + Class<?> elementType = clazz.getComponentType(); + + if (isNIOBufferArray()) { + return "jobjectArray /*elements are " + elementType.getName() + "*/"; + } + + if (elementType.isArray()) { + // Type is array-of-arrays-of-something + + if (elementType.getComponentType().isPrimitive()) { + // Type is an array-of-arrays-of-primitive + return "jobjectArray /* elements are " + elementType.getComponentType() + "[]*/"; + //return "jobjectArray"; + } else { + throw new RuntimeException("Multi-dimensional arrays of types that are not primitives or Strings are not supported."); + } + } + + // Some unusual type that we don't handle + throw new RuntimeException("Unexpected and unsupported array type: \"" + this + "\""); + } + + if (isString()) { + return "jstring"; + } + + return "jobject"; + } + + public boolean isNIOBuffer() { + return clazz != null && ( (java.nio.Buffer.class).isAssignableFrom(clazz) || + (com.jogamp.common.nio.PointerBuffer.class).isAssignableFrom(clazz) || + (com.jogamp.common.nio.Int64Buffer.class).isAssignableFrom(clazz) ) ; + } + + public boolean isNIOByteBuffer() { + return (clazz == java.nio.ByteBuffer.class); + } + + public boolean isNIOByteBufferArray() { + return (this == nioByteBufferArrayType); + } + + public boolean isNIOBufferArray() { + return (isArray() && (java.nio.Buffer.class.isAssignableFrom(clazz.getComponentType()))); + } + + public boolean isNIOLongBuffer() { + return (clazz == java.nio.LongBuffer.class); + } + + public boolean isNIOInt64Buffer() { + return (clazz == com.jogamp.common.nio.Int64Buffer.class); + } + + public boolean isNIOPointerBuffer() { + return (clazz == com.jogamp.common.nio.PointerBuffer.class); + } + + public boolean isString() { + return (clazz == java.lang.String.class); + } + + public boolean isArray() { + return ((clazz != null) && clazz.isArray()); + } + + public boolean isFloatArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Float.TYPE); + } + + public boolean isDoubleArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Double.TYPE); + } + + public boolean isByteArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Byte.TYPE); + } + + public boolean isIntArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Integer.TYPE); + } + + public boolean isShortArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Short.TYPE); + } + + public boolean isLongArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == Long.TYPE); + } + + public boolean isStringArray() { + return (clazz != null && clazz.isArray() && clazz.getComponentType() == java.lang.String.class); + } + + + public boolean isPrimitive() { + return ((clazz != null) && !isArray() && clazz.isPrimitive() && (clazz != Void.TYPE)); + } + + public boolean isPrimitiveArray() { + return (isArray() && (clazz.getComponentType().isPrimitive())); + } + + public boolean isShort() { + return (clazz == Short.TYPE); + } + + public boolean isFloat() { + return (clazz == Float.TYPE); + } + + public boolean isDouble() { + return (clazz == Double.TYPE); + } + + public boolean isByte() { + return (clazz == Byte.TYPE); + } + + public boolean isLong() { + return (clazz == Long.TYPE); + } + + public boolean isInt() { + return (clazz == Integer.TYPE); + } + + public boolean isVoid() { + return (clazz == Void.TYPE); + } + + public boolean isCompoundTypeWrapper() { + return (clazz == null && name != null && !isJNIEnv()); + } + + public boolean isArrayOfCompoundTypeWrappers() { + return elementType != null; + } + + public boolean isCPrimitivePointerType() { + return primitivePointerType != null; + } + + public boolean isCVoidPointerType() { + return C_PTR.VOID.equals(primitivePointerType); + } + + public boolean isCCharPointerType() { + return C_PTR.CHAR.equals(primitivePointerType); + } + + public boolean isCShortPointerType() { + return C_PTR.SHORT.equals(primitivePointerType); + } + + public boolean isCInt32PointerType() { + return C_PTR.INT32.equals(primitivePointerType); + } + + public boolean isCInt64PointerType() { + return C_PTR.INT64.equals(primitivePointerType); + } + + public boolean isCFloatPointerType() { + return C_PTR.FLOAT.equals(primitivePointerType); + } + + public boolean isCDoublePointerType() { + return C_PTR.DOUBLE.equals(primitivePointerType); + } + + public boolean isJNIEnv() { + return clazz == null && "JNIEnv".equals(name); + } + + @Override + public Object clone() { + JavaType clone = new JavaType(primitivePointerType); + + clone.clazz = this.clazz; + clone.name = this.name; + clone.elementType = this.elementType; + + return clone; + } + + @Override + public String toString() { + return getName(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + // For debugging + public String getDumpString() { + return "[clazz = " + clazz + " , name = " + name + " , elementType = " + elementType + " , primitivePointerType = " + primitivePointerType + "]"; + } + public void dump() { + System.err.println(getDumpString()); + } + + /** + * Constructs a representation for a type corresponding to the given Class + * argument. + */ + private JavaType(Class<?> clazz) { + this.clazz = clazz; + } + + /** Constructs a type representing a named C struct. */ + private JavaType(String name) { + this.name = name; + } + + /** Constructs a type representing an array of C pointers. */ + private JavaType(Type elementType) { + this.elementType = elementType; + } + + /** Constructs a type representing a pointer to a C primitive + (integer, floating-point, or void pointer) type. */ + private JavaType(C_PTR primitivePointerType) { + this.primitivePointerType = primitivePointerType; + } + + private String arrayName(Class<?> clazz) { + StringBuilder buf = new StringBuilder(); + int arrayCount = 0; + while (clazz.isArray()) { + ++arrayCount; + clazz = clazz.getComponentType(); + } + buf.append(clazz.getName()); + while (--arrayCount >= 0) { + buf.append("[]"); + } + return buf.toString(); + } + + private String arrayDescriptor(Class<?> clazz) { + StringBuilder buf = new StringBuilder(); + while (clazz.isArray()) { + buf.append("["); + clazz = clazz.getComponentType(); + } + buf.append(descriptor(clazz)); + return buf.toString(); + } + + private String descriptor(Class<?> clazz) { + if (clazz.isPrimitive()) { + if (clazz == Boolean.TYPE) return "Z"; + if (clazz == Byte.TYPE) return "B"; + if (clazz == Double.TYPE) return "D"; + if (clazz == Float.TYPE) return "F"; + if (clazz == Integer.TYPE) return "I"; + if (clazz == Long.TYPE) return "J"; + if (clazz == Short.TYPE) return "S"; + if (clazz == Void.TYPE) return "V"; + throw new RuntimeException("Unexpected primitive type " + clazz.getName()); + } + if (clazz.isArray()) { + return arrayDescriptor(clazz); + } + return descriptor(clazz.getName()); + } + + private String descriptor(String referenceTypeName) { + return "L" + referenceTypeName.replace('.', '/') + ";"; + } +} diff --git a/src/java/com/jogamp/gluegen/Logging.java b/src/java/com/jogamp/gluegen/Logging.java new file mode 100644 index 0000000..9ad3bf7 --- /dev/null +++ b/src/java/com/jogamp/gluegen/Logging.java @@ -0,0 +1,86 @@ +/** + * 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. + */ + +/* + * Created on Wednesday, March 31 2010 13:30 + */ +package com.jogamp.gluegen; + +import java.util.logging.ConsoleHandler; +import java.util.logging.Formatter; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * + * @author Michael Bien + */ +public class Logging { + + static void init() { + + String pakage = Logging.class.getPackage().getName(); + String property = System.getProperty(pakage+".level"); + Level level; + if(property != null) { + level = Level.parse(property); + }else{ + level = Level.WARNING; + } + + ConsoleHandler handler = new ConsoleHandler() { + @Override + public java.util.logging.Formatter getFormatter() { + return new PlainLogFormatter(); + } + }; + handler.setFormatter(new PlainLogFormatter()); + handler.setLevel(level); + + Logger rootPackageLogger = Logger.getLogger(pakage); + rootPackageLogger.setUseParentHandlers(false); + rootPackageLogger.setLevel(level); + rootPackageLogger.addHandler(handler); + } + + /** + * This log formatter needs usually one line per log record. + * @author Michael Bien + */ + private static class PlainLogFormatter extends Formatter { + + //@Override + public String format(LogRecord record) { + StringBuilder sb = new StringBuilder(128); + sb.append("[").append(record.getLevel()).append(' ').append(record.getSourceClassName()).append("]: "); + sb.append(formatMessage(record)).append("\n"); + return sb.toString(); + } + } +} diff --git a/src/java/com/jogamp/gluegen/MethodBinding.java b/src/java/com/jogamp/gluegen/MethodBinding.java new file mode 100644 index 0000000..a8d4b30 --- /dev/null +++ b/src/java/com/jogamp/gluegen/MethodBinding.java @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +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 + to represent calls through function pointers contained in + structs. */ + +public class MethodBinding { + + private FunctionSymbol sym; + private String renamedMethodName; + private HashSet<String> aliasedNames; + private JavaType javaReturnType; + private List<JavaType> javaArgumentTypes; + private boolean computedSignatureProperties; + private boolean argumentsUseNIO; + private boolean signatureUsesNIO; + private boolean signatureCanUseIndirectNIO; + private boolean signatureUsesCompoundTypeWrappers; + private boolean signatureUsesArraysOfCompoundTypeWrappers; + private boolean signatureUsesCVoidPointers; + private boolean signatureUsesCPrimitivePointers; + private boolean signatureUsesCArrays; + private boolean signatureUsesJavaPrimitiveArrays; + private JavaType containingType; + private Type containingCType; + private int thisPointerIndex = -1; + + /** + * Constructs a new MethodBinding that is an exact clone of the + * argument, including the java return type and java argument + * types. It's safe to modify this binding after construction. + */ + public MethodBinding(MethodBinding bindingToCopy) { + this.sym = bindingToCopy.sym; + + this.renamedMethodName = bindingToCopy.renamedMethodName; + this.aliasedNames = new HashSet<String>(bindingToCopy.aliasedNames); + this.containingType = bindingToCopy.containingType; + this.containingCType = bindingToCopy.containingCType; + this.javaReturnType = bindingToCopy.javaReturnType; + this.javaArgumentTypes = ( null != bindingToCopy.javaArgumentTypes ) ? new ArrayList<JavaType>(bindingToCopy.javaArgumentTypes) : null; + this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; + this.argumentsUseNIO = bindingToCopy.argumentsUseNIO; + this.signatureUsesNIO = bindingToCopy.signatureUsesNIO; + this.signatureCanUseIndirectNIO = bindingToCopy.signatureCanUseIndirectNIO; + this.signatureUsesCompoundTypeWrappers = bindingToCopy.signatureUsesCompoundTypeWrappers; + this.signatureUsesArraysOfCompoundTypeWrappers = bindingToCopy.signatureUsesArraysOfCompoundTypeWrappers; + this.signatureUsesCVoidPointers = bindingToCopy.signatureUsesCVoidPointers; + this.signatureUsesCPrimitivePointers = bindingToCopy.signatureUsesCPrimitivePointers; + this.signatureUsesCArrays = bindingToCopy.signatureUsesCArrays; + this.signatureUsesJavaPrimitiveArrays = bindingToCopy.signatureUsesJavaPrimitiveArrays; + this.thisPointerIndex = bindingToCopy.thisPointerIndex; + } + + /** Constructor for calling a C function. */ + public MethodBinding(FunctionSymbol sym) { + this.sym = sym; + this.aliasedNames = new HashSet<String>(); + } + + /** Constructor for calling a function pointer contained in a + struct. */ + public MethodBinding(FunctionSymbol sym, JavaType containingType, Type containingCType) { + this.sym = sym; + this.containingType = containingType; + this.containingCType = containingCType; + this.aliasedNames = new HashSet<String>(); + } + + public void setJavaReturnType(JavaType type) { + javaReturnType = type; + computedSignatureProperties = false; + } + + public void addJavaArgumentType(JavaType type) { + if (javaArgumentTypes == null) { + javaArgumentTypes = new ArrayList<JavaType>(); + } + javaArgumentTypes.add(type); + computedSignatureProperties = false; + } + + public JavaType getJavaReturnType() { + return javaReturnType; + } + + public int getNumArguments() { + return sym.getNumArguments(); + } + + public JavaType getJavaArgumentType(int i) { + return javaArgumentTypes.get(i); + } + + public Type getCReturnType() { + return sym.getReturnType(); + } + + public Type getCArgumentType(int i) { + return sym.getArgumentType(i); + } + + public FunctionSymbol getCSymbol() { + return sym; + } + + /** Returns either the argument name specified by the underlying + FunctionSymbol or a fabricated argument name based on the + position. Note that it is currently not guaranteed that there + are no namespace clashes with these fabricated argument + names. */ + public String getArgumentName(int i) { + String ret = sym.getArgumentName(i); + if (ret != null) { + return ret; + } + return "arg" + i; + } + + public String getOrigName() { + return sym.getName(); + } + + 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(String name) { + if (null != name) { + renamedMethodName = name; + aliasedNames.add(sym.getName()); + } + } + + public void addAliasedName(String name) { + aliasedNames.add(name); + } + + public Collection<String> getAliasedNames() { + return aliasedNames; + } + + /** Creates a new MethodBinding replacing the specified Java + argument type with a new argument type. If argumentNumber is + less than 0 then replaces the return type. */ + public MethodBinding replaceJavaArgumentType(int argumentNumber, JavaType newArgType) { + + MethodBinding binding = (MethodBinding) clone(); + binding.javaArgumentTypes = null; + if (argumentNumber < 0) { + binding.setJavaReturnType(newArgType); + } else { + binding.setJavaReturnType(javaReturnType); + } + for (int i = 0; i < getNumArguments(); i++) { + JavaType type = getJavaArgumentType(i); + if (i == argumentNumber) { + type = newArgType; + } + binding.addJavaArgumentType(type); + } + return binding; + } + + /** + * Returns true if any of the outgoing arguments in the method's + * signature require conversion or checking due to the use of New + * I/O. + */ + public boolean argumentsUseNIO() { + computeSignatureProperties(); + return argumentsUseNIO; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature require conversion or checking due to + * the use of New I/O. + */ + public boolean signatureUsesNIO() { + computeSignatureProperties(); + return signatureUsesNIO; + } + + /** + * Returns true if it is possible for any of the outgoing arguments + * to be indirect NIO buffers. + */ + public boolean signatureCanUseIndirectNIO() { + computeSignatureProperties(); + return signatureCanUseIndirectNIO; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature use "compound type wrappers", or + * NIO-based wrappers for C data structures. + */ + public boolean signatureUsesCompoundTypeWrappers() { + computeSignatureProperties(); + return signatureUsesCompoundTypeWrappers; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature use arrays of "compound type wrappers", + * or NIO-based wrappers for C data structures. + */ + public boolean signatureUsesArraysOfCompoundTypeWrappers() { + computeSignatureProperties(); + return signatureUsesArraysOfCompoundTypeWrappers; + } + + /** + * Returns true if the function needs NIO-related + * wrapping/unwrapping or conversion of various arguments. Currently + * this returns the logical OR of signatureUsesNIO() and + * signatureUsesCompoundTypeWrappers(). + */ + public boolean needsNIOWrappingOrUnwrapping() { + return (signatureUsesNIO() || signatureUsesCompoundTypeWrappers()); + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C void* pointers. + */ + public boolean signatureUsesCVoidPointers() { + computeSignatureProperties(); + return signatureUsesCVoidPointers; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C primitive pointers. + */ + public boolean signatureUsesCPrimitivePointers() { + computeSignatureProperties(); + return signatureUsesCPrimitivePointers; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent C arrays. + */ + public boolean signatureUsesCArrays() { + computeSignatureProperties(); + return signatureUsesCArrays; + } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature represent Java primitive arrays. + */ + public boolean signatureUsesJavaPrimitiveArrays() { + computeSignatureProperties(); + return signatureUsesJavaPrimitiveArrays; + } + + /** + * Computes summary information about the method's C and Java + * signatures. + */ + protected void computeSignatureProperties() { + if (computedSignatureProperties) + return; + + argumentsUseNIO = false; + signatureUsesNIO = false; + signatureCanUseIndirectNIO = false; + signatureUsesCompoundTypeWrappers = false; + signatureUsesArraysOfCompoundTypeWrappers = false; + signatureUsesCVoidPointers = false; + signatureUsesCPrimitivePointers = false; + signatureUsesCArrays = false; + signatureUsesJavaPrimitiveArrays = false; + + if (javaReturnType.isCompoundTypeWrapper()) { + // Needs wrapping and/or setting of byte order (neither of which + // can be done easily from native code) + signatureUsesCompoundTypeWrappers = true; + } + + if (javaReturnType.isNIOBuffer() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + // Needs setting of byte order and possibly viewing as a + // different buffer type which can't be done easily from native + // code + signatureUsesNIO = true; + } + + Type cRetType = sym.getReturnType(); + if (cRetType.isArray()) { + // Needs checking of array lengths + signatureUsesCArrays = true; + if (cRetType.asArray().getElementType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } + } + + if (cRetType.isPointer()) { + if (cRetType.asPointer().getTargetType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } else if (cRetType.asPointer().getTargetType().isVoid()) { + signatureUsesCVoidPointers = true; + } + } + + for (int i = 0; i < getNumArguments(); i++) { + JavaType javaArgType = getJavaArgumentType(i); + Type cArgType = getCArgumentType(i); + if (javaArgType.isCompoundTypeWrapper()) { + // Needs unwrapping of accessors + signatureUsesCompoundTypeWrappers = true; + } + + if (javaArgType.isArrayOfCompoundTypeWrappers()) { + // Needs to be duplicated and this array lowered to an array + // of Buffers for code emission + signatureUsesArraysOfCompoundTypeWrappers = true; + } + + if (javaArgType.isNIOBuffer() || + javaArgType.isNIOBufferArray()) { + // Needs checking of direct buffer property + signatureUsesNIO = true; + argumentsUseNIO = true; + + if (javaArgType.isNIOBuffer()) { + // Potential conversion to indirect buffer + signatureCanUseIndirectNIO = true; + } + } + + if (cArgType.isArray()) { + // Needs checking of array lengths + signatureUsesCArrays = true; + if (cArgType.asArray().getElementType().isPrimitive()) { + signatureUsesCPrimitivePointers = true; + } + } + + if (cArgType.isPointer()) { + // Handle both real C primitive pointers and any constructions + // due to opaque directives + if (cArgType.asPointer().getTargetType().isPrimitive() || + javaArgType.isCPrimitivePointerType()) { + signatureUsesCPrimitivePointers = true; + } else if (cArgType.asPointer().getTargetType().isVoid()) { + signatureUsesCVoidPointers = true; + } + } + + if (javaArgType.isPrimitiveArray()) { + // Needs getPrimitiveArrayCritical or similar construct + // depending on native code calling convention + signatureUsesJavaPrimitiveArrays = true; + } + } + + computedSignatureProperties = true; + } + + /** Indicates whether this MethodBinding is for a function pointer + contained in a struct. */ + public boolean hasContainingType() { + return (getContainingType() != null); + } + + /** Retrieves the containing type of this MethodBinding if it is for + a function pointer contained in a struct. */ + public JavaType getContainingType() { + return containingType; + } + + /** Retrieves the containing C type of this MethodBinding if it is for + a function pointer contained in a struct. */ + public Type getContainingCType() { + return containingCType; + } + + /** Find the leftmost argument matching the type of the containing + type (for function pointer MethodBindings) and record that as a + "this" pointer, meaning that it does not need to be explicitly + passed at the Java level. */ + public void findThisPointer() { + clearThisPointer(); + for (int i = 0; i < getNumArguments(); i++) { + JavaType arg = getJavaArgumentType(i); + if (arg.equals(containingType)) { + thisPointerIndex = i; + break; + } + + if (!arg.isJNIEnv()) { + break; // this pointer must be leftmost argument excluding JNIEnvs + } + } + } + + /** Clears any record of a this pointer for this MethodBinding. */ + public void clearThisPointer() { + thisPointerIndex = -1; + } + + /** Indicates whether the <i>i</i>th argument to this MethodBinding + is actually a "this" pointer. */ + public boolean isArgumentThisPointer(int i) { + return (thisPointerIndex == i); + } + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj == null || ! (obj instanceof MethodBinding)) { + return false; + } + + MethodBinding other = (MethodBinding)obj; + if ( !getName().equals(other.getName()) || + !sym.getType().equals(other.sym.getType()) ) { return false; } + if (!(javaReturnType.equals(other.getJavaReturnType()))) { return false; } + if (containingCType != null && + other.getContainingCType() != null && + (!(containingCType.equals(other.getContainingCType())))) { + return false; + } + if (javaArgumentTypes.size() != other.javaArgumentTypes.size()) { + return false; + } + + for (int i = 0; i < javaArgumentTypes.size(); ++i) { + Object typeThis = javaArgumentTypes.get(i); + Object typeOther = other.getJavaArgumentType(i); + if (!(typeThis.equals(typeOther))) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + StringBuilder buf = new StringBuilder(200); + buf.append(getName()); + buf.append(sym.getType().getName(true)); + buf.append(getJavaReturnType().getName()); + if (containingCType != null) { + buf.append(containingCType.getName(true)); + } + + for (int i = 0; i < getNumArguments(); i++) { + JavaType type = getJavaArgumentType(i); + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(getNumArguments() == 1); + continue; + } + + buf.append(type.getName()); + } + return buf.toString().hashCode(); + } + + /** Returns the signature of this binding. */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(200); + buf.append(getJavaReturnType().getName()); + buf.append(' '); + buf.append(getName()); + buf.append('('); + boolean needComma = false; + for (int i = 0; i < getNumArguments(); i++) { + JavaType type = getJavaArgumentType(i); + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(getNumArguments() == 1); + continue; + } + if (type.isJNIEnv() || isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (needComma) { + buf.append(", "); + } + + buf.append(type.getName()); + buf.append(' '); + buf.append(getArgumentName(i)); + needComma = true; + } + buf.append(')'); + return buf.toString(); + } + + @Override + public final Object clone() { + return new MethodBinding(this); + } + + /** Returns a String containing the descriptor (signature in + internal format) of this MethodBinding as it will be + emitted. This is used to disambiguate between overloadings when + manually specifying prologue and epilogue code, for example. */ + public String getDescriptor(boolean forImplementingMethodCall, + boolean eraseBufferAndArrayTypes) { + StringBuilder buf = new StringBuilder(); + + buf.append('('); + + if (forImplementingMethodCall && hasContainingType()) { + // Always emit outgoing "this" argument + buf.append("Ljava/nio/ByteBuffer;"); + } + + for (int i = 0; i < getNumArguments(); i++) { + JavaType type = getJavaArgumentType(i); + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + if (getNumArguments() != 1) { + throw new InternalError( + "\"void\" argument type found in " + + "multi-argument function \"" + this + "\""); + } + continue; + } + + if (type.isJNIEnv() || isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + buf.append(erasedTypeDescriptor(type, eraseBufferAndArrayTypes, false)); + + // Add Buffer and array index offset arguments after each associated argument + if (forImplementingMethodCall) { + if (type.isNIOBuffer()) { + buf.append('I'); + } else if (type.isNIOBufferArray()) { + buf.append("[I"); + } + } + + // Add offset argument after each primitive array + if (type.isPrimitiveArray()) { + buf.append('I'); + } + } + + buf.append(')'); + + // Emit return type for completeness even though we can't overload + // based solely on return type + buf.append(erasedTypeDescriptor(getJavaReturnType(), eraseBufferAndArrayTypes, false)); + + return buf.toString(); + } + + protected String erasedTypeDescriptor(JavaType type, boolean eraseBufferAndArrayTypes, boolean skipBuffers) { + if (eraseBufferAndArrayTypes) { + if (type.isNIOBuffer() || + type.isPrimitiveArray()) { + if (!skipBuffers) { + // Direct buffers and arrays sent down as Object (but + // returned as e.g. ByteBuffer) + return "Ljava/lang/Object;"; + } + } else if (type.isCompoundTypeWrapper()) { + // Compound type wrappers are unwrapped to ByteBuffer + return "Ljava/nio/ByteBuffer;"; + } else if (type.isArrayOfCompoundTypeWrappers()) { + return "Ljava/nio/ByteBuffer;"; + } + } + return type.getDescriptor(); + } +} + diff --git a/src/java/com/jogamp/gluegen/ReferencedStructs.java b/src/java/com/jogamp/gluegen/ReferencedStructs.java new file mode 100644 index 0000000..b8a176f --- /dev/null +++ b/src/java/com/jogamp/gluegen/ReferencedStructs.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import java.util.*; +import com.jogamp.gluegen.cgram.types.*; + +public class ReferencedStructs implements TypeVisitor { + + private final Set<Type> results = new HashSet<Type>(); + + public void clear() { + results.clear(); + } + + public Iterator<Type> results() { + return results.iterator(); + } + + public void visitType(Type t) { + if (t.isPointer()) { + PointerType p = t.asPointer(); + if (p.hasTypedefedName()) { + 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); + } + } + } else if (t.isCompound()) { + results.add(t); + } + } +} diff --git a/src/java/com/jogamp/gluegen/StructLayout.java b/src/java/com/jogamp/gluegen/StructLayout.java new file mode 100644 index 0000000..ea8768f --- /dev/null +++ b/src/java/com/jogamp/gluegen/StructLayout.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +import com.jogamp.gluegen.cgram.types.*; + +/** Encapsulates algorithm for laying out data structures. Note that + this ends up embedding code in various places via SizeThunks. If + the 32-bit and 64-bit ports on a given platform differ + fundamentally in their handling of struct layout then this code + will need to be updated and, most likely, two versions of the + SizeThunks maintained in various places. */ + +public class StructLayout { + private int baseOffset; + private int structAlignment; + + protected StructLayout(int baseOffset, + int structAlignment) { + this.baseOffset = baseOffset; + this.structAlignment = structAlignment; + } + + public void layout(CompoundType t) { + int n = t.getNumFields(); + SizeThunk curOffset = SizeThunk.constant(baseOffset); + SizeThunk maxSize = SizeThunk.constant(0); + for (int i = 0; i < n; i++) { + Field f = t.getField(i); + Type ft = f.getType(); + if (ft.isInt() || ft.isFloat() || ft.isDouble() || ft.isPointer()) { + SizeThunk sz = ft.getSize(); + curOffset = SizeThunk.roundUp(curOffset, sz); + f.setOffset(curOffset); + if (t.isUnion()) { + maxSize = SizeThunk.max(maxSize, sz); + } else { + curOffset = SizeThunk.add(curOffset, sz); + } + } else if (ft.isCompound()) { + new StructLayout(0, structAlignment).layout(ft.asCompound()); + curOffset = SizeThunk.roundUp(curOffset, SizeThunk.constant(structAlignment)); + f.setOffset(curOffset); + if (t.isUnion()) { + maxSize = SizeThunk.max(maxSize, ft.getSize()); + } else { + curOffset = SizeThunk.add(curOffset, ft.getSize()); + } + } else if (ft.isArray()) { + ArrayType arrayType = ft.asArray(); + CompoundType compoundElementType = arrayType.getBaseElementType().asCompound(); + if (compoundElementType != null) { + new StructLayout(0, structAlignment).layout(compoundElementType); + arrayType.recomputeSize(); + } + // Note: not sure how this rounding is done + curOffset = SizeThunk.roundUp(curOffset, SizeThunk.constant(structAlignment)); + f.setOffset(curOffset); + curOffset = SizeThunk.add(curOffset, ft.getSize()); + } else { + // FIXME + String name = t.getName(); + if (name == null) { + name = t.toString(); + } + throw new RuntimeException("Complicated field types (" + ft + + " " + f.getName() + + " in type " + name + + ") not implemented yet"); + } + } + // FIXME: I think the below is wrong; better check with some examples + // if ((curOffset % structAlignment) != 0) { + // curOffset += structAlignment - (curOffset % structAlignment); + // } + if (t.isUnion()) { + t.setSize(maxSize); + } else { + t.setSize(curOffset); + } + } + + + + public static StructLayout createForCurrentPlatform() { + // Note: this code is replicated in (from?) Platform.java + String os = System.getProperty("os.name").toLowerCase(); + String cpu = System.getProperty("os.arch").toLowerCase(); + if ((os.startsWith("windows") && cpu.equals("x86"))) { + // It appears that Windows uses a packing alignment of 4 bytes in 32-bit mode + return new StructLayout(0, 4); + } else if ((os.startsWith("windows") && cpu.equals("amd64")) || + (os.startsWith("linux") && cpu.equals("i386")) || + (os.startsWith("linux") && cpu.equals("x86")) || + (os.startsWith("linux") && cpu.equals("amd64")) || + (os.startsWith("linux") && cpu.equals("x86_64")) || + (os.startsWith("linux") && cpu.equals("ia64")) || + (os.startsWith("sunos") && cpu.equals("sparc")) || + (os.startsWith("sunos") && cpu.equals("sparcv9")) || + (os.startsWith("sunos") && cpu.equals("x86")) || + (os.startsWith("sunos") && cpu.equals("amd64")) || + (os.startsWith("mac os") && cpu.equals("ppc")) || + (os.startsWith("mac os") && cpu.equals("i386")) || + (os.startsWith("mac os") && cpu.equals("x86_64")) || + (os.startsWith("freebsd") && cpu.equals("i386")) || + (os.startsWith("freebsd") && cpu.equals("amd64")) || + (os.startsWith("hp-ux") && cpu.equals("pa_risc2.0")) + ) { + // FIXME: make struct alignment configurable? May need to change + // packing rules on a per-type basis? + return new StructLayout(0, 8); + } else { + // FIXME: add more ports + throw new RuntimeException("Please port StructLayout to your OS (" + os + ") and CPU (" + cpu + ")"); + } + } +} diff --git a/src/java/com/jogamp/gluegen/SymbolFilter.java b/src/java/com/jogamp/gluegen/SymbolFilter.java new file mode 100644 index 0000000..4e99caa --- /dev/null +++ b/src/java/com/jogamp/gluegen/SymbolFilter.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package com.jogamp.gluegen; + +import com.jogamp.gluegen.cgram.types.FunctionSymbol; +import java.util.List; + +/** Provides a mechanism by which the GlueEmitter can look at all of + the #defines, enum values and function symbols and perform certain + filtering and processing which requires all of them to be visible + simultaneously. */ + +public interface SymbolFilter { + /** + * Filters the given constant and function symbols. The caller + * will query the SymbolFilter for its resulting constant and + * function symbol lists after this routine returns. + * + * @param defines a list of {@link com.jogamp.gluegen.cgram.Define Define} objects + * @param functions a list of {@link com.jogamp.gluegen.cgram.types.FunctionSymbol FunctionSymbol} objects + */ + public void filterSymbols(List<ConstantDefinition> constants, List<FunctionSymbol> functions); + + /** Returns the filtered list of constants. This method may return + a new list, the original list, or null, in which case the + original list will be used. */ + public List<ConstantDefinition> getConstants(); + + /** Returns the filtered list of function symbols. This method may + return a new list, the original list, or null, in which case + the original list will be used. */ + public List<FunctionSymbol> getFunctions(); +} diff --git a/src/java/com/jogamp/gluegen/TypeInfo.java b/src/java/com/jogamp/gluegen/TypeInfo.java new file mode 100644 index 0000000..b62fc15 --- /dev/null +++ b/src/java/com/jogamp/gluegen/TypeInfo.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen; + +/** Utility class for handling Opaque directives for JavaEmitter. */ + +public class TypeInfo { + private String name; + private int pointerDepth; + private JavaType javaType; + private TypeInfo next; + + public TypeInfo(String name, int pointerDepth, JavaType javaType) { + this.name = name; + this.pointerDepth = pointerDepth; + this.javaType = javaType; + } + + public String name() { return name; } + public int pointerDepth() { return pointerDepth; } + public JavaType javaType() { return javaType; } + public void setNext(TypeInfo info) { this.next = info; } + public TypeInfo next() { return next; } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer("TypeInfo: "); + buf.append(name); + buf.append(" pointerDepth "); + buf.append(pointerDepth); + buf.append(" JavaType " + javaType); + return buf.toString(); + } +} diff --git a/src/java/com/jogamp/gluegen/ant/GlueGenTask.java b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java new file mode 100644 index 0000000..4cb88c2 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ant/GlueGenTask.java @@ -0,0 +1,542 @@ +package com.jogamp.gluegen.ant; + +/* + * GlueGenTask.java + * Copyright (C) 2003 Rob Grzywinski ([email protected]) + * + * Copying, distribution and use of this software in source and binary + * forms, with or without modification, is permitted provided that the + * following conditions are met: + * + * Distributions of source code must reproduce the copyright notice, + * this list of conditions and the following disclaimer in the source + * code header files; and Distributions of binary code must reproduce + * the copyright notice, this list of conditions and the following + * disclaimer in the documentation, Read me file, license file and/or + * other materials provided with the software distribution. + * + * The names of Sun Microsystems, Inc. ("Sun") and/or the copyright + * holder may not 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, NON-INTERFERENCE, ACCURACY OF + * INFORMATIONAL CONTENT OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. THE + * COPYRIGHT HOLDER, SUN AND SUN'S 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 THE + * COPYRIGHT HOLDER, SUN OR SUN'S 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 ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGES. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT + * DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, + * OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY. THE COPYRIGHT + * HOLDER, SUN AND SUN'S LICENSORS DISCLAIM ANY EXPRESS OR IMPLIED + * WARRANTY OF FITNESS FOR SUCH USES. + */ + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.LogStreamHandler; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.DirSet; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * <p>An <a href="http://ant.apache.org">ANT</a> {@link org.apache.tools.ant.Task} + * for using {@link com.jogamp.gluegen.GlueGen}.</p> + * + * <p>Usage:</p> + * <pre> + <gluegen src="[source C file]" + outputrootdir="[optional output root dir]" + includes="[optional directory pattern of include files to include]" + excludes="[optional directory pattern of include files to exclude]" + includeRefid="[optional FileSet or DirSet for include files]" + literalInclude="[optional hack to get around FileSet / DirSet issues with different drives]" + emitter="[emitter class name]" + config="[configuration file]" + debug="[optional boolean]" /> + * </pre> + * + * @author Rob Grzywinski <a href="mailto:[email protected]">[email protected]</a> + */ +// FIXME: blow out javadoc +// NOTE: this has not been exhaustively tested +public class GlueGenTask extends Task +{ + /** + * <p>The {@link com.jogamp.gluegen.GlueGen} classname.</p> + */ + private static final String GLUE_GEN = "com.jogamp.gluegen.GlueGen"; + + // ========================================================================= + /** + * <p>The {@link org.apache.tools.ant.types.CommandlineJava} that is used + * to execute {@link com.jogamp.gluegen.GlueGen}.</p> + */ + private CommandlineJava gluegenCommandline; + + // ========================================================================= + /** + * <p>The optional debug flag.</p> + */ + private boolean debug=false; + + /** + * <p>The optional output root dir.</p> + */ + private String outputRootDir; + + /** + * <p>The name of the emitter class.</p> + */ + private String emitter; + + /** + * <p>The configuration file name.</p> + */ + private String configuration; + + /** + * <p>The name of the source C file that is to be parsed.</p> + */ + private String sourceFile; + + /** + * <p>The {@link org.apache.tools.ant.types.FileSet} of includes.</p> + */ + private FileSet includeSet = new FileSet(); + + /** + * <p>Because a {@link org.apache.tools.ant.types.FileSet} will include + * everything in its base directory if it is left untouched, the <code>includeSet</code> + * must only be added to the set of includes if it has been <i>explicitly</i> + * set.</p> + */ + private boolean usedIncludeSet = false; // by default it is not used + + /** + * <p>The set of include sets. This allows includes to be added in multiple + * fashions.</p> + */ + // FIXME: rename to listXXXX + private List setOfIncludeSets = new LinkedList(); + + /** + * <p>A single literal directory to include. This is to get around the + * fact that neither {@link org.apache.tools.ant.types.FileSet} nor + * {@link org.apache.tools.ant.types.DirSet} can handle multiple drives in + * a sane manner. If <code>null</code> then it has not been specified.</p> + */ + private String literalInclude; + + // ========================================================================= + /** + * <p>Create and add the VM and classname to {@link org.apache.tools.ant.types.CommandlineJava}.</p> + */ + public GlueGenTask() + { + // create the CommandlineJava that will be used to call GlueGen + gluegenCommandline = new CommandlineJava(); + + // set the VM and classname in the commandline + gluegenCommandline.setVm(JavaEnvUtils.getJreExecutable("java")); + gluegenCommandline.setClassname(GLUE_GEN); + // gluegenCommandline.createVmArgument().setValue("-verbose:class"); + } + + // ========================================================================= + // ANT getters and setters + + /** + * <p>Set the debug flag (optional). This is called by ANT.</p> + * + * @param outputRootDir the optional output root dir + */ + public void setDebug(boolean debug) + { + log( ("Setting debug flag: " + debug), Project.MSG_VERBOSE); + this.debug=debug; + } + + /** + * <p>Set the output root dir (optional). This is called by ANT.</p> + * + * @param outputRootDir the optional output root dir + */ + public void setOutputRootDir(String outputRootDir) + { + log( ("Setting output root dir: " + outputRootDir), Project.MSG_VERBOSE); + this.outputRootDir=outputRootDir; + } + + /** + * <p>Set the emitter class name. This is called by ANT.</p> + * + * @param emitter the name of the emitter class + */ + public void setEmitter(String emitter) + { + log( ("Setting emitter class name to: " + emitter), Project.MSG_VERBOSE); + this.emitter = emitter; + } + + /** + * <p>Set the configuration file name. This is called by ANT.</p> + * + * @param configuration the name of the configuration file + */ + public void setConfig(String configuration) + { + log( ("Setting configuration file name to: " + configuration), + Project.MSG_VERBOSE); + this.configuration = configuration; + } + + /** + * <p>Set the source C file that is to be parsed. This is called by ANT.</p> + * + * @param sourceFile the name of the source file + */ + public void setSrc(String sourceFile) + { + log( ("Setting source file name to: " + sourceFile), Project.MSG_VERBOSE); + this.sourceFile = sourceFile; + } + + /** + * <p>Set a single literal include directory. See the <code>literalInclude</code> + * javadoc for more information.</p> + * + * @param directory the directory to include + */ + public void setLiteralInclude(String directory) + { + this.literalInclude = directory; + } + + /** + * <p>Add an include file to the list. This is called by ANT for a nested + * element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createInclude() + { + usedIncludeSet = true; + return includeSet.createInclude(); + } + + /** + * <p>Add an include file to the list. This is called by ANT for a nested + * element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createIncludesFile() + { + usedIncludeSet = true; + return includeSet.createIncludesFile(); + } + + /** + * <p>Set the set of include patterns. Patterns may be separated by a comma + * or a space. This is called by ANT.</p> + * + * @param includes the string containing the include patterns + */ + public void setIncludes(String includes) + { + usedIncludeSet = true; + includeSet.setIncludes(includes); + } + + /** + * <p>Add an include file to the list that is to be exluded. This is called + * by ANT for a nested element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createExclude() + { + usedIncludeSet = true; + return includeSet.createExclude(); + } + + /** + * <p>Add an exclude file to the list. This is called by ANT for a nested + * element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createExcludesFile() + { + usedIncludeSet = true; + return includeSet.createExcludesFile(); + } + + /** + * <p>Set the set of exclude patterns. Patterns may be separated by a comma + * or a space. This is called by ANT.</p> + * + * @param includes the string containing the exclude patterns + */ + public void setExcludes(String excludes) + { + usedIncludeSet = true; + includeSet.setExcludes(excludes); + } + + /** + * <p>Set a {@link org.apache.tools.ant.types.Reference} to simplify adding + * of complex sets of files to include. This is called by ANT.</p>? + * + * @param reference a <code>Reference</code> to a {@link org.apache.tools.ant.types.FileSet} + * or {@link org.apache.tools.ant.types.DirSet} + * @throws BuildException if the specified <code>Reference</code> is not + * either a <code>FileSet</code> or <code>DirSet</code> + */ + public void setIncludeRefid(Reference reference) + { + // ensure that the referenced object is either a FileSet or DirSet + final Object referencedObject = reference.getReferencedObject(getProject()); + if( !( (referencedObject instanceof FileSet) || + (referencedObject instanceof DirSet)) ) + { + throw new BuildException("Only FileSets or DirSets are allowed as an include refid."); + } + + // add the referenced object to the set of include sets + setOfIncludeSets.add(referencedObject); + } + + /** + * <p>Add a nested {@link org.apache.tools.ant.types.DirSet} to specify + * the files to include. This is called by ANT.</p> + * + * @param dirset the <code>DirSet</code> to be added + */ + public void addDirset(DirSet dirset) + { + setOfIncludeSets.add(dirset); + } + + /** + * <p>Add an optional classpath that defines the location of {@link com.jogamp.gluegen.GlueGen} + * and <code>GlueGen</code>'s dependencies.</p> + * + * @returns {@link org.apache.tools.ant.types.Path} + */ + public Path createClasspath() + { + return gluegenCommandline.createClasspath(project).createPath(); + } + + // ========================================================================= + /** + * <p>Run the task. This involves validating the set attributes, creating + * the command line to be executed and finally executing the command.</p> + * + * @see org.apache.tools.ant.Task#execute() + */ + public void execute() + throws BuildException + { + // validate that all of the required attributes have been set + validateAttributes(); + + // TODO: add logic to determine if the generated file needs to be + // regenerated + + // add the attributes to the CommandlineJava + addAttributes(); + + log(gluegenCommandline.describeCommand(), Project.MSG_VERBOSE); + + // execute the command and throw on error + final int error = execute(gluegenCommandline.getCommandline()); + if(error == 1) + throw new BuildException( ("GlueGen returned: " + error), location); + } + + /** + * <p>Ensure that the user specified all required arguments.</p> + * + * @throws BuildException if there are required arguments that are not + * present or not valid + */ + private void validateAttributes() + throws BuildException + { + // outputRootDir is optional .. + + // validate that the emitter class is set + if(!isValid(emitter)) + throw new BuildException("Invalid emitter class name: " + emitter); + + // validate that the configuration file is set + if(!isValid(configuration)) + throw new BuildException("Invalid configuration file name: " + configuration); + + // validate that the source file is set + if(!isValid(sourceFile)) + throw new BuildException("Invalid source file name: " + sourceFile); + + // CHECK: do there need to be includes to be valid? + } + + /** + * <p>Is the specified string valid? A valid string is non-<code>null</code> + * and has a non-zero length.</p> + * + * @param string the string to be tested for validity + * @return <code>true</code> if the string is valid. <code>false</code> + * otherwise. + */ + private boolean isValid(String string) + { + // check for null + if(string == null) + return false; + + // ensure that the string has a non-zero length + // NOTE: must trim() to remove leading and trailing whitespace + if(string.trim().length() < 1) + return false; + + // the string is valid + return true; + } + + /** + * <p>Add all of the attributes to the command line. They have already + * been validated.</p> + */ + private void addAttributes() + throws BuildException + { + // NOTE: GlueGen uses concatenated flag / value rather than two + // separate arguments + + // add the debug flag if enabled + if(debug) { + gluegenCommandline.createArgument().setValue("--debug"); + } + + // add the output root dir + if(null!=outputRootDir && outputRootDir.trim().length()>0) { + gluegenCommandline.createArgument().setValue("-O" + outputRootDir); + } + + // add the emitter class name + gluegenCommandline.createArgument().setValue("-E" + emitter); + + // add the configuration file name + gluegenCommandline.createArgument().setValue("-C" + configuration); + + // add the includedSet to the setOfIncludeSets to simplify processing + // all types of include sets ONLY if it has been set. + // NOTE: see the usedIncludeSet member javadoc for more info + // NOTE: references and nested DirSets have already been added to the + // set of include sets + if(usedIncludeSet) + { + includeSet.setDir(getProject().getBaseDir()); // NOTE: the base dir must be set + setOfIncludeSets.add(includeSet); + } + + // iterate over all include sets and add their directories to the + // list of included directories. + final List includedDirectories = new LinkedList(); + for(Iterator includes=setOfIncludeSets.iterator(); includes.hasNext(); ) + { + // get the included set and based on its type add the directories + // to includedDirectories + Object include = (Object)includes.next(); + final String[] directoryDirs; + if(include instanceof FileSet) + { + final FileSet fileSet = (FileSet)include; + DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject()); + directoryDirs = directoryScanner.getIncludedDirectories(); + } else if(include instanceof DirSet) + { + final DirSet dirSet = (DirSet)include; + DirectoryScanner directoryScanner = dirSet.getDirectoryScanner(getProject()); + directoryDirs = directoryScanner.getIncludedDirectories(); + } else + { + // NOTE: this cannot occur as it is checked on setXXX() but + // just to be pedantic this is here + throw new BuildException("Invalid included construct."); + } + + // add the directoryDirs to the includedDirectories + // TODO: exclude any directory that is already in the list + for(int i=0; i<directoryDirs.length; i++) + { + includedDirectories.add(directoryDirs[i]); + } + } + + // if literalInclude is valid then add it to the list of included + // directories + if(isValid(literalInclude)) + includedDirectories.add(literalInclude); + + // add the included directories to the command + for(Iterator includes=includedDirectories.iterator(); includes.hasNext(); ) + { + String directory = (String)includes.next(); + gluegenCommandline.createArgument().setValue("-I" + directory); + } + + // finally, add the source file + gluegenCommandline.createArgument().setValue(sourceFile); + } + + /** + * <p>Execute {@link com.jogamp.gluegen.GlueGen} in a forked JVM.</p> + * + * @throws BuildException + */ + private int execute(String[] command) + throws BuildException + { + // create the object that will perform the command execution + Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, + Project.MSG_WARN), + null); + + // set the project and command line + execute.setAntRun(project); + execute.setCommandline(command); + execute.setWorkingDirectory( project.getBaseDir() ); + + // execute the command + try + { + return execute.execute(); + } catch(IOException ioe) + { + throw new BuildException(ioe, location); + } + } +} diff --git a/src/java/com/jogamp/gluegen/ant/StaticGLGenTask.java b/src/java/com/jogamp/gluegen/ant/StaticGLGenTask.java new file mode 100644 index 0000000..2785344 --- /dev/null +++ b/src/java/com/jogamp/gluegen/ant/StaticGLGenTask.java @@ -0,0 +1,304 @@ +package com.jogamp.gluegen.ant; + +/* + * StaticGLGenTask.java + * Copyright (C) 2003 Rob Grzywinski ([email protected]) + * + * Copying, distribution and use of this software in source and binary + * forms, with or without modification, is permitted provided that the + * following conditions are met: + * + * Distributions of source code must reproduce the copyright notice, + * this list of conditions and the following disclaimer in the source + * code header files; and Distributions of binary code must reproduce + * the copyright notice, this list of conditions and the following + * disclaimer in the documentation, Read me file, license file and/or + * other materials provided with the software distribution. + * + * The names of Sun Microsystems, Inc. ("Sun") and/or the copyright + * holder may not 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, NON-INTERFERENCE, ACCURACY OF + * INFORMATIONAL CONTENT OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. THE + * COPYRIGHT HOLDER, SUN AND SUN'S 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 THE + * COPYRIGHT HOLDER, SUN OR SUN'S 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 ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGES. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT + * DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, + * OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY. THE COPYRIGHT + * HOLDER, SUN AND SUN'S LICENSORS DISCLAIM ANY EXPRESS OR IMPLIED + * WARRANTY OF FITNESS FOR SUCH USES. + */ + +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.LogStreamHandler; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.util.JavaEnvUtils; + +/** + * <p>An <a href="http://ant.apache.org">ANT</a> {@link org.apache.tools.ant.Task} + * for using {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo}.</p> + * + * <p>Usage:</p> + * <pre> + <staticglgen package="[generated files package]" + headers="[file pattern of GL headers]" + outputdir="[directory to output the generated files]" /> + * </pre> + * + * @author Rob Grzywinski <a href="mailto:[email protected]">[email protected]</a> + */ +// FIXME: blow out javadoc +public class StaticGLGenTask extends Task +{ + /** + * <p>The {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} classname.</p> + */ + private static final String GL_GEN = "com.jogamp.gluegen.opengl.BuildStaticGLInfo"; + + // ========================================================================= + /** + * <p>The {@link org.apache.tools.ant.types.CommandlineJava} that is used + * to execute {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo}.</p> + */ + private CommandlineJava glgenCommandline; + + // ========================================================================= + /** + * <p>The package name for the generated files.</p> + */ + private String packageName; + + /** + * <p>The output directory.</p> + */ + private String outputDirectory; + + /** + * <p>The {@link org.apache.tools.ant.types.FileSet} of GL headers.</p> + */ + private FileSet headerSet = new FileSet(); + + // ========================================================================= + /** + * <p>Create and add the VM and classname to {@link org.apache.tools.ant.types.CommandlineJava}.</p> + */ + public StaticGLGenTask() + { + // create the CommandlineJava that will be used to call BuildStaticGLInfo + glgenCommandline = new CommandlineJava(); + + // set the VM and classname in the commandline + glgenCommandline.setVm(JavaEnvUtils.getJreExecutable("java")); + glgenCommandline.setClassname(GL_GEN); + } + + // ========================================================================= + // ANT getters and setters + /** + * <p>Set the package name for the generated files. This is called by ANT.</p> + * + * @param packageName the name of the package for the generated files + */ + public void setPackage(String packageName) + { + log( ("Setting package name to: " + packageName), Project.MSG_VERBOSE); + this.packageName = packageName; + } + + /** + * <p>Set the output directory. This is called by ANT.</p> + * + * @param directory the output directory + */ + public void setOutputDir(String directory) + { + log( ("Setting output directory to: " + directory), + Project.MSG_VERBOSE); + this.outputDirectory = directory; + } + + /** + * <p>Add a header file to the list. This is called by ANT for a nested + * element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createHeader() + { + return headerSet.createInclude(); + } + + /** + * <p>Add a header file to the list. This is called by ANT for a nested + * element.</p> + * + * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} + */ + public PatternSet.NameEntry createHeadersFile() + { + return headerSet.createIncludesFile(); + } + + /** + * <p>Set the set of header patterns. Patterns may be separated by a comma + * or a space. This is called by ANT.</p> + * + * @param headers the string containing the header patterns + */ + public void setHeaders(String headers) + { + headerSet.setIncludes(headers); + } + + /** + * <p>Add an optional classpath that defines the location of {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} + * and <code>BuildStaticGLInfo</code>'s dependencies.</p> + * + * @returns {@link org.apache.tools.ant.types.Path} + */ + public Path createClasspath() + { + return glgenCommandline.createClasspath(project).createPath(); + } + + // ========================================================================= + /** + * <p>Run the task. This involves validating the set attributes, creating + * the command line to be executed and finally executing the command.</p> + * + * @see org.apache.tools.ant.Task#execute() + */ + public void execute() + throws BuildException + { + // validate that all of the required attributes have been set + validateAttributes(); + + // TODO: add logic to determine if the generated file needs to be + // regenerated + + // add the attributes to the CommandlineJava + addAttributes(); + + log(glgenCommandline.describeCommand(), Project.MSG_VERBOSE); + + // execute the command and throw on error + final int error = execute(glgenCommandline.getCommandline()); + if(error == 1) + throw new BuildException( ("BuildStaticGLInfo returned: " + error), location); + } + + /** + * <p>Ensure that the user specified all required arguments.</p> + * + * @throws BuildException if there are required arguments that are not + * present or not valid + */ + private void validateAttributes() + throws BuildException + { + // validate that the package name is set + if(!isValid(packageName)) + throw new BuildException("Invalid package name: " + packageName); + + // validate that the output directory is set + // TODO: switch to file and ensure that it exists + if(!isValid(outputDirectory)) + throw new BuildException("Invalid output directory name: " + outputDirectory); + + // TODO: validate that there are headers set + } + + /** + * <p>Is the specified string valid? A valid string is non-<code>null</code> + * and has a non-zero length.</p> + * + * @param string the string to be tested for validity + * @return <code>true</code> if the string is valid. <code>false</code> + * otherwise. + */ + private boolean isValid(String string) + { + // check for null + if(string == null) + return false; + + // ensure that the string has a non-zero length + // NOTE: must trim() to remove leading and trailing whitespace + if(string.trim().length() < 1) + return false; + + // the string is valid + return true; + } + + /** + * <p>Add all of the attributes to the command line. They have already + * been validated.</p> + */ + private void addAttributes() + { + // add the package name + glgenCommandline.createArgument().setValue(packageName); + + // add the output directory name + glgenCommandline.createArgument().setValue(outputDirectory); + + // add the header -files- from the FileSet + headerSet.setDir(getProject().getBaseDir()); + DirectoryScanner directoryScanner = headerSet.getDirectoryScanner(getProject()); + String[] directoryFiles = directoryScanner.getIncludedFiles(); + for(int i=0; i<directoryFiles.length; i++) + { + glgenCommandline.createArgument().setValue(directoryFiles[i]); + } + } + + /** + * <p>Execute {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} in a + * forked JVM.</p> + * + * @throws BuildException + */ + private int execute(String[] command) + throws BuildException + { + // create the object that will perform the command execution + Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, + Project.MSG_WARN), + null); + + // set the project and command line + execute.setAntRun(project); + execute.setCommandline(command); + execute.setWorkingDirectory( project.getBaseDir() ); + + // execute the command + try + { + return execute.execute(); + } catch(IOException ioe) + { + throw new BuildException(ioe, location); + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/CSymbolTable.java b/src/java/com/jogamp/gluegen/cgram/CSymbolTable.java new file mode 100644 index 0000000..0addf7b --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/CSymbolTable.java @@ -0,0 +1,132 @@ +package com.jogamp.gluegen.cgram; + +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; + + + +public class CSymbolTable { + + /** holds list of scopes */ + private Vector scopeStack; + + /** table where all defined names are mapped to TNode tree nodes */ + private Hashtable symTable; + + public CSymbolTable() { + scopeStack = new Vector(10); + symTable = new Hashtable(533); + } + + + /** push a new scope onto the scope stack. + */ + public void pushScope(String s) { + //System.out.println("push scope:" + s); + scopeStack.addElement(s); + } + + /** pop the last scope off the scope stack. + */ + public void popScope() { + //System.out.println("pop scope"); + int size = scopeStack.size(); + if(size > 0) + scopeStack.removeElementAt(size - 1); + } + + /** return the current scope as a string + */ + public String currentScopeAsString() { + StringBuffer buf = new StringBuffer(100); + boolean first = true; + Enumeration e = scopeStack.elements(); + while(e.hasMoreElements()) { + if(first) + first = false; + else + buf.append("::"); + buf.append(e.nextElement().toString()); + } + return buf.toString(); + } + + /** given a name for a type, append it with the + current scope. + */ + public String addCurrentScopeToName(String name) { + String currScope = currentScopeAsString(); + return addScopeToName(currScope, name); + } + + /** given a name for a type, append it with the + given scope. MBZ + */ + public String addScopeToName(String scope, String name) { + if(scope == null || scope.length() > 0) + return scope + "::" + name; + else + return name; + } + + /** remove one level of scope from name MBZ*/ + public String removeOneLevelScope(String scopeName) { + int index = scopeName.lastIndexOf("::"); + if (index > 0) { + return scopeName.substring(0,index); + } + if (scopeName.length() > 0) { + return ""; + } + return null; + } + + /** add a node to the table with it's key as + the current scope and the name */ + public TNode add(String name, TNode node) { + return (TNode)symTable.put(addCurrentScopeToName(name),node); + } + + + /** lookup a fully scoped name in the symbol table */ + public TNode lookupScopedName(String scopedName) { + return (TNode)symTable.get(scopedName); + } + + /** lookup an unscoped name in the table by prepending + the current scope. + MBZ -- if not found, pop scopes and look again + */ + public TNode lookupNameInCurrentScope(String name) { + String scope = currentScopeAsString(); + String scopedName; + TNode tnode = null; + + //System.out.println( "\n"+ this.toString() ); + + while (tnode == null && scope != null) { + scopedName = addScopeToName(scope, name); + //System.out.println("lookup trying " + scopedName); + tnode = (TNode)symTable.get(scopedName); + scope = removeOneLevelScope(scope); + } + return tnode; + } + + /** convert this table to a string */ + public String toString() { + StringBuffer buff = new StringBuffer(300); + buff.append("CSymbolTable { \nCurrentScope: " + currentScopeAsString() + + "\nDefinedSymbols:\n"); + Enumeration ke = symTable.keys(); + Enumeration ve = symTable.elements(); + while(ke.hasMoreElements()) { + buff.append(ke.nextElement().toString() + " (" + + TNode.getNameForType(((TNode)ve.nextElement()).getType()) + ")\n"); + } + buff.append("}\n"); + return buff.toString(); + } + +}; diff --git a/src/java/com/jogamp/gluegen/cgram/CToken.java b/src/java/com/jogamp/gluegen/cgram/CToken.java new file mode 100644 index 0000000..f6bbf51 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/CToken.java @@ -0,0 +1,32 @@ +package com.jogamp.gluegen.cgram; + +import antlr.CommonToken; + +public class CToken extends antlr.CommonToken { + String source = ""; + int tokenNumber; + + public String getSource() + { + return source; + } + + public void setSource(String src) + { + source = src; + } + + public int getTokenNumber() + { + return tokenNumber; + } + + public void setTokenNumber(int i) + { + tokenNumber = i; + } + + public String toString() { + return "CToken:" +"(" + hashCode() + ")" + "[" + getType() + "] "+ getText() + " line:" + getLine() + " source:" + source ; + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/Define.java b/src/java/com/jogamp/gluegen/cgram/Define.java new file mode 100644 index 0000000..c2510df --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/Define.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram; + +/** Represents a #define of a literal to a value (a number represented + in string form.) */ + +public class Define { + private String name; + private String value; + + public Define(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { return name; } + public String getValue() { return value; } +} diff --git a/src/java/com/jogamp/gluegen/cgram/GnuCEmitter.g b/src/java/com/jogamp/gluegen/cgram/GnuCEmitter.g new file mode 100644 index 0000000..78f8d68 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/GnuCEmitter.g @@ -0,0 +1,1145 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Copyright (c) Non, Inc. 1998 -- All Rights Reserved + +PROJECT: C Compiler +MODULE: GnuCEmitter +FILE: GnuCEmitter.g + +AUTHOR: Monty Zukowski ([email protected]) April 28, 1998 + +DESCRIPTION: + + This tree grammar is for a Gnu C AST. + It turns the tree back into source code. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + + +header { + package com.jogamp.gluegen.cgram; + + import java.io.*; + import java.util.*; + + import antlr.CommonAST; + import antlr.DumpASTVisitor; +} + + +class GnuCEmitter extends GnuCTreeParser; + +options + { + importVocab = GNUC; + buildAST = false; + ASTLabelType = "TNode"; + + // Copied following options from java grammar. + codeGenMakeSwitchThreshold = 2; + codeGenBitsetTestThreshold = 3; + } + + +{ + + +int tabs = 0; +PrintStream currentOutput = System.out; +int lineNum = 1; +String currentSource = ""; +LineObject trueSourceFile; +final int lineDirectiveThreshold = Integer.MAX_VALUE; +PreprocessorInfoChannel preprocessorInfoChannel = null; +Stack sourceFiles = new Stack(); + +public GnuCEmitter( PreprocessorInfoChannel preprocChannel ) +{ + preprocessorInfoChannel = preprocChannel; +} + +void initializePrinting() +{ + Vector preprocs = preprocessorInfoChannel.extractLinesPrecedingTokenNumber( new Integer(1) ); + printPreprocs(preprocs); +/* if ( currentSource.equals("") ) { + trueSourceFile = new LineObject(currentSource); + currentOutput.println("# 1 \"" + currentSource + "\"\n"); + sourceFiles.push(trueSourceFile); + } +*/ +} + +void finalizePrinting() { + // flush any leftover preprocessing instructions to the stream + + printPreprocs( + preprocessorInfoChannel.extractLinesPrecedingTokenNumber( + new Integer( preprocessorInfoChannel.getMaxTokenNumber() + 1 ) )); + //print a newline so file ends at a new line + currentOutput.println(); +} + +void printPreprocs( Vector preprocs ) +{ + // if there was a preprocessingDirective previous to this token then + // print a newline and the directive, line numbers handled later + if ( preprocs.size() > 0 ) { + if ( trueSourceFile != null ) { + currentOutput.println(); //make sure we're starting a new line unless this is the first line directive + } + lineNum++; + Enumeration e = preprocs.elements(); + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + if ( o.getClass().getName().equals("LineObject") ) { + LineObject l = (LineObject) o; + + // we always return to the trueSourceFile, we never enter it from another file + // force it to be returning if in fact we aren't currently in trueSourceFile + if (( trueSourceFile != null ) //trueSource exists + && ( !currentSource.equals(trueSourceFile.getSource()) ) //currently not in trueSource + && ( trueSourceFile.getSource().equals(l.getSource()) ) ) { //returning to trueSource + l.setEnteringFile( false ); + l.setReturningToFile( true ); + } + + + // print the line directive + currentOutput.println(l); + lineNum = l.getLine(); + currentSource = l.getSource(); + + + // the very first line directive always represents the true sourcefile + if ( trueSourceFile == null ) { + trueSourceFile = new LineObject(currentSource); + sourceFiles.push(trueSourceFile); + } + + // keep our own stack of files entered + if ( l.getEnteringFile() ) { + sourceFiles.push(l); + } + + // if returning to a file, pop the exited files off the stack + if ( l.getReturningToFile() ) { + LineObject top = (LineObject) sourceFiles.peek(); + while (( top != trueSourceFile ) && (! l.getSource().equals(top.getSource()) )) { + sourceFiles.pop(); + top = (LineObject) sourceFiles.peek(); + } + } + } + else { // it was a #pragma or such + currentOutput.println(o); + lineNum++; + } + } + } + +} + +void print( TNode t ) { + int tLineNum = t.getLocalLineNum(); + if ( tLineNum == 0 ) tLineNum = lineNum; + + Vector preprocs = preprocessorInfoChannel.extractLinesPrecedingTokenNumber((Integer)t.getAttribute("tokenNumber")); + printPreprocs(preprocs); + + if ( (lineNum != tLineNum) ) { + // we know we'll be newlines or a line directive or it probably + // is just the case that this token is on the next line + // either way start a new line and indent it + currentOutput.println(); + lineNum++; + printTabs(); + } + + if ( lineNum == tLineNum ){ + // do nothing special, we're at the right place + } + else { + int diff = tLineNum - lineNum; + if ( lineNum < tLineNum ) { + // print out the blank lines to bring us up to right line number + for ( ; lineNum < tLineNum ; lineNum++ ) { + currentOutput.println(); + } + printTabs(); + } + else { // just reset lineNum + lineNum = tLineNum; + } + } + currentOutput.print( t.getText() + " " ); +} + + +/* This was my attempt at being smart about line numbers + It didn't work quite right but I don't know why, I didn't + have enough test cases. Worked ok compiling rcs and ghostscript +*/ +void printAddingLineDirectives( TNode t ) { + int tLineNum = t.getLocalLineNum(); + String tSource = (String) t.getAttribute("source"); + + if ( tSource == null ) tSource = currentSource; + if ( tLineNum == 0 ) tLineNum = lineNum; + + Vector preprocs = preprocessorInfoChannel.extractLinesPrecedingTokenNumber((Integer)t.getAttribute("tokenNumber")); + printPreprocs(preprocs); + + if ( (lineNum != tLineNum) || !currentSource.equals(tSource) ) { + // we know we'll be newlines or a line directive or it probably + // is just the case that this token is on the next line + // either way start a new line and indent it + currentOutput.println(); + lineNum++; + printTabs(); + } + + if ( ( lineNum == tLineNum ) && ( currentSource.equals(tSource) ) ){ + // do nothing special, we're at the right place + } + else if ( currentSource.equals(tSource) ) { + int diff = tLineNum - lineNum; + if (diff > 0 && diff < lineDirectiveThreshold) { + // print out the blank lines to bring us up to right line number + for ( ; lineNum < tLineNum ; lineNum++ ) { + currentOutput.println(); + } + } + else { // print line directive to get us to right line number + // preserve flags 3 and 4 if present in current file + if ( ! sourceFiles.empty() ) { + LineObject l = (LineObject) sourceFiles.peek(); + StringBuffer tFlags = new StringBuffer(""); + if (l.getSystemHeader()) { + tFlags.append(" 3"); + } + if (l.getTreatAsC()) { + tFlags.append(" 4"); + } + currentOutput.println("# " + tLineNum + " \"" + tSource + "\"" + tFlags.toString()); + lineNum = tLineNum; + } + } + + printTabs(); + } + else { // different source + Enumeration sources = sourceFiles.elements(); + // see if we're returning to a file we entered earlier + boolean returningToEarlierFile = false; + while (sources.hasMoreElements()) { + LineObject l = (LineObject) sources.nextElement(); + if (l.getSource().equals(tSource)) { + returningToEarlierFile = true; + break; + } + } + if (returningToEarlierFile) { + // pop off the files we're exiting, but never pop the trueSourceFile + LineObject l = (LineObject) sourceFiles.peek(); + while ( ( l != trueSourceFile ) &&(! l.getSource().equals(tSource) ) ) { + sourceFiles.pop(); + l = (LineObject) sourceFiles.peek(); + } + + // put in the return flag, plus others as needed + StringBuffer tFlags = new StringBuffer(" 2"); + if (l.getSystemHeader()) { + tFlags.append(" 3"); + } + if (l.getTreatAsC()) { + tFlags.append(" 4"); + } + + currentOutput.println("# " + tLineNum + " \"" + tSource + "\"" + tFlags); + lineNum = tLineNum; + currentSource = tSource; + printTabs(); + } + else { // entering a file that wasn't in the original source + // pretend we're entering it from top of stack + currentOutput.println("# " + tLineNum + " \"" + tSource + "\"" + " 1"); + lineNum = tLineNum; + currentSource = tSource; + printTabs(); + } + } + currentOutput.print( t.getText() + " " ); +} + +/** It is not ok to print newlines from the String passed in as +it will screw up the line number handling **/ +void print( String s ) { + currentOutput.print( s + " " ); +} + +void printTabs() { + for ( int i = 0; i< tabs; i++ ) { + currentOutput.print( "\t" ); + } +} + +void commaSep( TNode t ) { + print( t ); + if ( t.getNextSibling() != null ) { + print( "," ); + } +} + + int traceDepth = 0; + public void reportError(RecognitionException ex) { + if ( ex != null) { + System.err.println("ANTLR Tree Parsing RecognitionException Error: " + ex.getClass().getName() + " " + ex ); + ex.printStackTrace(System.err); + } + } + public void reportError(NoViableAltException ex) { + System.err.println("ANTLR Tree Parsing NoViableAltException Error: " + ex.toString()); + TNode.printTree( ex.node ); + ex.printStackTrace(System.err); + } + public void reportError(MismatchedTokenException ex) { + if ( ex != null) { + TNode.printTree( ex.node ); + System.err.println("ANTLR Tree Parsing MismatchedTokenException Error: " + ex ); + ex.printStackTrace(System.err); + } + } + public void reportError(String s) { + System.err.println("ANTLR Error from String: " + s); + } + public void reportWarning(String s) { + System.err.println("ANTLR Warning from String: " + s); + } + protected void match(AST t, int ttype) throws MismatchedTokenException { + //System.out.println("match("+ttype+"); cursor is "+t); + super.match(t, ttype); + } + public void match(AST t, BitSet b) throws MismatchedTokenException { + //System.out.println("match("+b+"); cursor is "+t); + super.match(t, b); + } + protected void matchNot(AST t, int ttype) throws MismatchedTokenException { + //System.out.println("matchNot("+ttype+"); cursor is "+t); + super.matchNot(t, ttype); + } + public void traceIn(String rname, AST t) { + traceDepth += 1; + for (int x=0; x<traceDepth; x++) System.out.print(" "); + super.traceIn(rname, t); + } + public void traceOut(String rname, AST t) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + super.traceOut(rname, t); + traceDepth -= 1; + } + + + +} + + +translationUnit options { + defaultErrorHandler=false; +} + : + { initializePrinting(); } + ( externalList )? + { finalizePrinting(); } + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + +externalList + : ( externalDef )+ + ; + + +externalDef + : declaration + | functionDef + | asm_expr + | typelessDeclaration + | s:SEMI { print( s ); } + ; + +typelessDeclaration + : #(NTypeMissing initDeclList s: SEMI) { print( s ); } + ; + + + +asm_expr + : #( a:"asm" { print( a ); } + ( v:"volatile" { print( v ); } + )? + lc:LCURLY { print( lc ); tabs++; } + expr + rc:RCURLY { tabs--; print( rc ); } + s:SEMI { print( s ); } + ) + ; + + +declaration + : #( NDeclaration + declSpecifiers + ( + initDeclList + )? + ( s:SEMI { print( s ); } )+ + ) + ; + + +declSpecifiers + : ( storageClassSpecifier + | typeQualifier + | typeSpecifier + )+ + ; + +storageClassSpecifier + : a:"auto" { print( a ); } + | b:"register" { print( b ); } + | c:"typedef" { print( c ); } + | functionStorageClassSpecifier + ; + + +functionStorageClassSpecifier + : a:"extern" { print( a ); } + | b:"static" { print( b ); } + | c:"inline" { print( c ); } + ; + + +typeQualifier + : a:"const" { print( a ); } + | b:"volatile" { print( b ); } + ; + + +typeSpecifier + : a:"void" { print( a ); } + | b:"char" { print( b ); } + | c:"short" { print( c ); } + | d:"int" { print( d ); } + | e:"long" { print( e ); } + | f:"float" { print( f ); } + | g:"double" { print( g ); } + | h:"signed" { print( h ); } + | i:"unsigned" { print( i ); } + | structSpecifier ( attributeDecl )* + | unionSpecifier ( attributeDecl )* + | enumSpecifier + | typedefName + | #(n:"typeof" lp:LPAREN { print( n ); print( lp ); } + ( (typeName )=> typeName + | expr + ) + rp:RPAREN { print( rp ); } + ) + | p:"__complex" { print( p ); } + ; + + +typedefName + : #(NTypedefName i:ID { print( i ); } ) + ; + + +structSpecifier + : #( a:"struct" { print( a ); } + structOrUnionBody + ) + ; + +unionSpecifier + : #( a:"union" { print( a ); } + structOrUnionBody + ) + ; + +structOrUnionBody + : ( (ID LCURLY) => i1:ID lc1:LCURLY { print( i1 ); print ( "{" ); tabs++; } + ( structDeclarationList )? + rc1:RCURLY { tabs--; print( rc1 ); } + | lc2:LCURLY { print( lc2 ); tabs++; } + ( structDeclarationList )? + rc2:RCURLY { tabs--; print( rc2 ); } + | i2:ID { print( i2 ); } + ) + ; + +structDeclarationList + : ( structDeclaration { print( ";" ); } + )+ + ; + + +structDeclaration + : specifierQualifierList structDeclaratorList + ; + + +specifierQualifierList + : ( + typeSpecifier + | typeQualifier + )+ + ; + + +structDeclaratorList + : structDeclarator + ( { print(","); } structDeclarator )* + ; + + +structDeclarator + : + #( NStructDeclarator + ( declarator )? + ( c:COLON { print( c ); } expr )? + ( attributeDecl )* + ) + ; + + +enumSpecifier + : #( a:"enum" { print( a ); } + ( i:ID { print( i ); } )? + ( lc:LCURLY { print( lc ); tabs++; } + enumList + rc:RCURLY { tabs--; print( rc ); } + )? + ) + ; + + +enumList + : + enumerator ( {print(",");} enumerator)* + ; + + +enumerator + : i:ID { print( i ); } + ( b:ASSIGN { print( b ); } + expr + )? + ; + + +attributeDecl: + #( a:"__attribute" { print( a ); } + (b:. { print( b ); } )* + ) + | #( n:NAsmAttribute { print( n ); } + lp:LPAREN { print( lp ); } + expr { print( ")" ); } + rp:RPAREN { print( rp ); } + ) + ; + +initDeclList + : initDecl + ( { print( "," ); } initDecl )* + ; + + +initDecl + { String declName = ""; } + : #(NInitDecl + declarator + ( attributeDecl )* + ( a:ASSIGN { print( a ); } + initializer + | b:COLON { print( b ); } + expr + )? + ) + ; + + +pointerGroup + : #( NPointerGroup + ( a:STAR { print( a ); } + ( typeQualifier )* + )+ + ) + ; + + + +idList + : i:ID { print( i ); } + ( c:COMMA { print( c ); } + id:ID { print( id ); } + )* + ; + + + +initializer + : #( NInitializer (initializerElementLabel)? expr ) + | lcurlyInitializer + ; + +initializerElementLabel + : #( NInitializerElementLabel + ( + ( l:LBRACKET { print( l ); } + expr + r:RBRACKET { print( r ); } + (a1:ASSIGN { print( a1 ); } )? + ) + | i1:ID c:COLON { print( i1 ); print( c ); } + | d:DOT i2:ID a2:ASSIGN { print( d ); print( i2 ); print( a2 ); } + ) + ) + ; + +lcurlyInitializer + : #(n:NLcurlyInitializer { print( n ); tabs++; } + initializerList + rc:RCURLY { tabs--; print( rc ); } + ) + ; + +initializerList + : ( i:initializer { commaSep( i ); } + )* + ; + + +declarator + : #( NDeclarator + ( pointerGroup )? + + ( id:ID { print( id ); } + | lp:LPAREN { print( lp ); } declarator rp:RPAREN { print( rp ); } + ) + + ( #( n:NParameterTypeList { print( n ); } + ( + parameterTypeList + | (idList)? + ) + r:RPAREN { print( r ); } + ) + | lb:LBRACKET { print( lb );} ( expr )? rb:RBRACKET { print( rb ); } + )* + ) + ; + + + +parameterTypeList + : ( parameterDeclaration + ( c:COMMA { print( c ); } + | s:SEMI { print( s ); } + )? + )+ + ( v:VARARGS { print( v ); } )? + ; + + + +parameterDeclaration + : #( NParameterDeclaration + declSpecifiers + (declarator | nonemptyAbstractDeclarator)? + ) + ; + + +functionDef + : #( NFunctionDef + ( functionDeclSpecifiers)? + declarator + (declaration + | v:VARARGS { print( v ); } + )* + compoundStatement + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + +functionDeclSpecifiers + : + ( functionStorageClassSpecifier + | typeQualifier + | typeSpecifier + )+ + ; + +declarationList + : + ( //ANTLR doesn't know that declarationList properly eats all the declarations + //so it warns about the ambiguity + options { + warnWhenFollowAmbig = false; + } : + localLabelDecl + | declaration + )+ + ; + +localLabelDecl + : #(a:"__label__" { print( a ); } + ( i:ID { commaSep( i ); } + )+ + { print( ";" ); } + ) + ; + + + +compoundStatement + : #( cs:NCompoundStatement { print( cs ); tabs++; } + ( declarationList + | functionDef + )* + ( statementList )? + rc:RCURLY { tabs--; print( rc ); } + ) + + ; + +statementList + : ( statement )+ + ; + +statement + : statementBody + ; + +statementBody + : s:SEMI { print( s ); } + + | compoundStatement // Group of statements + + | #(NStatementExpr + expr { print( ";" ); } + ) // Expressions + +// Iteration statements: + + | #( w:"while" { print( w ); print( "(" ); } + expr { print( ")" ); } + statement ) + + | #( d:"do" { print( d ); } + statement + { print( " while ( " ); } + expr + { print( " );" ); } + ) + + | #( f:"for" { print( f ); print( "(" ); } + expr { print( ";" ); } + expr { print( ";" ); } + expr { print( ")" ); } + statement + ) + + +// Jump statements: + + | #( g:"goto" { print( g );} + expr { print( ";" ); } + ) + | c:"continue" { print( c ); print( ";" );} + | b:"break" { print( b ); print( ";" );} + | #( r:"return" { print( r ); } + ( expr )? + { print( ";" ); } + ) + + +// Labeled statements: + | #( NLabel + ni:ID { print( ni ); print( ":" ); } + ( statement )? + ) + + | #( + ca:"case" { print( ca ); } + expr { print( ":" ); } + (statement)? + ) + + | #( + de:"default" { print( de ); print( ":" ); } + (statement)? + ) + + + +// Selection statements: + + | #( i:"if" { print( i ); print( "(" ); } + expr { print( ")" ); } + statement + ( e:"else" { print( e ); } + statement + )? + ) + | #( sw:"switch" { print( sw ); print( "(" ); } + expr { print( ")" ); } + statement + ) + + + + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + + + + +expr + : + binaryExpr + | conditionalExpr + | castExpr + | unaryExpr + | postfixExpr + | primaryExpr + | emptyExpr + | compoundStatementExpr + | initializer + | rangeExpr + | gnuAsmExpr + ; + +emptyExpr + : NEmptyExpression + ; + +compoundStatementExpr + : #(l:LPAREN { print( l ); } + compoundStatement + r:RPAREN { print( r ); } + ) + ; + +rangeExpr + : #(NRangeExpr expr v:VARARGS{ print( v ); } expr) + ; + +gnuAsmExpr + : #(n:NGnuAsmExpr { print( n ); } + (v:"volatile" { print( v ); } )? + lp:LPAREN { print( lp ); } + stringConst + ( options { warnWhenFollowAmbig = false; }: + c1:COLON { print( c1 );} + (strOptExprPair + ( c2:COMMA { print( c2 ); } strOptExprPair)* + )? + ( options { warnWhenFollowAmbig = false; }: + c3:COLON { print( c3 ); } + (strOptExprPair + ( c4:COMMA { print( c4 ); } strOptExprPair)* + )? + )? + )? + ( c5:COLON { print( c5 ); } + stringConst + ( c6:COMMA { print( c6 ); } + stringConst + )* + )? + rp:RPAREN { print( rp ); } + ) + ; + +strOptExprPair + : stringConst + ( + l:LPAREN { print( l ); } + expr + r:RPAREN { print( r ); } + )? + ; + +binaryOperator + : ASSIGN + | DIV_ASSIGN + | PLUS_ASSIGN + | MINUS_ASSIGN + | STAR_ASSIGN + | MOD_ASSIGN + | RSHIFT_ASSIGN + | LSHIFT_ASSIGN + | BAND_ASSIGN + | BOR_ASSIGN + | BXOR_ASSIGN + | LOR + | LAND + | BOR + | BXOR + | BAND + | EQUAL + | NOT_EQUAL + | LT + | LTE + | GT + | GTE + | LSHIFT + | RSHIFT + | PLUS + | MINUS + | STAR + | DIV + | MOD + | NCommaExpr + ; + +binaryExpr + : b:binaryOperator + // no rules allowed as roots, so here I manually get + // the first and second children of the binary operator + // and then print them out in the right order + { TNode e1, e2; + e1 = (TNode) b.getFirstChild(); + e2 = (TNode) e1.getNextSibling(); + expr( e1 ); + print( b ); + expr( e2 ); + } + + ; + + +conditionalExpr + : #( q:QUESTION + expr { print( q ); } + ( expr )? + c:COLON { print( c ); } + expr + ) + ; + + +castExpr + : #( + c:NCast { print( c ); } + typeName + rp:RPAREN { print( rp ); } + expr + ) + ; + + +typeName + : specifierQualifierList (nonemptyAbstractDeclarator)? + ; + +nonemptyAbstractDeclarator + : #( NNonemptyAbstractDeclarator + ( pointerGroup + ( (lp1:LPAREN { print( lp1 ); } + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + rp1:RPAREN { print( rp1 ); } + ) + | ( + lb1:LBRACKET { print( lb1 ); } + (expr)? + rb1:RBRACKET { print( rb1 ); } + ) + )* + + | ( (lp2:LPAREN { print( lp2 ); } + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + rp2:RPAREN { print( rp2 ); } + ) + | ( + lb2:LBRACKET { print( lb2 ); } + (expr)? + rb2:RBRACKET { print( rb2 ); } + ) + )+ + ) + ) + ; + + + +unaryExpr + : #( i:INC { print( i ); } expr ) + | #( d:DEC { print( d ); } expr ) + | #( NUnaryExpr u:unaryOperator { print( u ); } expr) + | #( s:"sizeof" { print( s ); } + ( ( LPAREN typeName )=> + lps:LPAREN { print( lps ); } + typeName + rps:RPAREN { print( rps ); } + | expr + ) + ) + | #( a:"__alignof" { print( a ); } + ( ( LPAREN typeName )=> + lpa:LPAREN { print( lpa ); } + typeName + rpa:RPAREN { print( rpa ); } + | expr + ) + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + unaryOperator + : BAND + | STAR + | PLUS + | MINUS + | BNOT + | LNOT + | LAND + | "__real" + | "__imag" + ; + + +postfixExpr + : #( NPostfixExpr + primaryExpr + ( a:PTR b:ID { print( a ); print( b ); } + | c:DOT d:ID { print( c ); print( d ); } + | #( n:NFunctionCallArgs { print( n ); } + (argExprList)? + rp:RPAREN { print( rp ); } + ) + | lb:LBRACKET { print( lb ); } + expr + rb:RBRACKET { print( rb ); } + | f:INC { print( f ); } + | g:DEC { print( g ); } + )+ + ) + ; + + + +primaryExpr + : i:ID { print( i ); } + | n:Number { print( n ); } + | charConst + | stringConst + +// JTC: +// ID should catch the enumerator +// leaving it in gives ambiguous err +// | enumerator + + | #( eg:NExpressionGroup { print( eg ); } + expr { print( ")" ); } + ) + ; + + + +argExprList + : expr ( {print( "," );} expr )* + ; + + + +protected +charConst + : c:CharLiteral { print( c ); } + ; + + +protected +stringConst + : #( NStringSeq + ( + s:StringLiteral { print( s ); } + )+ + ) + ; + + +protected +intConst + : IntOctalConst + | LongOctalConst + | UnsignedOctalConst + | IntIntConst + | LongIntConst + | UnsignedIntConst + | IntHexConst + | LongHexConst + | UnsignedHexConst + ; + + +protected +floatConst + : FloatDoubleConst + | DoubleDoubleConst + | LongDoubleConst + ; + + + + + + + + + + diff --git a/src/java/com/jogamp/gluegen/cgram/GnuCParser.g b/src/java/com/jogamp/gluegen/cgram/GnuCParser.g new file mode 100644 index 0000000..f795702 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/GnuCParser.g @@ -0,0 +1,871 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Copyright (c) Non, Inc. 1998 -- All Rights Reserved + +PROJECT: C Compiler +MODULE: GnuCParser +FILE: GnuCParser.g + +AUTHOR: Monty Zukowski ([email protected]) April 28, 1998 + +DESCRIPTION: + This is a grammar for the GNU C compiler. It is a + grammar subclass of StdCParser, overriding only those + rules which are different from Standard C. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + + +header { + package com.jogamp.gluegen.cgram; + + import java.io.*; + + import antlr.CommonAST; + import antlr.DumpASTVisitor; +} + + +class GnuCParser extends StdCParser; + +options + { + k = 2; + exportVocab = GNUC; + buildAST = true; + ASTLabelType = "TNode"; + + // Copied following options from java grammar. + codeGenMakeSwitchThreshold = 2; + codeGenBitsetTestThreshold = 3; + } + + +{ + // Suppport C++-style single-line comments? + public static boolean CPPComments = true; + + // access to symbol table + public CSymbolTable symbolTable = new CSymbolTable(); + + // source for names to unnamed scopes + protected int unnamedScopeCounter = 0; + + public boolean isTypedefName(String name) { + boolean returnValue = false; + TNode node = symbolTable.lookupNameInCurrentScope(name); + for (; node != null; node = (TNode) node.getNextSibling() ) { + if(node.getType() == LITERAL_typedef) { + returnValue = true; + break; + } + } + return returnValue; + } + + + public String getAScopeName() { + return "" + (unnamedScopeCounter++); + } + + public void pushScope(String scopeName) { + symbolTable.pushScope(scopeName); + } + + public void popScope() { + symbolTable.popScope(); + } + + int traceDepth = 0; + public void reportError(RecognitionException ex) { + try { + System.err.println("ANTLR Parsing Error: "+ex + " token name:" + tokenNames[LA(1)]); + ex.printStackTrace(System.err); + } + catch (TokenStreamException e) { + System.err.println("ANTLR Parsing Error: "+ex); + ex.printStackTrace(System.err); + } + } + public void reportError(String s) { + System.err.println("ANTLR Parsing Error from String: " + s); + } + public void reportWarning(String s) { + System.err.println("ANTLR Parsing Warning from String: " + s); + } + public void match(int t) throws MismatchedTokenException { + boolean debugging = false; + + if ( debugging ) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("Match("+tokenNames[t]+") with LA(1)="+ + tokenNames[LA(1)] + ((inputState.guessing>0)?" [inputState.guessing "+ inputState.guessing + "]":"")); + } + catch (TokenStreamException e) { + System.out.println("Match("+tokenNames[t]+") " + ((inputState.guessing>0)?" [inputState.guessing "+ inputState.guessing + "]":"")); + + } + + } + try { + if ( LA(1)!=t ) { + if ( debugging ){ + for (int x=0; x<traceDepth; x++) System.out.print(" "); + System.out.println("token mismatch: "+tokenNames[LA(1)] + + "!="+tokenNames[t]); + } + throw new MismatchedTokenException(tokenNames, LT(1), t, false, getFilename()); + + } else { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } + } + catch (TokenStreamException e) { + } + + } + public void traceIn(String rname) { + traceDepth += 1; + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("> "+rname+"; LA(1)==("+ tokenNames[LT(1).getType()] + + ") " + LT(1).getText() + " [inputState.guessing "+ inputState.guessing + "]"); + } + catch (TokenStreamException e) { + } + } + public void traceOut(String rname) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("< "+rname+"; LA(1)==("+ tokenNames[LT(1).getType()] + + ") "+LT(1).getText() + " [inputState.guessing "+ inputState.guessing + "]"); + } + catch (TokenStreamException e) { + } + traceDepth -= 1; + } + +} + + +translationUnit + : ( externalList )? /* Empty source files are allowed. */ + ; +asm_expr + : "asm"^ + ("volatile")? LCURLY expr RCURLY ( SEMI )+ + ; + +idList + : ID ( options{warnWhenFollowAmbig=false;}: COMMA ID )* + ; + +externalDef + : ( "typedef" | declaration )=> declaration + | ( functionPrefix )=> functionDef + | typelessDeclaration + | asm_expr + | SEMI + ; + +/* these two are here because GCC allows "cat = 13;" as a valid program! */ +functionPrefix + { String declName; } + : ( (functionDeclSpecifiers)=> ds:functionDeclSpecifiers + | //epsilon + ) + declName = d:declarator[true] + ( declaration )* (VARARGS)? ( SEMI )* + LCURLY + ; + +typelessDeclaration + { AST typeMissing = #[NTypeMissing]; } + : initDeclList[typeMissing] SEMI { ## = #( #[NTypeMissing], ##); } + ; + +initializer + : ( ( ( (initializerElementLabel)=> initializerElementLabel )? + ( assignExpr | lcurlyInitializer ) { ## = #( #[NInitializer], ## ); } + ) + | lcurlyInitializer + ) + ; + +// GCC allows more specific initializers +initializerElementLabel + : ( ( LBRACKET ((constExpr VARARGS)=> rangeExpr | constExpr) RBRACKET (ASSIGN)? ) + | ID COLON + | DOT ID ASSIGN + ) + { ## = #( #[NInitializerElementLabel], ##) ; } + ; + +// GCC allows empty initializer lists +lcurlyInitializer + : + LCURLY^ (initializerList ( COMMA! )? )? RCURLY + { ##.setType( NLcurlyInitializer ); } + ; + +initializerList + : initializer ( options{warnWhenFollowAmbig=false;}:COMMA! initializer )* + ; + + +declarator[boolean isFunctionDefinition] returns [String declName] + { declName = ""; } + : + ( pointerGroup )? + + ( id:ID { declName = id.getText(); } + | LPAREN declName = declarator[false] RPAREN + ) + + ( declaratorParamaterList[isFunctionDefinition, declName] + | LBRACKET ( expr )? RBRACKET + )* + { ## = #( #[NDeclarator], ## ); } + ; + +declaratorParamaterList[boolean isFunctionDefinition, String declName] + : + LPAREN^ + { + if (isFunctionDefinition) { + pushScope(declName); + } + else { + pushScope("!"+declName); + } + } + ( + (declSpecifiers)=> parameterTypeList + | (idList)? + ) + { + popScope(); + } + ( COMMA! )? + RPAREN + { ##.setType(NParameterTypeList); } + ; + +parameterTypeList + : parameterDeclaration + ( options { + warnWhenFollowAmbig = false; + } : + ( COMMA | SEMI ) + parameterDeclaration + )* + ( ( COMMA | SEMI ) + VARARGS + )? + ; + + +declarationList + : ( options { // this loop properly aborts when + // it finds a non-typedefName ID MBZ + warnWhenFollowAmbig = false; + } : + + localLabelDeclaration + | ( declarationPredictor )=> declaration + )+ + ; +localLabelDeclaration + : ( //GNU note: any __label__ declarations must come before regular declarations. + "__label__"^ ID (options{warnWhenFollowAmbig=false;}: COMMA! ID)* ( COMMA! )? ( SEMI! )+ + ) + ; + + +declaration + { AST ds1 = null; } + : ds:declSpecifiers { ds1 = astFactory.dupList(#ds); } + ( + initDeclList[ds1] + )? + ( SEMI )+ + { ## = #( #[NDeclaration], ##); } + + ; + +functionStorageClassSpecifier + : "extern" + | "static" + | "inline" + ; + +typeSpecifier [int specCount] returns [int retSpecCount] + { retSpecCount = specCount + 1; } + : + ( "void" + | "char" + | "short" + | "int" + | "long" + | "float" + | "double" + | "signed" + | "unsigned" + | "__int32" + | "int32_t" + | "uint32_t" + | "__int64" + | "int64_t" + | "uint64_t" + | "ptrdiff_t" + | "size_t" + | structOrUnionSpecifier ( options{warnWhenFollowAmbig=false;}: attributeDecl )* + | enumSpecifier + | { specCount==0 }? typedefName + | "typeof"^ LPAREN + ( ( typeName )=> typeName + | expr + ) + RPAREN + | "__complex" + ) + ; + + +structOrUnionSpecifier + { String scopeName; } + : sou:structOrUnion! + ( ( ID LCURLY )=> i:ID l:LCURLY + { + scopeName = #sou.getText() + " " + #i.getText(); + #l.setText(scopeName); + pushScope(scopeName); + } + ( structDeclarationList )? + { popScope();} + RCURLY + | l1:LCURLY + { + scopeName = getAScopeName(); + #l1.setText(scopeName); + pushScope(scopeName); + } + ( structDeclarationList )? + { popScope(); } + RCURLY + | ID + ) + { + ## = #( #sou, ## ); + } + ; + + +structDeclaration + : specifierQualifierList structDeclaratorList ( COMMA! )? ( SEMI! )+ + ; + +structDeclaratorList + : structDeclarator ( options{warnWhenFollowAmbig=false;}: COMMA! structDeclarator )* + ; + +structDeclarator + : ( declarator[false] )? + ( COLON constExpr )? + ( attributeDecl )* + { ## = #( #[NStructDeclarator], ##); } + ; + + + +enumSpecifier + : "enum"^ + ( ( ID LCURLY )=> i:ID LCURLY enumList[i.getText()] RCURLY + | LCURLY enumList["anonymous"] RCURLY + | ID + ) + ; +enumList[String enumName] + : enumerator[enumName] ( options{warnWhenFollowAmbig=false;}: COMMA! enumerator[enumName] )* ( COMMA! )? + ; + + +initDeclList[AST declarationSpecifiers] + : initDecl[declarationSpecifiers] + ( options{warnWhenFollowAmbig=false;}: COMMA! initDecl[declarationSpecifiers] )* + ( COMMA! )? + ; + +initDecl[AST declarationSpecifiers] + { String declName = ""; } + : declName = d:declarator[false] + { AST ds1, d1; + ds1 = astFactory.dupList(declarationSpecifiers); + d1 = astFactory.dupList(#d); + symbolTable.add(declName, #(null, ds1, d1) ); + } + ( attributeDecl )* + ( ASSIGN initializer + | COLON expr + )? + { ## = #( #[NInitDecl], ## ); } + ; + +attributeDecl + : "__attribute"^ LPAREN LPAREN attributeList RPAREN RPAREN + | "asm"^ LPAREN stringConst RPAREN { ##.setType( NAsmAttribute ); } + ; + +attributeList + : attribute ( options{warnWhenFollowAmbig=false;}: COMMA attribute)* ( COMMA )? + ; + +attribute + : ( ~(LPAREN | RPAREN | COMMA) + | LPAREN attributeList RPAREN + )* + ; +compoundStatement[String scopeName] + : LCURLY^ + + { + pushScope(scopeName); + } + ( //this ambiguity is ok, declarationList and nestedFunctionDef end properly + options { + warnWhenFollowAmbig = false; + } : + ( "typedef" | "__label__" | declaration )=> declarationList + | (nestedFunctionDef)=> nestedFunctionDef + )* + ( statementList )? + { popScope(); } + RCURLY + { ##.setType( NCompoundStatement ); ##.setAttribute( "scopeName", scopeName ); } + ; + +nestedFunctionDef + { String declName; } + : ( "auto" )? //only for nested functions + ( (functionDeclSpecifiers)=> ds:functionDeclSpecifiers + )? + declName = d:declarator[false] + { + AST d2, ds2; + d2 = astFactory.dupList(#d); + ds2 = astFactory.dupList(#ds); + symbolTable.add(declName, #(null, ds2, d2)); + pushScope(declName); + } + ( declaration )* + { popScope(); } + compoundStatement[declName] + { ## = #( #[NFunctionDef], ## );} + ; + +statement + : SEMI // Empty statements + + | compoundStatement[getAScopeName()] // Group of statements + + | expr SEMI! { ## = #( #[NStatementExpr], ## );} // Expressions + +// Iteration statements: + + | "while"^ LPAREN! expr RPAREN! statement + | "do"^ statement "while"! LPAREN! expr RPAREN! SEMI! + |! "for" + LPAREN ( e1:expr )? SEMI ( e2:expr )? SEMI ( e3:expr )? RPAREN + s:statement + { + if ( #e1 == null) { #e1 = (TNode) #[ NEmptyExpression ]; } + if ( #e2 == null) { #e2 = (TNode) #[ NEmptyExpression ]; } + if ( #e3 == null) { #e3 = (TNode) #[ NEmptyExpression ]; } + ## = #( #[LITERAL_for, "for"], #e1, #e2, #e3, #s ); + } + + +// Jump statements: + + | "goto"^ expr SEMI! + | "continue" SEMI! + | "break" SEMI! + | "return"^ ( expr )? SEMI! + + + | ID COLON! (options {warnWhenFollowAmbig=false;}: statement)? { ## = #( #[NLabel], ## ); } +// GNU allows range expressions in case statements + | "case"^ ((constExpr VARARGS)=> rangeExpr | constExpr) COLON! ( options{warnWhenFollowAmbig=false;}:statement )? + | "default"^ COLON! ( options{warnWhenFollowAmbig=false;}: statement )? + +// Selection statements: + + | "if"^ + LPAREN! expr RPAREN! statement + ( //standard if-else ambiguity + options { + warnWhenFollowAmbig = false; + } : + "else" statement )? + | "switch"^ LPAREN! expr RPAREN! statement + ; + + + +conditionalExpr + : logicalOrExpr + ( QUESTION^ (expr)? COLON conditionalExpr )? + ; + +rangeExpr //used in initializers only + : constExpr VARARGS constExpr + { ## = #(#[NRangeExpr], ##); } + ; + +castExpr + : ( LPAREN typeName RPAREN )=> + LPAREN^ typeName RPAREN ( castExpr | lcurlyInitializer ) + { ##.setType(NCast); } + + | unaryExpr + ; +nonemptyAbstractDeclarator + : ( + pointerGroup + ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + ( COMMA! )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )* + + | ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + ( COMMA! )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )+ + ) + { ## = #( #[NNonemptyAbstractDeclarator], ## ); } + + ; + + + +unaryExpr + : postfixExpr + | INC^ castExpr + | DEC^ castExpr + | u:unaryOperator castExpr { ## = #( #[NUnaryExpr], ## ); } + + | "sizeof"^ + ( ( LPAREN typeName )=> LPAREN typeName RPAREN + | unaryExpr + ) + | "__alignof"^ + ( ( LPAREN typeName )=> LPAREN typeName RPAREN + | unaryExpr + ) + | gnuAsmExpr + ; + +unaryOperator + : BAND + | STAR + | PLUS + | MINUS + | BNOT //also stands for complex conjugation + | LNOT + | LAND //for label dereference (&&label) + | "__real" + | "__imag" + ; + +gnuAsmExpr + : "asm"^ ("volatile")? + LPAREN stringConst + ( options { warnWhenFollowAmbig = false; }: + COLON (strOptExprPair ( COMMA strOptExprPair)* )? + ( options { warnWhenFollowAmbig = false; }: + COLON (strOptExprPair ( COMMA strOptExprPair)* )? + )? + )? + ( COLON stringConst ( COMMA stringConst)* )? + RPAREN + { ##.setType(NGnuAsmExpr); } + ; + +//GCC requires the PARENs +strOptExprPair + : stringConst ( LPAREN expr RPAREN )? + ; + + +primaryExpr + : ID + | Number + | charConst + | stringConst +// JTC: +// ID should catch the enumerator +// leaving it in gives ambiguous err +// | enumerator + | (LPAREN LCURLY) => LPAREN^ compoundStatement[getAScopeName()] RPAREN + | LPAREN^ expr RPAREN { ##.setType(NExpressionGroup); } + ; + + +{ + import java.io.*; + import java.util.*; + import antlr.*; +} + +class GnuCLexer extends StdCLexer; +options + { + k = 3; + importVocab = GNUC; + testLiterals = false; + } +tokens { + LITERAL___extension__ = "__extension__"; +} + +{ + public void initialize(String src) + { + setOriginalSource(src); + initialize(); + } + + public void initialize() + { + literals.put(new ANTLRHashString("__alignof__", this), new Integer(LITERAL___alignof)); + literals.put(new ANTLRHashString("__asm", this), new Integer(LITERAL_asm)); + literals.put(new ANTLRHashString("__asm__", this), new Integer(LITERAL_asm)); + literals.put(new ANTLRHashString("__attribute__", this), new Integer(LITERAL___attribute)); + literals.put(new ANTLRHashString("__complex__", this), new Integer(LITERAL___complex)); + literals.put(new ANTLRHashString("__const", this), new Integer(LITERAL_const)); + literals.put(new ANTLRHashString("__const__", this), new Integer(LITERAL_const)); + literals.put(new ANTLRHashString("__imag__", this), new Integer(LITERAL___imag)); + literals.put(new ANTLRHashString("__inline", this), new Integer(LITERAL_inline)); + literals.put(new ANTLRHashString("__inline__", this), new Integer(LITERAL_inline)); + literals.put(new ANTLRHashString("__real__", this), new Integer(LITERAL___real)); + literals.put(new ANTLRHashString("__signed", this), new Integer(LITERAL_signed)); + literals.put(new ANTLRHashString("__signed__", this), new Integer(LITERAL_signed)); + literals.put(new ANTLRHashString("__typeof", this), new Integer(LITERAL_typeof)); + literals.put(new ANTLRHashString("__typeof__", this), new Integer(LITERAL_typeof)); + literals.put(new ANTLRHashString("__volatile", this), new Integer(LITERAL_volatile)); + literals.put(new ANTLRHashString("__volatile__", this), new Integer(LITERAL_volatile)); + } + + + LineObject lineObject = new LineObject(); + String originalSource = ""; + PreprocessorInfoChannel preprocessorInfoChannel = new PreprocessorInfoChannel(); + int tokenNumber = 0; + boolean countingTokens = true; + int deferredLineCount = 0; + List defines = new ArrayList(); + + public void setCountingTokens(boolean ct) + { + countingTokens = ct; + if ( countingTokens ) { + tokenNumber = 0; + } + else { + tokenNumber = 1; + } + } + + public void setOriginalSource(String src) + { + originalSource = src; + lineObject.setSource(src); + } + public void setSource(String src) + { + lineObject.setSource(src); + } + + public PreprocessorInfoChannel getPreprocessorInfoChannel() + { + return preprocessorInfoChannel; + } + + public void setPreprocessingDirective(String pre) + { + preprocessorInfoChannel.addLineForTokenNumber( pre, new Integer(tokenNumber) ); + } + + public void addDefine(String name, String value) + { + defines.add(new Define(name, value)); + } + + /** Returns a list of Define objects corresponding to the + preprocessor definitions seen during parsing. */ + public List getDefines() { + return defines; + } + + protected Token makeToken(int t) + { + if ( t != Token.SKIP && countingTokens) { + tokenNumber++; + } + CToken tok = (CToken) super.makeToken(t); + tok.setLine(lineObject.line); + tok.setSource(lineObject.source); + tok.setTokenNumber(tokenNumber); + + lineObject.line += deferredLineCount; + deferredLineCount = 0; + return tok; + } + + public void deferredNewline() { + deferredLineCount++; + } + + public void newline() { + lineObject.newline(); + } + + + + + + +} +Whitespace + : ( ( ' ' | '\t' | '\014') + | "\r\n" { newline(); } + | ( '\n' | '\r' ) { newline(); } + ) { _ttype = Token.SKIP; } + ; + + +protected +Escape + : '\\' + ( options{warnWhenFollowAmbig=false;}: + ~('0'..'7' | 'x') + | ('0'..'3') ( options{warnWhenFollowAmbig=false;}: Digit )* + | ('4'..'7') ( options{warnWhenFollowAmbig=false;}: Digit )* + | 'x' ( options{warnWhenFollowAmbig=false;}: Digit | 'a'..'f' | 'A'..'F' )+ + ) + ; + +protected IntSuffix + : 'L' + | 'l' + | 'U' + | 'u' + | 'I' + | 'i' + | 'J' + | 'j' + ; +protected NumberSuffix + : + IntSuffix + | 'F' + | 'f' + ; + +Number + : ( ('-')? ( Digit )+ ( '.' | 'e' | 'E' ) )=> ('-')? ( Digit )+ + ( '.' ( Digit )* ( Exponent )? + | Exponent + ) + ( NumberSuffix + )* + + | ( "..." )=> "..." { _ttype = VARARGS; } + + | '.' { _ttype = DOT; } + ( ( Digit )+ ( Exponent )? + { _ttype = Number; } + ( NumberSuffix + )* + )? + + | '0' ( '0'..'7' )* + ( NumberSuffix + )* + + | ('-')? '1'..'9' ( Digit )* + ( NumberSuffix + )* + + | '0' ( 'x' | 'X' ) ( 'a'..'f' | 'A'..'F' | Digit )+ + ( IntSuffix + )* + ; + +IDMEAT + : + i:ID { + + if ( i.getType() == LITERAL___extension__ ) { + $setType(Token.SKIP); + } + else { + $setType(i.getType()); + } + + } + ; + +protected ID + options + { + testLiterals = true; + } + : ( 'a'..'z' | 'A'..'Z' | '_' | '$') + ( 'a'..'z' | 'A'..'Z' | '_' | '$' | '0'..'9' )* + ; + +WideCharLiteral + : + 'L' CharLiteral + { $setType(CharLiteral); } + ; + + + +WideStringLiteral + : + 'L' StringLiteral + { $setType(StringLiteral); } + ; + +StringLiteral + : + '"' + ( ('\\' ~('\n'))=> Escape + | ( '\r' { newline(); } + | '\n' { + newline(); + } + | '\\' '\n' { + newline(); + } + ) + | ~( '"' | '\r' | '\n' | '\\' ) + )* + '"' + ; + + + + diff --git a/src/java/com/jogamp/gluegen/cgram/GnuCTreeParser.g b/src/java/com/jogamp/gluegen/cgram/GnuCTreeParser.g new file mode 100644 index 0000000..82792f3 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/GnuCTreeParser.g @@ -0,0 +1,852 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Copyright (c) Non, Inc. 1998 -- All Rights Reserved + +PROJECT: C Compiler +MODULE: GnuCTreeParser +FILE: GnuCTreeParser.g + +AUTHOR: Monty Zukowski ([email protected]) April 28, 1998 + +DESCRIPTION: + + This tree grammar is for a Gnu C AST. No actions in it, + subclass to do something useful. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + + +header { + package com.jogamp.gluegen.cgram; + + import java.io.*; + + import antlr.CommonAST; + import antlr.DumpASTVisitor; +} + + +class GnuCTreeParser extends TreeParser; + +options + { + importVocab = GNUC; + buildAST = false; + ASTLabelType = "TNode"; + + // Copied following options from java grammar. + codeGenMakeSwitchThreshold = 2; + codeGenBitsetTestThreshold = 3; + } + + +{ + int traceDepth = 0; + public void reportError(RecognitionException ex) { + if ( ex != null) { + System.err.println("ANTLR Tree Parsing RecognitionException Error: " + ex.getClass().getName() + " " + ex ); + ex.printStackTrace(System.err); + } + } + public void reportError(NoViableAltException ex) { + System.err.println("ANTLR Tree Parsing NoViableAltException Error: " + ex.toString()); + TNode.printTree( ex.node ); + ex.printStackTrace(System.err); + } + public void reportError(MismatchedTokenException ex) { + if ( ex != null) { + TNode.printTree( ex.node ); + System.err.println("ANTLR Tree Parsing MismatchedTokenException Error: " + ex ); + ex.printStackTrace(System.err); + } + } + public void reportError(String s) { + System.err.println("ANTLR Error from String: " + s); + } + public void reportWarning(String s) { + System.err.println("ANTLR Warning from String: " + s); + } + protected void match(AST t, int ttype) throws MismatchedTokenException { + //System.out.println("match("+ttype+"); cursor is "+t); + super.match(t, ttype); + } + public void match(AST t, BitSet b) throws MismatchedTokenException { + //System.out.println("match("+b+"); cursor is "+t); + super.match(t, b); + } + protected void matchNot(AST t, int ttype) throws MismatchedTokenException { + //System.out.println("matchNot("+ttype+"); cursor is "+t); + super.matchNot(t, ttype); + } + public void traceIn(String rname, AST t) { + traceDepth += 1; + for (int x=0; x<traceDepth; x++) System.out.print(" "); + super.traceIn(rname, t); + } + public void traceOut(String rname, AST t) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + super.traceOut(rname, t); + traceDepth -= 1; + } + + +} + +translationUnit options { + defaultErrorHandler=false; +} + : ( externalList )? + ; + +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + +externalList + : ( externalDef )+ + ; + + +externalDef + : declaration + | functionDef + | asm_expr + | SEMI + | typelessDeclaration + ; + +typelessDeclaration + : #(NTypeMissing initDeclList SEMI) + ; + + + +asm_expr + : #( "asm" ( "volatile" )? LCURLY expr RCURLY ( SEMI )+ ) + ; + + +declaration + : #( NDeclaration + declSpecifiers + ( + initDeclList + )? + ( SEMI )+ + ) + ; + + +declSpecifiers + : ( storageClassSpecifier + | typeQualifier + | typeSpecifier + )+ + ; + +storageClassSpecifier + : "auto" + | "register" + | "typedef" + | functionStorageClassSpecifier + ; + + +functionStorageClassSpecifier + : "extern" + | "static" + | "inline" + ; + + +typeQualifier + : "const" + | "volatile" + ; + + +typeSpecifier + : "void" + | "char" + | "short" + | "int" + | "long" + | "float" + | "double" + | "signed" + | "unsigned" + | structSpecifier ( attributeDecl )* + | unionSpecifier ( attributeDecl )* + | enumSpecifier + | typedefName + | #("typeof" LPAREN + ( (typeName )=> typeName + | expr + ) + RPAREN + ) + | "__complex" + ; + + +typedefName + : #(NTypedefName ID) + ; + + +structSpecifier + : #( "struct" structOrUnionBody ) + ; + +unionSpecifier + : #( "union" structOrUnionBody ) + ; + +structOrUnionBody + : ( (ID LCURLY) => ID LCURLY + ( structDeclarationList )? + RCURLY + | LCURLY + ( structDeclarationList )? + RCURLY + | ID + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + +structDeclarationList + : ( structDeclaration )+ + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + +structDeclaration + : specifierQualifierList structDeclaratorList + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + +specifierQualifierList + : ( + typeSpecifier + | typeQualifier + )+ + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + +structDeclaratorList + : ( structDeclarator )+ + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + +structDeclarator + : + #( NStructDeclarator + ( declarator )? + ( COLON expr )? + ( attributeDecl )* + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + + +enumSpecifier + : #( "enum" + ( ID )? + ( LCURLY enumList RCURLY )? + ) + ; + + +enumList + : ( enumerator )+ + ; + + +enumerator + : ID ( ASSIGN expr )? + ; + + + +attributeDecl: + #( "__attribute" (.)* ) + | #( NAsmAttribute LPAREN expr RPAREN ) + ; + +initDeclList + : ( initDecl )+ + ; + + +initDecl + { String declName = ""; } + : #( NInitDecl + declarator + ( attributeDecl )* + ( ASSIGN initializer + | COLON expr + )? + ) + ; + + +pointerGroup + : #( NPointerGroup ( STAR ( typeQualifier )* )+ ) + ; + + + +idList + : ID ( COMMA ID )* + ; + + + +initializer + : #( NInitializer (initializerElementLabel)? expr ) + | lcurlyInitializer + ; + +initializerElementLabel + : #( NInitializerElementLabel + ( + ( LBRACKET expr RBRACKET (ASSIGN)? ) + | ID COLON + | DOT ID ASSIGN + ) + ) + ; + +lcurlyInitializer + : #( NLcurlyInitializer + initializerList + RCURLY + ) + ; + +initializerList + : ( initializer )* + ; + + +declarator + : #( NDeclarator + ( pointerGroup )? + + ( id:ID + | LPAREN declarator RPAREN + ) + + ( #( NParameterTypeList + ( + parameterTypeList + | (idList)? + ) + RPAREN + ) + | LBRACKET ( expr )? RBRACKET + )* + ) + ; + + + +parameterTypeList + : ( parameterDeclaration ( COMMA | SEMI )? )+ ( VARARGS )? + ; + + + +parameterDeclaration + : #( NParameterDeclaration + declSpecifiers + (declarator | nonemptyAbstractDeclarator)? + ) + ; + + +functionDef + : #( NFunctionDef + ( functionDeclSpecifiers)? + declarator + (declaration | VARARGS)* + compoundStatement + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + +functionDeclSpecifiers + : + ( functionStorageClassSpecifier + | typeQualifier + | typeSpecifier + )+ + ; + +declarationList + : + ( //ANTLR doesn't know that declarationList properly eats all the declarations + //so it warns about the ambiguity + options { + warnWhenFollowAmbig = false; + } : + localLabelDecl + | declaration + )+ + ; + +localLabelDecl + : #("__label__" (ID)+ ) + ; + + + +compoundStatement + : #( NCompoundStatement + ( declarationList + | functionDef + )* + ( statementList )? + RCURLY + ) + ; + +statementList + : ( statement )+ + ; + +statement + : statementBody + ; + +statementBody + : SEMI // Empty statements + + | compoundStatement // Group of statements + + | #(NStatementExpr expr) // Expressions + +// Iteration statements: + + | #( "while" expr statement ) + | #( "do" statement expr ) + | #( "for" + expr expr expr + statement + ) + + +// Jump statements: + + | #( "goto" expr ) + | "continue" + | "break" + | #( "return" ( expr )? ) + + +// Labeled statements: + | #( NLabel ID (statement)? ) + | #( "case" expr (statement)? ) + | #( "default" (statement)? ) + + + +// Selection statements: + + | #( "if" + expr statement + ( "else" statement )? + ) + | #( "switch" expr statement ) + + + + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + + + + + +expr + : assignExpr + | conditionalExpr + | logicalOrExpr + | logicalAndExpr + | inclusiveOrExpr + | exclusiveOrExpr + | bitAndExpr + | equalityExpr + | relationalExpr + | shiftExpr + | additiveExpr + | multExpr + | castExpr + | unaryExpr + | postfixExpr + | primaryExpr + | commaExpr + | emptyExpr + | compoundStatementExpr + | initializer + | rangeExpr + | gnuAsmExpr + ; + +commaExpr + : #(NCommaExpr expr expr) + ; + +emptyExpr + : NEmptyExpression + ; + +compoundStatementExpr + : #(LPAREN compoundStatement RPAREN) + ; + +rangeExpr + : #(NRangeExpr expr VARARGS expr) + ; + +gnuAsmExpr + : #(NGnuAsmExpr + ("volatile")? + LPAREN stringConst + ( options { warnWhenFollowAmbig = false; }: + COLON (strOptExprPair ( COMMA strOptExprPair)* )? + ( options { warnWhenFollowAmbig = false; }: + COLON (strOptExprPair ( COMMA strOptExprPair)* )? + )? + )? + ( COLON stringConst ( COMMA stringConst)* )? + RPAREN + ) + ; + +strOptExprPair + : stringConst ( LPAREN expr RPAREN )? + ; + +assignExpr + : #( ASSIGN expr expr) + | #( DIV_ASSIGN expr expr) + | #( PLUS_ASSIGN expr expr) + | #( MINUS_ASSIGN expr expr) + | #( STAR_ASSIGN expr expr) + | #( MOD_ASSIGN expr expr) + | #( RSHIFT_ASSIGN expr expr) + | #( LSHIFT_ASSIGN expr expr) + | #( BAND_ASSIGN expr expr) + | #( BOR_ASSIGN expr expr) + | #( BXOR_ASSIGN expr expr) + ; + + +conditionalExpr + : #( QUESTION expr (expr)? COLON expr ) + ; + + +logicalOrExpr + : #( LOR expr expr) + ; + + +logicalAndExpr + : #( LAND expr expr ) + ; + + +inclusiveOrExpr + : #( BOR expr expr ) + ; + + +exclusiveOrExpr + : #( BXOR expr expr ) + ; + + +bitAndExpr + : #( BAND expr expr ) + ; + + + +equalityExpr + : #( EQUAL expr expr) + | #( NOT_EQUAL expr expr) + ; + + +relationalExpr + : #( LT expr expr) + | #( LTE expr expr) + | #( GT expr expr) + | #( GTE expr expr) + ; + + + +shiftExpr + : #( LSHIFT expr expr) + | #( RSHIFT expr expr) + ; + + +additiveExpr + : #( PLUS expr expr) + | #( MINUS expr expr) + ; + + +multExpr + : #( STAR expr expr) + | #( DIV expr expr) + | #( MOD expr expr) + ; + + + +castExpr + : #( NCast typeName RPAREN expr) + ; + + +typeName + : specifierQualifierList (nonemptyAbstractDeclarator)? + ; + +nonemptyAbstractDeclarator + : #( NNonemptyAbstractDeclarator + ( pointerGroup + ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )* + + | ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )+ + ) + ) + ; + + + +unaryExpr + : #( INC expr ) + | #( DEC expr ) + | #( NUnaryExpr unaryOperator expr) + | #( "sizeof" + ( ( LPAREN typeName )=> LPAREN typeName RPAREN + | expr + ) + ) + | #( "__alignof" + ( ( LPAREN typeName )=> LPAREN typeName RPAREN + | expr + ) + ) + ; +/* +exception +catch [RecognitionException ex] + { + reportError(ex); + System.out.println("PROBLEM TREE:\n" + + _t.toStringList()); + if (_t!=null) {_t = _t.getNextSibling();} + } +*/ + + unaryOperator + : BAND + | STAR + | PLUS + | MINUS + | BNOT + | LNOT + | LAND + | "__real" + | "__imag" + ; + + +postfixExpr + : #( NPostfixExpr + primaryExpr + ( PTR ID + | DOT ID + | #( NFunctionCallArgs (argExprList)? RPAREN ) + | LBRACKET expr RBRACKET + | INC + | DEC + )+ + ) + ; + + + +primaryExpr + : ID + | Number + | charConst + | stringConst + +// JTC: +// ID should catch the enumerator +// leaving it in gives ambiguous err +// | enumerator + + | #( NExpressionGroup expr ) + ; + + + +argExprList + : ( expr )+ + ; + + + +protected +charConst + : CharLiteral + ; + + +protected +stringConst + : #(NStringSeq (StringLiteral)+) + ; + + +protected +intConst + : IntOctalConst + | LongOctalConst + | UnsignedOctalConst + | IntIntConst + | LongIntConst + | UnsignedIntConst + | IntHexConst + | LongHexConst + | UnsignedHexConst + ; + + +protected +floatConst + : FloatDoubleConst + | DoubleDoubleConst + | LongDoubleConst + ; + + + + + + + + + diff --git a/src/java/com/jogamp/gluegen/cgram/HeaderParser.g b/src/java/com/jogamp/gluegen/cgram/HeaderParser.g new file mode 100644 index 0000000..fa6455f --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/HeaderParser.g @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +header { + package com.jogamp.gluegen.cgram; + + import java.io.*; + import java.util.*; + + import antlr.CommonAST; + import com.jogamp.gluegen.cgram.types.*; +} + +class HeaderParser extends GnuCTreeParser; +options { + k = 1; +} + +{ + /** Name assigned to a anonymous EnumType (e.g., "enum { ... }"). */ + public static final String ANONYMOUS_ENUM_NAME = "<anonymous>"; + + boolean debug = false; + + public boolean getDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** Set the dictionary mapping typedef names to types for this + HeaderParser. Must be done before parsing. */ + public void setTypedefDictionary(TypeDictionary dict) { + this.typedefDictionary = dict; + } + + /** Returns the typedef dictionary this HeaderParser uses. */ + public TypeDictionary getTypedefDictionary() { + return typedefDictionary; + } + + /** Set the dictionary mapping struct names (i.e., the "foo" in + "struct foo { ... };") to types for this HeaderParser. Must be done + before parsing. */ + public void setStructDictionary(TypeDictionary dict) { + this.structDictionary = dict; + } + + /** Returns the struct name dictionary this HeaderParser uses. */ + public TypeDictionary getStructDictionary() { + return structDictionary; + } + + /** Get the canonicalization map, which is a regular HashMap + mapping Type to Type and which is used for looking up the unique + instances of e.g. pointer-to-structure types that have been typedefed + and therefore have names. */ + public Map getCanonMap() { + return canonMap; + } + + /** Pre-define the list of EnumTypes for this HeaderParser. Must be + done before parsing. */ + public void setEnums(List/*<EnumType>*/ enumTypes) { + // FIXME: Need to take the input set of EnumTypes, extract all + // the enumerates from each EnumType, and fill in the enumHash + // so that each enumerate maps to the enumType to which it + // belongs. + throw new RuntimeException("setEnums is Unimplemented!"); + } + + /** Returns the EnumTypes this HeaderParser processed. */ + public List/*<EnumType>*/ getEnums() { + return new ArrayList(enumHash.values()); + } + + /** Clears the list of functions this HeaderParser has parsed. + Useful when reusing the same HeaderParser for more than one + header file. */ + public void clearParsedFunctions() { + functions.clear(); + } + + /** Returns the list of FunctionSymbols this HeaderParser has parsed. */ + public List getParsedFunctions() { + return functions; + } + + private CompoundType lookupInStructDictionary(String typeName, + CompoundTypeKind kind, + int cvAttrs) { + CompoundType t = (CompoundType) structDictionary.get(typeName); + if (t == null) { + t = new CompoundType(null, null, kind, cvAttrs); + t.setStructName(typeName); + structDictionary.put(typeName, t); + } + return t; + } + + private Type lookupInTypedefDictionary(String typeName) { + Type t = typedefDictionary.get(typeName); + if (t == null) { + throw new RuntimeException("Undefined reference to typedef name " + typeName); + } + return t; + } + + static class ParameterDeclaration { + private String id; + private Type type; + + ParameterDeclaration(String id, Type type) { + this.id = id; + this.type = type; + } + String id() { return id; } + Type type() { return type; } + } + + // A box for a Type. Allows type to be passed down to be modified by recursive rules. + static class TypeBox { + private Type origType; + private Type type; + private boolean isTypedef; + + TypeBox(Type type) { + this(type, false); + } + + TypeBox(Type type, boolean isTypedef) { + this.origType = type; + this.isTypedef = isTypedef; + } + + Type type() { + if (type == null) { + return origType; + } + return type; + } + void setType(Type type) { + this.type = type; + } + void reset() { + type = null; + } + + boolean isTypedef() { return isTypedef; } + + // for easier debugging + public String toString() { + String tStr = "Type=NULL_REF"; + if (type == origType) { + tStr = "Type=ORIG_TYPE"; + } else if (type != null) { + tStr = "Type: name=\"" + type.getCVAttributesString() + " " + + type.getName() + "\"; signature=\"" + type + "\"; class " + + type.getClass().getName(); + } + String oStr = "OrigType=NULL_REF"; + if (origType != null) { + oStr = "OrigType: name=\"" + origType.getCVAttributesString() + " " + + origType.getName() + "\"; signature=\"" + origType + "\"; class " + + origType.getClass().getName(); + } + return "<["+tStr + "] [" + oStr + "] " + " isTypedef=" + isTypedef+">"; + } + } + + private String getTypeString(Type t) { + StringBuffer sb = new StringBuffer(); + sb.append("["); + sb.append(t); + sb.append(", size: "); + if(null!=t) { + SizeThunk st = t.getSize(); + if(null!=st) { + sb.append(st.getClass().getName()); + } else { + sb.append("undef"); + } + } + sb.append("]"); + return sb.toString(); + } + + private void debugPrintln(String msg) { + if(debug) { + System.err.println(msg); + } + } + + private void debugPrint(String msg) { + if(debug) { + System.err.print(msg); + } + } + + private boolean doDeclaration; // Used to only process function typedefs + private String declId; + private List parameters; + private TypeDictionary typedefDictionary; + private TypeDictionary structDictionary; + private List/*<FunctionSymbol>*/ functions = new ArrayList(); + // hash from name of an enumerated value to the EnumType to which it belongs + private HashMap/*<String,EnumType>*/ enumHash = new HashMap(); + + // Storage class specifiers + private static final int AUTO = 1 << 0; + private static final int REGISTER = 1 << 1; + private static final int TYPEDEF = 1 << 2; + // Function storage class specifiers + private static final int EXTERN = 1 << 3; + private static final int STATIC = 1 << 4; + private static final int INLINE = 1 << 5; + // Type qualifiers + private static final int CONST = 1 << 6; + private static final int VOLATILE = 1 << 7; + private static final int SIGNED = 1 << 8; + private static final int UNSIGNED = 1 << 9; + + private void initDeclaration() { + doDeclaration = false; + declId = null; + } + + private void doDeclaration() { + doDeclaration = true; + } + + private void processDeclaration(Type returnType) { + if (doDeclaration) { + FunctionSymbol sym = new FunctionSymbol(declId, new FunctionType(null, null, returnType, 0)); + if (parameters != null) { // handle funcs w/ empty parameter lists (e.g., "foo()") + for (Iterator iter = parameters.iterator(); iter.hasNext(); ) { + ParameterDeclaration pd = (ParameterDeclaration) iter.next(); + sym.addArgument(pd.type(), pd.id()); + } + } + functions.add(sym); + } + } + + private int attrs2CVAttrs(int attrs) { + int cvAttrs = 0; + if ((attrs & CONST) != 0) { + cvAttrs |= CVAttributes.CONST; + } + if ((attrs & VOLATILE) != 0) { + cvAttrs |= CVAttributes.VOLATILE; + } + return cvAttrs; + } + + /** Helper routine which handles creating a pointer or array type + for [] expressions */ + private void handleArrayExpr(TypeBox tb, AST t) { + if (t != null) { + try { + // FIXME: this doesn't take into account struct alignment, which may be necessary + // See also FIXMEs in ArrayType.java + int len = parseIntConstExpr(t); + tb.setType(canonicalize(new ArrayType(tb.type(), SizeThunk.mul(SizeThunk.constant(len), tb.type().getSize()), len, 0))); + return; + } catch (RecognitionException e) { + // Fall through + } + } + tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, + tb.type(), + 0))); + } + + private int parseIntConstExpr(AST t) throws RecognitionException { + return intConstExpr(t); + } + + /** Utility function: creates a new EnumType with the given name, or + returns an existing one if it has already been created. */ + private EnumType getEnumType(String enumTypeName) { + EnumType enumType = null; + Iterator it = enumHash.values().iterator(); + while (it.hasNext()) { + EnumType potentialMatch = (EnumType)it.next(); + if (potentialMatch.getName().equals(enumTypeName)) { + enumType = potentialMatch; + break; + } + } + + if (enumType == null) { + // This isn't quite correct. In theory the enum should expand to + // the size of the largest element, so if there were a long long + // entry the enum should expand to e.g. int64. However, using + // "long" here (which is what used to be the case) was + // definitely incorrect and caused problems. + enumType = new EnumType(enumTypeName, SizeThunk.INT); + } + + return enumType; + } + + // Map used to canonicalize types. For example, we may typedef + // struct foo { ... } *pfoo; subsequent references to struct foo* should + // point to the same PointerType object that had its name set to "pfoo". + private Map canonMap = new HashMap(); + private Type canonicalize(Type t) { + Type res = (Type) canonMap.get(t); + if (res != null) { + return res; + } + canonMap.put(t, t); + return t; + } +} + +declarator[TypeBox tb] returns [String s] { + initDeclaration(); + s = null; + List params = null; + String funcPointerName = null; + TypeBox dummyTypeBox = null; +} + : #( NDeclarator + ( pointerGroup[tb] )? + + ( id:ID { s = id.getText(); } + | LPAREN funcPointerName = declarator[dummyTypeBox] RPAREN + ) + + ( #( NParameterTypeList + ( + params = parameterTypeList + | (idList)? + ) + RPAREN + ) { + if (id != null) { + declId = id.getText(); + parameters = params; // FIXME: Ken, why are we setting this class member here? + doDeclaration(); + } else if ( funcPointerName != null ) { + /* TypeBox becomes function pointer in this case */ + FunctionType ft = new FunctionType(null, null, tb.type(), 0); + if (params == null) { + // If the function pointer has no declared parameters, it's a + // void function. I'm not sure if the parameter name is + // ever referenced anywhere when the type is VoidType, so + // just in case I'll set it to a comment string so it will + // still compile if written out to code anywhere. + ft.addArgument(new VoidType(0), "/*unnamed-void*/"); + } else { + for (Iterator iter = params.iterator(); iter.hasNext(); ) { + ParameterDeclaration pd = (ParameterDeclaration) iter.next(); + ft.addArgument(pd.type(), pd.id()); + } + } + tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, + ft, + 0))); + s = funcPointerName; + } + } + | LBRACKET ( e:expr )? RBRACKET { handleArrayExpr(tb, e); } + )* + ) + ; + +typelessDeclaration { + TypeBox tb = null; +} + : #(NTypeMissing initDeclList[tb] SEMI) + ; + +declaration { + TypeBox tb = null; +} + : #( NDeclaration + tb = declSpecifiers + ( + initDeclList[tb] + )? + ( SEMI )+ + ) { processDeclaration(tb.type()); } + ; + +parameterTypeList returns [List l] { l = new ArrayList(); ParameterDeclaration decl = null; } + : ( decl = parameterDeclaration { if (decl != null) l.add(decl); } ( COMMA | SEMI )? )+ ( VARARGS )? + ; + +parameterDeclaration returns [ParameterDeclaration pd] { + Type t = null; + String decl = null; + pd = null; + TypeBox tb = null; +} + : #( NParameterDeclaration + tb = declSpecifiers + (decl = declarator[tb] | nonemptyAbstractDeclarator[tb])? + ) { pd = new ParameterDeclaration(decl, tb.type()); } + ; + +functionDef { + TypeBox tb = null; +} + : #( NFunctionDef + ( functionDeclSpecifiers)? + declarator[tb] + (declaration | VARARGS)* + compoundStatement + ) + ; + +declSpecifiers returns [TypeBox tb] { + tb = null; + Type t = null; + int x = 0; + int y = 0; +} + : ( y = storageClassSpecifier { x |= y; } + | y = typeQualifier { x |= y; } + | t = typeSpecifier[x] + )+ +{ + if (t == null && + (x & (SIGNED | UNSIGNED)) != 0) { + t = new IntType("int", SizeThunk.INT, ((x & UNSIGNED) != 0), attrs2CVAttrs(x)); + } + tb = new TypeBox(t, ((x & TYPEDEF) != 0)); +} + ; + +storageClassSpecifier returns [int x] { x = 0; } + : "auto" { x |= AUTO; } + | "register" { x |= REGISTER; } + | "typedef" { x |= TYPEDEF; } + | x = functionStorageClassSpecifier + ; + + +functionStorageClassSpecifier returns [int x] { x = 0; } + : "extern" { x |= EXTERN; } + | "static" { x |= STATIC; } + | "inline" { x |= INLINE; } + ; + + +typeQualifier returns [int x] { x = 0; } + : "const" { x |= CONST; } + | "volatile" { x |= VOLATILE; } + | "signed" { x |= SIGNED; } + | "unsigned" { x |= UNSIGNED; } + ; + +typeSpecifier[int attributes] returns [Type t] { + t = null; + int cvAttrs = attrs2CVAttrs(attributes); + boolean unsigned = ((attributes & UNSIGNED) != 0); +} + : "void" { t = new VoidType(cvAttrs); } + | "char" { t = new IntType("char" , SizeThunk.CHAR, unsigned, cvAttrs); } + | "short" { t = new IntType("short", SizeThunk.SHORT, unsigned, cvAttrs); } + | "int" { t = new IntType("int" , SizeThunk.INT, unsigned, cvAttrs); } + | "long" { t = new IntType("long" , SizeThunk.LONG, unsigned, cvAttrs); } + | "float" { t = new FloatType("float", SizeThunk.FLOAT, cvAttrs); } + | "double" { t = new DoubleType("double", SizeThunk.DOUBLE, cvAttrs); } + | "__int32" { t = new IntType("__int32", SizeThunk.INT, unsigned, cvAttrs); } + | "int32_t" { t = new IntType("int32_t", SizeThunk.INT, false, cvAttrs); /* TS: always signed */ } + | "uint32_t" { t = new IntType("uint32_t", SizeThunk.INT, true, cvAttrs); /* TS: always unsigned */ } + | "__int64" { t = new IntType("__int64", SizeThunk.INT64, unsigned, cvAttrs); } + | "int64_t" { t = new IntType("int64_t", SizeThunk.INT64, false, cvAttrs); /* TS: always signed */ } + | "uint64_t" { t = new IntType("uint64_t", SizeThunk.INT64, true, cvAttrs); /* TS: always unsigned */ } + | "ptrdiff_t" { t = new IntType("ptrdiff_t", SizeThunk.POINTER, false, cvAttrs); /* TS: always signed */ } + | "size_t" { t = new IntType("size_t", SizeThunk.POINTER, true, cvAttrs); /* TS: always unsigned */ } + | t = structSpecifier[cvAttrs] ( attributeDecl )* + | t = unionSpecifier [cvAttrs] ( attributeDecl )* + | t = enumSpecifier [cvAttrs] + | t = typedefName [cvAttrs] + | #("typeof" LPAREN + ( (typeName )=> typeName + | expr + ) + RPAREN + ) + | "__complex" + ; + +typedefName[int cvAttrs] returns [Type t] { t = null; } + : #(NTypedefName id : ID) + { + Type tdict = lookupInTypedefDictionary(id.getText()); + t = canonicalize(tdict.getCVVariant(cvAttrs)); + debugPrintln("Adding typedef canon : [" + id.getText() + "] -> [" + tdict + "] -> "+getTypeString(t)); + } + ; + +structSpecifier[int cvAttrs] returns [Type t] { t = null; } + : #( "struct" t = structOrUnionBody[CompoundTypeKind.STRUCT, cvAttrs] ) + ; + +unionSpecifier[int cvAttrs] returns [Type t] { t = null; } + : #( "union" t = structOrUnionBody[CompoundTypeKind.UNION, cvAttrs] ) + ; + +structOrUnionBody[CompoundTypeKind kind, int cvAttrs] returns [CompoundType t] { + t = null; +} + : ( (ID LCURLY) => id:ID LCURLY { + t = (CompoundType) canonicalize(lookupInStructDictionary(id.getText(), kind, cvAttrs)); + } ( structDeclarationList[t] )? + RCURLY { t.setBodyParsed(); } + | LCURLY { t = new CompoundType(null, null, kind, cvAttrs); } + ( structDeclarationList[t] )? + RCURLY { t.setBodyParsed(); } + | id2:ID { t = (CompoundType) canonicalize(lookupInStructDictionary(id2.getText(), kind, cvAttrs)); } + ) + ; + +structDeclarationList[CompoundType t] + : ( structDeclaration[t] )+ + ; + +structDeclaration[CompoundType containingType] { + Type t = null; + boolean addedAny = false; +} + : t = specifierQualifierList addedAny = structDeclaratorList[containingType, t] { + if (!addedAny) { + if (t != null) { + CompoundType ct = t.asCompound(); + if (ct.isUnion()) { + // Anonymous union + containingType.addField(new Field(null, t, null)); + } + } + } + } + ; + +specifierQualifierList returns [Type t] { + t = null; int x = 0; int y = 0; +} + : ( + t = typeSpecifier[x] + | y = typeQualifier { x |= y; } + )+ { + if (t == null && + (x & (SIGNED | UNSIGNED)) != 0) { + t = new IntType("int", SizeThunk.INT, ((x & UNSIGNED) != 0), attrs2CVAttrs(x)); + } +} + ; + +structDeclaratorList[CompoundType containingType, Type t] returns [boolean addedAny] { + addedAny = false; + boolean y = false; +} + : ( y = structDeclarator[containingType, t] { addedAny = y; })+ + ; + +structDeclarator[CompoundType containingType, Type t] returns [boolean addedAny] { + addedAny = false; + String s = null; + TypeBox tb = new TypeBox(t); +} + : + #( NStructDeclarator + ( s = declarator[tb] { containingType.addField(new Field(s, tb.type(), null)); addedAny = true; } )? + ( COLON expr { /* FIXME: bit types not handled yet */ } ) ? + ( attributeDecl )* + ) + ; + +// FIXME: this will not correctly set the name of the enumeration when +// encountering a declaration like this: +// +// typedef enum { } enumName; +// +// In this case calling getName() on the EnumType return value will +// incorrectly return HeaderParser.ANONYMOUS_ENUM_NAME instead of +// "enumName" +// +// I haven't implemented it yet because I'm not sure how to get the +// "enumName" *before* executing the enumList rule. +enumSpecifier [int cvAttrs] returns [Type t] { + t = null; +} + : #( "enum" + ( ( ID LCURLY )=> i:ID LCURLY enumList[(EnumType)(t = getEnumType(i.getText()))] RCURLY + | LCURLY enumList[(EnumType)(t = getEnumType(ANONYMOUS_ENUM_NAME))] RCURLY + | ID { t = getEnumType(i.getText()); } + ) + ) + ; + +enumList[EnumType enumeration] { + long defaultEnumerantValue = 0; +} + : ( defaultEnumerantValue = enumerator[enumeration, defaultEnumerantValue] )+ + ; + +enumerator[EnumType enumeration, long defaultValue] returns [long newDefaultValue] { + newDefaultValue = defaultValue; +} + : eName:ID ( ASSIGN eVal:expr )? { + long value = 0; + if (eVal != null) { + String vTxt = eVal.getAllChildrenText(); + if (enumHash.containsKey(vTxt)) { + EnumType oldEnumType = (EnumType) enumHash.get(vTxt); + value = oldEnumType.getEnumValue(vTxt); + } else { + try { + value = Long.decode(vTxt).longValue(); + } catch (NumberFormatException e) { + System.err.println("NumberFormatException: ID[" + eName.getText() + "], VALUE=[" + vTxt + "]"); + throw e; + } + } + } else { + value = defaultValue; + } + + newDefaultValue = value+1; + String eTxt = eName.getText(); + if (enumHash.containsKey(eTxt)) { + EnumType oldEnumType = (EnumType) enumHash.get(eTxt); + long oldValue = oldEnumType.getEnumValue(eTxt); + System.err.println("WARNING: redefinition of enumerated value '" + eTxt + "';" + + " existing definition is in enumeration '" + oldEnumType.getName() + + "' with value " + oldValue + " and new definition is in enumeration '" + + enumeration.getName() + "' with value " + value); + // remove old definition + oldEnumType.removeEnumerate(eTxt); + } + // insert new definition + enumeration.addEnum(eTxt, value); + enumHash.put(eTxt, enumeration); + debugPrintln("ENUM [" + enumeration.getName() + "]: " + eTxt + " = " + enumeration.getEnumValue(eTxt) + + " (new default = " + newDefaultValue + ")"); + } + ; + +initDeclList[TypeBox tb] + : ( initDecl[tb] )+ + ; + +initDecl[TypeBox tb] { + String declName = null; +} + : #( NInitDecl + declName = declarator[tb] { + debugPrintln("GOT declName: " + declName + " TB=" + tb); + } + ( attributeDecl )* + ( ASSIGN initializer + | COLON expr + )? + ) +{ + if ((declName != null) && (tb != null) && tb.isTypedef()) { + Type t = tb.type(); + debugPrint("Adding typedef mapping: [" + declName + "] -> "+getTypeString(t)); + if (!t.hasTypedefName()) { + t.setName(declName); + debugPrint(" - declName -> "+getTypeString(t)); + } else { + // copy type to preserve declName ! + t = (Type) t.clone(); + t.setName(declName); + debugPrint(" - copy -> "+getTypeString(t)); + } + t = canonicalize(t); + debugPrintln(" - canon -> "+getTypeString(t)); + typedefDictionary.put(declName, t); + // Clear out PointerGroup effects in case another typedef variant follows + tb.reset(); + } +} + ; + +pointerGroup[TypeBox tb] { int x = 0; int y = 0; } + : #( NPointerGroup ( STAR { x = 0; y = 0; } ( y = typeQualifier { x |= y; } )* + { + debugPrintln("IN PTR GROUP: TB=" + tb); + if (tb != null) { + tb.setType(canonicalize(new PointerType(SizeThunk.POINTER, + tb.type(), + attrs2CVAttrs(x)))); + } + } + )+ ) + ; + + +functionDeclSpecifiers + : + ( functionStorageClassSpecifier + | typeQualifier + | typeSpecifier[0] + )+ + ; + +typeName { + TypeBox tb = null; +} + : specifierQualifierList (nonemptyAbstractDeclarator[tb])? + ; + + +/* FIXME: the handling of types in this rule has not been well thought + out and is known to be incomplete. Currently it is only used to handle + pointerGroups for unnamed parameters. */ +nonemptyAbstractDeclarator[TypeBox tb] + : #( NNonemptyAbstractDeclarator + ( pointerGroup[tb] + ( (LPAREN + ( nonemptyAbstractDeclarator[tb] + | parameterTypeList + )? + RPAREN) + | (LBRACKET (e1:expr)? RBRACKET) { handleArrayExpr(tb, e1); } + )* + + | ( (LPAREN + ( nonemptyAbstractDeclarator[tb] + | parameterTypeList + )? + RPAREN) + | (LBRACKET (e2:expr)? RBRACKET) { handleArrayExpr(tb, e2); } + )+ + ) + ) + ; + +/* Helper routine for parsing expressions which evaluate to integer + constants. Can be made more complicated as necessary. */ +intConstExpr returns [int i] { i = -1; } + : n:Number { return Integer.parseInt(n.getText()); } + ; diff --git a/src/java/com/jogamp/gluegen/cgram/LineObject.java b/src/java/com/jogamp/gluegen/cgram/LineObject.java new file mode 100644 index 0000000..c03b3e8 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/LineObject.java @@ -0,0 +1,126 @@ +package com.jogamp.gluegen.cgram; + +class LineObject { + LineObject parent = null; + String source = ""; + int line = 1; + boolean enteringFile = false; + boolean returningToFile = false; + boolean systemHeader = false; + boolean treatAsC = false; + + public LineObject() + { + super(); + } + + public LineObject( LineObject lobj ) + { + parent = lobj.getParent(); + source = lobj.getSource(); + line = lobj.getLine(); + enteringFile = lobj.getEnteringFile(); + returningToFile = lobj.getReturningToFile(); + systemHeader = lobj.getSystemHeader(); + treatAsC = lobj.getTreatAsC(); + } + + public LineObject( String src) + { + source = src; + } + + public void setSource(String src) + { + source = src; + } + + public String getSource() + { + return source; + } + + public void setParent(LineObject par) + { + parent = par; + } + + public LineObject getParent() + { + return parent; + } + + public void setLine(int l) + { + line = l; + } + + public int getLine() + { + return line; + } + + public void newline() + { + line++; + } + + public void setEnteringFile(boolean v) + { + enteringFile = v; + } + + public boolean getEnteringFile() + { + return enteringFile; + } + + public void setReturningToFile(boolean v) + { + returningToFile = v; + } + + public boolean getReturningToFile() + { + return returningToFile; + } + + public void setSystemHeader(boolean v) + { + systemHeader = v; + } + + public boolean getSystemHeader() + { + return systemHeader; + } + + public void setTreatAsC(boolean v) + { + treatAsC = v; + } + + public boolean getTreatAsC() + { + return treatAsC; + } + + public String toString() { + StringBuffer ret; + ret = new StringBuffer("# " + line + " \"" + source + "\""); + if (enteringFile) { + ret.append(" 1"); + } + if (returningToFile) { + ret.append(" 2"); + } + if (systemHeader) { + ret.append(" 3"); + } + if (treatAsC) { + ret.append(" 4"); + } + return ret.toString(); + } +} + diff --git a/src/java/com/jogamp/gluegen/cgram/PreprocessorInfoChannel.java b/src/java/com/jogamp/gluegen/cgram/PreprocessorInfoChannel.java new file mode 100644 index 0000000..5e7018f --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/PreprocessorInfoChannel.java @@ -0,0 +1,73 @@ +package com.jogamp.gluegen.cgram; + +import java.util.*; + +public class PreprocessorInfoChannel +{ + Hashtable lineLists = new Hashtable(); // indexed by Token number + int firstValidTokenNumber = 0; + int maxTokenNumber = 0; + + public void addLineForTokenNumber( Object line, Integer toknum ) + { + if ( lineLists.containsKey( toknum ) ) { + Vector lines = (Vector) lineLists.get( toknum ); + lines.addElement(line); + } + else { + Vector lines = new Vector(); + lines.addElement(line); + lineLists.put(toknum, lines); + if ( maxTokenNumber < toknum.intValue() ) { + maxTokenNumber = toknum.intValue(); + } + } + } + + public int getMaxTokenNumber() + { + return maxTokenNumber; + } + + public Vector extractLinesPrecedingTokenNumber( Integer toknum ) + { + Vector lines = new Vector(); + if (toknum == null) return lines; + for (int i = firstValidTokenNumber; i < toknum.intValue(); i++){ + Integer inti = new Integer(i); + if ( lineLists.containsKey( inti ) ) { + Vector tokenLineVector = (Vector) lineLists.get( inti ); + if ( tokenLineVector != null) { + Enumeration tokenLines = tokenLineVector.elements(); + while ( tokenLines.hasMoreElements() ) { + lines.addElement( tokenLines.nextElement() ); + } + lineLists.remove(inti); + } + } + } + firstValidTokenNumber = toknum.intValue(); + return lines; + } + + public String toString() + { + StringBuffer sb = new StringBuffer("PreprocessorInfoChannel:\n"); + for (int i = 0; i <= maxTokenNumber + 1; i++){ + Integer inti = new Integer(i); + if ( lineLists.containsKey( inti ) ) { + Vector tokenLineVector = (Vector) lineLists.get( inti ); + if ( tokenLineVector != null) { + Enumeration tokenLines = tokenLineVector.elements(); + while ( tokenLines.hasMoreElements() ) { + sb.append(inti + ":" + tokenLines.nextElement() + '\n'); + } + } + } + } + return sb.toString(); + } +} + + + diff --git a/src/java/com/jogamp/gluegen/cgram/StdCParser.g b/src/java/com/jogamp/gluegen/cgram/StdCParser.g new file mode 100644 index 0000000..015c3e0 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/StdCParser.g @@ -0,0 +1,1386 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Copyright (c) Non, Inc. 1997 -- All Rights Reserved + +PROJECT: C Compiler +MODULE: Parser +FILE: stdc.g + +AUTHOR: John D. Mitchell ([email protected]), Jul 12, 1997 + +REVISION HISTORY: + + Name Date Description + ---- ---- ----------- + JDM 97.07.12 Initial version. + JTC 97.11.18 Declaration vs declarator & misc. hacking. + JDM 97.11.20 Fixed: declaration vs funcDef, + parenthesized expressions, + declarator iteration, + varargs recognition, + empty source file recognition, + and some typos. + + +DESCRIPTION: + + This grammar supports the Standard C language. + + Note clearly that this grammar does *NOT* deal with + preprocessor functionality (including things like trigraphs) + Nor does this grammar deal with multi-byte characters nor strings + containing multi-byte characters [these constructs are "exercises + for the reader" as it were :-)]. + + Please refer to the ISO/ANSI C Language Standard if you believe + this grammar to be in error. Please cite chapter and verse in any + correspondence to the author to back up your claim. + +TODO: + + - typedefName is commented out, needs a symbol table to resolve + ambiguity. + + - trees + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + + +header { + package com.jogamp.gluegen.cgram; + + import java.io.*; + + import antlr.CommonAST; + import antlr.DumpASTVisitor; +} + + +class StdCParser extends Parser; + +options + { + k = 2; + exportVocab = STDC; + buildAST = true; + ASTLabelType = "TNode"; + + // Copied following options from java grammar. + codeGenMakeSwitchThreshold = 2; + codeGenBitsetTestThreshold = 3; + } + + +{ + // Suppport C++-style single-line comments? + public static boolean CPPComments = true; + + // access to symbol table + public CSymbolTable symbolTable = new CSymbolTable(); + + // source for names to unnamed scopes + protected int unnamedScopeCounter = 0; + + public boolean isTypedefName(String name) { + boolean returnValue = false; + TNode node = symbolTable.lookupNameInCurrentScope(name); + for (; node != null; node = (TNode) node.getNextSibling() ) { + if(node.getType() == LITERAL_typedef) { + returnValue = true; + break; + } + } + return returnValue; + } + + + public String getAScopeName() { + return "" + (unnamedScopeCounter++); + } + + public void pushScope(String scopeName) { + symbolTable.pushScope(scopeName); + } + + public void popScope() { + symbolTable.popScope(); + } + + int traceDepth = 0; + public void reportError(RecognitionException ex) { + try { + System.err.println("ANTLR Parsing Error: "+ex + " token name:" + tokenNames[LA(1)]); + ex.printStackTrace(System.err); + } + catch (TokenStreamException e) { + System.err.println("ANTLR Parsing Error: "+ex); + ex.printStackTrace(System.err); + } + } + public void reportError(String s) { + System.err.println("ANTLR Parsing Error from String: " + s); + } + public void reportWarning(String s) { + System.err.println("ANTLR Parsing Warning from String: " + s); + } + public void match(int t) throws MismatchedTokenException { + boolean debugging = false; + + if ( debugging ) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("Match("+tokenNames[t]+") with LA(1)="+ + tokenNames[LA(1)] + ((inputState.guessing>0)?" [inputState.guessing "+ inputState.guessing + "]":"")); + } + catch (TokenStreamException e) { + System.out.println("Match("+tokenNames[t]+") " + ((inputState.guessing>0)?" [inputState.guessing "+ inputState.guessing + "]":"")); + + } + + } + try { + if ( LA(1)!=t ) { + if ( debugging ){ + for (int x=0; x<traceDepth; x++) System.out.print(" "); + System.out.println("token mismatch: "+tokenNames[LA(1)] + + "!="+tokenNames[t]); + } + throw new MismatchedTokenException(tokenNames, LT(1), t, false, getFilename()); + + } else { + // mark token as consumed -- fetch next token deferred until LA/LT + consume(); + } + } + catch (TokenStreamException e) { + } + + } + public void traceIn(String rname) { + traceDepth += 1; + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("> "+rname+"; LA(1)==("+ tokenNames[LT(1).getType()] + + ") " + LT(1).getText() + " [inputState.guessing "+ inputState.guessing + "]"); + } + catch (TokenStreamException e) { + } + } + public void traceOut(String rname) { + for (int x=0; x<traceDepth; x++) System.out.print(" "); + try { + System.out.println("< "+rname+"; LA(1)==("+ tokenNames[LT(1).getType()] + + ") "+LT(1).getText() + " [inputState.guessing "+ inputState.guessing + "]"); + } + catch (TokenStreamException e) { + } + traceDepth -= 1; + } + +} + + + +translationUnit + : externalList + + | /* Empty source files are *not* allowed. */ + { + System.err.println ( "Empty source file!" ); + } + ; + + +externalList + : ( externalDef )+ + ; + + +externalDef + : ( "typedef" | declaration )=> declaration + | functionDef + | asm_expr + ; + + +asm_expr + : "asm"^ + ("volatile")? LCURLY! expr RCURLY! SEMI! + ; + + +declaration + { AST ds1 = null; } + : ds:declSpecifiers { ds1 = astFactory.dupList(#ds); } + ( + initDeclList[ds1] + )? + SEMI! + { ## = #( #[NDeclaration], ##); } + + ; + + +declSpecifiers + { int specCount=0; } + : ( options { // this loop properly aborts when + // it finds a non-typedefName ID MBZ + warnWhenFollowAmbig = false; + } : + s:storageClassSpecifier + | typeQualifier + | ( "struct" | "union" | "enum" | typeSpecifier[specCount] )=> + specCount = typeSpecifier[specCount] + )+ + ; + +storageClassSpecifier + : "auto" + | "register" + | "typedef" + | functionStorageClassSpecifier + ; + + +functionStorageClassSpecifier + : "extern" + | "static" + ; + + +typeQualifier + : "const" + | "volatile" + ; + +typeSpecifier [int specCount] returns [int retSpecCount] + { retSpecCount = specCount + 1; } + : + ( "void" + | "char" + | "short" + | "int" + | "long" + | "float" + | "double" + | "signed" + | "unsigned" + | structOrUnionSpecifier + | enumSpecifier + | { specCount == 0 }? typedefName + ) + ; + + +typedefName + : { isTypedefName ( LT(1).getText() ) }? + i:ID { ## = #(#[NTypedefName], #i); } + ; + +structOrUnionSpecifier + { String scopeName; } + : sou:structOrUnion! + ( ( ID LCURLY )=> i:ID l:LCURLY + { + scopeName = #sou.getText() + " " + #i.getText(); + #l.setText(scopeName); + pushScope(scopeName); + } + structDeclarationList + { popScope();} + RCURLY! + | l1:LCURLY + { + scopeName = getAScopeName(); + #l1.setText(scopeName); + pushScope(scopeName); + } + structDeclarationList + { popScope(); } + RCURLY! + | ID + ) + { + ## = #( #sou, ## ); + } + ; + + +structOrUnion + : "struct" + | "union" + ; + + +structDeclarationList + : ( structDeclaration )+ + ; + + +structDeclaration + : specifierQualifierList structDeclaratorList ( SEMI! )+ + ; + + +specifierQualifierList + { int specCount = 0; } + : ( options { // this loop properly aborts when + // it finds a non-typedefName ID MBZ + warnWhenFollowAmbig = false; + } : + ( "struct" | "union" | "enum" | typeSpecifier[specCount] )=> + specCount = typeSpecifier[specCount] + | typeQualifier + )+ + ; + + +structDeclaratorList + : structDeclarator ( COMMA! structDeclarator )* + ; + + +structDeclarator + : + ( COLON constExpr + | declarator[false] ( COLON constExpr )? + ) + { ## = #( #[NStructDeclarator], ##); } + ; + + +enumSpecifier + : "enum"^ + ( ( ID LCURLY )=> i:ID LCURLY enumList[i.getText()] RCURLY! + | LCURLY enumList["anonymous"] RCURLY! + | ID + ) + ; + + +enumList[String enumName] + : enumerator[enumName] ( COMMA! enumerator[enumName] )* + ; + +enumerator[String enumName] + : i:ID { symbolTable.add( i.getText(), + #( null, + #[LITERAL_enum, "enum"], + #[ ID, enumName] + ) + ); + } + (ASSIGN constExpr)? + ; + + +initDeclList[AST declarationSpecifiers] + : initDecl[declarationSpecifiers] + ( COMMA! initDecl[declarationSpecifiers] )* + ; + + +initDecl[AST declarationSpecifiers] + { String declName = ""; } + : declName = d:declarator[false] + { AST ds1, d1; + ds1 = astFactory.dupList(declarationSpecifiers); + d1 = astFactory.dupList(#d); + symbolTable.add(declName, #(null, ds1, d1) ); + } + ( ASSIGN initializer + | COLON expr + )? + { ## = #( #[NInitDecl], ## ); } + + ; + +pointerGroup + : ( STAR ( typeQualifier )* )+ { ## = #( #[NPointerGroup], ##); } + ; + + + +idList + : ID ( COMMA! ID )* + ; + + +initializer + : ( assignExpr + | LCURLY initializerList ( COMMA! )? RCURLY! + ) + { ## = #( #[NInitializer], ## ); } + ; + + +initializerList + : initializer ( COMMA! initializer )* + ; + + +declarator[boolean isFunctionDefinition] returns [String declName] + { declName = ""; } + : + ( pointerGroup )? + + ( id:ID { declName = id.getText(); } + | LPAREN declName = declarator[false] RPAREN + ) + + ( ! LPAREN + { + if (isFunctionDefinition) { + pushScope(declName); + } + else { + pushScope("!"+declName); + } + } + ( + (declSpecifiers)=> p:parameterTypeList + { + ## = #( null, ##, #( #[NParameterTypeList], #p ) ); + } + + | (i:idList)? + { + ## = #( null, ##, #( #[NParameterTypeList], #i ) ); + } + ) + { + popScope(); + } + RPAREN + | LBRACKET ( constExpr )? RBRACKET + )* + { ## = #( #[NDeclarator], ## ); } + ; + +parameterTypeList + : parameterDeclaration + ( options { + warnWhenFollowAmbig = false; + } : + COMMA! + parameterDeclaration + )* + ( COMMA! + VARARGS + )? + ; + + +parameterDeclaration + { String declName; } + : ds:declSpecifiers + ( ( declarator[false] )=> declName = d:declarator[false] + { + AST d2, ds2; + d2 = astFactory.dupList(#d); + ds2 = astFactory.dupList(#ds); + symbolTable.add(declName, #(null, ds2, d2)); + } + | nonemptyAbstractDeclarator + )? + { + ## = #( #[NParameterDeclaration], ## ); + } + ; + +/* JTC: + * This handles both new and old style functions. + * see declarator rule to see differences in parameters + * and here (declaration SEMI)* is the param type decls for the + * old style. may want to do some checking to check for illegal + * combinations (but I assume all parsed code will be legal?) + */ + +functionDef + { String declName; } + : ( (functionDeclSpecifiers)=> ds:functionDeclSpecifiers + | //epsilon + ) + declName = d:declarator[true] + { + AST d2, ds2; + d2 = astFactory.dupList(#d); + ds2 = astFactory.dupList(#ds); + symbolTable.add(declName, #(null, ds2, d2)); + pushScope(declName); + } + ( declaration )* (VARARGS)? ( SEMI! )* + { popScope(); } + compoundStatement[declName] + { ## = #( #[NFunctionDef], ## );} + ; + +functionDeclSpecifiers + { int specCount = 0; } + : ( options { // this loop properly aborts when + // it finds a non-typedefName ID MBZ + warnWhenFollowAmbig = false; + } : + functionStorageClassSpecifier + | typeQualifier + | ( "struct" | "union" | "enum" | typeSpecifier[specCount] )=> + specCount = typeSpecifier[specCount] + )+ + ; + +declarationList + : ( options { // this loop properly aborts when + // it finds a non-typedefName ID MBZ + warnWhenFollowAmbig = false; + } : + ( declarationPredictor )=> declaration + )+ + ; + +declarationPredictor + : (options { //only want to look at declaration if I don't see typedef + warnWhenFollowAmbig = false; + }: + "typedef" + | declaration + ) + ; + + +compoundStatement[String scopeName] + : LCURLY! + { + pushScope(scopeName); + } + ( ( declarationPredictor)=> declarationList )? + ( statementList )? + { popScope(); } + RCURLY! + { ## = #( #[NCompoundStatement, scopeName], ##); } + ; + + +statementList + : ( statement )+ + ; +statement + : SEMI // Empty statements + + | compoundStatement[getAScopeName()] // Group of statements + + | expr SEMI! { ## = #( #[NStatementExpr], ## ); } // Expressions + +// Iteration statements: + + | "while"^ LPAREN! expr RPAREN! statement + | "do"^ statement "while"! LPAREN! expr RPAREN! SEMI! + |! "for" + LPAREN ( e1:expr )? SEMI ( e2:expr )? SEMI ( e3:expr )? RPAREN + s:statement + { + if ( #e1 == null) { #e1 = (TNode) #[ NEmptyExpression ]; } + if ( #e2 == null) { #e2 = (TNode) #[ NEmptyExpression ]; } + if ( #e3 == null) { #e3 = (TNode) #[ NEmptyExpression ]; } + ## = #( #[LITERAL_for, "for"], #e1, #e2, #e3, #s ); + } + + +// Jump statements: + + | "goto"^ ID SEMI! + | "continue" SEMI! + | "break" SEMI! + | "return"^ ( expr )? SEMI! + + +// Labeled statements: + | ID COLON! (options {warnWhenFollowAmbig=false;}:statement)? { ## = #( #[NLabel], ## ); } + | "case"^ constExpr COLON! statement + | "default"^ COLON! statement + + + +// Selection statements: + + | "if"^ + LPAREN! expr RPAREN! statement + ( //standard if-else ambiguity + options { + warnWhenFollowAmbig = false; + } : + "else" statement )? + | "switch"^ LPAREN! expr RPAREN! statement + ; + + + + + + +expr + : assignExpr (options { + /* MBZ: + COMMA is ambiguous between comma expressions and + argument lists. argExprList should get priority, + and it does by being deeper in the expr rule tree + and using (COMMA assignExpr)* + */ + warnWhenFollowAmbig = false; + } : + c:COMMA^ { #c.setType(NCommaExpr); } assignExpr + )* + ; + + +assignExpr + : conditionalExpr ( a:assignOperator! assignExpr { ## = #( #a, ## );} )? + ; + +assignOperator + : ASSIGN + | DIV_ASSIGN + | PLUS_ASSIGN + | MINUS_ASSIGN + | STAR_ASSIGN + | MOD_ASSIGN + | RSHIFT_ASSIGN + | LSHIFT_ASSIGN + | BAND_ASSIGN + | BOR_ASSIGN + | BXOR_ASSIGN + ; + + +conditionalExpr + : logicalOrExpr + ( QUESTION^ expr COLON! conditionalExpr )? + ; + + +constExpr + : conditionalExpr + ; + +logicalOrExpr + : logicalAndExpr ( LOR^ logicalAndExpr )* + ; + + +logicalAndExpr + : inclusiveOrExpr ( LAND^ inclusiveOrExpr )* + ; + +inclusiveOrExpr + : exclusiveOrExpr ( BOR^ exclusiveOrExpr )* + ; + + +exclusiveOrExpr + : bitAndExpr ( BXOR^ bitAndExpr )* + ; + + +bitAndExpr + : equalityExpr ( BAND^ equalityExpr )* + ; + + + +equalityExpr + : relationalExpr + ( ( EQUAL^ | NOT_EQUAL^ ) relationalExpr )* + ; + + +relationalExpr + : shiftExpr + ( ( LT^ | LTE^ | GT^ | GTE^ ) shiftExpr )* + ; + + + +shiftExpr + : additiveExpr + ( ( LSHIFT^ | RSHIFT^ ) additiveExpr )* + ; + + +additiveExpr + : multExpr + ( ( PLUS^ | MINUS^ ) multExpr )* + ; + + +multExpr + : castExpr + ( ( STAR^ | DIV^ | MOD^ ) castExpr )* + ; + + +castExpr + : ( LPAREN typeName RPAREN )=> + LPAREN! typeName RPAREN! ( castExpr ) + { ## = #( #[NCast, "("], ## ); } + + | unaryExpr + ; + + +typeName + : specifierQualifierList (nonemptyAbstractDeclarator)? + ; + +nonemptyAbstractDeclarator + : ( + pointerGroup + ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )* + + | ( (LPAREN + ( nonemptyAbstractDeclarator + | parameterTypeList + )? + RPAREN) + | (LBRACKET (expr)? RBRACKET) + )+ + ) + { ## = #( #[NNonemptyAbstractDeclarator], ## ); } + + ; + +/* JTC: + +LR rules: + +abstractDeclarator + : nonemptyAbstractDeclarator + | // null + ; + +nonemptyAbstractDeclarator + : LPAREN nonemptyAbstractDeclarator RPAREN + | abstractDeclarator LPAREN RPAREN + | abstractDeclarator (LBRACKET (expr)? RBRACKET) + | STAR abstractDeclarator + ; +*/ + +unaryExpr + : postfixExpr + | INC^ unaryExpr + | DEC^ unaryExpr + | u:unaryOperator castExpr { ## = #( #[NUnaryExpr], ## ); } + + | "sizeof"^ + ( ( LPAREN typeName )=> LPAREN typeName RPAREN + | unaryExpr + ) + ; + + +unaryOperator + : BAND + | STAR + | PLUS + | MINUS + | BNOT + | LNOT + ; + +postfixExpr + : primaryExpr + ( + postfixSuffix {## = #( #[NPostfixExpr], ## );} + )? + ; +postfixSuffix + : + ( PTR ID + | DOT ID + | functionCall + | LBRACKET expr RBRACKET + | INC + | DEC + )+ + ; + +functionCall + : + LPAREN^ (a:argExprList)? RPAREN + { + ##.setType( NFunctionCallArgs ); + } + ; + + +primaryExpr + : ID + | charConst + | intConst + | floatConst + | stringConst + +// JTC: +// ID should catch the enumerator +// leaving it in gives ambiguous err +// | enumerator + | LPAREN! expr RPAREN! { ## = #( #[NExpressionGroup, "("], ## ); } + ; + +argExprList + : assignExpr ( COMMA! assignExpr )* + ; + + + +protected +charConst + : CharLiteral + ; + + +protected +stringConst + : (StringLiteral)+ { ## = #(#[NStringSeq], ##); } + ; + + +protected +intConst + : IntOctalConst + | LongOctalConst + | UnsignedOctalConst + | IntIntConst + | LongIntConst + | UnsignedIntConst + | IntHexConst + | LongHexConst + | UnsignedHexConst + ; + + +protected +floatConst + : FloatDoubleConst + | DoubleDoubleConst + | LongDoubleConst + ; + + + + + + +dummy + : NTypedefName + | NInitDecl + | NDeclarator + | NStructDeclarator + | NDeclaration + | NCast + | NPointerGroup + | NExpressionGroup + | NFunctionCallArgs + | NNonemptyAbstractDeclarator + | NInitializer + | NStatementExpr + | NEmptyExpression + | NParameterTypeList + | NFunctionDef + | NCompoundStatement + | NParameterDeclaration + | NCommaExpr + | NUnaryExpr + | NLabel + | NPostfixExpr + | NRangeExpr + | NStringSeq + | NInitializerElementLabel + | NLcurlyInitializer + | NAsmAttribute + | NGnuAsmExpr + | NTypeMissing + ; + + + + + + +{ + import java.io.*; + import antlr.*; +} + +class StdCLexer extends Lexer; + +options + { + k = 3; + exportVocab = STDC; + testLiterals = false; + } + +{ + LineObject lineObject = new LineObject(); + String originalSource = ""; + PreprocessorInfoChannel preprocessorInfoChannel = new PreprocessorInfoChannel(); + int tokenNumber = 0; + boolean countingTokens = true; + int deferredLineCount = 0; + + public void setCountingTokens(boolean ct) + { + countingTokens = ct; + if ( countingTokens ) { + tokenNumber = 0; + } + else { + tokenNumber = 1; + } + } + + public void setOriginalSource(String src) + { + originalSource = src; + lineObject.setSource(src); + } + public void setSource(String src) + { + lineObject.setSource(src); + } + + public PreprocessorInfoChannel getPreprocessorInfoChannel() + { + return preprocessorInfoChannel; + } + + public void setPreprocessingDirective(String pre) + { + preprocessorInfoChannel.addLineForTokenNumber( pre, new Integer(tokenNumber) ); + } + + public void addDefine(String name, String value) + { + } + + protected Token makeToken(int t) + { + if ( t != Token.SKIP && countingTokens) { + tokenNumber++; + } + CToken tok = (CToken) super.makeToken(t); + tok.setLine(lineObject.line); + tok.setSource(lineObject.source); + tok.setTokenNumber(tokenNumber); + + lineObject.line += deferredLineCount; + deferredLineCount = 0; + return tok; + } + + public void deferredNewline() { + deferredLineCount++; + } + + public void newline() { + lineObject.newline(); + } + + + + + + +} + +protected +Vocabulary + : '\3'..'\377' + ; + + +/* Operators: */ + +ASSIGN : '=' ; +COLON : ':' ; +COMMA : ',' ; +QUESTION : '?' ; +SEMI : ';' ; +PTR : "->" ; + + +// DOT & VARARGS are commented out since they are generated as part of +// the Number rule below due to some bizarre lexical ambiguity shme. + +// DOT : '.' ; +protected +DOT:; + +// VARARGS : "..." ; +protected +VARARGS:; + + +LPAREN : '(' ; +RPAREN : ')' ; +LBRACKET : '[' ; +RBRACKET : ']' ; +LCURLY : '{' ; +RCURLY : '}' ; + +EQUAL : "==" ; +NOT_EQUAL : "!=" ; +LTE : "<=" ; +LT : "<" ; +GTE : ">=" ; +GT : ">" ; + +DIV : '/' ; +DIV_ASSIGN : "/=" ; +PLUS : '+' ; +PLUS_ASSIGN : "+=" ; +INC : "++" ; +MINUS : '-' ; +MINUS_ASSIGN : "-=" ; +DEC : "--" ; +STAR : '*' ; +STAR_ASSIGN : "*=" ; +MOD : '%' ; +MOD_ASSIGN : "%=" ; +RSHIFT : ">>" ; +RSHIFT_ASSIGN : ">>=" ; +LSHIFT : "<<" ; +LSHIFT_ASSIGN : "<<=" ; + +LAND : "&&" ; +LNOT : '!' ; +LOR : "||" ; + +BAND : '&' ; +BAND_ASSIGN : "&=" ; +BNOT : '~' ; +BOR : '|' ; +BOR_ASSIGN : "|=" ; +BXOR : '^' ; +BXOR_ASSIGN : "^=" ; + + +Whitespace + : ( ( '\003'..'\010' | '\t' | '\013' | '\f' | '\016'.. '\037' | '\177'..'\377' | ' ' ) + | "\r\n" { newline(); } + | ( '\n' | '\r' ) { newline(); } + ) { _ttype = Token.SKIP; } + ; + + +Comment + : "/*" + ( { LA(2) != '/' }? '*' + | "\r\n" { deferredNewline(); } + | ( '\r' | '\n' ) { deferredNewline(); } + | ~( '*'| '\r' | '\n' ) + )* + "*/" { _ttype = Token.SKIP; + } + ; + + +CPPComment + : + "//" ( ~('\n') )* + { + _ttype = Token.SKIP; + } + ; + +protected NonWhitespace + : (~('\r' | '\n'))* + ; + + +PREPROC_DIRECTIVE +options { + paraphrase = "a line directive"; +} + + : + '#' + ( ( "line" || (( ' ' | '\t' | '\014')+ '0'..'9')) => LineDirective + | ( (Space)* "define" (Space)* i:ID (Space)* (n:DefineExpr)? + nw:NonWhitespace + ("\r\n" | "\r" | "\n") ) { + if (n != null) { + //System.out.println("addDefine: #define " + i.getText() + " " + n.getText()); + addDefine(i.getText(), n.getText()); + } else { + setPreprocessingDirective("#define " + i.getText() + " " + nw.getText()); + } + } + | (~'\n')* { setPreprocessingDirective(getText()); } + ) + { + _ttype = Token.SKIP; + } + ; + +DefineExpr: + ((LPAREN) (Space)* (DefineExpr2) (Space)* (RPAREN)) | (DefineExpr2) +; + +DefineExpr2: + (Number) + ((Space)* (LSHIFT | RSHIFT | PLUS | MINUS | STAR | DIV | MOD) (Space)* (DefineExpr))? +; + + +protected Space: + ( ' ' | '\t' | '\014') + ; + +protected LineDirective +{ + boolean oldCountingTokens = countingTokens; + countingTokens = false; +} +: + { + lineObject = new LineObject(); + deferredLineCount = 0; + } + ("line")? //this would be for if the directive started "#line", but not there for GNU directives + (Space)+ + n:Number { lineObject.setLine(Integer.parseInt(n.getText())); } + (Space)+ + ( fn:StringLiteral { try { + lineObject.setSource(fn.getText().substring(1,fn.getText().length()-1)); + } + catch (StringIndexOutOfBoundsException e) { /*not possible*/ } + } + | fi:ID { lineObject.setSource(fi.getText()); } + )? + (Space)* + ("1" { lineObject.setEnteringFile(true); } )? + (Space)* + ("2" { lineObject.setReturningToFile(true); } )? + (Space)* + ("3" { lineObject.setSystemHeader(true); } )? + (Space)* + ("4" { lineObject.setTreatAsC(true); } )? + (~('\r' | '\n'))* + ("\r\n" | "\r" | "\n") + { + preprocessorInfoChannel.addLineForTokenNumber(new LineObject(lineObject), new Integer(tokenNumber)); + countingTokens = oldCountingTokens; + } + ; + + + +/* Literals: */ + +/* + * Note that we do NOT handle tri-graphs nor multi-byte sequences. + */ + + +/* + * Note that we can't have empty character constants (even though we + * can have empty strings :-). + */ +CharLiteral + : '\'' ( Escape | ~( '\'' ) ) '\'' + ; + + +/* + * Can't have raw imbedded newlines in string constants. Strict reading of + * the standard gives odd dichotomy between newlines & carriage returns. + * Go figure. + */ +StringLiteral + : '"' + ( Escape + | ( + '\r' { deferredNewline(); } + | '\n' { + deferredNewline(); + _ttype = BadStringLiteral; + } + | '\\' '\n' { + deferredNewline(); + } + ) + | ~( '"' | '\r' | '\n' | '\\' ) + )* + '"' + ; + + +protected BadStringLiteral + : // Imaginary token. + ; + + +/* + * Handle the various escape sequences. + * + * Note carefully that these numeric escape *sequences* are *not* of the + * same form as the C language numeric *constants*. + * + * There is no such thing as a binary numeric escape sequence. + * + * Octal escape sequences are either 1, 2, or 3 octal digits exactly. + * + * There is no such thing as a decimal escape sequence. + * + * Hexadecimal escape sequences are begun with a leading \x and continue + * until a non-hexadecimal character is found. + * + * No real handling of tri-graph sequences, yet. + */ + +protected +Escape + : '\\' + ( options{warnWhenFollowAmbig=false;}: + 'a' + | 'b' + | 'f' + | 'n' + | 'r' + | 't' + | 'v' + | '"' + | '\'' + | '\\' + | '?' + | ('0'..'3') ( options{warnWhenFollowAmbig=false;}: Digit ( options{warnWhenFollowAmbig=false;}: Digit )? )? + | ('4'..'7') ( options{warnWhenFollowAmbig=false;}: Digit )? + | 'x' ( options{warnWhenFollowAmbig=false;}: Digit | 'a'..'f' | 'A'..'F' )+ + ) + ; + + +/* Numeric Constants: */ + +protected +Digit + : '0'..'9' + ; + +protected +LongSuffix + : 'l' + | 'L' + ; + +protected +UnsignedSuffix + : 'u' + | 'U' + ; + +protected +FloatSuffix + : 'f' + | 'F' + ; + +protected +Exponent + : ( 'e' | 'E' ) ( '+' | '-' )? ( Digit )+ + ; + + +protected +DoubleDoubleConst:; + +protected +FloatDoubleConst:; + +protected +LongDoubleConst:; + +protected +IntOctalConst:; + +protected +LongOctalConst:; + +protected +UnsignedOctalConst:; + +protected +IntIntConst:; + +protected +LongIntConst:; + +protected +UnsignedIntConst:; + +protected +IntHexConst:; + +protected +LongHexConst:; + +protected +UnsignedHexConst:; + + + + +Number + : ( ( Digit )+ ( '.' | 'e' | 'E' ) )=> ( Digit )+ + ( '.' ( Digit )* ( Exponent )? + | Exponent + ) { _ttype = DoubleDoubleConst; } + ( FloatSuffix { _ttype = FloatDoubleConst; } + | LongSuffix { _ttype = LongDoubleConst; } + )? + + | ( "..." )=> "..." { _ttype = VARARGS; } + + | '.' { _ttype = DOT; } + ( ( Digit )+ ( Exponent )? + { _ttype = DoubleDoubleConst; } + ( FloatSuffix { _ttype = FloatDoubleConst; } + | LongSuffix { _ttype = LongDoubleConst; } + )? + )? + + | '0' ( '0'..'7' )* { _ttype = IntOctalConst; } + ( LongSuffix { _ttype = LongOctalConst; } + | UnsignedSuffix { _ttype = UnsignedOctalConst; } + )? + + | '1'..'9' ( Digit )* { _ttype = IntIntConst; } + ( LongSuffix { _ttype = LongIntConst; } + | UnsignedSuffix { _ttype = UnsignedIntConst; } + )? + + | '0' ( 'x' | 'X' ) ( 'a'..'f' | 'A'..'F' | Digit )+ + { _ttype = IntHexConst; } + ( LongSuffix { _ttype = LongHexConst; } + | UnsignedSuffix { _ttype = UnsignedHexConst; } + )? + ; + + +ID + options + { + testLiterals = true; + } + : ( 'a'..'z' | 'A'..'Z' | '_' ) + ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' )* + ; + + diff --git a/src/java/com/jogamp/gluegen/cgram/TNode.java b/src/java/com/jogamp/gluegen/cgram/TNode.java new file mode 100644 index 0000000..e9185f9 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/TNode.java @@ -0,0 +1,443 @@ +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; + +/** + Class TNode is an implementation of the AST interface + and adds many useful features: + + It is double-linked for reverse searching. + (this is currently incomplete, in that method doubleLink() must + be called after any changes to the tree to maintain the + reverse links). + + It can store a definition node (defNode), so that nodes such + as scoped names can refer to the node that defines the name. + + It stores line numbers for nodes. + + Searches for parents and children of a tree can be done + based on their type. + + The tree can be printed to System.out using a lisp-style syntax. + + + + */ +public class TNode extends CommonAST { + protected int ttype; + protected String text; + protected int lineNum = 0; + protected TNode defNode; + protected TNode up; + protected TNode left; + protected boolean marker = false; + protected Hashtable attributes = null; + static String tokenVocabulary; + + + + + /** Set the token vocabulary to a tokentypes class + generated by antlr. + */ + public static void setTokenVocabulary(String s) { + tokenVocabulary = s; + } + + +public void initialize(Token token) { + CToken tok = (CToken) token; + setText(tok.getText()); + setType(tok.getType()); + setLineNum(tok.getLine()); + setAttribute("source", tok.getSource()); + setAttribute("tokenNumber", new Integer(tok.getTokenNumber())); +} +public void initialize(AST tr) { + TNode t = (TNode) tr; + setText(t.getText()); + setType(t.getType()); + setLineNum(t.getLineNum()); + setDefNode(t.getDefNode()); + this.attributes = t.getAttributesTable(); +} + + + /** Get the token type for this node */ + public int getType() { return ttype; } + + /** Set the token type for this node */ + public void setType(int ttype_) { + ttype = ttype_; + } + + /** Get the marker value for this node. + This member is a general-use marker. + */ + public boolean getMarker() { return marker; } + + /** Set the marker value for this node. + This property is a general-use boolean marker. + */ + public void setMarker(boolean marker_) { + marker = marker_; + } + + /** get the hashtable that holds attribute values. + */ + public Hashtable getAttributesTable() { + if(attributes == null) + attributes = new Hashtable(7); + return attributes; + } + + /** set an attribute in the attribute table. + */ + public void setAttribute(String attrName, Object value) { + if(attributes == null) + attributes = new Hashtable(7); + attributes.put(attrName,value); + } + + /** lookup the attribute name in the attribute table. + If the value does not exist, it returns null. + */ + public Object getAttribute(String attrName) { + if(attributes == null) + return null; + else + return attributes.get(attrName); + } + + /** Get the line number for this node. + If the line number is 0, search for a non-zero line num among children */ + public int getLineNum() { + if(lineNum != 0) + return lineNum; + else + if(down == null) + return lineNum; + else + return ((TNode)down).getLocalLineNum(); + } + + public int getLocalLineNum() { + if(lineNum != 0) + return lineNum; + else + if(down == null) + if(right == null) + return lineNum; + else + return ((TNode)right).getLocalLineNum(); + else + return ((TNode)down).getLocalLineNum(); + } + + /** Set the line number for this node */ + public void setLineNum(int lineNum_) { + lineNum = lineNum_; + } + + /** Get the token text for this node */ + public String getText() { return text; } + + /** Set the token text for this node */ + public void setText(String text_) { + text = text_; + } + + /** Returns the text for this node and all children */ + public String getAllChildrenText() { + StringBuffer buf = new StringBuffer(); + buf.append(getText()); + for (TNode node = (TNode) getFirstChild(); node != null; node = (TNode) node.getNextSibling()) { + buf.append(node.getText()); + } + return buf.toString(); + } + + /** return the last child of this node, or null if there is none */ + public TNode getLastChild() { + TNode down = (TNode)getFirstChild(); + if(down != null) + return down.getLastSibling(); + else + return null; + } + + /** return the last sibling of this node, which is + this if the next sibling is null */ + public TNode getLastSibling() { + TNode next = (TNode)getNextSibling(); + if(next != null) + return next.getLastSibling(); + else + return this; + } + + /** return the first sibling of this node, which is + this if the prev sibling is null */ + public TNode getFirstSibling() { + TNode prev = (TNode)left; + if(prev != null) + return prev.getFirstSibling(); + else + return this; + } + + + /** return the parent node of this node */ + public TNode getParent() { + return (TNode)getFirstSibling().up; + } + + + /** add the new node as a new sibling, inserting it ahead of any + existing next sibling. This method maintains double-linking. + if node is null, nothing happens. If the node has siblings, + then they are added in as well. + */ + public void addSibling(AST node) { + if(node == null) return; + TNode next = (TNode)right; + right = (TNode)node; + ((TNode)node).left = this; + TNode nodeLastSib = ((TNode)node).getLastSibling(); + nodeLastSib.right = next; + if(next != null) + next.left = nodeLastSib; + } + + + /** return the number of children of this node */ + public int numberOfChildren() { + int count = 0; + AST child = getFirstChild(); + while(child != null) { + count++; + child = child.getNextSibling(); + } + return count; + } + + + /** remove this node from the tree, resetting sibling and parent + pointers as necessary. This method maintains double-linking */ + public void removeSelf() { + TNode parent = (TNode)up; + TNode prev = (TNode)left; + TNode next = (TNode)right; + + if(parent != null) { + parent.down = next; + if(next != null) { + next.up = parent; + next.left = prev; // which should be null + } + } + else { + if(prev != null) + prev.right = next; + if(next != null) + next.left = prev; + } + } + + + /** return the def node for this node */ + public TNode getDefNode() { + return defNode; + } + + /** set the def node for this node */ + public void setDefNode(TNode n) { + defNode = n; + } + + + /** return a deep copy of this node, and all sub nodes. + New tree is doubleLinked, with no parent or siblings. + Marker value is not copied! + */ + public TNode deepCopy() { + TNode copy = new TNode(); + copy.ttype = ttype; + copy.text = text; + copy.lineNum = lineNum; + copy.defNode = defNode; + if(attributes != null) + copy.attributes = (Hashtable)attributes.clone(); + if(down != null) + copy.down = ((TNode)down).deepCopyWithRightSiblings(); + copy.doubleLink(); + return copy; + } + + + /** return a deep copy of this node, all sub nodes, + and right siblings. + New tree is doubleLinked, with no parent or left siblings. + defNode is not copied */ + public TNode deepCopyWithRightSiblings() { + TNode copy = new TNode(); + copy.ttype = ttype; + copy.text = text; + copy.lineNum = lineNum; + copy.defNode = defNode; + if(attributes != null) + copy.attributes = (Hashtable)attributes.clone(); + if(down != null) + copy.down = ((TNode)down).deepCopyWithRightSiblings(); + if(right != null) + copy.right = ((TNode)right).deepCopyWithRightSiblings(); + copy.doubleLink(); + return copy; + } + + + /** return a short string representation of the node */ + public String toString() { + StringBuffer str = new StringBuffer( getNameForType(getType()) + + "[" + getText() + ", " + "]"); + + if(this.getLineNum() != 0) + str.append(" line:" + (this.getLineNum() ) ); + + Enumeration keys = (this.getAttributesTable().keys()); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + str.append(" " + key + ":" + (this.getAttribute(key))); + } + + return str.toString(); + } + + + /** print given tree to System.out */ + public static void printTree(AST t) { + if (t == null) return; + printASTNode(t,0); + System.out.print("\n"); + } + + + /** protected method that does the work of printing */ + protected static void printASTNode(AST t, int indent) { + AST child1, next; + child1 = t.getFirstChild(); + + System.out.print("\n"); + for(int i = 0; i < indent; i++) + System.out.print(" "); + + if(child1 != null) + System.out.print("("); + + String s = t.getText(); + if(s != null && s.length() > 0) { + System.out.print(getNameForType(t.getType())); + System.out.print(": \"" + s + "\""); + } + else + System.out.print(getNameForType(t.getType())); + if(((TNode)t).getLineNum() != 0) + System.out.print(" line:" + ((TNode)t).getLineNum() ); + + Enumeration keys = ((TNode)t).getAttributesTable().keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + System.out.print(" " + key + ":" + ((TNode)t).getAttribute(key)); + } + TNode def = ((TNode)t).getDefNode(); + if(def != null) + System.out.print("[" + getNameForType(def.getType()) + "]"); + + + if(child1 != null) { + printASTNode(child1,indent + 1); + + System.out.print("\n"); + for(int i = 0; i < indent; i++) + System.out.print(" "); + System.out.print(")"); + } + + next = t.getNextSibling(); + if(next != null) { + printASTNode(next,indent); + } + } + + /** converts an int tree token type to a name. + Does this by reflecting on nsdidl.IDLTreeTokenTypes, + and is dependent on how ANTLR 2.00 outputs that class. */ + public static String getNameForType(int t) { + try{ + Class c = Class.forName(tokenVocabulary); + Field[] fields = c.getDeclaredFields(); + if(t-2 < fields.length) + return fields[t-2].getName(); + } catch (Exception e) { System.out.println(e); } + return "unfoundtype: " + t; + } + + + /** set up reverse links between this node and its first + child and its first sibling, and link those as well */ + public void doubleLink() { + TNode right = (TNode)getNextSibling(); + if(right != null) { + right.left = this; + right.doubleLink(); + } + TNode down = (TNode)getFirstChild(); + if(down != null) { + down.up = this; + down.doubleLink(); + } + } + + /** find first parent of the given type, + return null on failure */ + public TNode parentOfType(int type) { + if(up == null) { + if(left == null) + return null; + else + return left.parentOfType(type); + } + if(up.getType() == type) + return up; + return up.parentOfType(type); + } + + /** find the first child of the node + of the given type, return null on failure */ + public TNode firstChildOfType(int type) { + TNode down = (TNode)getFirstChild(); + if(down == null) + return null; + if(down.getType() == type) + return down; + return down.firstSiblingOfType(type); + } + + /** find the first sibling of the node + of the given type, return null on failure */ + public TNode firstSiblingOfType(int type) { + TNode right = (TNode)getNextSibling(); + if(right == null) + return null; + if(right.getType() == type) + return right; + return right.firstSiblingOfType(type); + } + +} diff --git a/src/java/com/jogamp/gluegen/cgram/TNodeFactory.java b/src/java/com/jogamp/gluegen/cgram/TNodeFactory.java new file mode 100644 index 0000000..0a3fbcb --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/TNodeFactory.java @@ -0,0 +1,33 @@ +package com.jogamp.gluegen.cgram; + +import antlr.Token; +import antlr.ASTFactory; +import antlr.collections.AST; + +/** This class extends ASTFactory to build instances + of class TNode */ +public class TNodeFactory extends ASTFactory { + + /** Create a new ampty AST node */ + public AST create() { + return new TNode(); + } + + /** Create a new AST node from type and text */ + public AST create(int ttype, String text) { + AST ast = new TNode(); + ast.setType(ttype); + ast.setText(text); + return ast; + } + + /** Create a new AST node from an existing AST node */ + public AST create(AST ast) { + AST newast = new TNode(); + newast.setType(ast.getType()); + newast.setText(ast.getText()); + return newast; + } + + +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java new file mode 100644 index 0000000..675efb5 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/ArrayType.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** 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 + should be passed in to the constructor. */ + +public class ArrayType extends Type implements Cloneable { + private Type elementType; + private int length; + private String computedName; + + public ArrayType(Type elementType, SizeThunk sizeInBytes, int length, int cvAttributes) { + super(elementType.getName() + " *", sizeInBytes, cvAttributes); + this.elementType = elementType; + this.length = length; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) return true; + if (arg == null || (!(arg instanceof ArrayType))) { + return false; + } + ArrayType t = (ArrayType) arg; + return (super.equals(arg) && elementType.equals(t.elementType) && (length == t.length)); + } + + @Override + public String getName(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() + " *"; + computedName = computedName.intern(); + } + return computedName; + } + + @Override + public ArrayType asArray() { return this; } + + public Type getElementType() { return elementType; } + public int getLength() { return length; } + public boolean hasLength() { return length >= 0; } + + /** Return the bottommost element type if this is a multidimensional + array. */ + public Type getBaseElementType() { + ArrayType t = this; + while (t.getElementType().isArray()) { + t = t.getElementType().asArray(); + } + return t.getElementType(); + } + + /** Recompute the size of this array if necessary. This needs to be + done when the base element type is a compound type. */ + public void recomputeSize() { + ArrayType arrayElementType = getElementType().asArray(); + if (arrayElementType != null) { + arrayElementType.recomputeSize(); + } + // FIXME: this doesn't take into account struct alignment, which may be necessary + // See also FIXME below and in HeaderParser.g + super.setSize(SizeThunk.mul(SizeThunk.constant(getLength()), elementType.getSize())); + } + + @Override + public String toString() { + return toString(null); + } + + public String toString(String variableName) { + StringBuffer buf = new StringBuffer(); + buf.append(elementType.getName()); + if (variableName != null) { + buf.append(" "); + buf.append(variableName); + } + buf.append("["); + buf.append(length); + buf.append("]"); + return buf.toString(); + } + + @Override + public void visit(TypeVisitor arg) { + super.visit(arg); + elementType.visit(arg); + } + + Type newCVVariant(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 new file mode 100644 index 0000000..a7a1f55 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/BitType.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** Represents a bitfield in a struct. */ + +public class BitType extends IntType implements Cloneable { + private IntType underlyingType; + private int sizeInBits; + private int offset; + + public BitType(IntType underlyingType, int sizeInBits, int lsbOffset, int cvAttributes) { + super(underlyingType.getName(), underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); + this.underlyingType = underlyingType; + this.sizeInBits = sizeInBits; + this.offset = lsbOffset; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) return true; + if (arg == null || (!(arg instanceof BitType))) { + return false; + } + BitType t = (BitType) arg; + return (super.equals(arg) && underlyingType.equals(t.underlyingType) && + (sizeInBits == t.sizeInBits) && (offset == t.offset)); + } + + @Override + public BitType asBit() { return this; } + + /** Size in bits of this type. */ + public int getSizeInBits() { + return sizeInBits; + } + + /** Offset from the least-significant bit (LSB) of the LSB of this + type */ + public int getOffset() { + return offset; + } + + @Override + public void visit(TypeVisitor arg) { + super.visit(arg); + underlyingType.visit(arg); + } + + @Override + Type newCVVariant(int cvAttributes) { + return new BitType(underlyingType, sizeInBits, offset, cvAttributes); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/CVAttributes.java b/src/java/com/jogamp/gluegen/cgram/types/CVAttributes.java new file mode 100644 index 0000000..34b703e --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/CVAttributes.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** Enumeration for const/volatile attributes. These are passed in to + the constructor of the type. */ + +public interface CVAttributes { + public static final int CONST = 0x01; + public static final int VOLATILE = 0x02; +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java new file mode 100644 index 0000000..9e9ead7 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/CompoundType.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +import java.util.*; + +/** Models all compound types, i.e., those containing fields: structs + and unions. The boolean type accessors indicate how the type is + really defined. */ + +public class CompoundType extends Type implements Cloneable { + private CompoundTypeKind kind; + // The name "foo" in the construct "struct foo { ... }"; + private String structName; + private ArrayList<Field> fields; + private boolean visiting; + private boolean bodyParsed; + private boolean computedHashcode; + private int hashcode; + + public CompoundType(String name, SizeThunk size, CompoundTypeKind kind, int cvAttributes) { + this(name, size, kind, cvAttributes, null); + } + + private CompoundType(String name, SizeThunk size, CompoundTypeKind kind, int cvAttributes, String structName) { + super(name, size, cvAttributes); + assert kind != null; + this.kind = kind; + this.structName = structName; + } + + public Object clone() { + CompoundType n = (CompoundType) super.clone(); + if(null!=this.fields) { + n.fields = (ArrayList) this.fields.clone(); + } + return n; + } + + @Override + public int hashCode() { + if (computedHashcode) { + return hashcode; + } + + if (structName != null) { + hashcode = structName.hashCode(); + } else if (getName() != null) { + hashcode = getName().hashCode(); + } else { + hashcode = 0; + } + + computedHashcode = true; + return hashcode; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) return true; + if (arg == null || !(arg instanceof CompoundType)) { + return false; + } + CompoundType t = (CompoundType) arg; + return super.equals(arg) && + ((structName == null ? t.structName == null : structName.equals(t.structName)) || + (structName != null && structName.equals(t.structName))) && + kind == t.kind && listsEqual(fields, t.fields); + } + + /** Returns the struct name of this CompoundType, i.e. the "foo" in + the construct "struct foo { ... };". */ + public String getStructName() { + return structName; + } + + /** Sets the struct name of this CompoundType, i.e. the "foo" in the + construct "struct foo { ... };". */ + public void setStructName(String structName) { + this.structName = structName; + } + + @Override + public void setSize(SizeThunk size) { + super.setSize(size); + } + + @Override + public CompoundType asCompound() { return this; } + + /** Returns the number of fields in this type. */ + public int getNumFields() { + return ((fields == null) ? 0 : fields.size()); + } + + /** Returns the <i>i</i>th field of this type. */ + public Field getField(int i) { + return fields.get(i); + } + + /** Adds a field to this type. */ + public void addField(Field f) { + if (bodyParsed) { + throw new RuntimeException("Body of this CompoundType has already been parsed; should not be adding more fields"); + } + if (fields == null) { + fields = new ArrayList<Field>(); + } + fields.add(f); + } + + /** Indicates to this CompoundType that its body has been parsed and + that no more {@link #addField} operations will be made. */ + public void setBodyParsed() { + bodyParsed = true; + } + + /** Indicates whether this type was declared as a struct. */ + public boolean isStruct() { return (kind == CompoundTypeKind.STRUCT); } + /** Indicates whether this type was declared as a union. */ + public boolean isUnion() { return (kind == CompoundTypeKind.UNION); } + + @Override + public String toString() { + String cvAttributesString = getCVAttributesString(); + if (getName() != null) { + return cvAttributesString + getName(); + } else if (getStructName() != null) { + return cvAttributesString + "struct " + getStructName(); + } else { + return cvAttributesString + getStructString(); + } + } + + @Override + public void visit(TypeVisitor arg) { + if (visiting) { + return; + } + try { + visiting = true; + super.visit(arg); + int n = getNumFields(); + for (int i = 0; i < n; i++) { + Field f = getField(i); + f.getType().visit(arg); + } + } finally { + visiting = false; + } + } + + public String getStructString() { + if (visiting) { + if (getName() != null) { + return getName(); + } + return "struct {/*Recursive type reference*/}"; + } + + try { + visiting = true; + String kind = (isStruct() ? "struct {" : "union {"); + StringBuffer res = new StringBuffer(); + res.append(kind); + int n = getNumFields(); + for (int i = 0; i < n; i++) { + res.append(" "); + res.append(getField(i)); + } + res.append(" }"); + return res.toString(); + } finally { + visiting = false; + } + } + + Type newCVVariant(int cvAttributes) { + CompoundType t = new CompoundType(getName(), getSize(), kind, cvAttributes, structName); + t.fields = fields; + return t; + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/CompoundTypeKind.java b/src/java/com/jogamp/gluegen/cgram/types/CompoundTypeKind.java new file mode 100644 index 0000000..b07611c --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/CompoundTypeKind.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** Type-safe enum for discriminating between structs and unions, + which are both represented as compound types. */ + +public class CompoundTypeKind { + public static final CompoundTypeKind STRUCT = new CompoundTypeKind(); + public static final CompoundTypeKind UNION = new CompoundTypeKind(); + + private CompoundTypeKind() {} +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java new file mode 100644 index 0000000..280485a --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/DoubleType.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +/** Represents a double-word floating-point type (C type "double".) */ +public class DoubleType extends PrimitiveType implements Cloneable { + + public DoubleType(String name, SizeThunk size, int cvAttributes) { + super(name, size, cvAttributes); + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof DoubleType))) { + return false; + } + return super.equals(arg); + } + + @Override + public DoubleType asDouble() { + return this; + } + + Type newCVVariant(int cvAttributes) { + return new DoubleType(getName(), getSize(), cvAttributes); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java new file mode 100644 index 0000000..7967ba0 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + + +/** 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; + + private static class Enum { + + String name; + long value; + + Enum(String name, long value) { + this.name = name; + this.value = value; + } + + String getName() { + return name; + } + + long getValue() { + return value; + } + } + + private ArrayList<Enum> enums; + + public EnumType(String name) { + super(name, SizeThunk.LONG, false, CVAttributes.CONST); + this.underlyingType = new IntType(name, SizeThunk.LONG, false, CVAttributes.CONST); + } + + public EnumType(String name, SizeThunk enumSizeInBytes) { + super(name, enumSizeInBytes, false, CVAttributes.CONST); + this.underlyingType = new IntType(name, enumSizeInBytes, false, CVAttributes.CONST); + } + + protected EnumType(String name, IntType underlyingType, int cvAttributes) { + super(name, underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); + this.underlyingType = underlyingType; + } + + public Object clone() { + EnumType n = (EnumType) super.clone(); + if(null!=this.underlyingType) { + n.underlyingType = (IntType) this.underlyingType.clone(); + } + if(null!=this.enums) { + n.enums = (ArrayList) this.enums.clone(); + } + return n; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof EnumType))) { + return false; + } + EnumType t = (EnumType) arg; + return (super.equals(arg) + && underlyingType.equals(t.underlyingType) + && listsEqual(enums, t.enums)); + } + + @Override + public EnumType asEnum() { + return this; + } + + public void addEnum(String name, long val) { + if (enums == null) { + enums = new ArrayList<Enum>(); + } + enums.add(new Enum(name, val)); + } + + /** Number of enumerates defined in this enum. */ + public int getNumEnumerates() { + return enums.size(); + } + + /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) name */ + public String getEnumName(int i) { + return (enums.get(i)).getName(); + } + + /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) value */ + public long getEnumValue(int i) { + return (enums.get(i)).getValue(); + } + + /** Fetch the value of the enumerate with the given name. */ + public long getEnumValue(String name) { + for (int i = 0; i < enums.size(); ++i) { + Enum n = (enums.get(i)); + if (n.getName().equals(name)) { + return n.getValue(); + } + } + throw new NoSuchElementException( + "No enumerate named \"" + name + "\" in EnumType \"" + + getName() + "\""); + } + + /** Does this enum type contain an enumerate with the given name? */ + public boolean containsEnumerate(String name) { + for (int i = 0; i < enums.size(); ++i) { + if ((enums.get(i)).getName().equals(name)) { + return true; + } + } + return false; + } + + /** Remove the enumerate with the given name. Returns true if it was found + * and removed; false if it was not found. + */ + public boolean removeEnumerate(String name) { + for (int i = 0; i < enums.size(); ++i) { + Enum e = enums.get(i); + if (e.getName().equals(name)) { + enums.remove(e); + return true; + } + } + return false; + } + + @Override + public void visit(TypeVisitor arg) { + super.visit(arg); + underlyingType.visit(arg); + } + + @Override + Type newCVVariant(int cvAttributes) { + 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 new file mode 100644 index 0000000..07d90ea --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/Field.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** Represents a field in a struct or union. */ + +public class Field { + private String name; + private Type type; + private SizeThunk offset; + + public Field(String name, Type type, SizeThunk offset) { + this.name = name; + this.type = type; + this.offset = offset; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object arg) { + if (arg == null || (!(arg instanceof Field))) { + return false; + } + + 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)); + } + + /** Name of this field in the containing data structure. */ + public String getName() { return name; } + + /** Type of this field. */ + public Type getType() { return type; } + + /** SizeThunk computing offset, in bytes, of this field in the containing data structure. */ + public SizeThunk getOffset() { return offset; } + + /** Offset, in bytes, of this field in the containing data structure + given the specified MachineDescription. */ + public long getOffset(MachineDescription machDesc) { return offset.compute(machDesc); } + + /** Sets the offset of this field in the containing data structure. */ + public void setOffset(SizeThunk offset) { this.offset = offset; } + + @Override + public String toString() { + if (!getType().isFunctionPointer()) { + if (getName() == null && + getType().asCompound() != null && + getType().asCompound().isUnion()) { + return "" + getType() + ";"; + } + return "" + getType() + " " + getName() + ";"; + } else { + FunctionType ft = getType().asPointer().getTargetType().asFunction(); + // FIXME: pick up calling convention? + return ft.toString(getName(), null, false, true) + ";"; + } + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/FloatType.java b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java new file mode 100644 index 0000000..7766b8c --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/FloatType.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** Represents a single-word floating-point type (C type "float".) */ + +public class FloatType extends PrimitiveType implements Cloneable { + public FloatType(String name, SizeThunk size, int cvAttributes) { + super(name, size, cvAttributes); + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof FloatType))) { + return false; + } + return super.equals(arg); + } + + @Override + public FloatType asFloat() { return this; } + + Type newCVVariant(int cvAttributes) { + return new FloatType(getName(), getSize(), cvAttributes); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java new file mode 100644 index 0000000..545a6e8 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionSymbol.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +/** Describes a function symbol, which includes the name and +type. Since we are currently only concerned with processing +functions this is the only symbol type, though plausibly more +types should be added and a true symbol table constructed during +parsing. */ +public class FunctionSymbol { + + private String name; + private FunctionType type; + + public FunctionSymbol(String name, FunctionType type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + /** Returns the type of this function. Do not add arguments to it + directly; use addArgument instead. */ + public FunctionType getType() { + return type; + } + + /** Returns the return type of this function. */ + public Type getReturnType() { + return type.getReturnType(); + } + + public int getNumArguments() { + return type.getNumArguments(); + } + + /** Returns the name of the <i>i</i>th argument. May return null if + no argument names were available during parsing. */ + public String getArgumentName(int i) { + return type.getArgumentName(i); + } + + /** Returns the type of the <i>i</i>th argument. */ + public Type getArgumentType(int i) { + return type.getArgumentType(i); + } + + /** Add an argument's name and type. Use null for unknown argument + names. */ + public void addArgument(Type argumentType, String argumentName) { + type.addArgument(argumentType, argumentName); + } + + @Override + public String toString() { + return getType().toString(getName()); + } + + /** Helper routine for emitting native javadoc tags */ + public String toString(boolean emitNativeTag) { + return getType().toString(getName(), emitNativeTag); + } + + @Override + public int hashCode() { + if (name == null) { + return 0; + } + return name.hashCode(); + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + + if (arg == null || (!(arg instanceof FunctionSymbol))) { + return false; + } + + FunctionSymbol other = (FunctionSymbol) arg; + + if (getName() == null && other.getName() != null) { + return false; + } + + return (getName().equals(other.getName()) || getName().equals(other.getName())) && type.equals(other.type); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java new file mode 100644 index 0000000..cb430db --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/FunctionType.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +import java.util.*; + +/** Describes a function type, used to model both function +declarations and (via PointerType) function pointers. */ +public class FunctionType extends Type implements Cloneable { + + private Type returnType; + private ArrayList<Type> argumentTypes; + private ArrayList<String> argumentNames; + + public FunctionType(String name, SizeThunk size, Type returnType, int cvAttributes) { + super(name, size, cvAttributes); + this.returnType = returnType; + } + + public Object clone() { + FunctionType n = (FunctionType) super.clone(); + if(null!=this.argumentTypes) { + n.argumentTypes = (ArrayList) this.argumentTypes.clone(); + } + if(null!=this.argumentNames) { + n.argumentNames = (ArrayList) this.argumentNames.clone(); + } + return n; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof FunctionType))) { + return false; + } + FunctionType t = (FunctionType) arg; + return (super.equals(arg) + && returnType.equals(t.returnType) + && listsEqual(argumentTypes, t.argumentTypes)); + } + + @Override + public FunctionType asFunction() { + return this; + } + + /** Returns the return type of this function. */ + public Type getReturnType() { + return returnType; + } + + public int getNumArguments() { + return ((argumentTypes == null) ? 0 : argumentTypes.size()); + } + + /** Returns the name of the <i>i</i>th argument. May return null if + no argument names were available during parsing. */ + public String getArgumentName(int i) { + return argumentNames.get(i); + } + + /** Returns the type of the <i>i</i>th argument. */ + public Type getArgumentType(int i) { + return argumentTypes.get(i); + } + + /** + * Add an argument's name and type. Use null for unknown argument names. + */ + public void addArgument(Type argumentType, String argumentName) { + if (argumentTypes == null) { + argumentTypes = new ArrayList<Type>(); + argumentNames = new ArrayList<String>(); + } + argumentTypes.add(argumentType); + argumentNames.add(argumentName); + } + + public void setArgumentName(int i, String name) { + argumentNames.set(i, name); + } + + @Override + public String toString() { + return toString(null); + } + + public String toString(String functionName) { + return toString(functionName, false); + } + + public String toString(String functionName, boolean emitNativeTag) { + return toString(functionName, null, emitNativeTag, false); + } + + String toString(String functionName, String callingConvention, boolean emitNativeTag, boolean isPointer) { + StringBuffer res = new StringBuffer(); + res.append(getReturnType()); + res.append(" "); + if (isPointer) { + res.append("("); + if (callingConvention != null) { + res.append(callingConvention); + } + res.append("*"); + } + if (functionName != null) { + if (emitNativeTag) { + // Emit @native tag for javadoc purposes + res.append("{@native "); + } + res.append(functionName); + if (emitNativeTag) { + res.append("}"); + } + } + if (isPointer) { + res.append(")"); + } + res.append("("); + int n = getNumArguments(); + for (int i = 0; i < n; i++) { + Type t = getArgumentType(i); + if (t.isFunctionPointer()) { + FunctionType ft = t.asPointer().getTargetType().asFunction(); + res.append(ft.toString(getArgumentName(i), callingConvention, false, true)); + } else if (t.isArray()) { + res.append(t.asArray().toString(getArgumentName(i))); + } else { + res.append(t); + String argumentName = getArgumentName(i); + if (argumentName != null) { + res.append(" "); + res.append(argumentName); + } + } + if (i < n - 1) { + res.append(", "); + } + } + res.append(")"); + if (!isPointer) { + res.append(";"); + } + return res.toString(); + } + + @Override + public void visit(TypeVisitor arg) { + super.visit(arg); + returnType.visit(arg); + int n = getNumArguments(); + for (int i = 0; i < n; i++) { + getArgumentType(i).visit(arg); + } + } + + Type newCVVariant(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 new file mode 100644 index 0000000..c72fde4 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/IntType.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +public class IntType extends PrimitiveType implements Cloneable { + + private boolean unsigned; + private boolean typedefedUnsigned; + + public IntType(String name, SizeThunk size, boolean unsigned, int cvAttributes) { + this(name, size, unsigned, cvAttributes, false); + } + + private IntType(String name, SizeThunk size, boolean unsigned, int cvAttributes, boolean typedefedUnsigned) { + super(name, size, cvAttributes); + this.unsigned = unsigned; + this.typedefedUnsigned = typedefedUnsigned; + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof IntType))) { + return false; + } + IntType t = (IntType) arg; + return (super.equals(arg) && (unsigned == t.unsigned)); + } + + @Override + public void setName(String name) { + super.setName(name); + typedefedUnsigned = unsigned; + } + + @Override + public IntType asInt() { + return this; + } + + /** Indicates whether this type is unsigned */ + public boolean isUnsigned() { + return unsigned; + } + + @Override + public String toString() { + return getCVAttributesString() + ((isUnsigned() & (!typedefedUnsigned)) ? "unsigned " : "") + getName(); + } + + Type newCVVariant(int cvAttributes) { + return new IntType(getName(), getSize(), isUnsigned(), cvAttributes, typedefedUnsigned); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/MachineDescription.java b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription.java new file mode 100644 index 0000000..d2598e0 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +public class MachineDescription { + private int charSizeInBytes; + private int shortSizeInBytes; + private int intSizeInBytes; + private int longSizeInBytes; + private int int64SizeInBytes; + private int floatSizeInBytes; + private int doubleSizeInBytes; + private int pointerSizeInBytes; + private int structAlignmentInBytes; + + public MachineDescription(int charSizeInBytes, + int shortSizeInBytes, + int intSizeInBytes, + int longSizeInBytes, + int int64SizeInBytes, + int floatSizeInBytes, + int doubleSizeInBytes, + int pointerSizeInBytes, + int structAlignmentInBytes) { + this.charSizeInBytes = charSizeInBytes; + this.shortSizeInBytes = shortSizeInBytes; + this.intSizeInBytes = intSizeInBytes; + this.longSizeInBytes = longSizeInBytes; + this.int64SizeInBytes = int64SizeInBytes; + this.floatSizeInBytes = floatSizeInBytes; + this.doubleSizeInBytes = doubleSizeInBytes; + this.pointerSizeInBytes = pointerSizeInBytes; + this.structAlignmentInBytes = structAlignmentInBytes; + } + + public int charSizeInBytes() { return charSizeInBytes; } + public int shortSizeInBytes() { return shortSizeInBytes; } + public int intSizeInBytes() { return intSizeInBytes; } + public int longSizeInBytes() { return longSizeInBytes; } + public int int64SizeInBytes() { return int64SizeInBytes; } + public int floatSizeInBytes() { return floatSizeInBytes; } + public int doubleSizeInBytes() { return doubleSizeInBytes; } + public int pointerSizeInBytes() { return pointerSizeInBytes; } + public int structAlignmentInBytes() { return structAlignmentInBytes; } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/MachineDescription32Bit.java b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription32Bit.java new file mode 100644 index 0000000..6bbb801 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription32Bit.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +public class MachineDescription32Bit extends MachineDescription { + public MachineDescription32Bit() { + super(1, 2, 4, 4, 8, 4, 8, 4, 8); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/MachineDescription64Bit.java b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription64Bit.java new file mode 100644 index 0000000..38328e4 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/MachineDescription64Bit.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +public class MachineDescription64Bit extends MachineDescription { + public MachineDescription64Bit() { + super(1, 2, 4, 8, 8, 4, 8, 8, 16); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/PointerType.java b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java new file mode 100644 index 0000000..4666e48 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/PointerType.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +public class PointerType extends Type implements Cloneable { + + private Type targetType; + private String computedName; + private boolean hasTypedefedName; + + public PointerType(SizeThunk size, Type targetType, int cvAttributes) { + // can pass null for the final name parameter because the PointerType's getName() + // completely replaces superclass behavior + this(size, targetType, cvAttributes, false, null); + } + + private PointerType(SizeThunk size, Type targetType, int cvAttributes, boolean hasTypedefedName, String typedefedName) { + super(targetType.getName() + " *", size, cvAttributes); + this.hasTypedefedName = false; + this.targetType = targetType; + if (hasTypedefedName) { + setName(typedefedName); + } + } + + @Override + public int hashCode() { + return targetType.hashCode(); + } + + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof PointerType))) { + return false; + } + 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)); + } + + @Override + public void setName(String name) { + super.setName(name); + hasTypedefedName = true; + } + + @Override + public String getName(boolean includeCVAttrs) { + if (hasTypedefedName) { + return super.getName(includeCVAttrs); + } else { + // Lazy computation of name due to lazy setting of compound type + // names during parsing + if (computedName == null) { + computedName = targetType.getName(includeCVAttrs) + " *"; + computedName = computedName.intern(); + } + if (!includeCVAttrs) { + return computedName; + } + return targetType.getName(includeCVAttrs) + " * " + getCVAttributesString(); + } + } + + public boolean hasTypedefedName() { + return hasTypedefedName; + } + + @Override + public PointerType asPointer() { + return this; + } + + public Type getTargetType() { + return targetType; + } + + @Override + public boolean isFunctionPointer() { + return targetType.isFunction(); + } + + @Override + public String toString() { + if (hasTypedefedName) { + return super.getName(true); + } else { + if (!targetType.isFunction()) { + return targetType.toString() + " * " + getCVAttributesString(); + } + return toString(null, null); // this is a pointer to an unnamed function + } + } + + /** For use only when printing function pointers. Calling convention + string (i.e., "__stdcall") is optional and is generally only + needed on Windows. */ + public String toString(String functionName, String callingConvention) { + if (!targetType.isFunction()) { + throw new RuntimeException("<Internal error or misuse> This method is only for use when printing function pointers"); + } + return ((FunctionType) targetType).toString(functionName, callingConvention, false, true); + } + + @Override + public void visit(TypeVisitor arg) { + super.visit(arg); + targetType.visit(arg); + } + + Type newCVVariant(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 new file mode 100644 index 0000000..1eea9a4 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/PrimitiveType.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +public abstract class PrimitiveType extends Type implements Cloneable { + + protected PrimitiveType(String name, SizeThunk size, int cvAttributes) { + super(name, size, cvAttributes); + } + + @Override + public boolean isPrimitive() { + return true; + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java new file mode 100755 index 0000000..40ddd57 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/SizeThunk.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +/** 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 { + // Private constructor because there are only a few of these + private SizeThunk() {} + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + public abstract long compute(MachineDescription machDesc); + + public static final SizeThunk CHAR = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.charSizeInBytes(); + } + }; + + public static final SizeThunk SHORT = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.shortSizeInBytes(); + } + }; + + public static final SizeThunk INT = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.intSizeInBytes(); + } + }; + + public static final SizeThunk LONG = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.longSizeInBytes(); + } + }; + + public static final SizeThunk INT64 = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.int64SizeInBytes(); + } + }; + + public static final SizeThunk FLOAT = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.floatSizeInBytes(); + } + }; + + public static final SizeThunk DOUBLE = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.doubleSizeInBytes(); + } + }; + + public static final SizeThunk POINTER = new SizeThunk() { + public long compute(MachineDescription machDesc) { + return machDesc.pointerSizeInBytes(); + } + }; + + // Factory methods for performing certain limited kinds of + // arithmetic on these values + public static SizeThunk add(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return thunk1.compute(machDesc) + thunk2.compute(machDesc); + } + }; + } + + public static SizeThunk sub(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return thunk1.compute(machDesc) - thunk2.compute(machDesc); + } + }; + } + + public static SizeThunk mul(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return thunk1.compute(machDesc) * thunk2.compute(machDesc); + } + }; + } + + public static SizeThunk mod(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return thunk1.compute(machDesc) % thunk2.compute(machDesc); + } + }; + } + + public static SizeThunk roundUp(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + long sz1 = thunk1.compute(machDesc); + long sz2 = thunk2.compute(machDesc); + long rem = (sz1 % sz2); + if (rem == 0) { + return sz1; + } + return sz1 + (sz2 - rem); + } + }; + } + + public static SizeThunk max(final SizeThunk thunk1, + final SizeThunk thunk2) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return Math.max(thunk1.compute(machDesc), thunk2.compute(machDesc)); + } + }; + } + + public static SizeThunk constant(final int constant) { + return new SizeThunk() { + public long compute(MachineDescription machDesc) { + return constant; + } + }; + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/Type.java b/src/java/com/jogamp/gluegen/cgram/types/Type.java new file mode 100644 index 0000000..c58bfae --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/Type.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +import java.util.List; + +/** 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 { + + private String name; + private SizeThunk size; + private int cvAttributes; + private int typedefedCVAttributes; + private boolean hasTypedefName; + + protected Type(String name, SizeThunk size, int cvAttributes) { + setName(name); + this.size = size; + this.cvAttributes = cvAttributes; + hasTypedefName = false; + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + /** Returns the name of this type. The returned string is suitable + for use as a type specifier. Does not include any const/volatile + attributes. */ + public 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. */ + public String getName(boolean includeCVAttrs) { + if (!includeCVAttrs) { + return name; + } + return getCVAttributesString() + name; + } + + /** Set the name of this type; used for handling typedefs. */ + public void setName(String name) { + if (name == null) { + this.name = name; + } else { + this.name = name.intern(); + } + // 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; + } + + /** SizeThunk which computes size of this type in bytes. */ + public SizeThunk getSize() { return size; } + /** Size of this type in bytes according to the given MachineDescription. */ + public long getSize(MachineDescription machDesc) { + SizeThunk thunk = getSize(); + if (thunk == null) { + throw new RuntimeException("No size set for type \"" + getName() + "\""); + } + return thunk.compute(machDesc); + } + /** Set the size of this type; only available for CompoundTypes. */ + void setSize(SizeThunk size) { this.size = size; } + + /** Casts this to a BitType or returns null if not a BitType. */ + public BitType asBit() { return null; } + /** Casts this to an IntType or returns null if not an IntType. */ + public IntType asInt() { return null; } + /** Casts this to an EnumType or returns null if not an EnumType. */ + public EnumType asEnum() { return null; } + /** Casts this to a FloatType or returns null if not a FloatType. */ + public FloatType asFloat() { return null; } + /** Casts this to a DoubleType or returns null if not a DoubleType. */ + public DoubleType asDouble() { return null; } + /** Casts this to a PointerType or returns null if not a PointerType. */ + public PointerType asPointer() { return null; } + /** Casts this to an ArrayType or returns null if not an ArrayType. */ + public ArrayType asArray() { return null; } + /** Casts this to a CompoundType or returns null if not a CompoundType. */ + public CompoundType asCompound() { return null; } + /** Casts this to a FunctionType or returns null if not a FunctionType. */ + public FunctionType asFunction() { return null; } + /** Casts this to a VoidType or returns null if not a VoidType. */ + public VoidType asVoid() { return null; } + + /** Indicates whether this is a BitType. */ + public boolean isBit() { return (asBit() != null); } + /** Indicates whether this is an IntType. */ + public boolean isInt() { return (asInt() != null); } + /** Indicates whether this is an EnumType. */ + public boolean isEnum() { return (asEnum() != null); } + /** Indicates whether this is a FloatType. */ + public boolean isFloat() { return (asFloat() != null); } + /** Indicates whether this is a DoubleType. */ + public boolean isDouble() { return (asDouble() != null); } + /** Indicates whether this is a PointerType. */ + public boolean isPointer() { return (asPointer() != null); } + /** Indicates whether this is an ArrayType. */ + public boolean isArray() { return (asArray() != null); } + /** Indicates whether this is a CompoundType. */ + public boolean isCompound() { return (asCompound() != null); } + /** Indicates whether this is a FunctionType. */ + public boolean isFunction() { return (asFunction() != null); } + /** Indicates whether this is a VoidType. */ + public 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); } + + /** Indicates whether this type is a primitive type. */ + 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()); + } + + /** Hashcode for Types. */ + @Override + public int hashCode() { + if (name == null) { + return 0; + } + + if (cvAttributes != 0) { + String nameWithAttribs = name + cvAttributes; + return nameWithAttribs.hashCode(); + } + return name.hashCode(); + } + + /** + * Equality test for Types. + */ + @Override + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof Type))) { + return false; + } + Type t = (Type) arg; + return (((name == null ? t.name == null : name.equals(t.name)) || (name != null && name.equals(name))) && + (size == t.size) && (cvAttributes == t.cvAttributes)); + } + + /** 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); + } + + /** Visit this type and all of the component types of this one; for + example, the return type and argument types of a FunctionType. */ + public void visit(TypeVisitor visitor) { + visitor.visitType(this); + } + + public final int getCVAttributes() { + return cvAttributes; + } + + /** Returns a string indicating the const/volatile attributes of + this type. */ + public final String getCVAttributesString() { + if (isConst() && isVolatile()) return "const volatile "; + if (isConst()) return "const "; + if (isVolatile()) return "volatile "; + 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(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() { + PointerType pt = asPointer(); + if (pt == null) { + return 0; + } + return 1 + pt.getTargetType().pointerDepth(); + } + + /** 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() { + ArrayType arrayType = asArray(); + if (arrayType == null) { + return 0; + } + return 1 + arrayType.getElementType().arrayDimension(); + } + + /** Helper routine for list equality comparison */ + static boolean listsEqual(List a, List b) { + return ((a == null && b == null) || (a != null && b != null && a.equals(b))); + } +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java new file mode 100644 index 0000000..3bc4d87 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeDictionary.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +import java.util.*; + +/** Utility class for recording names of typedefs and structs. */ + +public class TypeDictionary { + /** Mapping from type name to type.*/ + private HashMap<String, Type> map = new HashMap<String, Type>(); + + /** Reverse mapping; created lazily from the regular map */ + private HashMap<Set<Type>, String> reverseMap = new HashMap<Set<Type>, String>(); + + /** Has a type been added/removed since the last time the reverse map was + * calculated? */ + private boolean reverseMapOutOfDate = false; + + /** + * Create a mapping from a type to its name. + * @param name the name to which the type is defined + * @param type the type that can be referred to by the specified name. + */ + public Type put(String name, Type type) { + reverseMapOutOfDate = true; + return map.put(name, type); + } + + /** Get the type corresponding to the given name. Returns null if no type + * was found corresponding to the given name. */ + public Type get(String name) { + return map.get(name); + } + + //this method is broken + /** + * Get the names that correspond to the given type. There will be more than + * one name in the returned list if the type has been defined to multiple + * names. Returns null if no names were found for given type. + */ +// public Set/*<String>*/ get(Type type) { +// if (reverseMapOutOfDate) { +// rebuildReverseMap(); +// reverseMapOutOfDate = false; +// } +// // Don't let callers muck with the set. +// return Collections.unmodifiableSet((Set)reverseMap.get(type)); +// } + + /** Remove the mapping from the specified name to its associated type.*/ + public Type remove(String name) { + reverseMapOutOfDate = true; + return map.remove(name); + } + + /** Get all the names that map to Types. + * @return a Set of Strings that are the typedef names that map to Types in the dictionary. + */ + public Set<String> keySet() { + return map.keySet(); + } + + public Set<Map.Entry<String, Type>> entrySet() { + return map.entrySet(); + } + + public boolean containsKey(String key) { + return map.containsKey(key); + } + + public boolean containsValue(Type value) { + return map.containsValue(value); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + /** Returns a collection of all the Types in the dictionary that are mapped via typedefs names. */ + public Collection<Type> values() { + return map.values(); + } + + /** Build the mapping of from each Type to all the names by which is may be + * referenced. Warning: this is a slow operation! + */ + /* + private void rebuildReverseMap() { + reverseMap.clear(); + for (Iterator<String> it = map.keySet().iterator(); it.hasNext(); ) { + String name = (String)it.next(); + Type type = (Type)map.get(name); + if (type == null) { + throw new IllegalStateException("Internal error; TypedefDictionary contains null Type for name \"" + name + "\""); + } + HashSet allNamesForType = (HashSet)reverseMap.get(type); + if (allNamesForType == null) { + allNamesForType = new HashSet<String>(); + reverseMap.put(type, allNamesForType); + } + allNamesForType.add(name); + } + } +*/ + /** + * Dumps the dictionary contents to the specified output stream, annotated + * with the specified description. Useful for debugging. + */ + /* + public void dumpDictionary(java.io.PrintStream out, String description) { + out.println("------------------------------------------------------------------------------"); + out.println("TypeDictionary: " + (description == null ? "" : description)); + out.println("------------------------------------------------------------------------------"); + out.println("Forward mapping: "); + for (Iterator names = keySet().iterator(); names.hasNext(); ) { + String typeName = (String)names.next(); + out.println(" [" + typeName + "]\t--> [" + get(typeName) + "]"); + } + out.println("Reverse mapping: "); + + // because the reverse mapping is built lazily upon query, we must force it to + // be built if it has not yet been built. + if (reverseMapOutOfDate) { + rebuildReverseMap(); + reverseMapOutOfDate = false; + } + for (Iterator types = reverseMap.keySet().iterator(); types.hasNext(); ) { + Type type = (Type)types.next(); + Set names = get(type); + out.println(" [" + type + "]\t--> " + names + ""); + } + out.println("------------------------------------------------------------------------------"); + } + */ +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java new file mode 100644 index 0000000..0889681 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/TypeVisitor.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.cgram.types; + +public interface TypeVisitor { + public void visitType(Type t); +} diff --git a/src/java/com/jogamp/gluegen/cgram/types/VoidType.java b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java new file mode 100644 index 0000000..fa098e7 --- /dev/null +++ b/src/java/com/jogamp/gluegen/cgram/types/VoidType.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.cgram.types; + +public class VoidType extends Type implements Cloneable { + + public VoidType(int cvAttributes) { + this("void", cvAttributes); + } + + private VoidType(String name, int cvAttributes) { + super(name, null, cvAttributes); + } + + @Override + public VoidType asVoid() { + return this; + } + + Type newCVVariant(int cvAttributes) { + return new VoidType(getName(), cvAttributes); + } +} diff --git a/src/java/com/jogamp/gluegen/jgram/JavaParser.g b/src/java/com/jogamp/gluegen/jgram/JavaParser.g new file mode 100644 index 0000000..18f2970 --- /dev/null +++ b/src/java/com/jogamp/gluegen/jgram/JavaParser.g @@ -0,0 +1,1315 @@ +/* Java 1.3 Recognizer + * + * Run 'java Main [-showtree] directory-full-of-java-files' + * + * [The -showtree option pops up a Swing frame that shows + * the AST constructed from the parser.] + * + * Run 'java Main <directory full of java files>' + * + * Contributing authors: + * John Mitchell [email protected] + * Terence Parr [email protected] + * John Lilley [email protected] + * Scott Stanchfield [email protected] + * Markus Mohnen [email protected] + * Peter Williams [email protected] + * Allan Jacobs [email protected] + * Steve Messick [email protected] + * John Pybus [email protected] + * + * Version 1.00 December 9, 1997 -- initial release + * Version 1.01 December 10, 1997 + * fixed bug in octal def (0..7 not 0..8) + * Version 1.10 August 1998 (parrt) + * added tree construction + * fixed definition of WS,comments for mac,pc,unix newlines + * added unary plus + * Version 1.11 (Nov 20, 1998) + * Added "shutup" option to turn off last ambig warning. + * Fixed inner class def to allow named class defs as statements + * synchronized requires compound not simple statement + * add [] after builtInType DOT class in primaryExpression + * "const" is reserved but not valid..removed from modifiers + * Version 1.12 (Feb 2, 1999) + * Changed LITERAL_xxx to xxx in tree grammar. + * Updated java.g to use tokens {...} now for 2.6.0 (new feature). + * + * Version 1.13 (Apr 23, 1999) + * Didn't have (stat)? for else clause in tree parser. + * Didn't gen ASTs for interface extends. Updated tree parser too. + * Updated to 2.6.0. + * Version 1.14 (Jun 20, 1999) + * Allowed final/abstract on local classes. + * Removed local interfaces from methods + * Put instanceof precedence where it belongs...in relationalExpr + * It also had expr not type as arg; fixed it. + * Missing ! on SEMI in classBlock + * fixed: (expr) + "string" was parsed incorrectly (+ as unary plus). + * fixed: didn't like Object[].class in parser or tree parser + * Version 1.15 (Jun 26, 1999) + * Screwed up rule with instanceof in it. :( Fixed. + * Tree parser didn't like (expr).something; fixed. + * Allowed multiple inheritance in tree grammar. oops. + * Version 1.16 (August 22, 1999) + * Extending an interface built a wacky tree: had extra EXTENDS. + * Tree grammar didn't allow multiple superinterfaces. + * Tree grammar didn't allow empty var initializer: {} + * Version 1.17 (October 12, 1999) + * ESC lexer rule allowed 399 max not 377 max. + * java.tree.g didn't handle the expression of synchronized + * statements. + * Version 1.18 (August 12, 2001) + * Terence updated to Java 2 Version 1.3 by + * observing/combining work of Allan Jacobs and Steve + * Messick. Handles 1.3 src. Summary: + * o primary didn't include boolean.class kind of thing + * o constructor calls parsed explicitly now: + * see explicitConstructorInvocation + * o add strictfp modifier + * o missing objBlock after new expression in tree grammar + * o merged local class definition alternatives, moved after declaration + * o fixed problem with ClassName.super.field + * o reordered some alternatives to make things more efficient + * o long and double constants were not differentiated from int/float + * o whitespace rule was inefficient: matched only one char + * o add an examples directory with some nasty 1.3 cases + * o made Main.java use buffered IO and a Reader for Unicode support + * o supports UNICODE? + * Using Unicode charVocabulay makes code file big, but only + * in the bitsets at the end. I need to make ANTLR generate + * unicode bitsets more efficiently. + * Version 1.19 (April 25, 2002) + * Terence added in nice fixes by John Pybus concerning floating + * constants and problems with super() calls. John did a nice + * reorg of the primary/postfix expression stuff to read better + * and makes f.g.super() parse properly (it was METHOD_CALL not + * a SUPER_CTOR_CALL). Also: + * + * o "finally" clause was a root...made it a child of "try" + * o Added stuff for asserts too for Java 1.4, but *commented out* + * as it is not backward compatible. + * + * Version 1.20 (October 27, 2002) + * + * Terence ended up reorging John Pybus' stuff to + * remove some nondeterminisms and some syntactic predicates. + * Note that the grammar is stricter now; e.g., this(...) must + * be the first statement. + * + * Trinary ?: operator wasn't working as array name: + * (isBig ? bigDigits : digits)[i]; + * + * Checked parser/tree parser on source for + * Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4, + * and the 110k-line jGuru server source. + * + * Version 1.21 (October 17, 2003) + * Fixed lots of problems including: + * Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g + * He found a problem/fix with floating point that start with 0 + * Ray also fixed problem that (int.class) was not recognized. + * Thorsten van Ellen noticed that \n are allowed incorrectly in strings. + * TJP fixed CHAR_LITERAL analogously. + * + * Version 1.22 (April 14, 2004) + * Changed vocab to be ..\uFFFE to avoid -1 char. removed dummy VOCAB rule. + * + * This grammar is in the PUBLIC DOMAIN + */ + +header { + package com.jogamp.gluegen.jgram; + + import java.util.*; + + import antlr.CommonAST; +} + +class JavaParser extends Parser; + +options { + k = 2; // two token lookahead + exportVocab=Java; // Call its vocabulary "Java" + codeGenMakeSwitchThreshold = 2; // Some optimizations + codeGenBitsetTestThreshold = 3; + defaultErrorHandler = false; // Don't generate parser error handlers + buildAST = true; + //buildAST = false; +} + +tokens { + BLOCK; MODIFIERS; OBJBLOCK; SLIST; CTOR_DEF; METHOD_DEF; VARIABLE_DEF; + INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; + PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE; + PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; + POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT; + IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; + FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract"; + STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; +} + +{ + public void clearParsedEnumNames() { + enumNames.clear(); + } + + /** Returns the EnumTypes this HeaderParser processed. */ + public Set getParsedEnumNames() { + return enumNames; + } + + /** Clears the list of functions this HeaderParser has parsed. + Useful when reusing the same HeaderParser for more than one + header file. */ + public void clearParsedFunctionNames() { + functionNames.clear(); + } + + /** Returns the list of FunctionSymbols this HeaderParser has parsed. */ + public Set getParsedFunctionNames() { + return functionNames; + } + + private Set/*<String>*/ functionNames = new HashSet(); + // hash from name of an enumerated value to the EnumType to which it belongs + private Set/*<String>*/ enumNames = new HashSet(); + + private int blockDepth = 0; +} + +// Compilation Unit: In Java, this is a single file. This is the start +// rule for this parser +compilationUnit + : // A compilation unit starts with an optional package definition + ( packageDefinition + | /* nothing */ + ) + + // Next we have a series of zero or more import statements + ( importDefinition )* + + // Wrapping things up with any number of class or interface + // definitions + ( typeDefinition )* + + EOF! + ; + + +// Package statement: "package" followed by an identifier. +packageDefinition + options {defaultErrorHandler = true;} // let ANTLR handle errors + : p:"package"^ {#p.setType(PACKAGE_DEF);} identifier SEMI! + ; + + +// Import statement: import followed by a package or class name +importDefinition + options {defaultErrorHandler = true;} + : i:"import"^ {#i.setType(IMPORT);} identifierStar SEMI! + ; + +// A type definition in a file is either a class or interface definition. +typeDefinition + options {defaultErrorHandler = true;} + : m:modifiers! + ( classDefinition[#m] + | interfaceDefinition[#m] + ) + | SEMI! + ; + +/** A declaration is the creation of a reference or primitive-type variable + * Create a separate Type/Var tree for each var in the var list. + */ +declaration! + : m:modifiers t:typeSpec[false] v:variableDefinitions[#m,#t] + {#declaration = #v;} + ; + +// A type specification is a type name with possible brackets afterwards +// (which would make it an array type). +typeSpec[boolean addImagNode] + : classTypeSpec[addImagNode] + | builtInTypeSpec[addImagNode] + ; + +// A class type specification is a class type with possible brackets afterwards +// (which would make it an array type). +classTypeSpec[boolean addImagNode] + : identifier (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + { + if ( addImagNode ) { + #classTypeSpec = #(#[TYPE,"TYPE"], #classTypeSpec); + } + } + ; + +// A builtin type specification is a builtin type with possible brackets +// afterwards (which would make it an array type). +builtInTypeSpec[boolean addImagNode] + : builtInType (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + { + if ( addImagNode ) { + #builtInTypeSpec = #(#[TYPE,"TYPE"], #builtInTypeSpec); + } + } + ; + +// A type name. which is either a (possibly qualified) class name or +// a primitive (builtin) type +type + : identifier + | builtInType + ; + +// The primitive types. +builtInType + : "void" + | "boolean" + | "byte" + | "char" + | "short" + | "int" + | "float" + | "long" + | "double" + ; + +// A (possibly-qualified) java identifier. We start with the first IDENT +// and expand its name by adding dots and following IDENTS +identifier + : IDENT ( DOT^ IDENT )* + ; + +identifierStar + : IDENT + ( DOT^ IDENT )* + ( DOT^ STAR )? + ; + +// A list of zero or more modifiers. We could have used (modifier)* in +// place of a call to modifiers, but I thought it was a good idea to keep +// this rule separate so they can easily be collected in a Vector if +// someone so desires +modifiers + : ( modifier )* + {#modifiers = #([MODIFIERS, "MODIFIERS"], #modifiers);} + ; + +// modifiers for Java classes, interfaces, class/instance vars and methods +modifier + : "private" + | "public" + | "protected" + | "static" + | "transient" + | "final" + | "abstract" + | "native" + | "threadsafe" + | "synchronized" +// | "const" // reserved word, but not valid + | "volatile" + | "strictfp" + ; + +// Definition of a Java class +classDefinition![AST modifiers] + : "class" IDENT + // it _might_ have a superclass... + sc:superClassClause + // it might implement some interfaces... + ic:implementsClause + // now parse the body of the class + cb:classBlock + {#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"], + modifiers,IDENT,sc,ic,cb);} + ; + +superClassClause! + : ( "extends" id:identifier )? + {#superClassClause = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"],id);} + ; + +// Definition of a Java Interface +interfaceDefinition![AST modifiers] + : "interface" IDENT + // it might extend some other interfaces + ie:interfaceExtends + // now parse the body of the interface (looks like a class...) + cb:classBlock + {#interfaceDefinition = #(#[INTERFACE_DEF,"INTERFACE_DEF"], + modifiers,IDENT,ie,cb);} + ; + + +// This is the body of a class. You can have fields and extra semicolons, +// That's about it (until you see what a field is...) +classBlock + : LCURLY! { blockDepth++; } + ( field | SEMI! )* + RCURLY! { blockDepth--; } + {#classBlock = #([OBJBLOCK, "OBJBLOCK"], #classBlock);} + ; + +// An interface can extend several other interfaces... +interfaceExtends + : ( + e:"extends"! + identifier ( COMMA! identifier )* + )? + {#interfaceExtends = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"], + #interfaceExtends);} + ; + +// A class can implement several interfaces... +implementsClause + : ( + i:"implements"! identifier ( COMMA! identifier )* + )? + {#implementsClause = #(#[IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE"], + #implementsClause);} + ; + +// Now the various things that can be defined inside a class or interface... +// Note that not all of these are really valid in an interface (constructors, +// for example), and if this grammar were used for a compiler there would +// need to be some semantic checks to make sure we're doing the right thing... +field! + : // method, constructor, or variable declaration + mods:modifiers + ( h:ctorHead s:constructorBody // constructor + {#field = #(#[CTOR_DEF,"CTOR_DEF"], mods, h, s);} + + | cd:classDefinition[#mods] // inner class + {#field = #cd;} + + | id:interfaceDefinition[#mods] // inner interface + {#field = #id;} + + | t:typeSpec[false] // method or variable declaration(s) + ( fn:IDENT // the name of the method + + // parse the formal parameter declarations. + LPAREN! param:parameterDeclarationList RPAREN! + + rt:declaratorBrackets[#t] + + // get the list of exceptions that this method is + // declared to throw + (tc:throwsClause)? + + ( s2:compoundStatement | SEMI ) + {#field = #(#[METHOD_DEF,"METHOD_DEF"], + mods, + #(#[TYPE,"TYPE"],rt), + fn, + param, + tc, + s2); + if(blockDepth==1) { + functionNames.add(fn.getText()); } } + | v:variableDefinitions[#mods,#t] SEMI +// {#field = #(#[VARIABLE_DEF,"VARIABLE_DEF"], v);} + {#field = #v;} + ) + ) + + // "static { ... }" class initializer + | "static" s3:compoundStatement + {#field = #(#[STATIC_INIT,"STATIC_INIT"], s3);} + + // "{ ... }" instance initializer + | s4:compoundStatement + {#field = #(#[INSTANCE_INIT,"INSTANCE_INIT"], s4);} + ; + +constructorBody + : lc:LCURLY^ {#lc.setType(SLIST); blockDepth++; } + ( options { greedy=true; } : explicitConstructorInvocation)? + (statement)* + RCURLY! { blockDepth--; } + ; + +/** Catch obvious constructor calls, but not the expr.super(...) calls */ +explicitConstructorInvocation + : "this"! lp1:LPAREN^ argList RPAREN! SEMI! + {#lp1.setType(CTOR_CALL);} + | "super"! lp2:LPAREN^ argList RPAREN! SEMI! + {#lp2.setType(SUPER_CTOR_CALL);} + ; + +variableDefinitions[AST mods, AST t] + : variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t)] + ( COMMA! + variableDeclarator[getASTFactory().dupTree(mods), + getASTFactory().dupTree(t)] + )* + ; + +/** Declaration of a variable. This can be a class/instance variable, + * or a local variable in a method + * It can also include possible initialization. + */ +variableDeclarator![AST mods, AST t] + : id:IDENT d:declaratorBrackets[t] v:varInitializer + {#variableDeclarator = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods, #(#[TYPE,"TYPE"],d), id, v); + if(blockDepth==1) { + enumNames.add(id.getText()); + } + } + ; + +declaratorBrackets[AST typ] + : {#declaratorBrackets=typ;} + (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)* + ; + +varInitializer + : ( ASSIGN^ initializer )? + ; + +// This is an initializer used to set up an array. +arrayInitializer + : lc:LCURLY^ {#lc.setType(ARRAY_INIT); blockDepth++; } + ( initializer + ( + // CONFLICT: does a COMMA after an initializer start a new + // initializer or start the option ',' at end? + // ANTLR generates proper code by matching + // the comma as soon as possible. + options { + warnWhenFollowAmbig = false; + } + : + COMMA! initializer + )* + (COMMA!)? + )? + RCURLY! { blockDepth--; } + ; + + +// The two "things" that can initialize an array element are an expression +// and another (nested) array initializer. +initializer + : expression + | arrayInitializer + ; + +// This is the header of a method. It includes the name and parameters +// for the method. +// This also watches for a list of exception classes in a "throws" clause. +ctorHead + : IDENT // the name of the method + + // parse the formal parameter declarations. + LPAREN! parameterDeclarationList RPAREN! + + // get the list of exceptions that this method is declared to throw + (throwsClause)? + ; + +// This is a list of exception classes that the method is declared to throw +throwsClause + : "throws"^ identifier ( COMMA! identifier )* + ; + + +// A list of formal parameters +parameterDeclarationList + : ( parameterDeclaration ( COMMA! parameterDeclaration )* )? + {#parameterDeclarationList = #(#[PARAMETERS,"PARAMETERS"], + #parameterDeclarationList);} + ; + +// A formal parameter. +parameterDeclaration! + : pm:parameterModifier t:typeSpec[false] id:IDENT + pd:declaratorBrackets[#t] + {#parameterDeclaration = #(#[PARAMETER_DEF,"PARAMETER_DEF"], + pm, #([TYPE,"TYPE"],pd), id);} + ; + +parameterModifier + : (f:"final")? + {#parameterModifier = #(#[MODIFIERS,"MODIFIERS"], f);} + ; + +// Compound statement. This is used in many contexts: +// Inside a class definition prefixed with "static": +// it is a class initializer +// Inside a class definition without "static": +// it is an instance initializer +// As the body of a method +// As a completely indepdent braced block of code inside a method +// it starts a new scope for variable definitions + +compoundStatement + : lc:LCURLY^ {#lc.setType(SLIST); blockDepth++; } + // include the (possibly-empty) list of statements + (statement)* + RCURLY! { blockDepth--; } + ; + + +statement + // A list of statements in curly braces -- start a new scope! + : compoundStatement + + // declarations are ambiguous with "ID DOT" relative to expression + // statements. Must backtrack to be sure. Could use a semantic + // predicate to test symbol table to see what the type was coming + // up, but that's pretty hard without a symbol table ;) + | (declaration)=> declaration SEMI! + + // An expression statement. This could be a method call, + // assignment statement, or any other expression evaluated for + // side-effects. + | expression SEMI! + + // class definition + | m:modifiers! classDefinition[#m] + + // Attach a label to the front of a statement + | IDENT c:COLON^ {#c.setType(LABELED_STAT);} statement + + // If-else statement + | "if"^ LPAREN! expression RPAREN! statement + ( + // CONFLICT: the old "dangling-else" problem... + // ANTLR generates proper code matching + // as soon as possible. Hush warning. + options { + warnWhenFollowAmbig = false; + } + : + "else"! statement + )? + + // For statement + | "for"^ + LPAREN! + forInit SEMI! // initializer + forCond SEMI! // condition test + forIter // updater + RPAREN! + statement // statement to loop over + + // While statement + | "while"^ LPAREN! expression RPAREN! statement + + // do-while statement + | "do"^ statement "while"! LPAREN! expression RPAREN! SEMI! + + // get out of a loop (or switch) + | "break"^ (IDENT)? SEMI! + + // do next iteration of a loop + | "continue"^ (IDENT)? SEMI! + + // Return an expression + | "return"^ (expression)? SEMI! + + // switch/case statement + | "switch"^ LPAREN! expression RPAREN! LCURLY! { blockDepth++; } + ( casesGroup )* + RCURLY! { blockDepth--; } + + // exception try-catch block + | tryBlock + + // throw an exception + | "throw"^ expression SEMI! + + // synchronize a statement + | "synchronized"^ LPAREN! expression RPAREN! compoundStatement + + // asserts (uncomment if you want 1.4 compatibility) + // | "assert"^ expression ( COLON! expression )? SEMI! + + // empty statement + | s:SEMI {#s.setType(EMPTY_STAT);} + ; + +casesGroup + : ( // CONFLICT: to which case group do the statements bind? + // ANTLR generates proper code: it groups the + // many "case"/"default" labels together then + // follows them with the statements + options { + greedy = true; + } + : + aCase + )+ + caseSList + {#casesGroup = #([CASE_GROUP, "CASE_GROUP"], #casesGroup);} + ; + +aCase + : ("case"^ expression | "default") COLON! + ; + +caseSList + : (statement)* + {#caseSList = #(#[SLIST,"SLIST"],#caseSList);} + ; + +// The initializer for a for loop +forInit + // if it looks like a declaration, it is + : ( (declaration)=> declaration + // otherwise it could be an expression list... + | expressionList + )? + {#forInit = #(#[FOR_INIT,"FOR_INIT"],#forInit);} + ; + +forCond + : (expression)? + {#forCond = #(#[FOR_CONDITION,"FOR_CONDITION"],#forCond);} + ; + +forIter + : (expressionList)? + {#forIter = #(#[FOR_ITERATOR,"FOR_ITERATOR"],#forIter);} + ; + +// an exception handler try/catch block +tryBlock + : "try"^ compoundStatement + (handler)* + ( finallyClause )? + ; + +finallyClause + : "finally"^ compoundStatement + ; + +// an exception handler +handler + : "catch"^ LPAREN! parameterDeclaration RPAREN! compoundStatement + ; + + +// expressions +// Note that most of these expressions follow the pattern +// thisLevelExpression : +// nextHigherPrecedenceExpression +// (OPERATOR nextHigherPrecedenceExpression)* +// which is a standard recursive definition for a parsing an expression. +// The operators in java have the following precedences: +// lowest (13) = *= /= %= += -= <<= >>= >>>= &= ^= |= +// (12) ?: +// (11) || +// (10) && +// ( 9) | +// ( 8) ^ +// ( 7) & +// ( 6) == != +// ( 5) < <= > >= +// ( 4) << >> +// ( 3) +(binary) -(binary) +// ( 2) * / % +// ( 1) ++ -- +(unary) -(unary) ~ ! (type) +// [] () (method call) . (dot -- identifier qualification) +// new () (explicit parenthesis) +// +// the last two are not usually on a precedence chart; I put them in +// to point out that new has a higher precedence than '.', so you +// can validy use +// new Frame().show() +// +// Note that the above precedence levels map to the rules below... +// Once you have a precedence chart, writing the appropriate rules as below +// is usually very straightfoward + + + +// the mother of all expressions +expression + : assignmentExpression + {#expression = #(#[EXPR,"EXPR"],#expression);} + ; + + +// This is a list of expressions. +expressionList + : expression (COMMA! expression)* + {#expressionList = #(#[ELIST,"ELIST"], expressionList);} + ; + + +// assignment expression (level 13) +assignmentExpression + : conditionalExpression + ( ( ASSIGN^ + | PLUS_ASSIGN^ + | MINUS_ASSIGN^ + | STAR_ASSIGN^ + | DIV_ASSIGN^ + | MOD_ASSIGN^ + | SR_ASSIGN^ + | BSR_ASSIGN^ + | SL_ASSIGN^ + | BAND_ASSIGN^ + | BXOR_ASSIGN^ + | BOR_ASSIGN^ + ) + assignmentExpression + )? + ; + + +// conditional test (level 12) +conditionalExpression + : logicalOrExpression + ( QUESTION^ assignmentExpression COLON! conditionalExpression )? + ; + + +// logical or (||) (level 11) +logicalOrExpression + : logicalAndExpression (LOR^ logicalAndExpression)* + ; + + +// logical and (&&) (level 10) +logicalAndExpression + : inclusiveOrExpression (LAND^ inclusiveOrExpression)* + ; + + +// bitwise or non-short-circuiting or (|) (level 9) +inclusiveOrExpression + : exclusiveOrExpression (BOR^ exclusiveOrExpression)* + ; + + +// exclusive or (^) (level 8) +exclusiveOrExpression + : andExpression (BXOR^ andExpression)* + ; + + +// bitwise or non-short-circuiting and (&) (level 7) +andExpression + : equalityExpression (BAND^ equalityExpression)* + ; + + +// equality/inequality (==/!=) (level 6) +equalityExpression + : relationalExpression ((NOT_EQUAL^ | EQUAL^) relationalExpression)* + ; + + +// boolean relational expressions (level 5) +relationalExpression + : shiftExpression + ( ( ( LT^ + | GT^ + | LE^ + | GE^ + ) + shiftExpression + )* + | "instanceof"^ typeSpec[true] + ) + ; + + +// bit shift expressions (level 4) +shiftExpression + : additiveExpression ((SL^ | SR^ | BSR^) additiveExpression)* + ; + + +// binary addition/subtraction (level 3) +additiveExpression + : multiplicativeExpression ((PLUS^ | MINUS^) multiplicativeExpression)* + ; + + +// multiplication/division/modulo (level 2) +multiplicativeExpression + : unaryExpression ((STAR^ | DIV^ | MOD^ ) unaryExpression)* + ; + +unaryExpression + : INC^ unaryExpression + | DEC^ unaryExpression + | MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression + | PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression + | unaryExpressionNotPlusMinus + ; + +unaryExpressionNotPlusMinus + : BNOT^ unaryExpression + | LNOT^ unaryExpression + + // use predicate to skip cases like: (int.class) + | (LPAREN builtInTypeSpec[true] RPAREN) => + lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN! + unaryExpression + + // Have to backtrack to see if operator follows. If no operator + // follows, it's a typecast. No semantic checking needed to parse. + // if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)" + | (LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus)=> + lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN! + unaryExpressionNotPlusMinus + + | postfixExpression + ; + +// qualified names, array expressions, method invocation, post inc/dec +postfixExpression + : + /* + "this"! lp1:LPAREN^ argList RPAREN! + {#lp1.setType(CTOR_CALL);} + + | "super"! lp2:LPAREN^ argList RPAREN! + {#lp2.setType(SUPER_CTOR_CALL);} + | + */ + primaryExpression + + ( + /* + options { + // the use of postfixExpression in SUPER_CTOR_CALL adds DOT + // to the lookahead set, and gives loads of false non-det + // warnings. + // shut them off. + generateAmbigWarnings=false; + } + : */ + DOT^ IDENT + ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} + argList + RPAREN! + )? + | DOT^ "this" + + | DOT^ "super" + ( // (new Outer()).super() (create enclosing instance) + lp3:LPAREN^ argList RPAREN! + {#lp3.setType(SUPER_CTOR_CALL);} + | DOT^ IDENT + ( lps:LPAREN^ {#lps.setType(METHOD_CALL);} + argList + RPAREN! + )? + ) + | DOT^ newExpression + | lb:LBRACK^ {#lb.setType(INDEX_OP);} expression RBRACK! + )* + + ( // possibly add on a post-increment or post-decrement. + // allows INC/DEC on too much, but semantics can check + in:INC^ {#in.setType(POST_INC);} + | de:DEC^ {#de.setType(POST_DEC);} + )? + ; + +// the basic element of an expression +primaryExpression + : identPrimary ( options {greedy=true;} : DOT^ "class" )? + | constant + | "true" + | "false" + | "null" + | newExpression + | "this" + | "super" + | LPAREN! assignmentExpression RPAREN! + // look for int.class and int[].class + | builtInType + ( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )* + DOT^ "class" + ; + +/** Match a, a.b.c refs, a.b.c(...) refs, a.b.c[], a.b.c[].class, + * and a.b.c.class refs. Also this(...) and super(...). Match + * this or super. + */ +identPrimary + : IDENT + ( + options { + // .ident could match here or in postfixExpression. + // We do want to match here. Turn off warning. + greedy=true; + } + : DOT^ IDENT + )* + ( + options { + // ARRAY_DECLARATOR here conflicts with INDEX_OP in + // postfixExpression on LBRACK RBRACK. + // We want to match [] here, so greedy. This overcomes + // limitation of linear approximate lookahead. + greedy=true; + } + : ( lp:LPAREN^ {#lp.setType(METHOD_CALL);} argList RPAREN! ) + | ( options {greedy=true;} : + lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK! + )+ + )? + ; + +/** object instantiation. + * Trees are built as illustrated by the following input/tree pairs: + * + * new T() + * + * new + * | + * T -- ELIST + * | + * arg1 -- arg2 -- .. -- argn + * + * new int[] + * + * new + * | + * int -- ARRAY_DECLARATOR + * + * new int[] {1,2} + * + * new + * | + * int -- ARRAY_DECLARATOR -- ARRAY_INIT + * | + * EXPR -- EXPR + * | | + * 1 2 + * + * new int[3] + * new + * | + * int -- ARRAY_DECLARATOR + * | + * EXPR + * | + * 3 + * + * new int[1][2] + * + * new + * | + * int -- ARRAY_DECLARATOR + * | + * ARRAY_DECLARATOR -- EXPR + * | | + * EXPR 1 + * | + * 2 + * + */ +newExpression + : "new"^ type + ( LPAREN! argList RPAREN! (classBlock)? + + //java 1.1 + // Note: This will allow bad constructs like + // new int[4][][3] {exp,exp}. + // There needs to be a semantic check here... + // to make sure: + // a) [ expr ] and [ ] are not mixed + // b) [ expr ] and an init are not used together + + | newArrayDeclarator (arrayInitializer)? + ) + ; + +argList + : ( expressionList + | /*nothing*/ + {#argList = #[ELIST,"ELIST"];} + ) + ; + +newArrayDeclarator + : ( + // CONFLICT: + // newExpression is a primaryExpression which can be + // followed by an array index reference. This is ok, + // as the generated code will stay in this loop as + // long as it sees an LBRACK (proper behavior) + options { + warnWhenFollowAmbig = false; + } + : + lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} + (expression)? + RBRACK! + )+ + ; + +constant + : NUM_INT + | CHAR_LITERAL + | STRING_LITERAL + | NUM_FLOAT + | NUM_LONG + | NUM_DOUBLE + ; + +//---------------------------------------------------------------------------- +// The Java scanner +//---------------------------------------------------------------------------- +class JavaLexer extends Lexer; + +options { + exportVocab=Java; // call the vocabulary "Java" + testLiterals=false; // don't automatically test for literals + k=4; // four characters of lookahead + charVocabulary='\u0003'..'\u7FFE'; + // without inlining some bitset tests, couldn't do unicode; + // I need to make ANTLR generate smaller bitsets; see + // bottom of JavaLexer.java + codeGenBitsetTestThreshold=20; +} + +// OPERATORS +QUESTION : '?' ; +LPAREN : '(' ; +RPAREN : ')' ; +LBRACK : '[' ; +RBRACK : ']' ; +LCURLY : '{' ; +RCURLY : '}' ; +COLON : ':' ; +COMMA : ',' ; +//DOT : '.' ; +ASSIGN : '=' ; +EQUAL : "==" ; +LNOT : '!' ; +BNOT : '~' ; +NOT_EQUAL : "!=" ; +DIV : '/' ; +DIV_ASSIGN : "/=" ; +PLUS : '+' ; +PLUS_ASSIGN : "+=" ; +INC : "++" ; +MINUS : '-' ; +MINUS_ASSIGN : "-=" ; +DEC : "--" ; +STAR : '*' ; +STAR_ASSIGN : "*=" ; +MOD : '%' ; +MOD_ASSIGN : "%=" ; +SR : ">>" ; +SR_ASSIGN : ">>=" ; +BSR : ">>>" ; +BSR_ASSIGN : ">>>=" ; +GE : ">=" ; +GT : ">" ; +SL : "<<" ; +SL_ASSIGN : "<<=" ; +LE : "<=" ; +LT : '<' ; +BXOR : '^' ; +BXOR_ASSIGN : "^=" ; +BOR : '|' ; +BOR_ASSIGN : "|=" ; +LOR : "||" ; +BAND : '&' ; +BAND_ASSIGN : "&=" ; +LAND : "&&" ; +SEMI : ';' ; + + +// Whitespace -- ignored +WS : ( ' ' + | '\t' + | '\f' + // handle newlines + | ( options {generateAmbigWarnings=false;} + : "\r\n" // Evil DOS + | '\r' // Macintosh + | '\n' // Unix (the right way) + ) + { newline(); } + )+ + { _ttype = Token.SKIP; } + ; + +// Single-line comments +SL_COMMENT + : "//" + (~('\n'|'\r'))* ('\n'|'\r'('\n')?)? + {$setType(Token.SKIP); newline();} + ; + +// multiple-line comments +ML_COMMENT + : "/*" + ( /* '\r' '\n' can be matched in one alternative or by matching + '\r' in one iteration and '\n' in another. I am trying to + handle any flavor of newline that comes in, but the language + that allows both "\r\n" and "\r" and "\n" to all be valid + newline is ambiguous. Consequently, the resulting grammar + must be ambiguous. I'm shutting this warning off. + */ + options { + generateAmbigWarnings=false; + } + : + { LA(2)!='/' }? '*' + | '\r' '\n' {newline();} + | '\r' {newline();} + | '\n' {newline();} + | ~('*'|'\n'|'\r') + )* + "*/" + {$setType(Token.SKIP);} + ; + + +// character literals +CHAR_LITERAL + : '\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\'' + ; + +// string literals +STRING_LITERAL + : '"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"' + ; + + +// escape sequence -- note that this is protected; it can only be called +// from another lexer rule -- it will not ever directly return a token to +// the parser +// There are various ambiguities hushed in this rule. The optional +// '0'...'9' digit matches should be matched here rather than letting +// them go back to STRING_LITERAL to be matched. ANTLR does the +// right thing by matching immediately; hence, it's ok to shut off +// the FOLLOW ambig warnings. +protected +ESC + : '\\' + ( 'n' + | 'r' + | 't' + | 'b' + | 'f' + | '"' + | '\'' + | '\\' + | ('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT + | '0'..'3' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + )? + | '4'..'7' + ( + options { + warnWhenFollowAmbig = false; + } + : '0'..'7' + )? + ) + ; + + +// hexadecimal digit (again, note it's protected!) +protected +HEX_DIGIT + : ('0'..'9'|'A'..'F'|'a'..'f') + ; + + +// an identifier. Note that testLiterals is set to true! This means +// that after we match the rule, we look in the literals table to see +// if it's a literal or really an identifer +IDENT + options {testLiterals=true;} + : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')* + ; + + +// a numeric literal +NUM_INT + {boolean isDecimal=false; Token t=null;} + : '.' {_ttype = DOT;} + ( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})? + { + if (t != null && t.getText().toUpperCase().indexOf('F')>=0) { + _ttype = NUM_FLOAT; + } + else { + _ttype = NUM_DOUBLE; // assume double + } + } + )? + + | ( '0' {isDecimal = true;} // special case for just '0' + ( ('x'|'X') + ( // hex + // the 'e'|'E' and float suffix stuff look + // like hex digits, hence the (...)+ doesn't + // know when to stop: ambig. ANTLR resolves + // it correctly by matching immediately. It + // is therefor ok to hush warning. + options { + warnWhenFollowAmbig=false; + } + : HEX_DIGIT + )+ + + | //float or double with leading zero + (('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+ + + | ('0'..'7')+ // octal + )? + | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal + ) + ( ('l'|'L') { _ttype = NUM_LONG; } + + // only check to see if it's a float if looks like decimal so far + | {isDecimal}? + ( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})? + | EXPONENT (f3:FLOAT_SUFFIX {t=f3;})? + | f4:FLOAT_SUFFIX {t=f4;} + ) + { + if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { + _ttype = NUM_FLOAT; + } + else { + _ttype = NUM_DOUBLE; // assume double + } + } + )? + ; + + +// a couple protected methods to assist in matching floating point numbers +protected +EXPONENT + : ('e'|'E') ('+'|'-')? ('0'..'9')+ + ; + + +protected +FLOAT_SUFFIX + : 'f'|'F'|'d'|'D' + ; + diff --git a/src/java/com/jogamp/gluegen/jgram/Test.java b/src/java/com/jogamp/gluegen/jgram/Test.java new file mode 100644 index 0000000..c890f67 --- /dev/null +++ b/src/java/com/jogamp/gluegen/jgram/Test.java @@ -0,0 +1,132 @@ +package com.jogamp.gluegen.jgram; + +import java.util.*; + +import java.io.*; +// import antlr.collections.AST; +import antlr.collections.impl.*; +import antlr.debug.misc.*; +import antlr.*; +// import java.awt.event.*; + +class Test { + + static boolean showTree = false; + public static void main(String[] args) { + // Use a try/catch block for parser exceptions + try { + // if we have at least one command-line argument + if (args.length > 0 ) { + System.err.println("Parsing..."); + + // for each directory/file specified on the command line + for(int i=0; i< args.length;i++) { + if ( args[i].equals("-showtree") ) { + showTree = true; + } + else { + doFile(new File(args[i])); // parse it + } + } } + else + System.err.println("Usage: java com.jogamp.gluegen.jgram.Test [-showtree] "+ + "<directory or file name>"); + } + catch(Exception e) { + System.err.println("exception: "+e); + e.printStackTrace(System.err); // so we can get stack trace + } + } + + + // This method decides what action to take based on the type of + // file we are looking at + public static void doFile(File f) + throws Exception { + // If this is a directory, walk each file/dir in that directory + if (f.isDirectory()) { + String files[] = f.list(); + for(int i=0; i < files.length; i++) + doFile(new File(f, files[i])); + } + + // otherwise, if this is a java file, parse it! + else if ((f.getName().length()>5) && + f.getName().substring(f.getName().length()-5).equals(".java")) { + System.err.println(" "+f.getAbsolutePath()); + // parseFile(f.getName(), new FileInputStream(f)); + parseFile(f.getName(), new BufferedReader(new FileReader(f))); + } + } + + // Here's where we do the real work... + public static void parseFile(String f, Reader r) + throws Exception { + try { + // Create a scanner that reads from the input stream passed to us + JavaLexer lexer = new JavaLexer(r); + lexer.setFilename(f); + + // Create a parser that reads from the scanner + JavaParser parser = new JavaParser(lexer); + parser.setFilename(f); + + // start parsing at the compilationUnit rule + parser.compilationUnit(); + + Set set = parser.getParsedEnumNames(); + System.out.println("Enums"); + for(Iterator iter = set.iterator(); iter.hasNext(); ) { + String s = (String) iter.next(); + System.out.println(s); + } + System.out.println("Functions"); + set = parser.getParsedFunctionNames(); + for(Iterator iter = set.iterator(); iter.hasNext(); ) { + String s = (String) iter.next(); + System.out.println(s); + } + + // do something with the tree + //doTreeAction(f, parser.getAST(), parser.getTokenNames()); + } + catch (Exception e) { + System.err.println("parser exception: "+e); + e.printStackTrace(); // so we can get stack trace + } + } + + /* + public static void doTreeAction(String f, AST t, String[] tokenNames) { + if ( t==null ) return; + if ( showTree ) { + ((CommonAST)t).setVerboseStringConversion(true, tokenNames); + ASTFactory factory = new ASTFactory(); + AST r = factory.create(0,"AST ROOT"); + r.setFirstChild(t); + final ASTFrame frame = new ASTFrame("Java AST", r); + frame.setVisible(true); + frame.addWindowListener( + new WindowAdapter() { + public void windowClosing (WindowEvent e) { + frame.setVisible(false); // hide the Frame + frame.dispose(); + System.exit(0); + } + } + ); + // System.out.println(t.toStringList()); + } + JavaTreeParser tparse = new JavaTreeParser(); + try { + tparse.compilationUnit(t); + // System.out.println("successful walk of result AST for "+f); + } + catch (RecognitionException e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + + } */ +} + diff --git a/src/java/com/jogamp/gluegen/nativesig/NativeSignatureEmitter.java b/src/java/com/jogamp/gluegen/nativesig/NativeSignatureEmitter.java new file mode 100755 index 0000000..bbc33d7 --- /dev/null +++ b/src/java/com/jogamp/gluegen/nativesig/NativeSignatureEmitter.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.nativesig; + +import com.jogamp.gluegen.MethodBinding; +import com.jogamp.gluegen.FunctionEmitter; +import com.jogamp.gluegen.JavaMethodBindingEmitter; +import com.jogamp.gluegen.JavaType; +import java.io.*; +import java.util.*; + +import com.jogamp.gluegen.*; +import com.jogamp.gluegen.cgram.types.*; +import com.jogamp.gluegen.opengl.*; +import com.jogamp.gluegen.procaddress.*; + +/** + * Emitter producing NativeSignature attributes. + */ +public class NativeSignatureEmitter extends GLEmitter { + + @Override + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception { + + // Allow superclass to do most of the work for us + List<? extends FunctionEmitter> res = super.generateMethodBindingEmitters(methodBindingSet, sym); + + // Filter out all non-JavaMethodBindingEmitters + for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + FunctionEmitter emitter = iter.next(); + if (!(emitter instanceof JavaMethodBindingEmitter)) { + iter.remove(); + } + } + + if (res.isEmpty()) { + return res; + } + + PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); + + List<FunctionEmitter> processed = new ArrayList<FunctionEmitter>(); + + // First, filter out all emitters going to the "other" (public) writer + for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + FunctionEmitter emitter = iter.next(); + if (emitter.getDefaultOutput() != writer) { + processed.add(emitter); + iter.remove(); + } + } + + // Now process all of the remaining emitters sorted by MethodBinding + while (!res.isEmpty()) { + List<JavaMethodBindingEmitter> emittersForBinding = new ArrayList<JavaMethodBindingEmitter>(); + JavaMethodBindingEmitter emitter = (JavaMethodBindingEmitter) res.remove(0); + emittersForBinding.add(emitter); + MethodBinding binding = emitter.getBinding(); + for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + JavaMethodBindingEmitter emitter2 = (JavaMethodBindingEmitter) iter.next(); + if (emitter2.getBinding() == binding) { + emittersForBinding.add(emitter2); + iter.remove(); + } + } + generateNativeSignatureEmitters(binding, emittersForBinding); + processed.addAll(emittersForBinding); + } + + return processed; + } + + protected void generateNativeSignatureEmitters(MethodBinding binding, List<JavaMethodBindingEmitter> allEmitters) { + + if (allEmitters.isEmpty()) { + return; + } + + PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); + + // Give ourselves the chance to interpose on the generation of all code to keep things simple + List<JavaMethodBindingEmitter> newEmitters = new ArrayList<JavaMethodBindingEmitter>(); + for (JavaMethodBindingEmitter javaEmitter : allEmitters) { + NativeSignatureJavaMethodBindingEmitter newEmitter = null; + if (javaEmitter instanceof GLJavaMethodBindingEmitter) { + newEmitter = new NativeSignatureJavaMethodBindingEmitter((GLJavaMethodBindingEmitter) javaEmitter); + } else if (javaEmitter instanceof ProcAddressJavaMethodBindingEmitter) { + newEmitter = new NativeSignatureJavaMethodBindingEmitter((ProcAddressJavaMethodBindingEmitter) javaEmitter); + } else { + newEmitter = new NativeSignatureJavaMethodBindingEmitter(javaEmitter, this); + } + newEmitters.add(newEmitter); + } + allEmitters.clear(); + allEmitters.addAll(newEmitters); + + // Detect whether we need to produce more or modify some of these emitters. + // Note that at this point we are assuming that generatePublicEmitters has + // been called with signatureOnly both true and false. + if (signatureContainsStrings(binding) && !haveEmitterWithBody(allEmitters)) { + // This basically handles glGetString but also any similar methods + NativeSignatureJavaMethodBindingEmitter javaEmitter = findEmitterWithWriter(allEmitters, writer); + + // First, we need to clone this emitter to produce the native + // entry point + NativeSignatureJavaMethodBindingEmitter emitter = new NativeSignatureJavaMethodBindingEmitter(javaEmitter); + emitter.removeModifier(JavaMethodBindingEmitter.PUBLIC); + emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); + emitter.setForImplementingMethodCall(true); + // Note: this is chosen so we don't have to change the logic in + // emitReturnVariableSetupAndCall which decides which variant + // (direct / indirect) to call + emitter.setForDirectBufferImplementation(true); + allEmitters.add(emitter); + + // Now make the original emitter non-native and cause it to emit a body + javaEmitter.removeModifier(JavaMethodBindingEmitter.NATIVE); + javaEmitter.setEmitBody(true); + } + } + + protected boolean signatureContainsStrings(MethodBinding binding) { + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isString() || type.isStringArray()) { + return true; + } + } + JavaType retType = binding.getJavaReturnType(); + if (retType.isString() || retType.isStringArray()) { + return true; + } + return false; + } + + protected boolean haveEmitterWithBody(List<JavaMethodBindingEmitter> allEmitters) { + for (JavaMethodBindingEmitter emitter : allEmitters) { + if (!emitter.signatureOnly()) { + return true; + } + } + return false; + } + + protected NativeSignatureJavaMethodBindingEmitter findEmitterWithWriter(List<JavaMethodBindingEmitter> allEmitters, PrintWriter writer) { + for (JavaMethodBindingEmitter jemitter : allEmitters) { + NativeSignatureJavaMethodBindingEmitter emitter = (NativeSignatureJavaMethodBindingEmitter)jemitter; + if (emitter.getDefaultOutput() == writer) { + return emitter; + } + } + throw new RuntimeException("Unexpectedly failed to find an emitter with the given writer"); + } +} diff --git a/src/java/com/jogamp/gluegen/nativesig/NativeSignatureJavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/nativesig/NativeSignatureJavaMethodBindingEmitter.java new file mode 100755 index 0000000..060d008 --- /dev/null +++ b/src/java/com/jogamp/gluegen/nativesig/NativeSignatureJavaMethodBindingEmitter.java @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.nativesig; + +import com.jogamp.gluegen.MethodBinding; +import com.jogamp.gluegen.JavaMethodBindingEmitter; +import com.jogamp.gluegen.JavaType; +import java.io.*; + +import com.jogamp.gluegen.*; +import com.jogamp.gluegen.cgram.types.*; +import com.jogamp.gluegen.opengl.*; +import com.jogamp.gluegen.procaddress.*; + +public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBindingEmitter { + + public NativeSignatureJavaMethodBindingEmitter(GLJavaMethodBindingEmitter methodToWrap) { + super(methodToWrap); + } + + public NativeSignatureJavaMethodBindingEmitter(ProcAddressJavaMethodBindingEmitter methodToWrap) { + super(methodToWrap, false); + } + + public NativeSignatureJavaMethodBindingEmitter(JavaMethodBindingEmitter methodToWrap, NativeSignatureEmitter emitter) { + super(methodToWrap, false, null, false, false, emitter); + } + + @Override + protected void emitSignature(PrintWriter writer) { + writer.print(getBaseIndentString()); + emitNativeSignatureAnnotation(writer); + super.emitSignature(writer); + } + + protected void emitNativeSignatureAnnotation(PrintWriter writer) { + if (hasModifier(JavaMethodBindingEmitter.NATIVE)) { + // Emit everything as a leaf for now + // FIXME: make this configurable + writer.print("@NativeSignature(\"l"); + MethodBinding binding = getBinding(); + if (callThroughProcAddress) { + writer.print("p"); + } + writer.print("("); + if (callThroughProcAddress) { + writer.print("P"); + } + for (int i = 0; i < binding.getNumArguments(); i++) { + emitNativeSignatureElement(writer, binding.getJavaArgumentType(i), binding.getCArgumentType(i), i); + } + writer.print(")"); + emitNativeSignatureElement(writer, binding.getJavaReturnType(), binding.getCReturnType(), -1); + writer.println("\")"); + } + } + + protected void emitNativeSignatureElement(PrintWriter writer, JavaType type, Type cType, int index) { + if (type.isVoid()) { + if (index > 0) { + throw new InternalError("Error parsing arguments -- void should not be seen aside from argument 0"); + } + return; + } + + if (type.isNIOBuffer()) { + writer.print("A"); + } else if (type.isPrimitiveArray()) { + writer.print("MO"); + } else if (type.isPrimitive()) { + Class clazz = type.getJavaClass(); + if (clazz == Byte.TYPE) { writer.print("B"); } + else if (clazz == Character.TYPE) { writer.print("C"); } + else if (clazz == Double.TYPE) { writer.print("D"); } + else if (clazz == Float.TYPE) { writer.print("F"); } + else if (clazz == Integer.TYPE) { writer.print("I"); } + else if (clazz == Long.TYPE) { + // See if this is intended to be a pointer at the C level + if (cType.isPointer()) { + writer.print("A"); + } else { + writer.print("J"); + } + } + else if (clazz == Short.TYPE) { writer.print("S"); } + else if (clazz == Boolean.TYPE) { writer.print("Z"); } + else throw new InternalError("Unhandled primitive type " + clazz); + } else if (type.isString()) { + writer.print("A"); + } else { + throw new RuntimeException("Type not yet handled: " + type); + } + } + + protected String getReturnTypeString(boolean skipArray) { + if (isForImplementingMethodCall()) { + JavaType returnType = getBinding().getJavaReturnType(); + if (returnType.isString() || returnType.isNIOByteBuffer()) { + // Treat these as addresses + return "long"; + } + } + return super.getReturnTypeString(skipArray); + } + + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + super.emitPreCallSetup(binding, writer); + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isNIOBuffer() && !directNIOOnly) { + // Emit declarations for variables holding primitive arrays as type Object + // We don't know 100% sure we're going to use these at this point in the code, though + writer.println(" Object " + getNIOBufferArrayName(i) + " = (_direct ? null : Buffers.getArray(" + + getArgumentName(i) + "));"); + } else if (type.isString()) { + writer.println(" long " + binding.getArgumentName(i) + "_c_str = BuffersInternal.newCString(" + binding.getArgumentName(i) + ");"); + } + // FIXME: going to need more of these for Buffer[] and String[], at least + } + } + + protected String getNIOBufferArrayName(int argNumber) { + return "__buffer_array_" + argNumber; + } + + protected int emitArguments(PrintWriter writer) + { + boolean needComma = false; + int numEmitted = 0; + + if (callThroughProcAddress) { + if (changeNameAndArguments) { + writer.print("long procAddress"); + ++numEmitted; + needComma = true; + } + } + + if (forImplementingMethodCall && binding.hasContainingType()) { + if (needComma) { + writer.print(", "); + } + + // Always emit outgoing "this" argument + writer.print("long "); + writer.print(javaThisArgumentName()); + ++numEmitted; + needComma = true; + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + if (binding.getNumArguments() != 1) { + throw new InternalError( + "\"void\" argument type found in " + + "multi-argument function \"" + binding + "\""); + } + continue; + } + + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (needComma) { + writer.print(", "); + } + + if (forImplementingMethodCall && + (forDirectBufferImplementation && type.isNIOBuffer() || + type.isString())) { + // Direct Buffers and Strings go out as longs + writer.print("long"); + // FIXME: will need more tests here to handle other constructs like String and direct Buffer arrays + } else { + writer.print(erasedTypeString(type, false)); + } + writer.print(" "); + writer.print(getArgumentName(i)); + + ++numEmitted; + needComma = true; + + // Add Buffer and array index offset arguments after each associated argument + if (forIndirectBufferAndArrayImplementation) { + if (type.isNIOBuffer()) { + writer.print(", int " + byteOffsetArgName(i)); + } else if (type.isNIOBufferArray()) { + writer.print(", int[] " + + byteOffsetArrayArgName(i)); + } + } + + // Add offset argument after each primitive array + if (type.isPrimitiveArray()) { + writer.print(", int " + offsetArgName(i)); + } + } + return numEmitted; + } + + protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { + writer.print(" "); + JavaType returnType = binding.getJavaReturnType(); + boolean needsResultAssignment = false; + + if (!returnType.isVoid()) { + if (returnType.isCompoundTypeWrapper() || + returnType.isNIOByteBuffer()) { + writer.println("java.nio.ByteBuffer _res;"); + needsResultAssignment = true; + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("java.nio.ByteBuffer[] _res;"); + needsResultAssignment = true; + } else if (returnType.isString() || returnType.isNIOByteBuffer()) { + writer.print(returnType); + writer.println(" _res;"); + needsResultAssignment = true; + } else { + // Always assign to "_res" variable so we can clean up + // outgoing String arguments, for example + emitReturnType(writer); + writer.println(" _res;"); + needsResultAssignment = true; + } + } + + if (binding.signatureCanUseIndirectNIO() && !directNIOOnly) { + // Must generate two calls for this gated on whether the NIO + // buffers coming in are all direct or indirect + writer.println("if (_direct) {"); + writer.print (" "); + } + + if (needsResultAssignment) { + writer.print(" _res = "); + if (returnType.isString()) { + writer.print("BuffersInternal.newJavaString("); + } else if (returnType.isNIOByteBuffer()) { + writer.print("BuffersInternal.newDirectByteBuffer("); + } + } else { + writer.print(" "); + if (!returnType.isVoid()) { + writer.print("return "); + } + } + + if (binding.signatureUsesJavaPrimitiveArrays() && + !binding.signatureCanUseIndirectNIO()) { + // FIXME: what happens with a C function of the form + // void foo(int* arg0, void* arg1); + // ? + + // Only one call being made in this body, going to indirect + // buffer / array entry point + emitCall(binding, writer, false); + if (returnType.isString() || returnType.isNIOByteBuffer()) { + writer.print(")"); + } + writer.print(";"); + writer.println(); + } else { + emitCall(binding, writer, true); + if (returnType.isString() || returnType.isNIOByteBuffer()) { + writer.print(")"); + } + writer.print(";"); + } + + if (binding.signatureCanUseIndirectNIO() && !directNIOOnly) { + // Must generate two calls for this gated on whether the NIO + // buffers coming in are all direct or indirect + writer.println(); + writer.println(" } else {"); + writer.print (" "); + if (needsResultAssignment) { + writer.print(" _res = "); + } else { + writer.print(" "); + if (!returnType.isVoid()) { + writer.print("return "); + } + } + emitCall(binding, writer, false); + writer.print(";"); + writer.println(); + writer.println(" }"); + } else { + writer.println(); + } + emitPrologueOrEpilogue(epilogue, writer); + if (needsResultAssignment) { + emitCallResultReturn(binding, writer); + } + } + + protected int emitCallArguments(MethodBinding binding, PrintWriter writer, boolean direct) { + // Note that we override this completely because we both need to + // move the potential location of the outgoing proc address as + // well as change the way we pass out Buffers, arrays, Strings, etc. + + boolean needComma = false; + int numArgsEmitted = 0; + + if (callThroughProcAddress) { + writer.print("__addr_"); + needComma = true; + ++numArgsEmitted; + } + + if (binding.hasContainingType()) { + // Emit this pointer + assert(binding.getContainingType().isCompoundTypeWrapper()); + writer.print("BuffersInternal.getDirectBufferAddress("); + writer.print("getBuffer()"); + writer.print(")"); + needComma = true; + ++numArgsEmitted; + } + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + // Don't need to expose these at the Java level + continue; + } + + if (type.isVoid()) { + // Make sure this is the only param to the method; if it isn't, + // there's something wrong with our parsing of the headers. + assert(binding.getNumArguments() == 1); + continue; + } + + if (needComma) { + writer.print(", "); + } + + if (type.isCompoundTypeWrapper()) { + writer.print("BuffersInternal.getDirectBufferAddress("); + writer.print("(("); + } + + if (type.isNIOBuffer()) { + if (!direct) { + writer.print(getNIOBufferArrayName(i)); + } else { + writer.print("BuffersInternal.getDirectBufferAddress("); + writer.print(getArgumentName(i)); + writer.print(")"); + } + } else { + writer.print(getArgumentName(i)); + } + + if (type.isCompoundTypeWrapper()) { + writer.print(" == null) ? null : "); + writer.print(getArgumentName(i)); + writer.print(".getBuffer())"); + writer.print(")"); + } + + if (type.isNIOBuffer()) { + if (direct) { + writer.print("+ Buffers.getDirectBufferByteOffset(" + getArgumentName(i) + ")"); + } else { + writer.print(", BuffersInternal.arrayBaseOffset(" + + getNIOBufferArrayName(i) + + ") + Buffers.getIndirectBufferByteOffset(" + getArgumentName(i) + ")"); + } + } else if (type.isNIOBufferArray()) { + writer.print(", " + byteOffsetArrayArgName(i)); + } + + // Add Array offset parameter for primitive arrays + if (type.isPrimitiveArray()) { + writer.print(", "); + writer.print("BuffersInternal.arrayBaseOffset(" + getArgumentName(i) + ") + "); + if(type.isFloatArray()) { + writer.print("Buffers.SIZEOF_FLOAT * "); + } else if(type.isDoubleArray()) { + writer.print("Buffers.SIZEOF_DOUBLE * "); + } else if(type.isByteArray()) { + writer.print("1 * "); + } else if(type.isLongArray()) { + writer.print("Buffers.SIZEOF_LONG * "); + } else if(type.isShortArray()) { + writer.print("Buffers.SIZEOF_SHORT * "); + } else if(type.isIntArray()) { + writer.print("Buffers.SIZEOF_INT * "); + } else { + throw new RuntimeException("Unsupported type for calculating array offset argument for " + + getArgumentName(i) + + "-- error occurred while processing Java glue code for " + getName()); + } + writer.print(offsetArgName(i)); + } + + if (type.isString()) { + writer.print("_c_str"); + } + + if (type.isCompoundTypeWrapper()) { + writer.print(")"); + } + + needComma = true; + ++numArgsEmitted; + } + return numArgsEmitted; + } + + protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isString()) { + writer.println(";"); + writer.println(" BuffersInternal.freeCString(" + binding.getArgumentName(i) + "_c_str);"); + } + // FIXME: will need more of these cleanups for things like Buffer[] and String[] (see above) + } + + super.emitCallResultReturn(binding, writer); + } + + public String getName() { + String res = super.getName(); + if (forImplementingMethodCall && bufferObjectVariant) { + return res + "BufObj"; + } + return res; + } + + protected String getImplMethodName(boolean direct) { + String name = null; + if (direct) { + name = binding.getName() + "$0"; + } else { + name = binding.getName() + "$1"; + } + if (bufferObjectVariant) { + return name + "BufObj"; + } + return name; + } +} diff --git a/src/java/com/jogamp/gluegen/pcpp/ConcatenatingReader.java b/src/java/com/jogamp/gluegen/pcpp/ConcatenatingReader.java new file mode 100755 index 0000000..1eea281 --- /dev/null +++ b/src/java/com/jogamp/gluegen/pcpp/ConcatenatingReader.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.pcpp; + +import java.io.*; + +/** A Reader implementation which finds lines ending in the backslash + character ('\') and concatenates them with the next line. */ + +public class ConcatenatingReader extends FilterReader { + // Any leftover characters go here + private char[] curBuf; + private int curPos; + private BufferedReader reader; + private static String newline = System.getProperty("line.separator"); + + /** This class requires that the input reader be a BufferedReader so + it can do line-oriented operations. */ + public ConcatenatingReader(BufferedReader in) { + super(in); + this.reader = in; + } + + @Override + public int read() throws IOException { + char[] tmp = new char[1]; + int num = read(tmp, 0, 1); + if (num < 0) + return -1; + return tmp[0]; + } + + // It's easier not to support mark/reset since we don't need it + @Override + public boolean markSupported() { + return false; + } + + @Override + public void mark(int readAheadLimit) throws IOException { + throw new IOException("mark/reset not supported"); + } + + @Override + public void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + @Override + public boolean ready() throws IOException { + if (curBuf != null || reader.ready()) + return true; + return false; + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + if (curBuf == null) { + nextLine(); + } + + if (curBuf == null) { + return -1; + } + + int numRead = 0; + + while ((len > 0) && (curBuf != null) && (curPos < curBuf.length)) { + cbuf[off] = curBuf[curPos]; + ++curPos; + ++off; + --len; + ++numRead; + if (curPos == curBuf.length) { + nextLine(); + } + } + + return numRead; + } + + @Override + public long skip(long n) throws IOException { + long numSkipped = 0; + + while (n > 0) { + int intN = (int) n; + char[] tmp = new char[intN]; + int numRead = read(tmp, 0, intN); + n -= numRead; + numSkipped += numRead; + if (numRead < intN) + break; + } + return numSkipped; + } + + private void nextLine() throws IOException { + String cur = reader.readLine(); + if (cur == null) { + curBuf = null; + return; + } + // The trailing newline was trimmed by the readLine() method. See + // whether we have to put it back or not, depending on whether the + // last character of the line is the concatenation character. + int numChars = cur.length(); + boolean needNewline = true; + if ((numChars > 0) && + (cur.charAt(cur.length() - 1) == '\\')) { + --numChars; + needNewline = false; + } + char[] buf = new char[numChars + (needNewline ? newline.length() : 0)]; + cur.getChars(0, numChars, buf, 0); + if (needNewline) { + newline.getChars(0, newline.length(), buf, numChars); + } + curBuf = buf; + curPos = 0; + } + + // Test harness + /* + public static void main(String[] args) throws IOException { + if (args.length != 1) { + System.out.println("Usage: java ConcatenatingReader [file name]"); + System.exit(1); + } + + ConcatenatingReader reader = new ConcatenatingReader(new BufferedReader(new FileReader(args[0]))); + OutputStreamWriter writer = new OutputStreamWriter(System.out); + char[] buf = new char[8192]; + boolean done = false; + while (!done && reader.ready()) { + int numRead = reader.read(buf, 0, buf.length); + writer.write(buf, 0, numRead); + if (numRead < buf.length) + done = true; + } + writer.flush(); + } + */ +} diff --git a/src/java/com/jogamp/gluegen/pcpp/PCPP.java b/src/java/com/jogamp/gluegen/pcpp/PCPP.java new file mode 100644 index 0000000..ab2c8b2 --- /dev/null +++ b/src/java/com/jogamp/gluegen/pcpp/PCPP.java @@ -0,0 +1,1107 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.gluegen.pcpp; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StreamTokenizer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +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 { + + private static final Logger LOG = Logger.getLogger(PCPP.class.getPackage().getName()); + + /** Map containing the results of #define statements. We must + evaluate certain very simple definitions (to properly handle + OpenGL's gl.h) but preserve the text of definitions evaluating + to constants. Macros and multi-line defines (which typically + contain either macro definitions or expressions) are currently + not handled. */ + private Map<String, String> defineMap = new HashMap<String, String>(128); + private Map<String, Macro> macroMap = new HashMap<String, Macro>(128); + private Set<String> nonConstantDefines = new HashSet<String>(128); + + /** List containing the #include paths as Strings */ + private List<String> includePaths; + + private ParseState state; + + private boolean enableDebugPrint; + + public PCPP(List<String> includePaths, boolean debug) { + this.includePaths = includePaths; + setOut(System.out); + enableDebugPrint = debug; + } + + public void run(Reader reader, String filename) throws IOException { + StreamTokenizer tok = null; + BufferedReader bufReader = null; + if (reader instanceof BufferedReader) { + bufReader = (BufferedReader) reader; + } else { + bufReader = new BufferedReader(reader); + } + + tok = new StreamTokenizer(new ConcatenatingReader(bufReader)); + initTokenizer(tok); + + ParseState curState = new ParseState(tok, filename); + ParseState oldState = state; + state = curState; + lineDirective(); + parse(); + state = oldState; + if (state != null) { + lineDirective(); + } + } + + private void initTokenizer(StreamTokenizer tok) { + tok.resetSyntax(); + tok.wordChars('a', 'z'); + tok.wordChars('A', 'Z'); + tok.wordChars('0', '9'); + tok.wordChars('_', '_'); + tok.wordChars('-', '.'); + tok.wordChars(128, 255); + tok.whitespaceChars(0, ' '); + tok.quoteChar('"'); + tok.quoteChar('\''); + tok.eolIsSignificant(true); + tok.slashSlashComments(true); + tok.slashStarComments(true); + } + + public String findFile(String filename) { + String sep = File.separator; + for (String inclPath : includePaths) { + String fullPath = inclPath + sep + filename; + File file = new File(fullPath); + if (file.exists()) { + return fullPath; + } + } + return null; + } + + public OutputStream out() { + return out; + } + + public void setOut(OutputStream out) { + this.out = out; + writer = new PrintWriter(out); + } + + // State + static class ParseState { + + private StreamTokenizer tok; + private String filename; + private boolean startOfLine; + private boolean startOfFile; + + ParseState(StreamTokenizer tok, String filename) { + this.tok = tok; + this.filename = filename; + startOfLine = true; + startOfFile = true; + } + + void pushBackToken() throws IOException { + tok.pushBack(); + } + + int curToken() { + return tok.ttype; + } + + int nextToken() throws IOException { + return tok.nextToken(); + } + + String curWord() { + return tok.sval; + } + + String filename() { + return filename; + } + + int lineNumber() { + return tok.lineno(); + } + + boolean startOfLine() { + return startOfLine; + } + + void setStartOfLine(boolean val) { + startOfLine = val; + } + + boolean startOfFile() { + return startOfFile; + } + + void setStartOfFile(boolean val) { + startOfFile = val; + } + + } + + private static class Macro { + + private final List<String> values; + private final List<String> params; + + Macro(List<String> params, List<String> values) { + this.values = values; + this.params = params; + } + + @Override + public String toString() { + return "params: "+params+" values: "+values; + } + + } + + // Accessors + + /** Equivalent to nextToken(false) */ + private int nextToken() throws IOException { + return nextToken(false); + } + + private int nextToken(boolean returnEOLs) throws IOException { + int lineno = lineNumber(); + // Check to see whether the previous call to nextToken() left an + // EOL on the stream + if (state.curToken() == StreamTokenizer.TT_EOL) { + state.setStartOfLine(true); + } else if (!state.startOfFile()) { + state.setStartOfLine(false); + } + state.setStartOfFile(false); + int val = state.nextToken(); + if (!returnEOLs) { + if (val == StreamTokenizer.TT_EOL) { + do { + // Consume and return next token, setting state appropriately + val = state.nextToken(); + state.setStartOfLine(true); + println(); + } while (val == StreamTokenizer.TT_EOL); + } + } + if (lineNumber() > lineno + 1) { + // This is a little noisier than it needs to be, but does handle + // the case of multi-line comments properly + lineDirective(); + } + return val; + } + + /** + * Reads the next token and throws an IOException if it is not the specified + * token character. + */ + private void nextRequiredToken(int requiredToken) throws IOException { + int nextTok = nextToken(); + if (nextTok != requiredToken) { + String msg = "Expected token '" + requiredToken + "' but got "; + switch (nextTok) { + case StreamTokenizer.TT_EOF: msg += "<EOF>"; break; + case StreamTokenizer.TT_EOL: msg += "<EOL>"; break; + default: msg += "'" + curTokenAsString() + "'"; break; + } + msg += " at file " + filename() + ", line " + lineNumber(); + throw new IOException(msg); + } + } + + + private String curTokenAsString() { + int t = state.curToken(); + if (t == StreamTokenizer.TT_WORD) { + return state.curWord(); + } + if (t == StreamTokenizer.TT_EOL) { + throw new RuntimeException("Should not be converting EOL characters to strings"); + } + char c = (char) t; + if (c == '"' || c == '\'') { + StringBuilder sb = new StringBuilder(); + sb.append(c); + sb.append(state.curWord()); + sb.append(c); + return sb.toString(); + } + return new String(new char[] { c }); + } + + private String nextWordOrString() throws IOException { + nextToken(); + return curTokenAsString(); + } + + private String nextWord() throws IOException { + int val = nextToken(); + if (val != StreamTokenizer.TT_WORD) { + throw new RuntimeException("Expected word at file " + filename() + + ", line " + lineNumber()); + } + return state.curWord(); + } + + private boolean startOfLine() { + return state.startOfLine(); + } + + private String filename() { + return state.filename(); + } + + private int lineNumber() { + return state.lineNumber(); + } + + ///////////// + // Parsing // + ///////////// + + private void parse() throws IOException { + int tok = 0; + while ((tok = nextToken()) != StreamTokenizer.TT_EOF) { + // A '#' at the beginning of a line is a preprocessor directive + if (startOfLine() && (tok == '#')) { + preprocessorDirective(); + } else { + // Output white space plus current token, handling #defines + // (though not properly -- only handling #defines to constants and the empty string) + + // !!HACK!! - print space only for word tokens. This way multicharacter + // operators such as ==, != etc. are property printed. + if (tok == StreamTokenizer.TT_WORD) { + print(" "); + } + String s = curTokenAsString(); + String newS = defineMap.get(s); + if (newS == null) { + newS = s; + } + + Macro macro = macroMap.get(newS); + if(macro != null) { + newS = ""; + List<String> args = new ArrayList<String>(); + while (nextToken() != StreamTokenizer.TT_EOL) { + String token = curTokenAsString(); + if(")".equals(token)) { + break; + }else if(!",".equals(token) && !"(".equals(token)) { + args.add(token); + } + } + + for (int i = 0; i < macro.values.size(); i++) { + String value = macro.values.get(i); + + for (int j = 0; j < macro.params.size(); j++) { + String param = macro.params.get(j); + if(param.equals(value)) { + value = args.get(j); + break; + } + } + + if(isIdentifier(value)) { + newS +=" "; + } + + newS += value; + + } + + } + + print(newS); + } + } + flush(); + } + + private void preprocessorDirective() throws IOException { + String w = nextWord(); + boolean shouldPrint = true; + if (w.equals("warning")) { + handleWarning(); + shouldPrint = false; + } else if (w.equals("error")) { + handleError(); + shouldPrint = false; + } else if (w.equals("define")) { + handleDefine(); + shouldPrint = false; + } else if (w.equals("undef")) { + handleUndefine(); + shouldPrint = false; + } else if (w.equals("if") || w.equals("elif")) { + handleIf(w.equals("if")); + shouldPrint = false; + } else if (w.equals("ifdef") || w.equals("ifndef")) { + handleIfdef(w.equals("ifdef")); + shouldPrint = false; + } else if (w.equals("else")) { + handleElse(); + shouldPrint = false; + } else if (w.equals("endif")) { + handleEndif(); + shouldPrint = false; + } else if (w.equals("include")) { + handleInclude(); + shouldPrint = false; + } else { + // Unknown preprocessor directive (#pragma?) -- ignore + } + if (shouldPrint) { + print("# "); + printToken(); + } + } + + //////////////////////////////////// + // Handling of #define directives // + //////////////////////////////////// + + private void handleUndefine() throws IOException { + // Next token is the name of the #undef + String name = nextWord(); + + debugPrint(true, "#undef " + name); + + // there shouldn't be any extra symbols after the name, but just in case... + List<String> values = new ArrayList<String>(); + while (nextToken(true) != StreamTokenizer.TT_EOL) { + values.add(curTokenAsString()); + } + + if (enabled()) { + 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}); + } 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()}); + } + } + + private void handleWarning() throws IOException { + String msg = nextWordOrString(); + if (enabled()) { + LOG.log(WARNING, "#warning {0} at \"{1}\" line \"{2}\"", new Object[]{msg, filename(), lineNumber()}); + } + } + + private void handleError() throws IOException { + String msg = nextWordOrString(); + if (enabled()) { + LOG.log(WARNING, "#error {0} at \"{1}\" line \"{2}\"", new Object[]{msg, filename(), lineNumber()}); + } + } + + private void handleDefine() throws IOException { + + // (workaround for not having a lookahead) + // macro functions have no space between identifier and '(' + // since whitespace is our delimiter we can't determine wether we are dealing with + // macros or normal defines starting with a brace. + // this will glue the brace to the token if there is no whitespace between both + state.tok.wordChars('(', '('); + + // Next token is the name of the #define + String name = nextWord(); + + boolean macroDefinition = name.contains("("); + + //System.err.println("IN HANDLE_DEFINE: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); + // (Note that this is not actually proper handling for multi-line #defines) + List<String> values = new ArrayList<String>(); + + if(macroDefinition) { + int index = name.indexOf('('); + String var = name.substring(index+1); + name = name.substring(0, index); + + values.add("("); + values.add(var); + } + + // restore normal syntax + state.tok.ordinaryChar('('); + + while (nextToken(true) != StreamTokenizer.TT_EOL) { + values.add(curTokenAsString()); + } + // if we're not within an active block of code (like inside an "#ifdef + // FOO" where FOO isn't defined), then don't actually alter the definition + // map. + debugPrint(true, "#define " + name); + if (enabled()) { + boolean emitDefine = true; + + // Handle #definitions to nothing or to a constant value + int sz = values.size(); + if (sz == 0) { + // definition to nothing, like "#define FOO" + String value = ""; + String oldDef = defineMap.put(name, value); + if (oldDef != null && !oldDef.equals(value)) { + LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"\"", new Object[]{name, oldDef}); + } + // We don't want to emit the define, because it would serve no purpose + // and cause GlueGen errors (confuse the GnuCParser) + emitDefine = false; + //System.out.println("//---DEFINED: " + name + "to \"\""); + } else if (sz == 1) { + // See whether the value is a constant + String value = values.get(0); + + if (isConstant(value)) { + // Value is numeric constant like "#define FOO 5". + // Put it in the #define map + 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}); + } + debugPrint(true, "#define " + name + " ["+oldDef+" ] -> "+value + " CONST"); + //System.out.println("//---DEFINED: " + name + " to \"" + value + "\""); + } else { + debugPrint(true, "#define " + name + " -> "+value + " SYMB"); + // Value is a symbolic constant like "#define FOO BAR". + // Try to look up the symbol's value + String newValue = resolveDefine(value, true); + if (newValue != null) { + // Set the value to the value of the symbol. + // + // TO DO: Is this correct? Why not output the symbol unchanged? + // I think that it's a good thing to see that some symbols are + // defined in terms of others. -chris + values.set(0, newValue); + } else { + // Still perform textual replacement + defineMap.put(name, value); + nonConstantDefines.add(name); + emitDefine = false; + } + } + + } else if (macroDefinition) { + + // list parameters + List<String> params = new ArrayList<String>(); + for (int i = 1; i < values.size(); i++) { + String v = values.get(i); + if(")".equals(v)) { // end of params + if(i != values.size()-1) { + values = values.subList(i+1, values.size()); + }else{ + values = Collections.emptyList(); + } + break; + }else if(!",".equals(v)) { + params.add(v); + } + } + + Macro macro = new Macro(params, values); + Macro oldDef = macroMap.put(name, macro); + if (oldDef != null) { + LOG.log(WARNING, "\"{0}\" redefined from \"{1}\" to \"{2}\"", new Object[]{name, oldDef, macro}); + } + emitDefine = false; + + }else{ + + // 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) + + boolean containsIdentifier = false; + for (String value : values) { + if(isIdentifier(value)) { + containsIdentifier = true; + break; + } + } + + //TODO more work here e.g casts are currently not handled + if(containsIdentifier) { //skip + + // Non-constant define; try to do reasonable textual substitution anyway + // (FIXME: should identify some of these, like (-1), as constants) + emitDefine = false; + StringBuilder val = new StringBuilder(); + for (int i = 0; i < sz; i++) { + if (i != 0) { + val.append(" "); + } + val.append(resolveDefine(values.get(i), false)); + } + if (defineMap.get(name) != null) { + // This is probably something the user should investigate. + throw new RuntimeException("Cannot redefine symbol \"" + name + + " from \"" + defineMap.get(name) + "\" to non-constant " + + " definition \"" + val.toString() + "\""); + } + defineMap.put(name, val.toString()); + nonConstantDefines.add(name); + + }else{ // constant expression -> pass through + + StringBuilder sb = new StringBuilder(); + for (String v : values) { + sb.append(v); + } + String value = sb.toString(); + + 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}); + } + debugPrint(true, "#define " + name + " ["+oldDef+" ] -> "+value + " CONST"); +// System.out.println("#define " + name +" "+value + " CONST EXPRESSION"); + } + + } + + if (emitDefine) { + // Print name and value + print("# define "); + print(name); + print(" "); + for (String v : values) { + print(v); + } + println(); + } + + } // end if (enabled()) + + //System.err.println("OUT HANDLE_DEFINE: " + name); + } + + private boolean isIdentifier(String value) { + + boolean identifier = false; + + char[] chars = value.toCharArray(); + + for (int i = 0; i < chars.length; i++) { + 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(String s) { + if (s.startsWith("0x") || s.startsWith("0X")) { + return checkHex(s); + } else { + return checkDecimal(s); + } + } + + private boolean checkHex(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(String s) { + try { + Float.valueOf(s); + } catch (NumberFormatException e) { + // not parsable as a number + return false; + } + return true; + } + + private String resolveDefine(String word, boolean returnNullIfNotFound) { + String lastWord = defineMap.get(word); + if (lastWord == null) { + if (returnNullIfNotFound) { + return null; + } + return word; + } + String nextWord = null; + do { + nextWord = defineMap.get(lastWord); + if (nextWord != null) { + lastWord = nextWord; + } + } while (nextWord != null); + return lastWord; + } + + //////////////////////////////////////////////// + // Handling of #if/#ifdef/ifndef/endif directives // + //////////////////////////////////////////////// + + /** + * @param isIfdef if true, we're processing #ifdef; if false, we're + * processing #ifndef. + */ + private void handleIfdef(boolean isIfdef) throws IOException { + // Next token is the name of the #ifdef + String symbolName = nextWord(); + debugPrint(true, (isIfdef ? "#ifdef " : "#ifndef ") + symbolName); + boolean symbolIsDefined = defineMap.get(symbolName) != null; + debugPrint(true, (isIfdef ? "#ifdef " : "#ifndef ") + symbolName + "(defined: "+symbolIsDefined+")"); + pushEnableBit(enabled() && symbolIsDefined == isIfdef); + } + + /** Handles #else directives */ + private void handleElse() throws IOException { + boolean enabledStatusBeforeElse = enabled(); + popEnableBit(); + pushEnableBit(enabled() && !enabledStatusBeforeElse); + debugPrint(true, "#else "); + } + + private void handleEndif() { + boolean enabledBeforePopping = enabled(); + popEnableBit(); + + // print the endif if we were enabled prior to popEnableBit() (sending + // false to debugPrint means "print regardless of current enabled() state). + debugPrint(!enabledBeforePopping, "#endif/end-else"); + } + + /** + * @param isIf if true, we're processing #if; if false, we're + * processing #elif. + */ + private void handleIf(boolean isIf) throws IOException { + //System.out.println("IN HANDLE_" + (isIf ? "IF" : "ELIF") + " file \"" + filename() + " line " + lineNumber()); + debugPrint(true, (isIf ? "#if" : "#elif")); + boolean defineEvaluatedToTrue = handleIfRecursive(true); + if (!isIf) { + popEnableBit(); + } + pushEnableBit(enabled() && defineEvaluatedToTrue); + //System.out.println("OUT HANDLE_" + (isIf ? "IF" : "ELIF") +" (evaluated to " + defineEvaluatedToTrue + ")"); + } + + //static int tmp = -1; + + /** + * This method is called recursively to process nested sub-expressions such as: + * <pre> + * #if !defined(OPENSTEP) && !(defined(NeXT) || !defined(NeXT_PDO)) + *</pre> + * + * @param greedy if true, continue evaluating sub-expressions until EOL is + * reached. If false, return as soon as the first sub-expression is + * processed. + * @return the value of the sub-expression or (if greedy==true) + * series of sub-expressions. + */ + private boolean handleIfRecursive(boolean greedy) throws IOException { + //System.out.println("IN HANDLE_IF_RECURSIVE (" + ++tmp + ", greedy = " + greedy + ")"); System.out.flush(); + + // ifValue keeps track of the current value of the potentially nested + // "defined()" expressions as we process them. + boolean ifValue = true; + int openParens = 0; + int tok; + do { + tok = nextToken(true); + //System.out.println("-- READ: [" + (tok == StreamTokenizer.TT_EOL ? "<EOL>" :curTokenAsString()) + "]"); + switch (tok) { + case '(': + ++openParens; + //System.out.println("OPEN PARENS = " + openParens); + ifValue = ifValue && handleIfRecursive(true); + break; + case ')': + --openParens; + //System.out.println("OPEN PARENS = " + openParens); + break; + case '!': + { + //System.out.println("HANDLE_IF_RECURSIVE HANDLING !"); + boolean rhs = handleIfRecursive(false); + ifValue = !rhs; + //System.out.println("HANDLE_IF_RECURSIVE HANDLED OUT !, RHS = " + rhs); + } + break; + case '&': + { + nextRequiredToken('&'); + //System.out.println("HANDLE_IF_RECURSIVE HANDLING &&, LHS = " + ifValue); + boolean rhs = handleIfRecursive(true); + //System.out.println("HANDLE_IF_RECURSIVE HANDLED &&, RHS = " + rhs); + ifValue = ifValue && rhs; + } + break; + case '|': + { + nextRequiredToken('|'); + //System.out.println("HANDLE_IF_RECURSIVE HANDLING ||, LHS = " + ifValue); + boolean rhs = handleIfRecursive(true); + //System.out.println("HANDLE_IF_RECURSIVE HANDLED ||, RHS = " + rhs); + ifValue = ifValue || rhs; + } + break; + case '>': + { + // NOTE: we don't handle expressions like this properly + boolean rhs = handleIfRecursive(true); + ifValue = false; + } + break; + case '<': + { + // NOTE: we don't handle expressions like this properly + boolean rhs = handleIfRecursive(true); + ifValue = false; + } + break; + case '=': + { + // NOTE: we don't handle expressions like this properly + boolean rhs = handleIfRecursive(true); + ifValue = false; + } + break; + case StreamTokenizer.TT_WORD: + { + String word = curTokenAsString(); + if (word.equals("defined")) { + // Handle things like #if defined(SOMESYMBOL) + nextRequiredToken('('); + String symbol = nextWord(); + boolean isDefined = defineMap.get(symbol) != null; + //System.out.println("HANDLE_IF_RECURSIVE HANDLING defined(" + symbol + ") = " + isDefined); + ifValue = ifValue && isDefined; + nextRequiredToken(')'); + } else { + // Handle things like #if SOME_SYMBOL. + String symbolValue = defineMap.get(word); + + // See if the statement is "true"; i.e., a non-zero expression + if (symbolValue != null) { + // The statement is true if the symbol is defined and is a constant expression + return (!nonConstantDefines.contains(word)); + } else { + // The statement is true if the symbol evaluates to a non-zero value + // + // NOTE: This doesn't yet handle evaluable expressions like "#if + // SOME_SYMBOL > 5" or "#if SOME_SYMBOL == 0", both of which are + // valid syntax. It only handles numeric symbols like "#if 1" + + try { + // see if it's in decimal form + return Double.parseDouble(word) != 0; + } catch (NumberFormatException nfe1) { + try { + // ok, it's not a valid decimal value, try hex/octal value + return Long.parseLong(word) != 0; + } catch (NumberFormatException nfe2) { + try { + // ok, it's not a valid hex/octal value, try boolean + return Boolean.valueOf(word) == Boolean.TRUE; + } catch (NumberFormatException nfe3) { + // give up; the symbol isn't a numeric or boolean value + return false; + } + } + } + } + } + } // end case TT_WORD + break; + case StreamTokenizer.TT_EOL: + //System.out.println("HANDLE_IF_RECURSIVE HIT <EOL>!"); + state.pushBackToken(); // so caller hits EOL as well if we're recursing + break; + case StreamTokenizer.TT_EOF: + throw new RuntimeException("Unexpected end of file while parsing " + + "#if statement at file " + filename() + ", line " + lineNumber()); + + default: + throw new RuntimeException("Unexpected token (" + curTokenAsString() + + ") while parsing " + "#if statement at file " + filename() + + ", line " + lineNumber()); + } + //System.out.println("END OF WHILE: greedy = " + greedy + " parens = " +openParens + " not EOL = " + (tok != StreamTokenizer.TT_EOL) + " --> " + ((greedy && openParens >= 0) && tok != StreamTokenizer.TT_EOL)); + } while ((greedy && openParens >= 0) && tok != StreamTokenizer.TT_EOL); + //System.out.println("OUT HANDLE_IF_RECURSIVE (" + tmp-- + ", returning " + ifValue + ")"); + //System.out.flush(); + return ifValue; + } + + ///////////////////////////////////// + // Handling of #include directives // + ///////////////////////////////////// + + private void handleInclude() throws IOException { + // Two kinds of #includes: one with quoted string for argument, + // one with angle brackets surrounding argument + int t = nextToken(); + String filename = null; + if (t == '"') { + filename = state.curWord(); + } else if (t == '<') { + // Components of path name are coming in as separate tokens; + // concatenate them + StringBuilder buf = new StringBuilder(); + while ((t = nextToken()) != '>' && (t != StreamTokenizer.TT_EOF)) { + buf.append(curTokenAsString()); + } + if (t == StreamTokenizer.TT_EOF) { + LOG.warning("unexpected EOF while processing #include directive"); + } + filename = buf.toString(); + } + // if we're not within an active block of code (like inside an "#ifdef + // FOO" where FOO isn't defined), then don't actually process the + // #included file. + debugPrint(true, "#include [" + filename + "]"); + if (enabled()) { + // Look up file in known #include path + String fullname = findFile(filename); + //System.out.println("ACTIVE BLOCK, LOADING " + filename); + if (fullname == null) { + LOG.log(WARNING, "unable to find #include file \"{0}\"", filename); + return; + } + // Process this file in-line + Reader reader = new BufferedReader(new FileReader(fullname)); + run(reader, fullname); + } else { + //System.out.println("INACTIVE BLOCK, SKIPPING " + filename); + } + } + + //////////// + // Output // + //////////// + + private OutputStream out; + private PrintWriter writer; + private List<Boolean> enabledBits = new ArrayList<Boolean>(); + + private static int debugPrintIndentLevel = 0; + + private void debugPrint(boolean onlyPrintIfEnabled, String msg) { + if (!enableDebugPrint) { + return; + } + + if (!onlyPrintIfEnabled || (onlyPrintIfEnabled && enabled())) { + for (int i = debugPrintIndentLevel; --i > 0;) { + System.out.print(" "); + } + System.out.println(msg + " (line " + lineNumber() + " file " + filename() + ")"); + } + } + + private void pushEnableBit(boolean enabled) { + enabledBits.add(enabled); + ++debugPrintIndentLevel; + debugPrint(false, "PUSH_ENABLED, NOW: " + enabled()); + } + + private void popEnableBit() { + if (enabledBits.isEmpty()) { + LOG.warning("mismatched #ifdef/endif pairs"); + return; + } + enabledBits.remove(enabledBits.size() - 1); + --debugPrintIndentLevel; + debugPrint(false, "POP_ENABLED, NOW: " + enabled()); + } + + private boolean enabled() { + return (enabledBits.isEmpty() || enabledBits.get(enabledBits.size() - 1)); + } + + private void print(String s) { + if (enabled()) { + writer.print(s); + //System.out.print(s);//debug + } + } + + private void print(char c) { + if (enabled()) { + writer.print(c); + //System.err.print(c); //debug + } + } + + private void println() { + if (enabled()) { + writer.println(); + //System.err.println();//debug + } + } + + private void printToken() { + print(curTokenAsString()); + } + + private void flush() { + if (enabled()) { + writer.flush(); + //System.err.flush(); //debug + } + } + + private void lineDirective() { + print("# " + lineNumber() + " \"" + filename() + "\""); + println(); + } + + private static void usage() { + System.out.println("Usage: java PCPP [filename | -]"); + System.out.println("Minimal pseudo-C-preprocessor."); + System.out.println("Output goes to standard output. Standard input can be used as input"); + System.out.println("by passing '-' as the argument."); + System.out.println(" --debug enables debug mode"); + System.exit(1); + } + + public static void main(String[] args) throws IOException { + Reader reader = null; + String filename = null; + boolean debug = false; + + if (args.length == 0) { + usage(); + } + + List<String> includePaths = new ArrayList<String>(); + for (int i = 0; i < args.length; i++) { + if (i < args.length - 1) { + String arg = args[i]; + if (arg.startsWith("-I")) { + String[] paths = arg.substring(2).split(System.getProperty("path.separator")); + for (int j = 0; j < paths.length; j++) { + includePaths.add(paths[j]); + } + } else if (arg.equals("--debug")) { + debug = true; + } else { + usage(); + } + } else { + String arg = args[i]; + if (arg.equals("-")) { + reader = new InputStreamReader(System.in); + filename = "standard input"; + } else { + if (arg.startsWith("-")) { + usage(); + } + filename = arg; + reader = new BufferedReader(new FileReader(filename)); + } + } + } + + new PCPP(includePaths, debug).run(reader, filename); + } + +} diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java new file mode 100755 index 0000000..fedb1e6 --- /dev/null +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressCMethodBindingEmitter.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +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.*; +import com.jogamp.gluegen.cgram.types.*; + +public class ProcAddressCMethodBindingEmitter extends CMethodBindingEmitter { + + private boolean callThroughProcAddress; + private boolean needsLocalTypedef; + + private String localTypedefCallingConvention; + + private static final String procAddressJavaTypeName = JavaType.createForClass(Long.TYPE).jniTypeName(); + private ProcAddressEmitter emitter; + + public ProcAddressCMethodBindingEmitter(CMethodBindingEmitter methodToWrap, final boolean callThroughProcAddress, + boolean needsLocalTypedef, String localTypedefCallingConvention, ProcAddressEmitter emitter) { + + super( + new MethodBinding(methodToWrap.getBinding()) { + @Override + public String getName() { + if (callThroughProcAddress) { + return ProcAddressEmitter.WRAP_PREFIX + super.getName(); + } else { + return super.getName(); + } + } + }, + methodToWrap.getDefaultOutput(), + methodToWrap.getJavaPackageName(), + methodToWrap.getJavaClassName(), + methodToWrap.getIsOverloadedBinding(), + methodToWrap.getIsJavaMethodStatic(), + true, + methodToWrap.forIndirectBufferAndArrayImplementation(), + methodToWrap.getMachineDescription() + ); + + if (methodToWrap.getReturnValueCapacityExpression() != null) { + setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); + } + if (methodToWrap.getReturnValueLengthExpression() != null) { + setReturnValueLengthExpression(methodToWrap.getReturnValueLengthExpression()); + } + setTemporaryCVariableDeclarations(methodToWrap.getTemporaryCVariableDeclarations()); + setTemporaryCVariableAssignments(methodToWrap.getTemporaryCVariableAssignments()); + + setCommentEmitter(defaultCommentEmitter); + + this.callThroughProcAddress = callThroughProcAddress; + this.needsLocalTypedef = needsLocalTypedef; + this.localTypedefCallingConvention = localTypedefCallingConvention; + this.emitter = emitter; + } + + @Override + protected int emitArguments(PrintWriter writer) { + int numEmitted = super.emitArguments(writer); + if (callThroughProcAddress) { + if (numEmitted > 0) { + writer.print(", "); + } + writer.print(procAddressJavaTypeName); + writer.print(" procAddress"); + ++numEmitted; + } + + return numEmitted; + } + + @Override + protected void emitBodyVariableDeclarations(PrintWriter writer) { + if (callThroughProcAddress) { + // create variable for the function pointer with the right type, and set + // it to the value of the passed-in proc address + 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. + 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(";"); + } + + writer.print(" "); + writer.print(funcPointerTypedefName); + writer.print(" ptr_"); + writer.print(cSym.getName()); + writer.println(";"); + } + + super.emitBodyVariableDeclarations(writer); + } + + @Override + protected void emitBodyVariablePreCallSetup(PrintWriter writer) { + super.emitBodyVariablePreCallSetup(writer); + + if (callThroughProcAddress) { + // set the function pointer to the value of the passed-in procAddress + FunctionSymbol cSym = getBinding().getCSymbol(); + String funcPointerTypedefName = emitter.getFunctionPointerTypedefName(cSym); + if (needsLocalTypedef) { + funcPointerTypedefName = "_local_" + funcPointerTypedefName; + } + + String ptrVarName = "ptr_" + cSym.getName(); + + writer.print(" "); + writer.print(ptrVarName); + writer.print(" = ("); + writer.print(funcPointerTypedefName); + writer.println(") (intptr_t) procAddress;"); + + writer.println(" assert(" + ptrVarName + " != NULL);"); + } + } + + @Override + protected void emitBodyCallCFunction(PrintWriter writer) { + if (!callThroughProcAddress) { + super.emitBodyCallCFunction(writer); + } else { + // Make the call to the actual C function + writer.print(" "); + + // WARNING: this code assumes that the return type has already been + // typedef-resolved. + Type cReturnType = binding.getCReturnType(); + + if (!cReturnType.isVoid()) { + writer.print("_res = "); + } + MethodBinding mBinding = getBinding(); + if (mBinding.hasContainingType()) { + // FIXME: this can and should be handled and unified with the + // associated code in the CMethodBindingEmitter + throw new IllegalStateException("Cannot call through function pointer because binding has containing type: " + mBinding); + } + + // call throught the run-time function pointer + writer.print("(* ptr_"); + writer.print(mBinding.getCSymbol().getName()); + writer.print(") "); + writer.print("("); + emitBodyPassCArguments(writer); + writer.println(");"); + } + } + + @Override + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(super.jniMangle(binding)); + if (callThroughProcAddress) { + jniMangle(Long.TYPE, buf, false); // to account for the additional _addr_ parameter + } + return buf.toString(); + } +} diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java new file mode 100755 index 0000000..51e3166 --- /dev/null +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressConfiguration.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.procaddress; + +import com.jogamp.gluegen.JavaConfiguration; +import java.io.*; +import java.text.*; +import java.util.*; + +import com.jogamp.gluegen.*; + +public class ProcAddressConfiguration extends JavaConfiguration { + + private boolean emitProcAddressTable = false; + private boolean forceProcAddressGen4All = false; + + private String tableClassPackage; + private String tableClassName = "ProcAddressTable"; + private String getProcAddressTableExpr; + private String localProcAddressCallingConvention4All = null; + + private ConvNode procAddressNameConverter; + private final Set<String> skipProcAddressGen = new HashSet<String>(); + private final List<String> forceProcAddressGen = new ArrayList<String>(); + private final Set<String> forceProcAddressGenSet = new HashSet<String>(); + + // This is needed only on Windows. Ideally we would modify the + // HeaderParser and PCPP to automatically pick up the calling + // convention from the headers + private Map<String, String> localProcAddressCallingConventionMap = new HashMap<String, String>(); + + @Override + protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { + if (cmd.equalsIgnoreCase("EmitProcAddressTable")) { + emitProcAddressTable = readBoolean("EmitProcAddressTable", tok, filename, lineNo).booleanValue(); + } else if (cmd.equalsIgnoreCase("ProcAddressTablePackage")) { + tableClassPackage = readString("ProcAddressTablePackage", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ProcAddressTableClassName")) { + tableClassName = readString("ProcAddressTableClassName", tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("SkipProcAddressGen")) { + String sym = readString("SkipProcAddressGen", tok, filename, lineNo); + skipProcAddressGen.add(sym); + } else if (cmd.equalsIgnoreCase("ForceProcAddressGen")) { + String funcName = readString("ForceProcAddressGen", tok, filename, lineNo); + if (funcName.equals("__ALL__")) { + forceProcAddressGen4All = true; + } else { + addForceProcAddressGen(funcName); + } + } else if (cmd.equalsIgnoreCase("GetProcAddressTableExpr")) { + getProcAddressTableExpr = readGetProcAddressTableExpr(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("ProcAddressNameExpr")) { + readProcAddressNameExpr(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("LocalProcAddressCallingConvention")) { + readLocalProcAddressCallingConvention(tok, filename, lineNo); + } else { + super.dispatch(cmd, tok, file, filename, lineNo); + } + } + + protected String readGetProcAddressTableExpr(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + return restOfLine.trim(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"GetProcAddressTableExpr\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void setProcAddressNameExpr(String expr) { + // Parse this into something allowing us to map from a function + // name to the typedef'ed function pointer name + List<String> tokens = new ArrayList<String>(); + StringTokenizer tok1 = new StringTokenizer(expr); + while (tok1.hasMoreTokens()) { + String sstr = tok1.nextToken(); + StringTokenizer tok2 = new StringTokenizer(sstr, "$()", true); + while (tok2.hasMoreTokens()) { + tokens.add(tok2.nextToken()); + } + } + + // Now that the string is flattened out, convert it to nodes + procAddressNameConverter = makeConverter(tokens.iterator()); + if (procAddressNameConverter == null) { + throw new NoSuchElementException("Error creating converter from string"); + } + } + + protected void readProcAddressNameExpr(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + setProcAddressNameExpr(restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ProcAddressNameExpr\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readLocalProcAddressCallingConvention(StringTokenizer tok, String filename, int lineNo) throws IOException { + try { + String functionName = tok.nextToken(); + String callingConvention = tok.nextToken(); + if (functionName.equals("__ALL__")) { + localProcAddressCallingConvention4All = callingConvention; + } else { + localProcAddressCallingConventionMap.put(functionName, callingConvention); + } + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"LocalProcAddressCallingConvention\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + private static ConvNode makeConverter(Iterator<String> iter) { + List<ConvNode> result = new ArrayList<ConvNode>(); + + while (iter.hasNext()) { + String str = iter.next(); + if (str.equals("$")) { + String command = iter.next(); + String openParen = iter.next(); + if (!openParen.equals("(")) { + throw new NoSuchElementException("Expected \"(\""); + } + boolean uppercase = false; + if (command.equalsIgnoreCase("UPPERCASE")) { + uppercase = true; + } else if (!command.equalsIgnoreCase("LOWERCASE")) { + throw new NoSuchElementException("Unknown ProcAddressNameExpr command \"" + command + "\""); + } + result.add(new CaseNode(uppercase, makeConverter(iter))); + } else if (str.equals(")")) { + // Fall through and return + } else if (str.indexOf('{') >= 0) { + result.add(new FormatNode(str)); + } else { + result.add(new ConstStringNode(str)); + } + } + if (result.isEmpty()) { + return null; + } else if (result.size() == 1) { + return result.get(0); + } else { + return new ConcatNode(result); + } + } + + /** Helper class for converting a function name to the typedef'ed + function pointer name */ + static abstract class ConvNode { + abstract String convert(String funcName); + } + + static class FormatNode extends ConvNode { + + private MessageFormat msgFmt; + + FormatNode(String fmt) { + msgFmt = new MessageFormat(fmt); + } + + String convert(String funcName) { + StringBuffer buf = new StringBuffer(); + msgFmt.format(new Object[]{funcName}, buf, null); + return buf.toString(); + } + } + + static class ConstStringNode extends ConvNode { + + private String str; + + ConstStringNode(String str) { + this.str = str; + } + + String convert(String funcName) { + return str; + } + } + + static class ConcatNode extends ConvNode { + + private List<ConvNode> children; + + ConcatNode(List<ConvNode> children) { + this.children = children; + } + + String convert(String funcName) { + StringBuilder res = new StringBuilder(); + for (ConvNode node : children) { + res.append(node.convert(funcName)); + } + return res.toString(); + } + } + + static class CaseNode extends ConvNode { + + private boolean upperCase; + private ConvNode child; + + CaseNode(boolean upperCase, ConvNode child) { + this.upperCase = upperCase; + this.child = child; + } + + public String convert(String funcName) { + if (upperCase) { + return child.convert(funcName).toUpperCase(); + } else { + return child.convert(funcName).toLowerCase(); + } + } + } + + public boolean emitProcAddressTable() { + return emitProcAddressTable; + } + + public String tableClassPackage() { + return tableClassPackage; + } + + public String tableClassName() { + return tableClassName; + } + + public boolean skipProcAddressGen(String name) { + return skipProcAddressGen.contains(name); + } + + public boolean isForceProcAddressGen4All() { + return forceProcAddressGen4All; + } + + public List<String> getForceProcAddressGen() { + return forceProcAddressGen; + } + + public String getProcAddressTableExpr() { + if (getProcAddressTableExpr == null) { + throw new RuntimeException("GetProcAddressTableExpr was not defined in .cfg file"); + } + return getProcAddressTableExpr; + } + + public String convertToFunctionPointerName(String funcName) { + if (procAddressNameConverter == null) { + throw new RuntimeException("ProcAddressNameExpr was not defined in .cfg file"); + } + return procAddressNameConverter.convert(funcName); + } + + public boolean forceProcAddressGen(String funcName) { + return forceProcAddressGen4All || forceProcAddressGenSet.contains(funcName); + } + + public void addForceProcAddressGen(String funcName) { + forceProcAddressGen.add(funcName); + forceProcAddressGenSet.add(funcName); + } + + public void addLocalProcAddressCallingConvention(String funcName, String callingConvention) { + localProcAddressCallingConventionMap.put(funcName, callingConvention); + } + + public String getLocalProcAddressCallingConvention(String funcName) { + if (isLocalProcAddressCallingConvention4All()) { + return getLocalProcAddressCallingConvention4All(); + } + return localProcAddressCallingConventionMap.get(funcName); + } + + public boolean isLocalProcAddressCallingConvention4All() { + return localProcAddressCallingConvention4All != null; + } + + public String getLocalProcAddressCallingConvention4All() { + return localProcAddressCallingConvention4All; + } +} diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java new file mode 100755 index 0000000..c07dab7 --- /dev/null +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressEmitter.java @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.procaddress; + +import com.jogamp.gluegen.CMethodBindingEmitter; +import com.jogamp.gluegen.JavaConfiguration; +import com.jogamp.gluegen.MethodBinding; +import com.jogamp.gluegen.FunctionEmitter; +import com.jogamp.gluegen.CodeGenUtils; +import com.jogamp.gluegen.JavaMethodBindingEmitter; +import com.jogamp.gluegen.JavaEmitter; +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import com.jogamp.gluegen.*; +import com.jogamp.gluegen.cgram.types.*; +import com.jogamp.gluegen.runtime.*; + +/** + * A subclass of JavaEmitter that modifies the normal emission of C + * and Java code to allow dynamic lookups of the C entry points + * associated with the Java methods. + */ +public class ProcAddressEmitter extends JavaEmitter { + + public static final String PROCADDRESS_VAR_PREFIX = ProcAddressTable.PROCADDRESS_VAR_PREFIX; + protected static final String WRAP_PREFIX = "dispatch_"; + private TypeDictionary typedefDictionary; + protected PrintWriter tableWriter; + protected Set<String> emittedTableEntries; + protected String tableClassPackage; + protected String tableClassName; + + @Override + public void beginFunctions(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map<Type, Type> canonMap) throws Exception { + this.typedefDictionary = typedefDictionary; + + if (getProcAddressConfig().emitProcAddressTable()) { + beginProcAddressTable(); + } + super.beginFunctions(typedefDictionary, structDictionary, canonMap); + } + + @Override + public void endFunctions() throws Exception { + if (getProcAddressConfig().emitProcAddressTable()) { + endProcAddressTable(); + } + super.endFunctions(); + } + + @Override + public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map<Type, Type> canonMap) throws Exception { + super.beginStructs(typedefDictionary, structDictionary, canonMap); + } + + public String runtimeExceptionType() { + return getConfig().runtimeExceptionType(); + } + + public String unsupportedExceptionType() { + return getConfig().unsupportedExceptionType(); + } + + @Override + protected JavaConfiguration createConfig() { + return new ProcAddressConfiguration(); + } + + @Override + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception { + return generateMethodBindingEmittersImpl(methodBindingSet, sym); + } + + protected boolean needsModifiedEmitters(FunctionSymbol sym) { + if (!needsProcAddressWrapper(sym) + || getConfig().isUnimplemented(getAliasedSymName(sym))) { + return false; + } + + return true; + } + + private List<? extends FunctionEmitter> generateMethodBindingEmittersImpl(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception { + List<? extends FunctionEmitter> defaultEmitters = super.generateMethodBindingEmitters(methodBindingSet, 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()) { + return defaultEmitters; + } + + // Don't do anything special if this symbol doesn't require + // modifications + if (!needsModifiedEmitters(sym)) { + return defaultEmitters; + } + + ArrayList<FunctionEmitter> modifiedEmitters = new ArrayList<FunctionEmitter>(defaultEmitters.size()); + + if (needsProcAddressWrapper(sym)) { + if (getProcAddressConfig().emitProcAddressTable()) { + // emit an entry in the GL proc address table for this method. + emitProcAddressTableEntryForString(getAliasedSymName(sym)); + } + } + for (FunctionEmitter emitter : defaultEmitters) { + if (emitter instanceof JavaMethodBindingEmitter) { + generateModifiedEmitters((JavaMethodBindingEmitter)emitter, modifiedEmitters); + } else if (emitter instanceof CMethodBindingEmitter) { + generateModifiedEmitters((CMethodBindingEmitter) emitter, modifiedEmitters); + } else { + throw new RuntimeException("Unexpected emitter type: " + emitter.getClass().getName()); + } + } + + return modifiedEmitters; + } + + /** + * Returns the name of the typedef for a pointer to the function + * represented by the argument as defined by the ProcAddressNameExpr + * in the .cfg file. For example, in the OpenGL headers, if the + * argument is the function "glFuncName", the value returned will be + * "PFNGLFUNCNAMEPROC". This returns a valid string regardless of + * whether or not the typedef is actually defined. + */ + protected String getFunctionPointerTypedefName(FunctionSymbol sym) { + return getProcAddressConfig().convertToFunctionPointerName(sym.getName()); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + protected void generateModifiedEmitters(JavaMethodBindingEmitter baseJavaEmitter, List<FunctionEmitter> emitters) { + if (getConfig().manuallyImplement(baseJavaEmitter.getName())) { + // User will provide Java-side implementation of this routine; + // pass through any emitters which will produce signatures for + // it unmodified + emitters.add(baseJavaEmitter); + return; + } + + // See whether we need a proc address entry for this one + boolean callThroughProcAddress = needsProcAddressWrapper(baseJavaEmitter.getBinding().getCSymbol()); + + ProcAddressJavaMethodBindingEmitter emitter = + new ProcAddressJavaMethodBindingEmitter(baseJavaEmitter, + callThroughProcAddress, + getProcAddressConfig().getProcAddressTableExpr(), + baseJavaEmitter.isForImplementingMethodCall(), + this); + emitters.add(emitter); + + // 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 + if (baseJavaEmitter.signatureOnly() + && !baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.PRIVATE) + && baseJavaEmitter.hasModifier(JavaMethodBindingEmitter.NATIVE) + && callThroughProcAddress) { + emitter.setEmitBody(true); + emitter.removeModifier(JavaMethodBindingEmitter.NATIVE); + emitter = new ProcAddressJavaMethodBindingEmitter(baseJavaEmitter, + callThroughProcAddress, + getProcAddressConfig().getProcAddressTableExpr(), + true, + this); + emitter.setForImplementingMethodCall(true); + emitters.add(emitter); + } + } + + protected void generateModifiedEmitters(CMethodBindingEmitter baseCEmitter, List<FunctionEmitter> emitters) { + + FunctionSymbol cSymbol = baseCEmitter.getBinding().getCSymbol(); + + // See whether we need a proc address entry for this one + boolean callThroughProcAddress = needsProcAddressWrapper(cSymbol); + boolean forceProcAddress = getProcAddressConfig().forceProcAddressGen(cSymbol.getName()); + + 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 + + // The C-side JNI binding for this particular function will have an + // extra final argument, which is the address (the OpenGL procedure + // address) of the function it needs to call + ProcAddressCMethodBindingEmitter res = new ProcAddressCMethodBindingEmitter( + baseCEmitter, callThroughProcAddress, forceProcAddress, forcedCallingConvention, this); + + MessageFormat exp = baseCEmitter.getReturnValueCapacityExpression(); + if (exp != null) { + res.setReturnValueCapacityExpression(exp); + } + emitters.add(res); + } + + private String getAliasedSymName(FunctionSymbol sym) { + String symName = getConfig().getJavaSymbolRename(sym.getName()); + if (null == symName) { + symName = sym.getName(); + } + return symName; + } + + protected boolean needsProcAddressWrapper(FunctionSymbol sym) { + String symName = getAliasedSymName(sym); + + ProcAddressConfiguration config = getProcAddressConfig(); + + // We should only generate code to call through a function pointer + // if the symbol has an associated function pointer typedef. + 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; + } + + if (shouldWrap) { + // Hoist argument names from function pointer if not supplied in prototype + Type funcPointerType = typedefDictionary.get(funcPointerTypedefName); + if (funcPointerType != null) { + FunctionType typedef = funcPointerType.asPointer().getTargetType().asFunction(); + FunctionType fun = sym.getType(); + int numarg = typedef.getNumArguments(); + for (int i = 0; i < numarg; i++) { + if (fun.getArgumentName(i) == null) { + fun.setArgumentName(i, typedef.getArgumentName(i)); + } + } + } + } + + return shouldWrap; + } + + protected void beginProcAddressTable() throws Exception { + tableClassPackage = getProcAddressConfig().tableClassPackage(); + tableClassName = getProcAddressConfig().tableClassName(); + + // Table defaults to going into the impl directory unless otherwise overridden + String implPackageName = tableClassPackage; + if (implPackageName == null) { + implPackageName = getImplPackageName(); + } + String jImplRoot = getJavaOutputDir() + File.separator + CodeGenUtils.packageAsPath(implPackageName); + + tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); + emittedTableEntries = new HashSet<String>(); + + CodeGenUtils.emitAutogeneratedWarning(tableWriter, this); + + tableWriter.println("package " + implPackageName + ";"); + tableWriter.println(); + for (String imporT : getConfig().imports()) { + tableWriter.println("import " + imporT + ";"); + } + tableWriter.println("import " + ProcAddressTable.class.getName() + ";"); + tableWriter.println(); + + tableWriter.println("/**"); + tableWriter.println(" * This table is a cache of pointers to the dynamically-linkable C library."); + tableWriter.println(" * @see " + ProcAddressTable.class.getSimpleName()); + tableWriter.println(" */"); + tableWriter.println("public class " + tableClassName + " extends "+ ProcAddressTable.class.getSimpleName() + " {"); + tableWriter.println(); + + for (String string : getProcAddressConfig().getForceProcAddressGen()) { + emitProcAddressTableEntryForString(string); + } + + tableWriter.println(); + tableWriter.println(" public "+tableClassName+"(){ super(); }"); + tableWriter.println(); + tableWriter.println(" public "+tableClassName+"("+FunctionAddressResolver.class.getName()+" resolver){ super(resolver); }"); + tableWriter.println(); + + } + + protected void endProcAddressTable() throws Exception { + tableWriter.println("} // end of class " + tableClassName); + tableWriter.flush(); + tableWriter.close(); + } + + protected void emitProcAddressTableEntryForString(String str) { + // Deal gracefully with forced proc address generation in the face + // of having the function pointer typedef in the header file too + if (emittedTableEntries.contains(str)) { + return; + } + emittedTableEntries.add(str); + tableWriter.print(" public long "); + tableWriter.print(PROCADDRESS_VAR_PREFIX); + tableWriter.print(str); + tableWriter.println(";"); + } + + protected ProcAddressConfiguration getProcAddressConfig() { + return (ProcAddressConfiguration) getConfig(); + } +} diff --git a/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java new file mode 100755 index 0000000..443c1d8 --- /dev/null +++ b/src/java/com/jogamp/gluegen/procaddress/ProcAddressJavaMethodBindingEmitter.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ +package com.jogamp.gluegen.procaddress; + +import com.jogamp.gluegen.MethodBinding; +import com.jogamp.gluegen.FunctionEmitter; +import com.jogamp.gluegen.JavaMethodBindingEmitter; +import java.io.*; +import com.jogamp.gluegen.*; + +/** A specialization of JavaMethodBindingEmitter with knowledge of how +to call through a function pointer. */ +public class ProcAddressJavaMethodBindingEmitter extends JavaMethodBindingEmitter { + + protected boolean callThroughProcAddress; + protected boolean changeNameAndArguments; + + protected String getProcAddressTableExpr; + protected ProcAddressEmitter emitter; + + public ProcAddressJavaMethodBindingEmitter(JavaMethodBindingEmitter methodToWrap, boolean callThroughProcAddress, + String getProcAddressTableExpr, boolean changeNameAndArguments, ProcAddressEmitter emitter) { + + super(methodToWrap); + + this.callThroughProcAddress = callThroughProcAddress; + this.getProcAddressTableExpr = getProcAddressTableExpr; + this.changeNameAndArguments = changeNameAndArguments; + this.emitter = emitter; + + if (callThroughProcAddress) { + setCommentEmitter(new WrappedMethodCommentEmitter()); + } + + if (methodToWrap.getBinding().hasContainingType()) { + throw new IllegalArgumentException( + "Cannot create proc. address wrapper; method has containing type: \"" + + methodToWrap.getBinding() + "\""); + } + } + + public ProcAddressJavaMethodBindingEmitter(ProcAddressJavaMethodBindingEmitter methodToWrap) { + this(methodToWrap, methodToWrap.callThroughProcAddress, methodToWrap.getProcAddressTableExpr, + methodToWrap.changeNameAndArguments, methodToWrap.emitter); + } + + @Override + public String getName() { + String res = super.getName(); + if (changeNameAndArguments) { + return ProcAddressEmitter.WRAP_PREFIX + res; + } + return res; + } + + @Override + protected int emitArguments(PrintWriter writer) { + int numEmitted = super.emitArguments(writer); + if (callThroughProcAddress) { + if (changeNameAndArguments) { + if (numEmitted > 0) { + writer.print(", "); + } + + writer.print("long procAddress"); + ++numEmitted; + } + } + + return numEmitted; + } + + @Override + protected String getImplMethodName() { + String name = super.getImplMethodName(); + if (callThroughProcAddress) { + return ProcAddressEmitter.WRAP_PREFIX + name; + } + return name; + } + + @Override + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + super.emitPreCallSetup(binding, writer); + + if (callThroughProcAddress) { + String procAddressVariable = ProcAddressEmitter.PROCADDRESS_VAR_PREFIX + binding.getName(); + writer.println(" final long __addr_ = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); + writer.println(" if (__addr_ == 0) {"); + writer.println(" throw new " + emitter.unsupportedExceptionType() + "(\"Method \\\"" + binding.getName() + "\\\" not available\");"); + writer.println(" }"); + } + } + + @Override + protected int emitCallArguments(MethodBinding binding, PrintWriter writer) { + int numEmitted = super.emitCallArguments(binding, writer); + if (callThroughProcAddress) { + if (numEmitted > 0) { + writer.print(", "); + } + writer.print("__addr_"); + ++numEmitted; + } + + return numEmitted; + } + + /** This class emits the comment for the wrapper method */ + public class WrappedMethodCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { + + @Override + protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { + writer.print("Entry point (through function pointer) to C language function: <br> "); + } + } +} |