diff options
Diffstat (limited to 'src/net/java/games/gluegen')
60 files changed, 16472 insertions, 0 deletions
diff --git a/src/net/java/games/gluegen/ArrayTypes.java b/src/net/java/games/gluegen/ArrayTypes.java new file mode 100644 index 000000000..86867866a --- /dev/null +++ b/src/net/java/games/gluegen/ArrayTypes.java @@ -0,0 +1,107 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +/** + * 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 string[] */ + public static final Class stringArrayClass; + /** 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 stringArrayArrayClass; + /** Class for Java type boolean[][] */ + public static final Class booleanArrayArrayClass; + /** Class for Java type byte[][] */ + public static final Class byteArrayArrayClass; + /** Class for Java type char[][] */ + public static final Class charArrayArrayClass; + /** Class for Java type short[][] */ + public static final Class shortArrayArrayClass; + /** Class for Java type int[][] */ + public static final Class intArrayArrayClass; + /** Class for Java type long[][] */ + public static final Class longArrayArrayClass; + /** Class for Java type float[][] */ + public static final Class floatArrayArrayClass; + /** Class for Java type double[][] */ + public static final Class doubleArrayArrayClass; + + static { + stringArrayClass = new String [0].getClass(); + 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(); + + stringArrayArrayClass = new String [0][0].getClass(); + booleanArrayArrayClass = new boolean[0][0].getClass(); + byteArrayArrayClass = new byte [0][0].getClass(); + charArrayArrayClass = new char [0][0].getClass(); + shortArrayArrayClass = new short [0][0].getClass(); + intArrayArrayClass = new int [0][0].getClass(); + longArrayArrayClass = new long [0][0].getClass(); + floatArrayArrayClass = new float [0][0].getClass(); + doubleArrayArrayClass = new double [0][0].getClass(); + } +} diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java new file mode 100644 index 000000000..45035f640 --- /dev/null +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -0,0 +1,1141 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import java.io.*; +import java.text.MessageFormat; + +import net.java.games.gluegen.cgram.types.*; + +/** Emits the C-side component of the Java<->C JNI binding. */ +public class CMethodBindingEmitter extends FunctionEmitter +{ + protected static final CommentEmitter defaultCommentEmitter = + new DefaultCommentEmitter(); + + private 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; + + /** + * 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; + + // Note: the VC++ 6.0 compiler emits hundreds of warnings when the + // (necessary) null-checking code is enabled. This appears to just + // be a compiler bug, but would be good to track down exactly why it + // is happening. When the null checking is enabled for just the + // GetPrimitiveArrayCritical calls, there are five warnings + // generated for several thousand new if tests added to the code. + // Which ones are the ones at fault? The line numbers for the + // warnings are incorrect. + private static final boolean EMIT_NULL_CHECKS = true; + + /** + * 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, + boolean isOverloadedBinding, + String javaPackageName, + String javaClassName, + boolean isJavaMethodStatic, + PrintWriter output) + { + super(output); + + assert(binding != null); + assert(javaClassName != null); + assert(javaPackageName != null); + + this.binding = binding; + this.packageName = javaPackageName; + this.className = javaClassName; + this.isOverloadedBinding = isOverloadedBinding; + this.isJavaMethodStatic = isJavaMethodStatic; + 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, 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 + * </code> + */ + public final void setReturnValueCapacityExpression(MessageFormat expression) + { + returnValueCapacityExpression = expression; + + if (!binding.getJavaReturnType().isNIOBuffer()) + { + throw new IllegalArgumentException( + "Cannot specify return value capacity for a method that does not " + + "return java.nio.Buffer: \"" + binding + "\""); + } + } + + /** + * 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; + } + + /** + * 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; } + + 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 " + binding.getName() + + // " = " + jniMangle(binding)); + } + else + { + writer.print(jniMangle(binding.getName())); + //System.err.println(" NORMAL MANGLING FOR " + binding.getName() + + // " = " + jniMangle(binding.getName())); + } + } + + 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; + } + + return numEmitted; + } + + + protected void emitBody(PrintWriter writer) + { + writer.println(" {"); + 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()); + } + + 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()) { + String convName = pointerConversionArgumentName(i); + // handle array/buffer argument types + boolean needsDataCopy = + emitPointerDeclaration(writer, + binding.getJavaArgumentType(i), + binding.getCArgumentType(i), + convName); + 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 _arrayLen"+convName+";"); + emittedDataCopyTemps = true; + } + } else if (type.isString()) { + writer.print(" const char* _UTF8"); + writer.print(binding.getArgumentName(i)); + writer.println(" = NULL;"); + } + + } + + // Emit declaration for return value if necessary + Type cReturnType = binding.getCReturnType(); + + JavaType javaReturnType = binding.getJavaReturnType(); + String arrayResLength = "_array_res_length"; + String arrayRes = "_array_res"; + 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.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 (Iterator iter = temporaryCVariableDeclarations.iterator(); iter.hasNext(); ) { + String val = (String) iter.next(); + writer.print(" "); + writer.println(val); + } + } + } + + /** + * 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()); + } + + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { + continue; + } + if (type.isNIOBuffer()) { + emitPointerConversion(writer, binding, type, + binding.getCArgumentType(i), + binding.getArgumentName(i), + pointerConversionArgumentName(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; + } + + if (javaArgType.isArray()) { + + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + writer.print(" "); + String convName = pointerConversionArgumentName(i); + writer.print(convName); + writer.print(" = ("); + Type cArgType = binding.getCArgumentType(i); + String cArgTypeName = cArgType.getName(); + if (javaArgType.isArray() && + javaArgType.getJavaClass().getComponentType() == java.lang.String.class) { + // java-side type is String[] + cArgTypeName = "jstring *"; + } + writer.print(cArgTypeName); + writer.print(") (*env)->GetPrimitiveArrayCritical(env, "); + writer.print(binding.getArgumentName(i)); + writer.println(", NULL);"); + + // 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 + Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); + boolean needsDataCopy = + subArrayElementJavaType.isArray() || + subArrayElementJavaType == java.lang.String.class; + if (needsDataCopy) + { + if (cArgType.toString().indexOf("const") == -1) { + // FIXME: if the arg type is non-const, the sematics might be that + // the function modifies the argument -- we don't yet support + // this. + // + // Note: the check for "const" in the CVAttributes string isn't + // truly checking the constness of the target types at both + // pointer depths. However, it's a quick approximation, and quite + // often C code doesn't get the constness right anyhow. + 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 " + convName + + " into " + convName + "_copy */"); + + // get length of array being copied + String arrayLenName = "_arrayLen"+convName; + writer.print(" "); + writer.print(arrayLenName); + writer.print(" = (*env)->GetArrayLength(env, "); + writer.print(binding.getArgumentName(i)); + 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-primitive."); + } + 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(), + arrayLenName, + "Could not allocate buffer for copying data in argument \\\""+binding.getArgumentName(i)+"\\\""); + + // 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 \"" + binding.getArgumentName(i) + "\" */"); + String subArrayElementJNITypeString = jniType(subArrayElementJavaType); + writer.print(" _tmpObj = ("); + writer.print(subArrayElementJNITypeString); + writer.print(") (*env)->GetObjectArrayElement(env, "); + writer.print(binding.getArgumentName(i)); + writer.println(", _copyIndex);"); + + if (subArrayElementJNITypeString == "jstring") + { + writer.print(" "); + emitGetStringUTFChars(writer, + "(jstring) _tmpObj", + "(const char*)"+convName+"_copy[_copyIndex]"); + } + 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 !! + "(*env)->GetArrayLength(env, _tmpObj)", + "Could not allocate buffer during copying of data in argument \\\""+binding.getArgumentName(i)+"\\\""); + // FIXME: copy the data (use Get<type>ArrayRegion() 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(" }"); + + writer.println(); + } // end of data copy + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + + } else if (javaArgType.isString()) { + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + emitGetStringUTFChars(writer, + binding.getArgumentName(i), + "_UTF8" + binding.getArgumentName(i)); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + } + } + + + /** + * 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; + } + if (javaArgType.isArray()) { + String convName = pointerConversionArgumentName(i); + + // Release array + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + // 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 + Class subArrayElementJavaType = javaArgType.getJavaClass().getComponentType(); + boolean needsDataCopy = + subArrayElementJavaType.isArray() || + subArrayElementJavaType == java.lang.String.class; + if (needsDataCopy) + { + Type cArgType = binding.getCArgumentType(i); + String cArgTypeName = cArgType.getName(); + + if (cArgType.toString().indexOf("const") == -1) { + // 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). + throw new RuntimeException( + "Cannot clean up copied data for ptr-to-ptr arg type \"" + cArgType + + "\": support for cleaning up non-const ptr-to-ptr types not implemented."); + } + + writer.println(" /* Clean up " + convName + "_copy */"); + String arrayLenName = "_arrayLen" + convName; + + // 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."); + } + PointerType cArgElementType = cArgPtrType.getTargetType().asPointer(); + + // 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 */"); + String subArrayElementJNITypeString = jniType(subArrayElementJavaType); + writer.print(" _tmpObj = ("); + writer.print(subArrayElementJNITypeString); + writer.print(") (*env)->GetObjectArrayElement(env, "); + writer.print(binding.getArgumentName(i)); + writer.println(", _copyIndex);"); + + if (subArrayElementJNITypeString == "jstring") + { + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print("(jstring) _tmpObj"); + writer.print(", "); + writer.print(convName+"_copy[_copyIndex]"); + writer.println(");"); + } + else + { + // FIXME: free up stuff here + 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("); + writer.print(convName+"_copy"); + writer.println(");"); + + + writer.println(); + } // end of cleaning up copied data + + writer.print(" (*env)->ReleasePrimitiveArrayCritical(env, "); + writer.print(binding.getArgumentName(i)); + writer.print(", "); + writer.print(convName); + writer.println(", JNI_ABORT);"); + + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } else if (javaArgType.isString()) { + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(binding.getArgumentName(i)); + writer.println(" != NULL) {"); + } + + writer.print(" (*env)->ReleaseStringUTFChars(env, "); + writer.print(binding.getArgumentName(i)); + writer.print(", _UTF8"); + writer.print(binding.getArgumentName(i)); + writer.println(");"); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + } + } + + 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("("); + 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); + writer.print(cArgType.getName()); + writer.print(") "); + if (binding.getCArgumentType(i).isPointer() && binding.getJavaArgumentType(i).isPrimitive()) { + writer.print("(intptr_t) "); + } + if (javaArgType.isArray() || javaArgType.isNIOBuffer()) { + writer.print(pointerConversionArgumentName(i)); + } else { + if (javaArgType.isString()) { writer.print("_UTF8"); } + writer.print(binding.getArgumentName(i)); + } + } + } + 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 (Iterator iter = temporaryCVariableAssignments.iterator(); iter.hasNext(); ) { + String val = (String) iter.next(); + writer.print(" "); + writer.println(val); + } + } + } + + // FIXME: refactor this so that subclasses (in particular, + // net.java.games.gluegen.opengl.CGLPAWrapperEmitter) don't have to copy the whole + // method + 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()) { + writer.println(" if (_res == NULL) return NULL;"); + writer.print(" return (*env)->NewDirectByteBuffer(env, _res, "); + // See whether capacity has been specified + if (returnValueCapacityExpression != null) { + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) + { + argumentNames[i] = binding.getArgumentName(i); + } + writer.print( + returnValueCapacityExpression.format(argumentNames)); + } else { + int sz = 0; + if (cReturnType.isPointer() && + cReturnType.asPointer().getTargetType().isCompound()) { + sz = cReturnType.asPointer().getTargetType().getSize(); + if (sz == -1) { + throw new InternalError( + "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)" + ); + } + } else { + sz = cReturnType.getSize(); + } + writer.print(sz); + System.err.println( + "WARNING: No capacity specified for java.nio.Buffer return " + + "value for function \"" + binding + "\";" + + " assuming size of equivalent C return type (" + sz + " bytes): " + binding); + } + writer.println(");"); + } else if (javaReturnType.isString()) { + writer.print(" if (_res == NULL) return NULL;"); + writer.println(" return (*env)->NewStringUTF(env, _res);"); + } 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(";"); + + } + } + } + + 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(binding.getName())); + buf.append("__"); + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + Class c = type.getJavaClass(); + if (c != null) { + jniMangle(c, buf); + } 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) { + if (c.isArray()) { + res.append("_3"); + jniMangle(c.getComponentType(), res); + } else 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 InternalError("Illegal primitive type"); + } else { + res.append("L"); + res.append(c.getName().replace('.', '_')); + res.append("_2"); + } + } + + private String jniType(Class javaType) + { + if (javaType.isPrimitive()) { + return "j" + javaType.getName(); + } else if (javaType == java.lang.String.class) { + return "jstring"; + } else { + throw new RuntimeException( + "Could not determine JNI type for Java class \"" + + javaType.getName() + "\"; was not String or primitive"); + } + } + + private void emitOutOfMemoryCheck(PrintWriter writer, String varName, + String errorMessage) + { + writer.print(" if ("); + writer.print(varName); + writer.println(" == NULL) {"); + writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/OutOfMemoryException\"),"); + writer.print(" \"" + errorMessage); + writer.print(" in native dispatcher for \\\""); + writer.print(binding.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, + String numElementsExpression, + String mallocFailureErrorString) + { + writer.print(" "); + writer.print(targetVarName); + writer.print(" = ("); + writer.print(elementTypeString); + writer.print(" *) malloc("); + writer.print(numElementsExpression); + writer.print(" * sizeof("); + writer.print(elementTypeString); + writer.println("));"); + // Catch memory allocation failure + if (EMIT_NULL_CHECKS) { + emitOutOfMemoryCheck( + writer, targetVarName, + mallocFailureErrorString); + } + } + + private void emitGetStringUTFChars(PrintWriter writer, + String sourceVarName, + String receivingVarName) + { + 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 + if (EMIT_NULL_CHECKS) { + emitOutOfMemoryCheck( + writer, receivingVarName, + "Failed to get UTF-8 chars for argument \\\""+sourceVarName+"\\\""); + } + } + + // 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 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()) { + // It's an array; get the type of the elements in the array + Class elementType = javaType.getJavaClass().getComponentType(); + if (elementType.isPrimitive()) + { + ptrTypeString = cType.getName(); + } + else if (elementType == java.lang.String.class) + { + ptrTypeString = "jstring *"; + needsDataCopy = true; + } + else if (elementType.isArray()) + { + needsDataCopy = true; + + 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 something we can't/don't handle + throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); + } + } + else + { + ptrTypeString = cType.getName(); + } + + // declare the pointer variable + writer.print(" "); + writer.print(ptrTypeString); + writer.print(" "); + writer.print(cVariableName); + writer.println(" = NULL;"); + + if (needsDataCopy) + { + // 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 + //writer.print(" const "); + Class elementType = javaType.getJavaClass().getComponentType(); + if (javaType.isArray() && + javaType.getJavaClass().getComponentType() == java.lang.String.class) { + writer.print(" char **"); + } else { + writer.print(ptrTypeString); + } + writer.print(" "); + writer.print(cVariableName); + writer.print("_copy = NULL; /* copy of data in "); + writer.print(cVariableName); + 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) { + if (EMIT_NULL_CHECKS) { + writer.print(" if ("); + writer.print(incomingArgumentName); + writer.println(" != NULL) {"); + } + + writer.print(" "); + writer.print(cVariableName); + writer.print(" = ("); + writer.print(cType.getName()); + writer.print(") (*env)->GetDirectBufferAddress(env, "); + writer.print(incomingArgumentName); + writer.println(");"); + + writer.print(" if ("); + writer.print(cVariableName); + writer.println(" == NULL) {"); + writer.println(" (*env)->ThrowNew(env, (*env)->FindClass(env, \"java/lang/RuntimeException\"),"); + writer.print (" \"Argument \\\""); + writer.print(incomingArgumentName); + writer.println("\\\" was not a direct buffer\");"); + writer.print (" return"); + if (!binding.getJavaReturnType().isVoid()) { + writer.print(" 0"); + } + writer.println(";"); + writer.println(" }"); + + if (EMIT_NULL_CHECKS) { + writer.println(" }"); + } + } + + protected String pointerConversionArgumentName(int i) { + return "_ptr" + i; + } + + /** + * 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) { + } + } + +} + diff --git a/src/net/java/games/gluegen/CMethodBindingImplEmitter.java b/src/net/java/games/gluegen/CMethodBindingImplEmitter.java new file mode 100644 index 000000000..95f04d235 --- /dev/null +++ b/src/net/java/games/gluegen/CMethodBindingImplEmitter.java @@ -0,0 +1,99 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import java.io.*; +import java.text.MessageFormat; + +public class CMethodBindingImplEmitter extends CMethodBindingEmitter +{ + protected static final CommentEmitter defaultCImplCommentEmitter = + new CImplCommentEmitter(); + + public CMethodBindingImplEmitter(MethodBinding binding, + boolean isOverloadedBinding, + String javaPackageName, + String javaClassName, + boolean isJavaMethodStatic, + PrintWriter output) + { + super(binding, isOverloadedBinding, + javaPackageName, javaClassName, + isJavaMethodStatic, output); + setCommentEmitter(defaultCImplCommentEmitter); + } + + protected void emitName(PrintWriter writer) + { + super.emitName(writer); + if (!getIsOverloadedBinding()) { + writer.print("0"); + } + } + + /** + * Gets the mangled name for the binding, but assumes that this is an Impl + * routine + */ + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(jniMangle(binding.getName())); + buf.append("0"); + buf.append("__"); + for (int i = 0; i < binding.getNumArguments(); i++) { + JavaType type = binding.getJavaArgumentType(i); + Class c = type.getJavaClass(); + if (c != null) { + jniMangle(c, buf); + } else { + // FIXME: add support for char* -> String conversion + throw new RuntimeException("Unknown kind of JavaType: name="+type.getName()); + } + } + return buf.toString(); + } + + protected static class CImplCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { + protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { + writer.print(" -- FIXME: PUT A COMMENT HERE -- "); + } + } +} diff --git a/src/net/java/games/gluegen/CodeGenUtils.java b/src/net/java/games/gluegen/CodeGenUtils.java new file mode 100644 index 000000000..313896d13 --- /dev/null +++ b/src/net/java/games/gluegen/CodeGenUtils.java @@ -0,0 +1,148 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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, + String[] imports, + String[] accessModifiers, + String[] interfaces, + String classExtended, + EmissionCallback classDocComment) throws IOException + { + w.println("package " + packageName + ";"); + w.println(); + + for (int i = 0; imports != null && i < imports.length; ++i) { + w.print("import "); + w.print(imports[i]); + 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(); + 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/net/java/games/gluegen/CommentEmitter.java b/src/net/java/games/gluegen/CommentEmitter.java new file mode 100644 index 000000000..337161ce0 --- /dev/null +++ b/src/net/java/games/gluegen/CommentEmitter.java @@ -0,0 +1,52 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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/net/java/games/gluegen/DebugEmitter.java b/src/net/java/games/gluegen/DebugEmitter.java new file mode 100644 index 000000000..677c3e7e6 --- /dev/null +++ b/src/net/java/games/gluegen/DebugEmitter.java @@ -0,0 +1,102 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; + +import net.java.games.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 md) {} + + 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(String name, String value, String optionalComment) { + System.out.println("#define " + name + " " + value + + (optionalComment != null ? ("// " + optionalComment) : "")); + } + public void endDefines() {} + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map canonMap) { + Set keys = typedefDictionary.keySet(); + for (Iterator iter = keys.iterator(); iter.hasNext(); ) { + String key = (String) iter.next(); + Type value = typedefDictionary.get(key); + System.out.println("typedef " + value + " " + key + ";"); + } + } + public Iterator emitFunctions(List/*<FunctionSymbol>*/ originalCFunctions) + throws Exception { + for (Iterator iter = originalCFunctions.iterator(); iter.hasNext(); ) { + FunctionSymbol sym = (FunctionSymbol) iter.next(); + 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 canonMap) { + } + public void emitStruct(CompoundType t) { + System.out.println("Referenced type \"" + t.getName() + "\""); + } + public void endStructs() {} +} diff --git a/src/net/java/games/gluegen/FunctionEmitter.java b/src/net/java/games/gluegen/FunctionEmitter.java new file mode 100644 index 000000000..cb5ef4159 --- /dev/null +++ b/src/net/java/games/gluegen/FunctionEmitter.java @@ -0,0 +1,199 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import java.io.*; + +public abstract class FunctionEmitter +{ + public static final EmissionModifier STATIC = new EmissionModifier("static"); + + private HashSet modifiers = new HashSet(4); + private CommentEmitter commentEmitter = null; + private PrintWriter defaultOutput; + + /** + * Constructs the FunctionEmitter with a CommentEmitter that emits nothing. + */ + public FunctionEmitter(PrintWriter defaultOutput) + { + assert(defaultOutput != null); + this.defaultOutput = defaultOutput; + } + + public PrintWriter getDefaultOutput() { return defaultOutput; } + + public void addModifiers(Iterator/*<EmissionModifier>*/ mi) + { + while (mi.hasNext()) + { + modifiers.add((EmissionModifier) 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 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. */ + 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 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 + { + public final String toString() { return emittedForm; } + + private String emittedForm; + protected EmissionModifier(String emittedForm) { this.emittedForm = emittedForm; } + } +} + diff --git a/src/net/java/games/gluegen/GlueEmitter.java b/src/net/java/games/gluegen/GlueEmitter.java new file mode 100644 index 000000000..3d55474e0 --- /dev/null +++ b/src/net/java/games/gluegen/GlueEmitter.java @@ -0,0 +1,99 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import net.java.games.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; + + /** Set the description of the underlying hardware. */ + public void setMachineDescription(MachineDescription md); + + /** + * 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(String name, String value, String optionalComment) throws Exception; + public void endDefines() throws Exception; + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map canonMap) throws Exception; + + /** Emit glue code for the list of FunctionSymbols. */ + public Iterator emitFunctions(java.util.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 canonMap) throws Exception; + /** Emit glue code for the given CompoundType. */ + public void emitStruct(CompoundType t) throws Exception; + public void endStructs() throws Exception; +} diff --git a/src/net/java/games/gluegen/GlueEmitterControls.java b/src/net/java/games/gluegen/GlueEmitterControls.java new file mode 100644 index 000000000..f4f48a399 --- /dev/null +++ b/src/net/java/games/gluegen/GlueEmitterControls.java @@ -0,0 +1,49 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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); +} diff --git a/src/net/java/games/gluegen/GlueGen.java b/src/net/java/games/gluegen/GlueGen.java new file mode 100644 index 000000000..374611c81 --- /dev/null +++ b/src/net/java/games/gluegen/GlueGen.java @@ -0,0 +1,299 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.io.*; +import java.util.*; + +import antlr.*; +import antlr.collections.*; +import net.java.games.gluegen.cgram.*; +import net.java.games.gluegen.cgram.types.*; +import net.java.games.gluegen.pcpp.*; + +/** Glue code generator for C functions and data structures. */ + +public class GlueGen implements GlueEmitterControls { + private java.util.List forcedStructNames = new ArrayList(); + + public void forceStructEmission(String typedefName) { + forcedStructNames.add(typedefName); + } + + public void run(String[] args) { + try { + Reader reader = null; + String filename = null; + String emitterClass = null; + java.util.List cfgFiles = new ArrayList(); + + if (args.length == 0) { + usage(); + } + + java.util.List includePaths = new ArrayList(); + 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.startsWith("-E")) { + emitterClass = arg.substring(2); + } else if (arg.startsWith("-C")) { + cfgFiles.add(arg.substring(2)); + } 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)); + } + } + } + + final PCPP preprocessor = new PCPP(includePaths); + PipedInputStream ppIn = new PipedInputStream(); + final PipedOutputStream ppOut = new PipedOutputStream(ppIn); + preprocessor.setOut(ppOut); + final Reader rdr = reader; + final String fn = filename; + new Thread(new Runnable() { + public void run() { + try { + preprocessor.run(rdr, fn); + ppOut.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }).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.setASTNodeType(TNode.class.getName()); + TNode.setTokenVocabulary(GNUCTokenTypes.class.getName()); + + // invoke parser + try { + parser.translationUnit(); + } + catch (RecognitionException e) { + System.err.println("Fatal IO error:\n"+e); + System.exit(1); + } + catch (TokenStreamException e) { + System.err.println("Fatal IO error:\n"+e); + System.exit(1); + } + + HeaderParser headerParser = new HeaderParser(); + MachineDescription machDesc = new MachineDescription32Bit(); + headerParser.setMachineDescription(machDesc); + 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.setASTNodeType(TNode.class.getName()); + // walk that tree + headerParser.translationUnit( parser.getAST() ); + + // For debugging: Dump type dictionary and struct dictionary to System.err + //td.dumpDictionary(System.err, "All Types"); + //sd.dumpDictionary(System.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) Class.forName(emitterClass).newInstance(); + } catch (Exception e) { + System.err.println("Exception occurred while instantiating emitter class. Exiting."); + e.printStackTrace(); + System.exit(1); + } + } + + for (Iterator iter = cfgFiles.iterator(); iter.hasNext(); ) { + emit.readConfigurationFile((String) iter.next()); + } + + // provide MachineDescription to emitter if it needs it + emit.setMachineDescription(machDesc); + + // begin emission of glue code + emit.beginEmission(this); + + emit.beginDefines(); + Set emittedDefines = new HashSet(100); + // emit java equivalent of enum { ... } statements + for (Iterator iter = headerParser.getEnums().iterator(); iter.hasNext(); ) { + EnumType enumeration = (EnumType)iter.next(); + // iterate over all values in the enumeration + for (int i = 0; i < enumeration.getNumEnumerates(); ++i) { + String enumElementName = enumeration.getEnumName(i); + if (emittedDefines.contains(enumElementName) == false) { + emittedDefines.add(enumElementName); + String comment = null; + if (! enumeration.getName().equals("<anonymous>")) { + comment = "Defined as part of enum type \"" + + enumeration.getName() + "\""; + } + emit.emitDefine( + enumElementName, + String.valueOf(enumeration.getEnumValue(i)), + comment); + } + } + } + // emit java equivalent of #define statements + for (Iterator iter = lexer.getDefines().iterator(); iter.hasNext(); ) { + Define def = (Define) iter.next(); + if (emittedDefines.contains(def.getName()) == false) { + emittedDefines.add(def.getName()); + emit.emitDefine(def.getName(), def.getValue(), null); + } + } + emit.endDefines(); + + java.util.List functions = headerParser.getParsedFunctions(); + + // 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 (Iterator iter = functions.iterator(); iter.hasNext(); ) { + FunctionSymbol sym = (FunctionSymbol) iter.next(); + // 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 (Iterator iter = forcedStructNames.iterator(); iter.hasNext(); ) { + String name = (String) iter.next(); + Type type = td.get(name); + if (type == null) { + System.err.println("WARNING: during forced struct emission: struct \"" + name + "\" not found"); + } else if (!type.isCompound()) { + System.err.println("WARNING: during forced struct emission: type \"" + name + "\" was not a struct"); + } else { + type.visit(referencedStructs); + } + } + + // Lay out structs + emit.beginStructLayout(); + for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { + emit.layoutStruct((CompoundType) iter.next()); + } + emit.endStructLayout(); + + // Emit structs + emit.beginStructs(td, sd, headerParser.getCanonMap()); + for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { + emit.emitStruct((CompoundType) iter.next()); + } + 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 ) { + e.printStackTrace(); + System.err.println("Exception occurred while generating glue code. Exiting."); + System.exit(1); + } + } + + public static void main(String[] args) { + new GlueGen().run(args); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static void usage() { + System.out.println("Usage: java GlueGen [-I...] [-Eemitter_class_name] [-Ccfg_file_name...] <filename | ->"); + System.out.println(); + System.out.println("Runs C header parser on input file or standard input, first"); + System.out.println("passing input through minimal pseudo-C-preprocessor. Use -I"); + System.out.println("command-line arguments to specify the search path for #includes."); + System.out.println("Emitter class name can be specified with -E option: i.e.,"); + System.out.println("-Enet.java.games.gluegen.JavaEmitter (the default). Use"); + System.out.println("-Enet.java.games.gluegen.DebugEmitter to print recognized entities"); + System.out.println("(#define directives to constant numbers, typedefs, and function"); + System.out.println("declarations) to standard output. Emitter-specific configuration"); + System.out.println("file or files can be specified with -C option; e.g,"); + System.out.println("-Cjava-emitter.cfg."); + System.exit(1); + } +} diff --git a/src/net/java/games/gluegen/JavaConfiguration.java b/src/net/java/games/gluegen/JavaConfiguration.java new file mode 100644 index 000000000..ecd953480 --- /dev/null +++ b/src/net/java/games/gluegen/JavaConfiguration.java @@ -0,0 +1,1051 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import net.java.games.gluegen.cgram.types.*; + +/** 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; + /** + * Root directory for the hierarchy of generated java classes. Default is + * working directory. + */ + private String javaOutputDir = "."; + /** + * 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; + /** + * 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 int emissionStyle = JavaEmitter.ALL_STATIC; + /** + * List of imports to emit at the head of the output files. + */ + private List/*<String>*/ imports = new ArrayList(); + /** + * The kind of exception raised by the generated code if run-time + * checks fail. Defaults to RuntimeException. + */ + private String runtimeExceptionType = "RuntimeException"; + private Map/*<String,TypeInfo>*/ typeInfoMap = new HashMap(); + private Set/*<String>*/ returnsString = new HashSet(); + private Map/*<String, String>*/ returnedArrayLengths = new HashMap(); + /** + * Key is function that has some byte[] arguments that should be + * converted to String args; value is List of Integer argument indices + */ + private Map/*<String,List<Integer>>*/ argumentsAreString = new HashMap(); + private Set/*<Pattern>*/ ignores = new HashSet(); + private Set/*<Pattern>*/ ignoreNots = new HashSet(); + private Set/*<Pattern>*/ unimplemented = new HashSet(); + private Set/*<String>*/ nioOnly = new HashSet(); + private Set/*<String>*/ manuallyImplement = new HashSet(); + private Map/*<String,List<String>>*/ customJavaCode = new HashMap(); + private Map/*<String,List<String>>*/ classJavadoc = new HashMap(); + private Map/*<String,String>*/ structPackages = new HashMap(); + private List/*<String>*/ customCCode = new ArrayList(); + private List/*<String>*/ forcedStructs = new ArrayList(); + private Map/*<String,List<Integer>>*/ mirroredArgs = new HashMap(); + private Map/*<String, String>*/ returnValueCapacities = new HashMap(); + private Map/*<String, List<String>>*/ temporaryCVariableDeclarations = new HashMap(); + private Map/*<String, List<String>>*/ temporaryCVariableAssignments = new HashMap(); + private Map/*<String,List<String>>*/ extendedInterfaces = new HashMap(); + private Map/*<String,String>*/ javaTypeRenames = new HashMap(); + + /** Reads the configuration file. + @param 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 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) { + throw new RuntimeException("Output class name was not specified in configuration file"); + } + if (packageName == null) { + throw new RuntimeException("Output package name was not specified in configuration file"); + } + + 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 + implClassName = className + "Impl"; + } + if (implPackageName == null) { + // implPackageName defaults to "<packageName>.impl" if ImplPackage + // directive is not used + implPackageName = packageName + ".impl"; + } + } + } + } + + /** 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; } + /** Returns the Java code output directory parsed from the configuration file. */ + public String javaOutputDir() { return javaOutputDir; } + /** Returns the native code output directory parsed from the configuration file. */ + public String nativeOutputDir() { return nativeOutputDir; } + /** Returns whether the native code directory structure mirrors the Java hierarchy. */ + public boolean nativeOutputUsesJavaHierarchy() { return nativeOutputUsesJavaHierarchy; } + /** Returns the code emission style (constants in JavaEmitter) parsed from the configuration file. */ + public int emissionStyle() { return emissionStyle; } + /** Returns the kind of exception to raise if run-time checks fail in the generated code. */ + public String runtimeExceptionType() { return runtimeExceptionType; } + /** Returns the list of imports that should be emitted at the top of each .java file. */ + public List/*<String>*/ imports() { return imports; } + + /** 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. + int pointerDepth = type.pointerDepth(); + for (int i = 0; i <= pointerDepth; i++) { + String name = type.getName(); + if (name != null) { + TypeInfo info = (TypeInfo) typeInfoMap.get(name); + while (info != null) { + if (info.name().equals(name) && info.pointerDepth() == i) { + return info; + } + info = info.next(); + } + } + + if (type.isCompound()) { + // Try struct name as well + name = type.asCompound().getStructName(); + if (name != null) { + TypeInfo info = (TypeInfo) typeInfoMap.get(name); + while (info != null) { + if (info.name().equals(name) && info.pointerDepth() == i) { + return info; + } + info = info.next(); + } + } + } + + // Try all typedef names that map to this type + Set entrySet = typedefDictionary.entrySet(); + for (Iterator iter = entrySet.iterator(); iter.hasNext(); ) { + Map.Entry entry = (Map.Entry) iter.next(); + // "eq" equality is OK to use here since all types have been canonicalized + if (entry.getValue() == type) { + name = (String) entry.getKey(); + TypeInfo info = (TypeInfo) typeInfoMap.get(name); + while (info != null) { + if (info.name().equals(name) && info.pointerDepth() == i) { + return info; + } + info = info.next(); + } + } + } + + if (type.isPointer()) { + type = type.asPointer().getTargetType(); + } + } + + return null; + } + + /** 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 as defined by the ReturnsArray directive. */ + public String returnedArrayLength(String functionName) { + return (String) 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 (List) argumentsAreString.get(functionName); + } + + /** Returns true if the given function should only create a java.nio + variant, and no array variants, for <code>void*</code> + pointers. */ + public boolean nioOnly(String functionName) { + return nioOnly.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 customJavaCodeForClass(String className) { + List res = (List) customJavaCode.get(className); + if (res == null) { + res = new ArrayList(); + 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 javadocForClass(String className) { + List res = (List) classJavadoc.get(className); + if (res == null) { + res = new ArrayList(); + 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 getPackage}}. */ + public String packageForStruct(String structName) { + String res = (String) 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 List of Integers indicating the indices of arguments + in this function that should be expanded to the same type when + binding functions with multiple void* arguments. Returns null if + no such indices were specified. */ + public List/*<Integer>*/ mirroredArgs(String functionName) { + return (List) mirroredArgs.get(functionName); + } + + /** 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 (String) returnValueCapacities.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 (List) 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 (List) 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 res = (List) extendedInterfaces.get(interfaceName); + if (res == null) { + res = new ArrayList(); + extendedInterfaces.put(interfaceName, res); + } + return res; + } + + /** Returns true if this #define, function, struct, or field within + a struct should be ignored during glue code generation. */ + public boolean shouldIgnore(String symbol) { + + //System.err.println("CHECKING IGNORE: " + symbol); + + // Simple case; the entire symbol is in the ignore table. + if (ignores.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 (Iterator iter = ignores.iterator(); iter.hasNext(); ) { + Pattern regexp = (Pattern)iter.next(); + Matcher matcher = regexp.matcher(symbol); + if (matcher.matches()) { + 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 (Iterator iter = ignoreNots.iterator(); iter.hasNext(); ) { + Pattern regexp = (Pattern)iter.next(); + Matcher matcher = regexp.matcher(symbol); + if (!matcher.matches()) { + 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 (Iterator iter = unimplemented.iterator(); iter.hasNext(); ) { + Pattern regexp = (Pattern)iter.next(); + 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 = (String) javaTypeRenames.get(javaTypeName); + if (rename != null) { + return rename; + } + return javaTypeName; + } + + /** Returns true if the emission style is AllStatic. */ + public boolean allStatic() { + return (emissionStyle == JavaEmitter.ALL_STATIC); + } + + /** Returns true if an interface should be emitted during glue code generation. */ + public boolean emitInterface() { + return (emissionStyle() == JavaEmitter.INTERFACE_AND_IMPL || + emissionStyle() == JavaEmitter.INTERFACE_ONLY); + } + + /** Returns true if an implementing class should be emitted during glue code generation. */ + public boolean emitImpl() { + return (emissionStyle() == JavaEmitter.ALL_STATIC || + emissionStyle() == JavaEmitter.INTERFACE_AND_IMPL || + emissionStyle() == JavaEmitter.IMPL_ONLY); + } + + //---------------------------------------------------------------------- + // 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("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("Style")) { + String style = readString("Style", tok, filename, lineNo); + if (style.equalsIgnoreCase("AllStatic")) { + emissionStyle = JavaEmitter.ALL_STATIC; + } else if (style.equalsIgnoreCase("InterfaceAndImpl")) { + emissionStyle = JavaEmitter.INTERFACE_AND_IMPL; + } else if (style.equalsIgnoreCase("InterfaceOnly")) { + emissionStyle = JavaEmitter.INTERFACE_ONLY; + } else if (style.equalsIgnoreCase("ImplOnly")) { + emissionStyle = JavaEmitter.IMPL_ONLY; + } else { + System.err.println("WARNING: Error parsing \"style\" command at line " + lineNo + + " in file \"" + filename + "\""); + } + } 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("Ignore")) { + readIgnore(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")) { + nioOnly.add(readString("NioOnly", tok, filename, lineNo)); + } else if (cmd.equalsIgnoreCase("EmitStruct")) { + forcedStructs.add(readString("EmitStruct", tok, filename, lineNo)); + } else if (cmd.equalsIgnoreCase("MirrorExpandedBindingArgs")) { + readMirrorExpandedBindingArgs(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("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("RenameJavaType")) { + readRenameJavaType(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("RuntimeExceptionType")) { + runtimeExceptionType = readString("RuntimeExceptionType", tok, filename, lineNo); + } 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 Integer.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 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); + } + } + + protected void readIgnore(StringTokenizer tok, String filename, int lineNo) { + try { + String regex = tok.nextToken(); + ignores.add(Pattern.compile(regex)); + //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 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 className = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + addCustomJavaCode(className, restOfLine); + } 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 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) { + throw new RuntimeException("Error parsing \"CustomCCode\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + protected void readClassJavadoc(StringTokenizer tok, String filename, int lineNo) { + try { + String className = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + addClassJavadoc(className, 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 codeList = javadocForClass(className); + codeList.add(code); + } + + /** + * When void* arguments in the C function prototypes are encountered, the + * emitter will try to expand the binding and create Java entry points for + * all possible array types. If there are 2 or more void* arguments in the C + * prototype, this directive lets you specify which of those arguments + * should always be expanded to the same type. <p> + * + * For example, given the C prototype: + * <pre> + * void FuncName(void *foo, void *bar); + * </pre> + * + * The emitter will normally emit multiple Java entry points: + * <pre> + * public abstract void FuncName(boolean[] foo, java.nio.Buffer bar); + * public abstract void FuncName(boolean[] foo, boolean[] bar); + * public abstract void FuncName(boolean[] foo, byte[] bar); + * public abstract void FuncName(boolean[] foo, char[] bar); + * public abstract void FuncName(boolean[] foo, short[] bar); + * public abstract void FuncName(boolean[] foo, int[] bar); + * public abstract void FuncName(boolean[] foo, long[] bar); + * public abstract void FuncName(boolean[] foo, float[] bar); + * public abstract void FuncName(boolean[] foo, double[] bar); + * + * public abstract void FuncName(byte[] foo, java.nio.Buffer bar); + * public abstract void FuncName(byte[] foo, boolean[] bar); + * public abstract void FuncName(byte[] foo, byte[] bar); + * <...etc for all variants on the second parameter...> + * + * public abstract void FuncName(char[] foo, java.nio.Buffer bar); + * public abstract void FuncName(char[] foo, boolean[] bar); + * public abstract void FuncName(char[] foo, byte[] bar); + * <...etc for all variants on the second parameter...> + * <...and so on for all remaining variants on the first parameter...> + * </pre> + * + * This directive lets you specify that arguments at a particular index + * should always be expanded to the same type. For example, the directive: + * <pre> + * MirrorExpandedBindingArgs FuncName 0 1 + * </pre> + * will force the first and second arguments in function FuncName to be + * expanded identically. This would result in the emission of the following + * entry points only: + * <pre> + * public abstract void FuncName(java.nio.Buffer[] foo, java.nio.Buffer bar); + * public abstract void FuncName(boolean[] foo, boolean[] bar); + * public abstract void FuncName(byte[] foo, byte[] bar); + * public abstract void FuncName(char[] foo, char[] bar); + * public abstract void FuncName(short[] foo, short[] bar); + * public abstract void FuncName(int[] foo, int[] bar); + * public abstract void FuncName(long[] foo, long[] bar); + * public abstract void FuncName(float[] foo, float[] bar); + * public abstract void FuncName(double[] foo, double[] bar); + * </pre> + */ + protected void readMirrorExpandedBindingArgs(StringTokenizer tok, String filename, int lineNo) { + try { + String methodName = tok.nextToken(); + ArrayList argIndices = new ArrayList(2); + while (tok.hasMoreTokens()) + { + Integer idx = Integer.valueOf(tok.nextToken()); + argIndices.add(idx); + } + + if(argIndices.size() > 1) + { + mirroredArgs.put(methodName, argIndices); + } + else + { + throw new RuntimeException("ERROR: Error parsing \"MirrorExpandedBindingArgs\" command at line " + lineNo + + " in file \"" + filename + "\": directive requires at least 2 argument indices"); + } + } catch (NoSuchElementException e) { + throw new RuntimeException( + "Error parsing \"MirrorExpandedBindingArgs\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + /** + * 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 argIndices = new ArrayList(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 readTemporaryCVariableDeclaration(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + List list = (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 list = (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 { + StringBuffer linePrefix = new StringBuffer(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 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 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 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 = (TypeInfo) typeInfoMap.get(info.name()); + if (tmp == null) { + typeInfoMap.put(info.name(), info); + return; + } + while (tmp.next() != null) { + tmp = tmp.next(); + } + tmp.setNext(info); + } +} diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java new file mode 100644 index 000000000..228b5aff2 --- /dev/null +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -0,0 +1,1246 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.io.*; +import java.util.*; +import java.text.MessageFormat; + +import net.java.games.gluegen.cgram.types.*; + +// 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 canonMap; + private 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). + */ + static final int ALL_STATIC = 1; + static final int INTERFACE_AND_IMPL = 2; + static final int INTERFACE_ONLY = 3; + static final int IMPL_ONLY = 4; + + 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 machDesc; + + public void readConfigurationFile(String filename) throws Exception { + cfg = createConfig(); + cfg.read(filename); + } + + public void setMachineDescription(MachineDescription md) { + machDesc = md; + } + + public void beginEmission(GlueEmitterControls controls) throws IOException + { + try + { + openWriters(); + } + catch (Exception e) + { + throw new RuntimeException( + "Unable to open files for writing", e); + } + + emitAllFileHeaders(); + + // Request emission of any structs requested + for (Iterator iter = cfg.forcedStructs().iterator(); iter.hasNext(); ) { + controls.forceStructEmission((String) iter.next()); + } + } + + public void endEmission() + { + 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()) { + javaWriter().println(); + } + } + + public void emitDefine(String name, String value, 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. + + if (!cfg.shouldIgnore(name)) { + String type = null; + + // 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); + type = "long"; + // if constant is small enough, store it as an int instead of a long + if (longVal > Integer.MIN_VALUE && longVal < Integer.MAX_VALUE) { + type = "int"; + } + + } catch (NumberFormatException e) { + try { + // see if it's a double or float + double dVal = Double.parseDouble(value); + type = "double"; + // if constant is small enough, store it as a float instead of a double + if (dVal > Float.MIN_VALUE && dVal < Float.MAX_VALUE) { + type = "float"; + } + + } catch (NumberFormatException e2) { + throw new RuntimeException( + "Cannot emit define \""+name+"\": value \""+value+ + "\" cannot be assigned to a int, long, float, or double", e2); + } + } + + if (type == null) { + throw new RuntimeException( + "Cannot emit define (2) \""+name+"\": value \""+value+ + "\" cannot be assigned to a int, long, float, or double"); + } + if (optionalComment != null && optionalComment.length() != 0) { + javaWriter().println(" /** " + optionalComment + " */"); + } + javaWriter().println(" public static final " + type + " " + name + " = " + value + ";"); + } + } + } + + public void endDefines() throws Exception + { + } + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map canonMap) throws Exception { + this.typedefDictionary = typedefDictionary; + this.structDictionary = structDictionary; + this.canonMap = canonMap; + if (cfg.allStatic() || cfg.emitInterface()) { + javaWriter().println(); + } + } + + public Iterator 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 funcsToBindSet = new HashSet(100); + for (Iterator cIter = originalCFunctions.iterator(); cIter.hasNext(); ) { + FunctionSymbol cFunc = (FunctionSymbol) cIter.next(); + if (!funcsToBindSet.contains(cFunc)) { + funcsToBindSet.add(cFunc); + } + } + + ArrayList funcsToBind = new ArrayList(funcsToBindSet.size()); + funcsToBind.addAll(funcsToBindSet); + // sort functions to make them easier to find in native code + Collections.sort( + funcsToBind, + new Comparator() { + public int compare(Object o1, Object o2) { + return ((FunctionSymbol)o1).getName().compareTo( + ((FunctionSymbol)o2).getName()); + } + public boolean equals(Object obj) { + return obj.getClass() == this.getClass(); + } + }); + + // Bind all the C funcs to Java methods + ArrayList/*<FunctionEmitter>*/ methodBindingEmitters = new ArrayList(2*funcsToBind.size()); + for (Iterator iter = funcsToBind.iterator(); iter.hasNext(); ) { + FunctionSymbol cFunc = (FunctionSymbol) iter.next(); + // Check to see whether this function should be ignored + if (cfg.shouldIgnore(cFunc.getName())) { + continue; // don't generate bindings for this symbol + } + + Iterator allBindings = generateMethodBindingEmitters(cFunc); + while (allBindings.hasNext()) { + methodBindingEmitters.add(allBindings.next()); + } + } + + // Emit all the methods + for (int i = 0; i < methodBindingEmitters.size(); ++i) { + FunctionEmitter emitter = (FunctionEmitter)methodBindingEmitters.get(i); + try { + emitter.emit(); + } catch (Exception e) { + throw new RuntimeException( + "Error while emitting binding for \"" + emitter.getName() + "\"", e); + } + emitter.getDefaultOutput().println(); // put newline after method body + } + + // 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; + } + + /** + * Generate all appropriate Java bindings for the specified C function + * symbols. + */ + protected Iterator generateMethodBindingEmitters(FunctionSymbol sym) throws Exception { + + ArrayList/*<FunctionEmitter>*/ allEmitters = new ArrayList(1); + + try { + // Get Java binding for the function + MethodBinding mb = bindFunction(sym, null, null); + + // Expand all void* arguments + List bindings = expandMethodBinding(mb); + boolean overloaded = (bindings.size() > 1); + if (overloaded) { + // resize ahead of time for speed + allEmitters.ensureCapacity(bindings.size()); + } + + // List of the indices of the arguments in this function that should be + // expanded to the same type when binding functions with multiple void* + // arguments + List mirrorIdxs = cfg.mirroredArgs(sym.getName()); + + for (Iterator iter = bindings.iterator(); iter.hasNext(); ) { + MethodBinding binding = (MethodBinding) iter.next(); + + // Honor the MirrorExpandedBindingArgs directive in .cfg files + if (mirrorIdxs != null) { + assert(mirrorIdxs.size() >= 2); // sanity check. + boolean typesMatch = true; + int argIndex = ((Integer)mirrorIdxs.get(0)).intValue(); + JavaType leftArgType = binding.getJavaArgumentType(argIndex); + for (int i = 1; i < mirrorIdxs.size(); ++i) { + argIndex = ((Integer)mirrorIdxs.get(i)).intValue(); + JavaType rightArgType = binding.getJavaArgumentType(argIndex); + if (!(leftArgType.equals(rightArgType))) { + typesMatch = false; + break; + } + leftArgType = rightArgType; + } + // Don't emit the binding if the specified args aren't the same type + if (!typesMatch) { continue; } // skip this binding + } + + // Try to create an NIOBuffer variant for this expanded binding. If + // it's the same as the original binding, then we'll be able to emit + // the binding like any normal binding because no special binding + // generation (wrapper methods, etc) will be necessary. + MethodBinding specialBinding = binding.createNIOBufferVariant(); + + 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 + "\""); + } + + boolean isUnimplemented = cfg.isUnimplemented(binding.getName()); + + if (cfg.emitImpl()) { + // Generate the emitter for the method which may do conversion + // from type wrappers to NIO Buffers or which may call the + // underlying function directly + JavaMethodBindingImplEmitter entryPoint = + new JavaMethodBindingImplEmitter(binding, + (cfg.allStatic() ? javaWriter() : javaImplWriter()), + cfg.runtimeExceptionType(), + isUnimplemented); + entryPoint.addModifier(JavaMethodBindingEmitter.PUBLIC); + if (cfg.allStatic()) { + entryPoint.addModifier(JavaMethodBindingEmitter.STATIC); + } + if (!isUnimplemented && !binding.needsBody()) { + entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); + } + entryPoint.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(entryPoint); + } + + if (cfg.emitInterface()) { + // Generate an emitter that will emit just the interface to the function + JavaMethodBindingEmitter entryPointInterface = + new JavaMethodBindingEmitter(binding, javaWriter(), cfg.runtimeExceptionType()); + entryPointInterface.addModifier(JavaMethodBindingEmitter.PUBLIC); + entryPointInterface.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); + allEmitters.add(entryPointInterface); + } + + if (cfg.emitImpl() && binding.needsBody() && !isUnimplemented) { + // Generate the method which calls the underlying function + // after unboxing has occurred + PrintWriter output = cfg.allStatic() ? javaWriter() : javaImplWriter(); + JavaMethodBindingEmitter wrappedEntryPoint = + new JavaMethodBindingEmitter(specialBinding, output, cfg.runtimeExceptionType(), true); + wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.PRIVATE); + wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.STATIC); // Doesn't really matter + wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); + allEmitters.add(wrappedEntryPoint); + } + + // If the user has stated that the function will be + // manually implemented, then don't auto-generate a function body. + if (cfg.emitImpl()) { + if (!cfg.manuallyImplement(sym.getName()) && !isUnimplemented) + { + CMethodBindingEmitter cEmitter = + makeCEmitter(specialBinding, + overloaded, + (binding != specialBinding), + cfg.implPackageName(), cfg.implClassName(), + cWriter()); + allEmitters.add(cEmitter); + } + } + } // end iteration over expanded bindings + } catch (Exception e) { + throw new RuntimeException( + "Error while generating bindings for \"" + sym + "\"", e); + } + + return allEmitters.iterator(); + } + + + public void endFunctions() throws Exception + { + 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 canonMap) throws Exception { + this.typedefDictionary = typedefDictionary; + this.structDictionary = structDictionary; + this.canonMap = canonMap; + } + + public void emitStruct(CompoundType structType) throws Exception { + if (structType.getName() == null) { + System.err.println("WARNING: skipping emission of unnamed struct \"" + structType + "\""); + return; + } + + if (cfg.shouldIgnore(structType.getName())) { + return; + } + + Type containingCType = canonicalize(new PointerType(machDesc.pointerSizeInBytes(), structType, 0)); + JavaType containingType = typeToJavaType(containingCType, false); + String containingTypeName = containingType.getName(); + + boolean needsNativeCode = false; + for (int i = 0; i < structType.getNumFields(); i++) { + if (structType.getField(i).getType().isFunctionPointer()) { + needsNativeCode = true; + break; + } + } + + String structClassPkg = cfg.packageForStruct(structType.getName()); + PrintWriter writer = null; + PrintWriter cWriter = null; + try + { + writer = openFile( + cfg.javaOutputDir() + File.separator + + CodeGenUtils.packageAsPath(structClassPkg) + + File.separator + containingTypeName + ".java"); + CodeGenUtils.emitAutogeneratedWarning(writer, this); + if (needsNativeCode) { + String nRoot = cfg.nativeOutputDir(); + if (cfg.nativeOutputUsesJavaHierarchy()) { + nRoot += + File.separator + + CodeGenUtils.packageAsPath(cfg.packageName()); + } + cWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c"); + CodeGenUtils.emitAutogeneratedWarning(cWriter, this); + emitCHeader(cWriter, 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 net.java.games.gluegen.runtime.*;"); + writer.println(); + List/*<String>*/ javadoc = cfg.javadocForClass(containingTypeName); + for (Iterator iter = javadoc.iterator(); iter.hasNext(); ) { + writer.println((String) iter.next()); + } + writer.println(); + writer.println("public class " + containingTypeName + " {"); + writer.println(" private StructAccessor accessor;"); + writer.println(); + writer.println(" public static int size() {"); + writer.println(" return " + structType.getSize() + ";"); + writer.println(" }"); + writer.println(); + writer.println(" public " + containingTypeName + "() {"); + writer.println(" this(BufferFactory.newDirectByteBuffer(size()));"); + writer.println(" }"); + writer.println(); + writer.println(" public " + containingTypeName + "(ByteBuffer buf) {"); + writer.println(" accessor = new StructAccessor(buf);"); + writer.println(" }"); + writer.println(); + writer.println(" public ByteBuffer getBuffer() {"); + writer.println(" return accessor.getBuffer();"); + writer.println(" }"); + for (int i = 0; i < structType.getNumFields(); i++) { + Field field = structType.getField(i); + Type fieldType = field.getType(); + if (!cfg.shouldIgnore(structType.getName() + " " + field.getName())) { + if (fieldType.isFunctionPointer()) { + try { + // Emit method call and associated native code + FunctionType funcType = fieldType.asPointer().getTargetType().asFunction(); + FunctionSymbol funcSym = new FunctionSymbol(field.getName(), funcType); + MethodBinding binding = bindFunction(funcSym, containingType, containingCType); + binding.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis + MethodBinding specialBinding = binding.createNIOBufferVariant(); + writer.println(); + + JavaMethodBindingEmitter entryPoint = new JavaMethodBindingImplEmitter(binding, writer, cfg.runtimeExceptionType()); + entryPoint.addModifier(JavaMethodBindingEmitter.PUBLIC); + if (!binding.needsBody() && !binding.hasContainingType()) { + entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); + } + entryPoint.emit(); + + JavaMethodBindingEmitter wrappedEntryPoint = new JavaMethodBindingEmitter(specialBinding, writer, cfg.runtimeExceptionType(), true); + wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.PRIVATE); + wrappedEntryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); + wrappedEntryPoint.emit(); + + CMethodBindingEmitter cEmitter = + makeCEmitter(specialBinding, + false, // overloaded + true, // doing impl routine? + structClassPkg, + containingTypeName, + cWriter); + cEmitter.emit(); + } catch (Exception e) { + System.err.println("While processing field " + field + " of type " + structType.getName() + ":"); + 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 \"" + structType.getName() + "\")"); + } + + writer.println(); + writer.println(" public " + fieldType.getName() + " " + field.getName() + "() {"); + writer.println(" return new " + fieldType.getName() + "(accessor.slice(" + + field.getOffset() + ", " + fieldType.getSize() + "));"); + writer.println(" }"); + + // FIXME: add setter by autogenerating "copyTo" for all compound type wrappers + } else if (fieldType.isArray()) { + System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + structType.getName() + + "\") not implemented yet"); + } else { + JavaType javaType = null; + try { + javaType = typeToJavaType(fieldType, false); + } catch (Exception e) { + System.err.println("Error occurred while creating accessor for field \"" + + field.getName() + "\" in type \"" + structType.getName() + "\""); + e.printStackTrace(); + throw(e); + } + if (javaType.isPrimitive()) { + // Primitive type + String externalJavaTypeName = javaType.getName(); + String internalJavaTypeName = externalJavaTypeName; + if (isOpaque(fieldType)) { + internalJavaTypeName = compatiblePrimitiveJavaTypeName(fieldType, javaType); + } + String capitalized = + "" + Character.toUpperCase(internalJavaTypeName.charAt(0)) + internalJavaTypeName.substring(1); + int slot = slot(fieldType, (int) field.getOffset()); + // Setter + writer.println(); + writer.println(" public " + containingTypeName + " " + field.getName() + "(" + externalJavaTypeName + " val) {"); + writer.print (" accessor.set" + capitalized + "At(" + slot + ", "); + if (!externalJavaTypeName.equals(internalJavaTypeName)) { + writer.print("(" + internalJavaTypeName + ") "); + } + writer.println("val);"); + writer.println(" return this;"); + writer.println(" }"); + // Getter + writer.println(); + writer.println(" public " + externalJavaTypeName + " " + field.getName() + "() {"); + writer.print (" return "); + if (!externalJavaTypeName.equals(internalJavaTypeName)) { + writer.print("(" + externalJavaTypeName + ") "); + } + writer.println("accessor.get" + capitalized + "At(" + slot + ");"); + writer.println(" }"); + } else { + // FIXME + String name = structType.getName(); + if (name == null) { + name = structType.toString(); + } + System.err.println("WARNING: Complicated fields (field \"" + field + "\" of type \"" + name + + "\") not implemented yet"); + // throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t + + // "\") not implemented yet"); + } + } + } + } + emitCustomJavaCode(writer, containingTypeName); + writer.println("}"); + writer.flush(); + writer.close(); + if (needsNativeCode) { + cWriter.flush(); + cWriter.close(); + } + } + public void endStructs() throws Exception {} + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private CMethodBindingEmitter makeCEmitter(MethodBinding binding, + boolean overloaded, + boolean doingImplRoutine, + String bindingJavaPackageName, + String bindingJavaClassName, + PrintWriter output) { + MessageFormat returnValueCapacityFormat = null; + JavaType javaReturnType = binding.getJavaReturnType(); + if (javaReturnType.isNIOBuffer()) { + // See whether capacity has been specified + String capacity = cfg.returnValueCapacity(binding.getName()); + if (capacity != null) { + returnValueCapacityFormat = new MessageFormat(capacity); + } + } + CMethodBindingEmitter cEmitter; + if (doingImplRoutine) { + cEmitter = new CMethodBindingImplEmitter(binding, overloaded, + bindingJavaPackageName, + bindingJavaClassName, + cfg.allStatic(), output); + } else { + cEmitter = new CMethodBindingEmitter(binding, overloaded, + bindingJavaPackageName, + bindingJavaClassName, + cfg.allStatic(), output); + } + if (returnValueCapacityFormat != null) { + cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); + } + cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); + cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); + return cEmitter; + } + + private JavaType typeToJavaType(Type cType, boolean outgoingArgument) { + // Recognize JNIEnv* case up front + PointerType opt = cType.asPointer(); + if ((opt != null) && + (opt.getTargetType().getName() != null) && + (opt.getTargetType().getName().equals("JNIEnv"))) { + return JavaType.createForJNIEnv(); + } + + // Opaque specifications override automatic conversions + TypeInfo info = cfg.typeInfo(cType, typedefDictionary); + if (info != null) { + return info.javaType(); + } + Type t = cType; + if (t.isInt() || t.isEnum()) { + switch (t.getSize()) { + 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() + " 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 || arrayDimension(t) > 0) { + // FIXME: add option to disable generation of typed pointer -> + // array conversions so that these will be handled with direct + // buffers (Should this be done later? in expandMethodBinding?) + 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[] + if (t.pointerDepth() == 1 || arrayDimension(t) == 1) { + if (targetType.isVoid()) { + return JavaType.createForVoidPointer(); + } else if (targetType.isInt()) { + switch (targetType.getSize()) { + case 1: return javaType(ArrayTypes.byteArrayClass); + case 2: return javaType(ArrayTypes.shortArrayClass); + case 4: return javaType(ArrayTypes.intArrayClass); + case 8: return javaType(ArrayTypes.longArrayClass); + default: throw new RuntimeException("Unknown integer array type of size " + + t.getSize() + " and name " + t.getName()); + } + } else if (targetType.isFloat()) { + return javaType(ArrayTypes.floatArrayClass); + } else if (targetType.isDouble()) { + return javaType(ArrayTypes.doubleArrayClass); + } 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 (cType.getName() != null && + cType.getName().equals("jobject")) { + return javaType(java.lang.Object.class); + } + + return JavaType.createForCStruct(cfg.renameJavaType(targetType.getName())); + } 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 || arrayDimension(t) == 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()) { + // t is<type>**, targetType is <type>*, we need to get <type> + targetType = targetType.asPointer().getTargetType(); + } else { + // t is<type>[][], targetType is <type>[], we need to get <type> + targetType = targetType.asArray().getElementType(); + } + if (targetType.isInt()) { + switch (targetType.getSize()) + { + case 1: return javaType(ArrayTypes.byteArrayArrayClass); + // FIXME: handle 2,4,8-byte int types here + default: + throw new RuntimeException( + "Could not convert C type \"" + t + "\" to appropriate " + + "Java type; Currently, the only supported depth=2 " + + "pointer/array integer types are \"char**\" and \"char[][]\""); + } + } else { + throw new RuntimeException( + "Could not convert C type \"" + t + "\" " + + "to appropriate Java type; need to add support for " + + "depth=2 pointer/array types with non-integral target " + + "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=" + + arrayDimension(t) + " targetType=\"" + targetType + "\"]"); + } + + } else { + throw new RuntimeException( + "Could not convert C type \"" + t + "\" 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) { + if (t.isInt()) { + switch (t.getSize()) { + case 1: + case 2: + case 4: + case 8: return byteOffset / t.getSize(); + 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 / machDesc.pointerSizeInBytes(); + } 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 int arrayDimension(Type type) { + ArrayType arrayType = type.asArray(); + if (arrayType == null) { + return 0; + } + return 1 + arrayDimension(arrayType.getElementType()); + } + + private boolean isOpaque(Type type) { + return (cfg.typeInfo(type, typedefDictionary) != null); + } + + private String compatiblePrimitiveJavaTypeName(Type fieldType, + JavaType javaType) { + 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 (fieldType.getSize()) { + 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 = + 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 code = cfg.customJavaCodeForClass(className); + if (code.size() == 0) + return; + + writer.println(); + writer.println(" // --- Begin CustomJavaCode .cfg declarations"); + for (Iterator iter = code.iterator(); iter.hasNext(); ) { + writer.println((String) iter.next()); + } + 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 { + if (cfg.allStatic() || cfg.emitInterface()) { + String[] interfaces; + if (cfg.emitInterface()) { + List userSpecifiedInterfaces = cfg.extendedInterfaces(cfg.className()); + interfaces = new String[userSpecifiedInterfaces.size()]; + userSpecifiedInterfaces.toArray(interfaces); + } else { + interfaces = null; + } + + 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()); + } + } + }; + + CodeGenUtils.emitJavaHeaders( + javaWriter, + cfg.packageName(), + cfg.className(), + cfg.allStatic() ? true : false, + (String[]) cfg.imports().toArray(new String[] {}), + new String[] { "public" }, + interfaces, + null, + docEmitter); + } + + if (!cfg.allStatic() && cfg.emitImpl()) { + final List/*<String>*/ implDocs = cfg.javadocForClass(cfg.className()); + CodeGenUtils.EmissionCallback docEmitter = + new CodeGenUtils.EmissionCallback() { + public void emit(PrintWriter w) { + for (Iterator iter = implDocs.iterator(); iter.hasNext(); ) { + w.println((String) iter.next()); + } + } + }; + + CodeGenUtils.emitJavaHeaders( + javaImplWriter, + cfg.implPackageName(), + cfg.implClassName(), + true, + new String[] { "java.nio.*", cfg.packageName() + ".*" }, + new String[] { "public" }, + new String[] { cfg.className() }, + null, + docEmitter); + } + + if (cfg.emitImpl()) { + PrintWriter cWriter = cWriter(); + 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(); + for (Iterator iter = cfg.customCCode().iterator(); iter.hasNext(); ) { + cWriter.println((String) iter.next()); + } + 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); + } + + private MethodBinding bindFunction(FunctionSymbol sym, + JavaType containingType, + Type containingCType) { + + MethodBinding binding = new MethodBinding(sym, containingType, containingCType); + + if (cfg.returnsString(binding.getName())) { + PointerType prt = sym.getReturnType().asPointer(); + if (prt == null || + prt.getTargetType().asInt() == null || + prt.getTargetType().getSize() != 1) { + throw new RuntimeException( + "Cannot apply ReturnsString configuration directive to \"" + sym + + "\". ReturnsString requires native method to have return type \"char *\""); + } + binding.setJavaReturnType(JavaType.createForClass(java.lang.String.class)); + } else { + binding.setJavaReturnType(typeToJavaType(sym.getReturnType(), false)); + } + + // List of the indices of the arguments in this function that should be + // converted from byte[] to String + List stringArgIndices = cfg.stringArguments(binding.getName()); + + for (int i = 0; i < sym.getNumArguments(); i++) { + Type cArgType = sym.getArgumentType(i); + JavaType mappedType = typeToJavaType(cArgType, true); + //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(new Integer(i))) { + //System.out.println("Forcing conversion of " + binding.getName() + " arg #" + i + " from byte[] to String "); + if ((mappedType.isArray() && + (mappedType.getJavaClass() == ArrayTypes.byteArrayClass || + mappedType.getJavaClass() == ArrayTypes.byteArrayArrayClass)) || + (mappedType.isVoidPointerType())) { + // convert mapped type from void* and byte[] to String, or byte[][] to String[] + if (mappedType.getJavaClass() == ArrayTypes.byteArrayArrayClass) { + 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 *\", or \"char**\" equivalent"); + } + } + binding.addJavaArgumentType(mappedType); + //System.out.println("During binding of [" + sym + "], added mapping from C type: " + cArgType + " to Java type: " + mappedType); + } + + //System.err.println("---> " + binding); + //System.err.println(" ---> " + binding.getCSymbol()); + return binding; + } + + // Expands a MethodBinding containing void pointer types into + // multiple variants taking double arrays and NIO buffers, subject + // to the per-function "NIO only" rule in the configuration file + private List/*<MethodBinding>*/ expandMethodBinding(MethodBinding binding) { + List result = new ArrayList(); + result.add(binding); + int i = 0; + while (i < result.size()) { + MethodBinding mb = (MethodBinding) result.get(i); + boolean shouldRemoveCurrent = false; + for (int j = 0; j < mb.getNumArguments(); j++) { + JavaType t = mb.getJavaArgumentType(j); + if (t.isVoidPointerType()) { + // Create variants + MethodBinding variant = null; + if (!cfg.nioOnly(mb.getCSymbol().getName())) { + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.booleanArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.byteArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.charArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.shortArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.intArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.longArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.floatArrayClass)); + if (! result.contains(variant)) result.add(variant); + variant = mb.createVoidPointerVariant(j, javaType(ArrayTypes.doubleArrayClass)); + if (! result.contains(variant)) result.add(variant); + } + variant = mb.createVoidPointerVariant(j, JavaType.forNIOBufferClass()); + if (! result.contains(variant)) result.add(variant); + + // Remove original from list + shouldRemoveCurrent = true; + } + } + if (mb.getJavaReturnType().isVoidPointerType()) { + MethodBinding variant = mb.createVoidPointerVariant(-1, JavaType.forNIOByteBufferClass()); + if (! result.contains(variant)) result.add(variant); + shouldRemoveCurrent = true; + } + if (shouldRemoveCurrent) { + result.remove(i); + --i; + } + ++i; + } + return result; + } + + private String resultName() { + return "_res"; + } + + private Type canonicalize(Type t) { + Type res = (Type) canonMap.get(t); + if (res != null) { + return res; + } + canonMap.put(t, t); + return t; + } +} diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java new file mode 100644 index 000000000..003ea8643 --- /dev/null +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -0,0 +1,260 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import java.io.*; + +import net.java.games.gluegen.cgram.types.*; +import net.java.games.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 static final CommentEmitter defaultJavaCommentEmitter = new DefaultCommentEmitter(); + protected static final CommentEmitter defaultInterfaceCommentEmitter = + new InterfaceCommentEmitter(); + + // Exception type raised in the generated code if runtime checks fail + private String runtimeExceptionType; + + private MethodBinding binding; + private boolean forNIOBufferBaseRoutine; + + // 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; + + public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType) + { + this(binding, output, runtimeExceptionType, false); + } + + public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType, boolean forNIOBufferBaseRoutine) + { + super(output); + this.binding = binding; + this.forNIOBufferBaseRoutine = forNIOBufferBaseRoutine; + this.runtimeExceptionType = runtimeExceptionType; + setCommentEmitter(defaultInterfaceCommentEmitter); + } + + public final MethodBinding getBinding() { return binding; } + + public final boolean isForNIOBufferBaseRoutine() { return forNIOBufferBaseRoutine; } + + public String getName() { + return binding.getName(); + } + + + /** 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; + } + + /** 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; + } + + protected void emitReturnType(PrintWriter writer) + { + writer.print(getReturnTypeString(false)); + } + + protected String getReturnTypeString(boolean skipArray) { + if (skipArray || getReturnedArrayLengthExpression() == null) { + return binding.getJavaReturnType().getName(); + } + return binding.getJavaReturnType().getName() + "[]"; + } + + protected void emitName(PrintWriter writer) + { + writer.print(binding.getName()); + if (forNIOBufferBaseRoutine) { + writer.print("0"); + } + } + + protected int emitArguments(PrintWriter writer) + { + boolean needComma = false; + int numEmitted = 0; + + if (forNIOBufferBaseRoutine && binding.hasContainingType()) { + // Always emit outgoing "this" argument + writer.print("java.nio.Buffer "); + 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(type.getName()); + writer.print(" "); + writer.print(binding.getArgumentName(i)); + ++numEmitted; + needComma = true; + } + return numEmitted; + } + + protected String getImplMethodName() + { + return binding.getName() + "0"; + } + + protected void emitBody(PrintWriter writer) + { + writer.println(';'); + } + + protected static String javaThisArgumentName() { + return "jthis0"; + } + + protected String getCommentStartString() { return "/** "; } + + 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 static 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: <br> "); + } + protected void emitBindingCSignature(MethodBinding binding, PrintWriter writer) { + writer.print("<code> "); + writer.print(binding.getCSymbol()); + 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. + MethodBinding binding = ((JavaMethodBindingEmitter)emitter).getBinding(); + for (int i = 0; i < binding.getNumArguments(); i++) { + Type type = binding.getCArgumentType(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() && type.getName() != HeaderParser.ANONYMOUS_ENUM_NAME) { + EnumType enum = (EnumType)type; + writer.println(); + writer.print(emitter.getBaseIndentString()); + writer.print(" "); + writer.print("@param "); + writer.print(binding.getArgumentName(i)); + writer.print(" valid values are: <code>"); + for (int j = 0; j < enum.getNumEnumerates(); ++j) { + if (j>0) writer.print(", "); + writer.print(enum.getEnumName(j)); + } + writer.println("</code>"); + } + } + } + } + + protected static class InterfaceCommentEmitter + extends JavaMethodBindingEmitter.DefaultCommentEmitter + { + protected void emitBeginning(FunctionEmitter emitter, + PrintWriter writer) { + writer.print("Interface to C language function: <br> "); + } + } +} + diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java new file mode 100644 index 000000000..2357e7c38 --- /dev/null +++ b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java @@ -0,0 +1,220 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.io.*; +import java.util.*; +import java.text.MessageFormat; + +import net.java.games.gluegen.cgram.types.*; + +/** Emits the Java-side component of the Java<->C JNI binding. */ +public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter +{ + private boolean isUnimplemented; + + public JavaMethodBindingImplEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType) + { + this(binding, output, runtimeExceptionType, false); + } + + public JavaMethodBindingImplEmitter(MethodBinding binding, + PrintWriter output, + String runtimeExceptionType, + boolean isUnimplemented) + { + super(binding, output, runtimeExceptionType); + setCommentEmitter(defaultJavaCommentEmitter); + this.isUnimplemented = isUnimplemented; + } + + protected void emitBody(PrintWriter writer) + { + MethodBinding binding = getBinding(); + if (needsBody()) { + writer.println(); + writer.println(" {"); + if (isUnimplemented) { + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Unimplemented\");"); + } else { + emitPreCallSetup(binding, writer); + emitReturnVariableSetup(binding, writer); + emitCall(binding, writer); + } + writer.println(" }"); + } else { + writer.println(";"); + } + } + + protected boolean needsBody() { + return isUnimplemented || getBinding().needsBody() || getBinding().hasContainingType(); + } + + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + emitArrayLengthChecks(binding, writer); + } + + protected void emitArrayLengthChecks(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 (" + binding.getArgumentName(i) + ".length < " + arrayType.getLength() + ")"); + writer.println(" throw new " + getRuntimeExceptionType() + "(\"Length of array \\\"" + binding.getArgumentName(i) + + "\\\" was less than the required " + arrayType.getLength() + "\");"); + } + } + } + + protected void emitReturnVariableSetup(MethodBinding binding, PrintWriter writer) { + writer.print(" "); + JavaType returnType = binding.getJavaReturnType(); + if (!returnType.isVoid()) { + if (returnType.isCompoundTypeWrapper() || + returnType.isNIOByteBuffer()) { + writer.println("ByteBuffer _res;"); + writer.print(" _res = "); + } else { + writer.print("return "); + } + } + } + + protected void emitCall(MethodBinding binding, PrintWriter writer) { + writer.print(getImplMethodName()); + writer.print("("); + emitCallArguments(binding, writer); + writer.print(")"); + 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("(("); + } + writer.print(binding.getArgumentName(i)); + if (type.isCompoundTypeWrapper()) { + writer.print(" == null) ? null : "); + writer.print(binding.getArgumentName(i)); + writer.print(".getBuffer())"); + } + needComma = true; + ++numArgsEmitted; + } + return numArgsEmitted; + } + + protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { + JavaType returnType = binding.getJavaReturnType(); + if (returnType.isCompoundTypeWrapper()) { + writer.println(";"); + String fmt = getReturnedArrayLengthExpression(); + writer.println(" if (_res == null) return null;"); + if (fmt == null) { + writer.print(" return new " + returnType.getName() + "(_res.order(ByteOrder.nativeOrder()))"); + } else { + writer.println(" _res.order(ByteOrder.nativeOrder());"); + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + String expr = new MessageFormat(fmt).format(argumentNames); + 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 " + binding.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 * " + cReturnType.getSize() + ");"); + writer.println(" _res.limit ((1 + _count) * " + cReturnType.getSize() + ");"); + writer.println(" ByteBuffer _tmp = _res.slice();"); + writer.println(" _tmp.order(ByteOrder.nativeOrder());"); + writer.println(" _res.position(0);"); + writer.println(" _res.limit(_res.capacity());"); + writer.println(" _retarray[_count] = new " + returnType.getName() + "(_tmp);"); + writer.println(" }"); + writer.print (" return _retarray"); + } + } else if (returnType.isNIOBuffer()) { + writer.println(";"); + writer.println(" if (_res == null) return null;"); + writer.print(" return _res.order(ByteOrder.nativeOrder())"); + } + writer.println(";"); + } +} + diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java new file mode 100644 index 000000000..ac42aa0f1 --- /dev/null +++ b/src/net/java/games/gluegen/JavaType.java @@ -0,0 +1,277 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +/** + * 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 { + 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 static JavaType nioBufferType; + private static JavaType nioByteBufferType; + + public boolean equals(Object arg) { + if ((arg == null) || (!(arg instanceof JavaType))) { + return false; + } + JavaType t = (JavaType) arg; + return (t.clazz == clazz && + ((t.name == name) || + ((name != null) && (t.name != null) && (t.name.equals(name))))); + } + + public int hashCode() { + if (clazz == null) { + if (name == null) { + return 0; + } + return name.hashCode(); + } + return clazz.hashCode(); + } + + public static JavaType createForClass(Class clazz) { + return new JavaType(clazz); + } + + public static JavaType createForCStruct(String name) { + return new JavaType(name); + } + + public static JavaType createForVoidPointer() { + return new JavaType(); + } + + 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; + } + + /** + * Returns the Java Class corresponding to this type. Returns null if this + * object corresponds to a C "void*" type. + */ + public Class getJavaClass() { + return clazz; + } + + /** + * Returns the name corresponding to this type. Returns null when this + * object does not represent a C-language "struct" type. + */ + public String getName() { + if (clazz != null) { + if (clazz.isArray()) { + return arrayName(clazz); + } + return clazz.getName(); + } + return 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 (clazz == null) { + return null; + } + + if (isVoid()) { + return "void"; + } + + if (clazz.isPrimitive()) { + return "j" + clazz.getName(); + } + + if (clazz.isArray()) { + Class elementType = clazz.getComponentType(); + if (elementType.isPrimitive()) + { + // Type is array-of-primitive + return "j" + elementType.getName() + "Array"; + } + else if (elementType == java.lang.String.class) + { + // Type is array-of-string + return "jobjectArray /*elements are String*/"; + //return "jobjectArray"; + } + else 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."); + } + } + else + { + // Some unusual type that we don't handle + throw new RuntimeException("Unexpected and unsupported type: \"" + this + "\""); + } + } // end array type case + + if (isString()) { + return "jstring"; + } + + return "jobject"; + } + + public boolean isNIOBuffer() { + return (clazz == java.nio.Buffer.class || + clazz == java.nio.ByteBuffer.class); + } + + public boolean isNIOByteBuffer() { + return (clazz == java.nio.ByteBuffer.class); + } + + public boolean isString() { + return (clazz == java.lang.String.class); + } + + public boolean isArray() { + return ((clazz != null) && clazz.isArray()); + } + + public boolean isPrimitive() { + return ((clazz != null) && !isArray() && clazz.isPrimitive() && (clazz != Void.TYPE)); + } + + public boolean isVoid() { + return (clazz == Void.TYPE); + } + + public boolean isObjectType() { + // FIXME: what about char* -> String conversion? + return (isNIOBuffer() || isArray()); + } + + public boolean isCompoundTypeWrapper() { + return (clazz == null && name != null && !isJNIEnv()); + } + + public boolean isVoidPointerType() { + return (clazz == null && name == null); + } + + public boolean isJNIEnv() { + return clazz == null && name == "JNIEnv"; + } + + public Object clone() { + JavaType clone = new JavaType(); + + clone.clazz = this.clazz; + clone.name = this.name; + + return clone; + } + + public String toString() { + return getName(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + /** + * 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; + } + + /** + * Default constructor; the type is initialized to the equivalent of a + * C-language "void *". + */ + private JavaType() { + + } + + private String arrayName(Class clazz) { + StringBuffer buf = new StringBuffer(); + int arrayCount = 0; + while (clazz.isArray()) { + ++arrayCount; + clazz = clazz.getComponentType(); + } + buf.append(clazz.getName()); + while (--arrayCount >= 0) { + buf.append("[]"); + } + return buf.toString(); + } + +} diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java new file mode 100644 index 000000000..9cdcd4b0b --- /dev/null +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -0,0 +1,340 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; + +import net.java.games.gluegen.cgram.types.*; + +/** 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 JavaType javaReturnType; + private List javaArgumentTypes; + private boolean computedNeedsBody; + private boolean needsBody; + 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.containingType = bindingToCopy.containingType; + this.containingCType = bindingToCopy.containingCType; + this.javaReturnType = bindingToCopy.javaReturnType; + this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); + this.computedNeedsBody = bindingToCopy.computedNeedsBody; + this.needsBody = bindingToCopy.needsBody; + this.thisPointerIndex = bindingToCopy.thisPointerIndex; + } + + /** Constructor for calling a C function. */ + public MethodBinding(FunctionSymbol sym) { + this.sym = sym; + } + + /** 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; + } + + public void setJavaReturnType(JavaType type) { + javaReturnType = type; + computedNeedsBody = false; + } + + public void addJavaArgumentType(JavaType type) { + if (javaArgumentTypes == null) { + javaArgumentTypes = new ArrayList(); + } + javaArgumentTypes.add(type); + computedNeedsBody = false; + } + + public JavaType getJavaReturnType() { + return javaReturnType; + } + + public int getNumArguments() { + return sym.getNumArguments(); + } + + public JavaType getJavaArgumentType(int i) { + return (JavaType) 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 getName() { + return sym.getName(); + } + + /** Replaces the void* argument at slot <i>argumentNumber</i> + (0..getNumArguments() - 1) with the specified type. If + argumentNumber is less than 0 then replaces the return type. */ + public MethodBinding createVoidPointerVariant(int argumentNumber, + JavaType newArgType) { + MethodBinding binding = new MethodBinding(sym); + 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 this method needs a special implementation to wrap and/or + * set the byte order of its arguments or return type (i.e., needs special + * pre-processing of the data passed to the native function, or + * post-processing of the data returned from the native function). <P> + * + * Returns false if this binding can be implemented via a one-to-one + * correspondence between a Java method and its native implementation. + */ + public boolean needsBody() { + if (!computedNeedsBody) { + if (javaReturnType.isCompoundTypeWrapper() || + javaReturnType.isNIOByteBuffer()) { + // Needs wrapping and/or setting of byte order (neither of + // which can be done easily from native code) + needsBody = true; + } else { + for (int i = 0; i < getNumArguments(); i++) { + JavaType javaArgType = getJavaArgumentType(i); + Type cArgType = getCArgumentType(i); + if (javaArgType.isCompoundTypeWrapper() || + cArgType.isArray()) { + // Needs unwrapping of accessors or checking of array lengths + needsBody = true; + break; + } + } + } + computedNeedsBody = true; + } + + return needsBody; + } + + public MethodBinding createNIOBufferVariant() { + if (!needsBody()) { + return this; + } + MethodBinding binding = new MethodBinding(sym, containingType, containingCType); + binding.thisPointerIndex = thisPointerIndex; + if (javaReturnType.isCompoundTypeWrapper()) { + binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); + } else { + binding.setJavaReturnType(javaReturnType); + } + for (int i = 0; i < getNumArguments(); i++) { + JavaType type = getJavaArgumentType(i); + if (type.isCompoundTypeWrapper()) { + type = JavaType.forNIOBufferClass(); + } + binding.addJavaArgumentType(type); + } + return binding; + } + + /** 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); + } + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj == null || ! (obj instanceof MethodBinding)) { + return false; + } + + MethodBinding other = (MethodBinding)obj; + if (!(sym.equals(other.sym))) { return false; } + if (!(javaReturnType.equals(other.getJavaReturnType()))) { return false; } + if (containingType != 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; + } + + // FIXME!! Implement hashCode() to match equals(Object) + + /** Returns the signature of this binding. */ + public String toString() { + StringBuffer buf = new StringBuffer(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(); + } + + public final Object clone() { + return new MethodBinding(this); + } + +} + diff --git a/src/net/java/games/gluegen/ReferencedStructs.java b/src/net/java/games/gluegen/ReferencedStructs.java new file mode 100644 index 000000000..1b3b75198 --- /dev/null +++ b/src/net/java/games/gluegen/ReferencedStructs.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 + * 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. + */ + +package net.java.games.gluegen; + +import java.util.*; +import net.java.games.gluegen.cgram.types.*; + +public class ReferencedStructs implements TypeVisitor { + private Set results = new HashSet(); + + public void clear() { + results.clear(); + } + + public Iterator results() { + return results.iterator(); + } + + public void visitType(Type t) { + if (t.isCompound()) { + results.add(t); + } + } +} diff --git a/src/net/java/games/gluegen/StructLayout.java b/src/net/java/games/gluegen/StructLayout.java new file mode 100644 index 000000000..2a500ceae --- /dev/null +++ b/src/net/java/games/gluegen/StructLayout.java @@ -0,0 +1,138 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen; + +import net.java.games.gluegen.cgram.types.*; + +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(); + int curOffset = baseOffset; + int maxSize = 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()) { + int sz = ft.getSize(); + if ((curOffset % sz) != 0) { + curOffset += sz - (curOffset % sz); + } + f.setOffset(curOffset); + if (t.isUnion()) { + maxSize = Math.max(maxSize, sz); + } else { + curOffset += sz; + } + } else if (ft.isCompound()) { + new StructLayout(0, structAlignment).layout(ft.asCompound()); + if ((curOffset % structAlignment) != 0) { + curOffset += structAlignment - (curOffset % structAlignment); + } + f.setOffset(curOffset); + if (t.isUnion()) { + maxSize = Math.max(maxSize, ft.getSize()); + } else { + 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 + if ((curOffset % structAlignment) != 0) { + curOffset += structAlignment - (curOffset % structAlignment); + } + f.setOffset(curOffset); + 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() { + String os = System.getProperty("os.name").toLowerCase(); + String cpu = System.getProperty("os.arch").toLowerCase(); + if ((os.startsWith("windows") && cpu.equals("x86")) || + (os.startsWith("linux") && cpu.equals("i386")) || + (os.startsWith("sunos") && cpu.equals("sparc")) || + (os.startsWith("sunos") && cpu.equals("i386"))|| + (os.startsWith("mac os") && cpu.equals("ppc")) + ) { + // 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/net/java/games/gluegen/TypeInfo.java b/src/net/java/games/gluegen/TypeInfo.java new file mode 100644 index 000000000..5df72e411 --- /dev/null +++ b/src/net/java/games/gluegen/TypeInfo.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 + * 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. + */ + +package net.java.games.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; } +} diff --git a/src/net/java/games/gluegen/cgram/CSymbolTable.java b/src/net/java/games/gluegen/cgram/CSymbolTable.java new file mode 100644 index 000000000..e22274b90 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/CSymbolTable.java @@ -0,0 +1,132 @@ +package net.java.games.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/net/java/games/gluegen/cgram/CToken.java b/src/net/java/games/gluegen/cgram/CToken.java new file mode 100644 index 000000000..facd95a08 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/CToken.java @@ -0,0 +1,32 @@ +package net.java.games.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/net/java/games/gluegen/cgram/Define.java b/src/net/java/games/gluegen/cgram/Define.java new file mode 100644 index 000000000..3ab7c9063 --- /dev/null +++ b/src/net/java/games/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 + * 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. + */ + +package net.java.games.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/net/java/games/gluegen/cgram/GnuCEmitter.g b/src/net/java/games/gluegen/cgram/GnuCEmitter.g new file mode 100644 index 000000000..87294fc53 --- /dev/null +++ b/src/net/java/games/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 net.java.games.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/net/java/games/gluegen/cgram/GnuCParser.g b/src/net/java/games/gluegen/cgram/GnuCParser.g new file mode 100644 index 000000000..feed4518e --- /dev/null +++ b/src/net/java/games/gluegen/cgram/GnuCParser.g @@ -0,0 +1,864 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + 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 net.java.games.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" + | "__int64" + | "long" + | "float" + | "double" + | "signed" + | "unsigned" + | 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/net/java/games/gluegen/cgram/GnuCTreeParser.g b/src/net/java/games/gluegen/cgram/GnuCTreeParser.g new file mode 100644 index 000000000..8400e3e59 --- /dev/null +++ b/src/net/java/games/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 net.java.games.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/net/java/games/gluegen/cgram/HeaderParser.g b/src/net/java/games/gluegen/cgram/HeaderParser.g new file mode 100644 index 000000000..1263c30b8 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/HeaderParser.g @@ -0,0 +1,715 @@ +/* + * 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 + * 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 net.java.games.gluegen.cgram; + + import java.io.*; + import java.util.*; + + import antlr.CommonAST; + import net.java.games.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>"; + + /** Set the machine description for this HeaderParser. Must be + done before parsing. */ + public void setMachineDescription(MachineDescription machDesc) { + this.machDesc = machDesc; + } + + /** Returns the MachineDescription this HeaderParser uses. */ + public MachineDescription machineDescription() { + return machDesc; + } + + /** 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, -1, 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 MachineDescription machDesc; + 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, -1, 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(), len * tb.type().getSize(), len, 0))); + return; + } catch (RecognitionException e) { + // Fall through + } + } + tb.setType(canonicalize(new PointerType(machDesc.pointerSizeInBytes(), + 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) { + enumType = new EnumType(enumTypeName, machDesc.longSizeInBytes()); + } + + 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, -1, 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(machDesc.pointerSizeInBytes(), + 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", machDesc.intSizeInBytes(), ((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" , machDesc.charSizeInBytes(), unsigned, cvAttrs); } + | "short" { t = new IntType("short", machDesc.shortSizeInBytes(), unsigned, cvAttrs); } + | "int" { t = new IntType("int" , machDesc.intSizeInBytes(), unsigned, cvAttrs); } + | "long" { t = new IntType("long" , machDesc.longSizeInBytes(), unsigned, cvAttrs); } + | "__int64" { t = new IntType("__int64", machDesc.int64SizeInBytes(), unsigned, cvAttrs); } + | "float" { t = new FloatType("float", machDesc.floatSizeInBytes(), cvAttrs); } + | "double" { t = new DoubleType("double", machDesc.doubleSizeInBytes(), cvAttrs); } + | 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) + { + t = canonicalize(lookupInTypedefDictionary(id.getText()).getCVVariant(cvAttrs)); + } + ; + +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, -1, 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, -1)); + } + } + } + } + ; + +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", machDesc.intSizeInBytes(), ((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(), -1)); 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 )? { + // FIXME! Integer.parseInt() will throw if its argument is in octal or hex format. + long value = (eVal == null) ? defaultValue : Long.parseLong(eVal.getText()); + 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); + //System.err.println("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] { + //System.err.println("GOT declName: " + declName + " TB=" + tb); + } + ( attributeDecl )* + ( ASSIGN initializer + | COLON expr + )? + ) +{ + if ((declName != null) && (tb != null) && tb.isTypedef()) { + Type t = tb.type(); + //System.err.println("Adding typedef mapping: [" + declName + "] -> [" + t + "]"); + if (!t.hasTypedefName()) { + t.setName(declName); + } + t = canonicalize(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; } )* + { + //System.err.println("IN PTR GROUP: TB=" + tb); + if (tb != null) { + tb.setType(canonicalize(new PointerType(machDesc.pointerSizeInBytes(), + 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/net/java/games/gluegen/cgram/LineObject.java b/src/net/java/games/gluegen/cgram/LineObject.java new file mode 100644 index 000000000..578e14194 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/LineObject.java @@ -0,0 +1,126 @@ +package net.java.games.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/net/java/games/gluegen/cgram/PreprocessorInfoChannel.java b/src/net/java/games/gluegen/cgram/PreprocessorInfoChannel.java new file mode 100644 index 000000000..f2de592c7 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/PreprocessorInfoChannel.java @@ -0,0 +1,73 @@ +package net.java.games.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/net/java/games/gluegen/cgram/StdCParser.g b/src/net/java/games/gluegen/cgram/StdCParser.g new file mode 100644 index 000000000..65f7468bf --- /dev/null +++ b/src/net/java/games/gluegen/cgram/StdCParser.g @@ -0,0 +1,1375 @@ +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + 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 net.java.games.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:Number)? + nw:NonWhitespace + ("\r\n" | "\r" | "\n") ) { if (n != null) { + addDefine(i.getText(), n.getText()); + } else { + setPreprocessingDirective("#define " + i.getText() + " " + + nw.getText()); + } + } + | (~'\n')* { setPreprocessingDirective(getText()); } + ) + { + _ttype = Token.SKIP; + } + ; + +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/net/java/games/gluegen/cgram/TNode.java b/src/net/java/games/gluegen/cgram/TNode.java new file mode 100644 index 000000000..2a93b939c --- /dev/null +++ b/src/net/java/games/gluegen/cgram/TNode.java @@ -0,0 +1,433 @@ +package net.java.games.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_; + } + + /** 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/net/java/games/gluegen/cgram/TNodeFactory.java b/src/net/java/games/gluegen/cgram/TNodeFactory.java new file mode 100644 index 000000000..8cda2cfa9 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/TNodeFactory.java @@ -0,0 +1,33 @@ +package net.java.games.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/net/java/games/gluegen/cgram/types/ArrayType.java b/src/net/java/games/gluegen/cgram/types/ArrayType.java new file mode 100644 index 000000000..6394a39cb --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/ArrayType.java @@ -0,0 +1,131 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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 { + private Type elementType; + private int length; + private String computedName; + + public ArrayType(Type elementType, int sizeInBytes, int length, int cvAttributes) { + super(elementType.getName() + " *", sizeInBytes, cvAttributes); + this.elementType = elementType; + this.length = length; + } + + 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)); + } + + 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; + } + + 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(getLength() * elementType.getSize()); + } + + 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(); + } + + 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/net/java/games/gluegen/cgram/types/BitType.java b/src/net/java/games/gluegen/cgram/types/BitType.java new file mode 100644 index 000000000..48d81e933 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/BitType.java @@ -0,0 +1,87 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +/** Represents a bitfield in a struct. */ + +public class BitType extends IntType { + 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; + } + + 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)); + } + + 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; + } + + public void visit(TypeVisitor arg) { + super.visit(arg); + underlyingType.visit(arg); + } + + Type newCVVariant(int cvAttributes) { + return new BitType(underlyingType, sizeInBits, offset, cvAttributes); + } +} diff --git a/src/net/java/games/gluegen/cgram/types/CVAttributes.java b/src/net/java/games/gluegen/cgram/types/CVAttributes.java new file mode 100644 index 000000000..e93d38cc5 --- /dev/null +++ b/src/net/java/games/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 + * 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. + */ + +package net.java.games.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/net/java/games/gluegen/cgram/types/CompoundType.java b/src/net/java/games/gluegen/cgram/types/CompoundType.java new file mode 100644 index 000000000..a08193a13 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/CompoundType.java @@ -0,0 +1,205 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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 { + private CompoundTypeKind kind; + // The name "foo" in the construct "struct foo { ... }"; + private String structName; + private ArrayList fields; + private boolean visiting; + private boolean bodyParsed; + private boolean computedHashcode; + private int hashcode; + + public CompoundType(String name, int size, CompoundTypeKind kind, int cvAttributes) { + this(name, size, kind, cvAttributes, null); + } + + private CompoundType(String name, int size, CompoundTypeKind kind, int cvAttributes, String structName) { + super(name, size, cvAttributes); + assert kind != null; + this.kind = kind; + this.structName = structName; + } + + public int hashCode() { + if (computedHashcode) { + return hashcode; + } + + if (structName != null) { + hashcode = structName.hashCode(); + } else if (getName() != null) { + hashcode = getName().hashCode(); + } else { + hashcode = System.identityHashCode(this); + } + + computedHashcode = true; + return hashcode; + } + + 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) && + 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; + } + + public void setSize(int size) { + super.setSize(size); + } + + 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 (Field) 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(); + } + 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); } + + public String toString() { + String cvAttributesString = getCVAttributesString(); + if (getName() != null) { + return cvAttributesString + getName(); + } else if (getStructName() != null) { + return cvAttributesString + "struct " + getStructName(); + } else { + return cvAttributesString + getStructString(); + } + } + + 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/net/java/games/gluegen/cgram/types/CompoundTypeKind.java b/src/net/java/games/gluegen/cgram/types/CompoundTypeKind.java new file mode 100644 index 000000000..9a1475d70 --- /dev/null +++ b/src/net/java/games/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 + * 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. + */ + +package net.java.games.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/net/java/games/gluegen/cgram/types/DoubleType.java b/src/net/java/games/gluegen/cgram/types/DoubleType.java new file mode 100644 index 000000000..3c2869e58 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/DoubleType.java @@ -0,0 +1,64 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +/** Represents a double-word floating-point type (C type "double".) */ + +public class DoubleType extends PrimitiveType { + public DoubleType(String name, int size, int cvAttributes) { + super(name, size, cvAttributes); + } + + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof DoubleType))) { + return false; + } + return super.equals(arg); + } + + public DoubleType asDouble() { return this; } + + Type newCVVariant(int cvAttributes) { + return new DoubleType(getName(), getSize(), cvAttributes); + } +} diff --git a/src/net/java/games/gluegen/cgram/types/EnumType.java b/src/net/java/games/gluegen/cgram/types/EnumType.java new file mode 100644 index 000000000..f85f64e23 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/EnumType.java @@ -0,0 +1,147 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +import java.util.*; + +/** Describes enumerated types. Enumerations are like ints except that + they have a set of named values. */ + +public class EnumType extends IntType { + 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 List/*<Enum>*/ enums; + + private static final int longSizeBytes = 8; + + public EnumType(String name) { + super(name, longSizeBytes, false, CVAttributes.CONST ); + this.underlyingType = new IntType(name, longSizeBytes, false, CVAttributes.CONST); + } + + public EnumType(String name, int enumSizeBytes) { + super(name, enumSizeBytes, false, CVAttributes.CONST ); + this.underlyingType = new IntType(name, enumSizeBytes, false, CVAttributes.CONST); + } + + protected EnumType(String name, IntType underlyingType, int cvAttributes) { + super(name, underlyingType.getSize(), underlyingType.isUnsigned(), cvAttributes); + this.underlyingType = underlyingType; + } + + 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)); + } + + public EnumType asEnum() { return this; } + + public void addEnum(String name, long val) { + if (enums == null) { + enums = new ArrayList(); + } + 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 ((Enum) enums.get(i)).getName(); } + /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) value */ + public long getEnumValue(int i) { return ((Enum) 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 = ((Enum)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 (((Enum)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 = (Enum)enums.get(i); + if (e.getName().equals(name)) { + enums.remove(e); + return true; + } + } + return false; + } + + public void visit(TypeVisitor arg) { + super.visit(arg); + underlyingType.visit(arg); + } + + Type newCVVariant(int cvAttributes) { + EnumType t = new EnumType(getName(), underlyingType, cvAttributes); + t.enums = enums; + return t; + } +} diff --git a/src/net/java/games/gluegen/cgram/types/Field.java b/src/net/java/games/gluegen/cgram/types/Field.java new file mode 100644 index 000000000..1c6f6f8a0 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/Field.java @@ -0,0 +1,96 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +/** Represents a field in a struct or union. */ + +public class Field { + private String name; + private Type type; + private long offset; + + public Field(String name, Type type, long offset) { + this.name = name; + this.type = type; + this.offset = offset; + } + + public int hashCode() { + return name.hashCode(); + } + + public boolean equals(Object arg) { + if (arg == null || (!(arg instanceof Field))) { + return false; + } + + Field f = (Field) arg; + return (((name != null && name.equals(f.name)) || + (name == null && f.name == null)) && + type.equals(f.type) && + offset == f.offset); + } + + /** Name of this field in the containing data structure. */ + public String getName() { return name; } + + /** Type of this field. */ + public Type getType() { return type; } + + /** Offset, in bytes, of this field in the containing data structure. */ + public long getOffset() { return offset; } + + /** Sets the offset of this field in the containing data structure. */ + public void setOffset(long offset) { this.offset = offset; } + + 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(); + return ft.toString(getName(), true) + ";"; + } + } +} diff --git a/src/net/java/games/gluegen/cgram/types/FloatType.java b/src/net/java/games/gluegen/cgram/types/FloatType.java new file mode 100644 index 000000000..1ad28f1ba --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/FloatType.java @@ -0,0 +1,64 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +/** Represents a single-word floating-point type (C type "float".) */ + +public class FloatType extends PrimitiveType { + public FloatType(String name, int size, int cvAttributes) { + super(name, size, cvAttributes); + } + + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof FloatType))) { + return false; + } + return super.equals(arg); + } + + public FloatType asFloat() { return this; } + + Type newCVVariant(int cvAttributes) { + return new FloatType(getName(), getSize(), cvAttributes); + } +} diff --git a/src/net/java/games/gluegen/cgram/types/FunctionSymbol.java b/src/net/java/games/gluegen/cgram/types/FunctionSymbol.java new file mode 100644 index 000000000..491961c5b --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/FunctionSymbol.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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +import java.util.*; + +/** 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); + } + + public String toString() { + return getType().toString(getName()); + } + + public int hashCode() { + if (name == null) { + return 0; + } + return name.hashCode(); + } + + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + + if (arg == null || (!(arg instanceof FunctionSymbol))) { + return false; + } + + FunctionSymbol other = (FunctionSymbol) arg; + return ( + (getName() == other.getName() || getName().equals(other.getName())) + && type.equals(other.type)); + } +} diff --git a/src/net/java/games/gluegen/cgram/types/FunctionType.java b/src/net/java/games/gluegen/cgram/types/FunctionType.java new file mode 100644 index 000000000..f864b4d2c --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/FunctionType.java @@ -0,0 +1,159 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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 { + private Type returnType; + private ArrayList argumentTypes; + private ArrayList argumentNames; + + public FunctionType(String name, int size, Type returnType, int cvAttributes) { + super(name, size, cvAttributes); + this.returnType = returnType; + } + + 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)); + } + + 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 (String) argumentNames.get(i); + } + + /** Returns the type of the <i>i</i>th argument. */ + public Type getArgumentType(int i) { + return (Type) 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(); + argumentNames = new ArrayList(); + } + argumentTypes.add(argumentType); + argumentNames.add(argumentName); + } + + public String toString() { + return toString(null); + } + + public String toString(String functionName) { + return toString(functionName, false); + } + + String toString(String functionName, boolean isPointer) { + StringBuffer res = new StringBuffer(); + res.append(getReturnType()); + res.append(" "); + if (isPointer) { + res.append("(*"); + } + if (functionName != null) { + res.append(functionName); + } + 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), 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(); + } + + 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/net/java/games/gluegen/cgram/types/IntType.java b/src/net/java/games/gluegen/cgram/types/IntType.java new file mode 100644 index 000000000..84f9b4c13 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/IntType.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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public class IntType extends PrimitiveType { + private boolean unsigned; + private boolean typedefedUnsigned; + + public IntType(String name, int size, boolean unsigned, int cvAttributes) { + this(name, size, unsigned, cvAttributes, false); + } + + private IntType(String name, int size, boolean unsigned, int cvAttributes, boolean typedefedUnsigned) { + super(name, size, cvAttributes); + this.unsigned = unsigned; + this.typedefedUnsigned = typedefedUnsigned; + } + + 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)); + } + + public void setName(String name) { + super.setName(name); + typedefedUnsigned = unsigned; + } + + public IntType asInt() { return this; } + + /** Indicates whether this type is unsigned */ + public boolean isUnsigned() { return unsigned; } + + 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/net/java/games/gluegen/cgram/types/MachineDescription.java b/src/net/java/games/gluegen/cgram/types/MachineDescription.java new file mode 100644 index 000000000..3db50d554 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/MachineDescription.java @@ -0,0 +1,78 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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; + + public MachineDescription(int charSizeInBytes, + int shortSizeInBytes, + int intSizeInBytes, + int longSizeInBytes, + int int64SizeInBytes, + int floatSizeInBytes, + int doubleSizeInBytes, + int pointerSizeInBytes) { + this.charSizeInBytes = charSizeInBytes; + this.shortSizeInBytes = shortSizeInBytes; + this.intSizeInBytes = intSizeInBytes; + this.longSizeInBytes = longSizeInBytes; + this.int64SizeInBytes = int64SizeInBytes; + this.floatSizeInBytes = floatSizeInBytes; + this.doubleSizeInBytes = doubleSizeInBytes; + this.pointerSizeInBytes = pointerSizeInBytes; + } + + 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; } +} diff --git a/src/net/java/games/gluegen/cgram/types/MachineDescription32Bit.java b/src/net/java/games/gluegen/cgram/types/MachineDescription32Bit.java new file mode 100644 index 000000000..337747047 --- /dev/null +++ b/src/net/java/games/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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public class MachineDescription32Bit extends MachineDescription { + public MachineDescription32Bit() { + super(1, 2, 4, 4, 8, 4, 8, 4); + } +} diff --git a/src/net/java/games/gluegen/cgram/types/PointerType.java b/src/net/java/games/gluegen/cgram/types/PointerType.java new file mode 100644 index 000000000..62bfe9c1a --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/PointerType.java @@ -0,0 +1,136 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public class PointerType extends Type { + private Type targetType; + private String computedName; + private boolean hasTypedefedName; + + public PointerType(int 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(int size, Type targetType, int cvAttributes, boolean hasTypedefedName, String typedefedName) { + super(targetType.getName() + " *", size, cvAttributes); + this.hasTypedefedName = false; + this.targetType = targetType; + if (hasTypedefedName) { + setName(typedefedName); + } + } + + public int hashCode() { + return targetType.hashCode(); + } + + 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)); + } + + public void setName(String name) { + super.setName(name); + hasTypedefedName = true; + } + + 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 PointerType asPointer() { return this; } + + public Type getTargetType() { return targetType; } + + public boolean isFunctionPointer() { return targetType.isFunction(); } + + public String toString() { + if (hasTypedefedName) { + return super.getName(true); + } else { + if (!targetType.isFunction()) { + return targetType.toString() + " * " + getCVAttributesString(); + } + return toString(null); // this is a pointer to an unnamed function + } + } + + /** For use only when printing function pointers */ + public String toString(String functionName) { + 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, true); + } + + 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/net/java/games/gluegen/cgram/types/PrimitiveType.java b/src/net/java/games/gluegen/cgram/types/PrimitiveType.java new file mode 100644 index 000000000..7b41510df --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/PrimitiveType.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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public abstract class PrimitiveType extends Type { + protected PrimitiveType(String name, int size, int cvAttributes) { + super(name, size, cvAttributes); + } + + public boolean isPrimitive() { + return true; + } +} diff --git a/src/net/java/games/gluegen/cgram/types/Type.java b/src/net/java/games/gluegen/cgram/types/Type.java new file mode 100644 index 000000000..531393aa5 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/Type.java @@ -0,0 +1,242 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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 { + private String name; + private int size; + private int cvAttributes; + private int typedefedCVAttributes; + private boolean hasTypedefName; + + protected Type(String name, int size, int cvAttributes) { + setName(name); + this.size = size; + this.cvAttributes = cvAttributes; + hasTypedefName = false; + } + + /** 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; + } + + /** Size of this type in bytes. */ + public int getSize() { return size; } + /** Set the size of this type; only available for CompoundTypes. */ + void setSize(int 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. */ + 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. + */ + public boolean equals(Object arg) { + if (arg == this) { + return true; + } + if (arg == null || (!(arg instanceof Type))) { + return false; + } + Type t = (Type) arg; + return ((name == 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. */ + 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). */ + public int pointerDepth() { + PointerType pt = asPointer(); + if (pt == null) { + return 0; + } + return 1 + pt.getTargetType().pointerDepth(); + } + + /** 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/net/java/games/gluegen/cgram/types/TypeDictionary.java b/src/net/java/games/gluegen/cgram/types/TypeDictionary.java new file mode 100644 index 000000000..bbf69b3c9 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/TypeDictionary.java @@ -0,0 +1,169 @@ +/* + * 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 + * 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. + */ + +package net.java.games.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 (Type) 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 (Type) map.get(name); + } + + /** + * 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 (Type) map.remove(name); + } + + /** Get all the names that map to Types. + * @returns a Set of Strings that are the typedef names that map to Types in the dictionary. + */ + public Set keySet() { + return map.keySet(); + } + + public Set 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 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/net/java/games/gluegen/cgram/types/TypeVisitor.java b/src/net/java/games/gluegen/cgram/types/TypeVisitor.java new file mode 100644 index 000000000..e0bc57ed4 --- /dev/null +++ b/src/net/java/games/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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public interface TypeVisitor { + public void visitType(Type t); +} diff --git a/src/net/java/games/gluegen/cgram/types/VoidType.java b/src/net/java/games/gluegen/cgram/types/VoidType.java new file mode 100644 index 000000000..948c156c0 --- /dev/null +++ b/src/net/java/games/gluegen/cgram/types/VoidType.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 + * 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. + */ + +package net.java.games.gluegen.cgram.types; + +public class VoidType extends Type { + public VoidType(int cvAttributes) { + this("void", cvAttributes); + } + + private VoidType(String name, int cvAttributes) { + super(name, 0, cvAttributes); + } + + public VoidType asVoid() { return this; } + + Type newCVVariant(int cvAttributes) { + return new VoidType(getName(), cvAttributes); + } +} diff --git a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java new file mode 100644 index 000000000..90ec97113 --- /dev/null +++ b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java @@ -0,0 +1,498 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import net.java.games.gluegen.*; + +import java.lang.reflect.*; +import java.io.*; +import java.util.*; +import java.util.regex.*; + +public class BuildComposablePipeline +{ + private String outputDirectory; + private Class classToComposeAround; + + public static void main(String[] args) + { + String nameOfClassToComposeAround = args[0]; + Class classToComposeAround; + try { + classToComposeAround = Class.forName(nameOfClassToComposeAround); + } catch (Exception e) { + throw new RuntimeException( + "Could not find class \"" + nameOfClassToComposeAround + "\"", e); + } + + String outputDir = args[1]; + + BuildComposablePipeline composer = + new BuildComposablePipeline(classToComposeAround, outputDir); + + try + { + composer.emit(); + } + catch (IOException e) + { + throw new RuntimeException( + "Error generating composable pipeline source files", e); + } + } + + protected BuildComposablePipeline(Class classToComposeAround, String outputDirectory) + { + this.outputDirectory = outputDirectory; + this.classToComposeAround = classToComposeAround; + + if (! classToComposeAround.isInterface()) + { + throw new IllegalArgumentException( + classToComposeAround.getName() + " is not an interface class"); + } + } + + /** + * Emit the java source code for the classes that comprise the composable + * pipeline. + */ + public void emit() throws IOException + { + String pDir = outputDirectory; + String pInterface = classToComposeAround.getName(); + List/*<Method>*/ publicMethods = Arrays.asList(classToComposeAround.getMethods()); + + (new DebugPipeline(pDir, pInterface)).emit(publicMethods); + (new TracePipeline(pDir, pInterface)).emit(publicMethods); + } + + //------------------------------------------------------- + + /** + * Emits a Java source file that represents one element of the composable + * pipeline. + */ + protected static abstract class PipelineEmitter + { + private File file; + private String basePackage; + private String baseName; // does not include package! + private String outputDir; + + /** + * @param outputDir the directory into which the pipeline classes will be + * generated. + * @param baseInterfaceClassName the full class name (including package, + * e.g. "java.lang.String") of the interface that the pipeline wraps + * @exception IllegalArgumentException if classToComposeAround is not an + * interface. + */ + public PipelineEmitter(String outputDir, String baseInterfaceClassName) + { + int lastDot = baseInterfaceClassName.lastIndexOf('.'); + if (lastDot == -1) + { + // no package, class is at root level + this.baseName = baseInterfaceClassName; + this.basePackage = null; + } + else + { + this.baseName = baseInterfaceClassName.substring(lastDot+1); + this.basePackage = baseInterfaceClassName.substring(0, lastDot); + } + + this.outputDir = outputDir; + } + + public void emit(List/*<Method>*/ methodsToWrap) throws IOException + { + String pipelineClassName = getPipelineName(); + this.file = new File(outputDir + File.separatorChar + pipelineClassName + ".java"); + String parentDir = file.getParent(); + if (parentDir != null) + { + File pDirFile = new File(parentDir); + pDirFile.mkdirs(); + } + + PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter(file))); + + CodeGenUtils.emitJavaHeaders(output, + basePackage, + pipelineClassName, + true, + new String[] { "java.io.*" }, + new String[] { "public" }, + new String[] { baseName }, + null, + new CodeGenUtils.EmissionCallback() { + public void emit(PrintWriter w) { emitClassDocComment(w); } + } + ); + + preMethodEmissionHook(output); + + constructorHook(output); + + for (int i = 0; i < methodsToWrap.size(); ++i) + { + Method m = (Method)methodsToWrap.get(i); + emitMethodDocComment(output, m); + emitSignature(output, m); + emitBody(output, m); + } + + postMethodEmissionHook(output); + + output.println(); + output.print(" private " + baseName + " " + getDownstreamObjectName() + ";"); + + // end the class + output.println(); + output.print("} // end class "); + output.println(pipelineClassName); + + output.flush(); + output.close(); + } + + /** Get the name of the object through which API calls should be routed. */ + protected String getDownstreamObjectName() + { + return "downstream" + baseName; + } + + protected void emitMethodDocComment(PrintWriter output, Method m) + { + } + + protected void emitSignature(PrintWriter output, Method m) + { + output.print(" public "); + output.print(' '); + output.print(m.getReturnType().getName()); + output.print(' '); + output.print(m.getName()); + output.print('('); + output.print(getArgListAsString(m, true, true)); + output.println(")"); + } + + protected void emitBody(PrintWriter output, Method m) + { + output.println(" {"); + output.print(" "); + Class retType = m.getReturnType(); + + preDownstreamCallHook(output, m); + + if (retType != Void.TYPE) + { + output.print(JavaType.createForClass(retType).getName()); + output.print(" _res = "); + } + output.print(getDownstreamObjectName()); + output.print('.'); + output.print(m.getName()); + output.print('('); + output.print(getArgListAsString(m, false, true)); + output.println(");"); + + postDownstreamCallHook(output, m); + + if (retType != Void.TYPE) + { + output.println(" return _res;"); + } + output.println(" }"); + + } + + private String getArgListAsString(Method m, boolean includeArgTypes, boolean includeArgNames) + { + StringBuffer buf = new StringBuffer(256); + if (!includeArgNames && !includeArgTypes) + { + throw new IllegalArgumentException( + "Cannot generate arglist without both arg types and arg names"); + } + + Class[] argTypes = m.getParameterTypes(); + for (int i = 0; i < argTypes.length; ++i) + { + if (includeArgTypes) + { + buf.append(JavaType.createForClass(argTypes[i]).getName()); + buf.append(' '); + } + + if (includeArgNames) + { + buf.append("arg"); + buf.append(i); + } + if (i < argTypes.length-1) { buf.append(','); } + } + + return buf.toString(); + } + + /** The name of the class around which this pipeline is being + * composed. E.g., if this pipeline was constructed with + * "java.util.Set" as the baseInterfaceClassName, then this method will + * return "Set". + */ + protected String getBaseInterfaceName() + { + return baseName; + } + + /** Get the name for this pipeline class. */ + protected abstract String getPipelineName(); + + /** + * Called after the class headers have been generated, but before any + * method wrappers have been generated. + */ + protected abstract void preMethodEmissionHook(PrintWriter output); + + /** + * Emits the constructor for the pipeline; called after the preMethodEmissionHook. + */ + protected void constructorHook(PrintWriter output) { + output.print( " public " + getPipelineName() + "(" + baseName + " "); + output.println(getDownstreamObjectName() + ")"); + output.println(" {"); + output.print( " this." + getDownstreamObjectName()); + output.println(" = " + getDownstreamObjectName() + ";"); + output.println(" }"); + output.println(); + } + + /** + * Called after the method wrappers have been generated, but before the + * closing parenthesis of the class is emitted. + */ + protected abstract void postMethodEmissionHook(PrintWriter output); + + /** + * Called before the pipeline routes the call to the downstream object. + */ + protected abstract void preDownstreamCallHook(PrintWriter output, Method m); + + /** + * Called after the pipeline has routed the call to the downstream object, + * but before the calling function exits or returns a value. + */ + protected abstract void postDownstreamCallHook(PrintWriter output, Method m); + + /** Emit a Javadoc comment for this pipeline class. */ + protected abstract void emitClassDocComment(PrintWriter output); + + } // end class PipelineEmitter + + //------------------------------------------------------- + + protected class DebugPipeline extends PipelineEmitter + { + String className; + String baseInterfaceClassName; + public DebugPipeline(String outputDir, String baseInterfaceClassName) + { + super(outputDir, baseInterfaceClassName); + className = "Debug" + getBaseInterfaceName(); + } + + protected String getPipelineName() + { + return className; + } + + protected void preMethodEmissionHook(PrintWriter output) + { + } + + protected void postMethodEmissionHook(PrintWriter output) + { + output.println(" private void checkGLGetError(String caller)"); + output.println(" {"); + output.println(" // Debug code to make sure the pipeline is working; leave commented out unless testing this class"); + output.println(" //System.err.println(\"Checking for GL errors " + + "after call to \" + caller + \"()\");"); + output.println(); + output.println(" int err = " + + getDownstreamObjectName() + + ".glGetError();"); + output.println(" if (err == GL_NO_ERROR) { return; }"); + output.println(); + output.println(" StringBuffer buf = new StringBuffer("); + output.println(" \"glGetError() returned the following error codes " + + "after a call to \" + caller + \"(): \");"); + output.println(); + output.println(" // Loop repeatedly to allow for distributed GL implementations,"); + output.println(" // as detailed in the glGetError() specification"); + output.println(" do {"); + output.println(" switch (err) {"); + output.println(" case GL_INVALID_ENUM: buf.append(\"GL_INVALID_ENUM \"); break;"); + output.println(" case GL_INVALID_VALUE: buf.append(\"GL_INVALID_VALUE \"); break;"); + output.println(" case GL_INVALID_OPERATION: buf.append(\"GL_INVALID_OPERATION \"); break;"); + output.println(" case GL_STACK_OVERFLOW: buf.append(\"GL_STACK_OVERFLOW \"); break;"); + output.println(" case GL_STACK_UNDERFLOW: buf.append(\"GL_STACK_UNDERFLOW \"); break;"); + output.println(" case GL_OUT_OF_MEMORY: buf.append(\"GL_OUT_OF_MEMORY \"); break;"); + output.println(" case GL_NO_ERROR: throw new InternalError(\"Should not be treating GL_NO_ERROR as error\");"); + output.println(" default: throw new InternalError(\"Unknown glGetError() return value: \" + err);"); + output.println(" }"); + output.println( " } while ((err = " + + getDownstreamObjectName() + + ".glGetError()) != GL_NO_ERROR);"); + output.println(" throw new GLException(buf.toString());"); + output.println(" }"); + + output.println(" /** True if the pipeline is inside a glBegin/glEnd pair.*/"); + output.println(" private boolean insideBeginEndPair = false;"); + output.println(); + + } + protected void emitClassDocComment(PrintWriter output) + { + output.println("/** <P> Composable pipline which wraps an underlying {@link GL} implementation,"); + output.println(" providing error checking after each OpenGL method call. If an error occurs,"); + output.println(" causes a {@link GLException} to be thrown at exactly the point of failure."); + output.println(" Sample code which installs this pipeline: </P>"); + output.println(); + output.println("<PRE>"); + output.println(" drawable.setGL(new DebugGL(drawable.getGL()));"); + output.println("</PRE>"); + output.println("*/"); + } + + protected void preDownstreamCallHook(PrintWriter output, Method m) + { + } + + protected void postDownstreamCallHook(PrintWriter output, Method m) + { + if (m.getName().equals("glBegin")) + { + output.println(" insideBeginEndPair = true;"); + output.println(" // NOTE: can't check glGetError(); it's not allowed inside glBegin/glEnd pair"); + } + else + { + if (m.getName().equals("glEnd")) + { + output.println(" insideBeginEndPair = false;"); + } + + // calls to glGetError() are only allowed outside of glBegin/glEnd pairs + output.println(" checkGLGetError(\"" + m.getName() + "\");"); + } + } + + } // end class DebugPipeline + + //------------------------------------------------------- + + protected class TracePipeline extends PipelineEmitter + { + String className; + String baseInterfaceClassName; + public TracePipeline(String outputDir, String baseInterfaceClassName) + { + super(outputDir, baseInterfaceClassName); + className = "Trace" + getBaseInterfaceName(); + } + + protected String getPipelineName() + { + return className; + } + + protected void preMethodEmissionHook(PrintWriter output) + { + } + + protected void constructorHook(PrintWriter output) { + output.print( " public " + getPipelineName() + "(" + getBaseInterfaceName() + " "); + output.println(getDownstreamObjectName() + ", PrintStream " + getOutputStreamName() + ")"); + output.println(" {"); + output.print( " this." + getDownstreamObjectName()); + output.println(" = " + getDownstreamObjectName() + ";"); + output.print( " this." + getOutputStreamName()); + output.println(" = " + getOutputStreamName() + ";"); + output.println(" }"); + output.println(); + } + + protected void postMethodEmissionHook(PrintWriter output) + { + output.println("private PrintStream " + getOutputStreamName() + ";"); + } + protected void emitClassDocComment(PrintWriter output) + { + output.println("/** <P> Composable pipline which wraps an underlying {@link GL} implementation,"); + output.println(" providing tracing information to a user-specified {@link java.io.PrintStream}"); + output.println(" before after each OpenGL method call. Sample code which installs this pipeline: </P>"); + output.println(); + output.println("<PRE>"); + output.println(" drawable.setGL(new TraceGL(drawable.getGL(), System.err));"); + output.println("</PRE>"); + output.println("*/"); + } + + protected void preDownstreamCallHook(PrintWriter output, Method m) + { + output.println(getOutputStreamName() + ".println(\"Entered " + m.getName() + "\");"); + output.print(" "); + } + + protected void postDownstreamCallHook(PrintWriter output, Method m) + { + output.println(" " + getOutputStreamName() + ".println(\"Exited " + m.getName() + "\");"); + } + + private String getOutputStreamName() { + return "stream"; + } + + } // end class TracePipeline +} diff --git a/src/net/java/games/gluegen/opengl/BuildStaticGLInfo.java b/src/net/java/games/gluegen/opengl/BuildStaticGLInfo.java new file mode 100644 index 000000000..ed0c83445 --- /dev/null +++ b/src/net/java/games/gluegen/opengl/BuildStaticGLInfo.java @@ -0,0 +1,258 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + + /** + * Builds the StaticGLInfo class from the OpenGL header files (i.e., gl.h + * and glext.h) whose paths were passed as arguments to {@link + * #main(String[])}. + * + * It relies upon the assumption that a function's membership is scoped by + * preprocessor blocks in the header files that match the following pattern: + * <br> + * + * <pre> + * + * #ifndef GL_XXXX + * GLAPI <returnType> <APIENTRY|GLAPIENTRY> glFuncName(<params>) + * #endif GL_XXXX + * + * </pre> + * + * For example, if it parses the following data: + * + * <pre> + * + * #ifndef GL_VERSION_1_3 + * GLAPI void APIENTRY glActiveTexture (GLenum); + * GLAPI void APIENTRY glMultiTexCoord1dv (GLenum, const GLdouble *); + * GLAPI void <APIENTRY|GLAPIENTRY> glFuncName(<params>) + * #endif GL_VERSION_1_3 + * + * #ifndef GL_ARB_texture_compression + * GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); + * GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); + * #endif + * + * </pre> + * + * It will associate + * <code> glActiveTexture </code> and + * <code> glMultiTexCoord1dv </code> + * with the symbol + * <code> GL_VERSION_1_3 </code>, + * and associate + * <code> glCompressedTexImage2DARB </code> and + * <code> glCompressedTexImage3DARB </code> + * with the symbol + * <code> GL_ARB_texture_compression </code>. + * */ +public class BuildStaticGLInfo +{ + protected static Pattern funcPattern = + Pattern.compile("^(GLAPI|extern)?(\\s*)(\\w+)(\\*)?(\\s+)(APIENTRY|WINAPI)?(\\s*)([w]?gl\\w+)\\s?(\\(.*)"); + protected static Pattern associationPattern = + Pattern.compile("\\#ifndef ([W]?GL[X]?_[A-Za-z0-9_]+)"); + + /** + * The first argument is the package to which the StaticGLInfo class + * belongs, the second is the path to the directory in which that package's + * classes reside, and the remaining arguments are paths to the C header + * files that should be parsed + */ + public static void main(String[] args) + { + String packageName = args[0]; + String packageDir = args[1]; + + String[] cHeaderFilePaths = new String[args.length-2]; + System.arraycopy(args, 2, cHeaderFilePaths, 0, cHeaderFilePaths.length); + + BuildStaticGLInfo builder = new BuildStaticGLInfo(); + try + { + File file = new File(packageDir + File.separatorChar + "StaticGLInfo.java"); + String parentDir = file.getParent(); + if (parentDir != null) + { + File pDirFile = new File(parentDir); + pDirFile.mkdirs(); + } + + PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file))); + builder.build(writer, packageName, cHeaderFilePaths); + + writer.flush(); + writer.close(); + } + catch (Exception e) + { + StringBuffer buf = new StringBuffer("{ "); + for (int i = 0; i < cHeaderFilePaths.length; ++i) + { + buf.append(cHeaderFilePaths[i]); + buf.append(" "); + } + buf.append('}'); + throw new RuntimeException( + "Error building StaticGLInfo.java from " + buf.toString(), e); + } + } + + protected void build(PrintWriter output, String packageName, String[] cHeaderFilePaths) throws IOException + { + HashMap groupToFuncHash = new HashMap(50); + for (int i = 0; i < cHeaderFilePaths.length; ++i) + { + process(groupToFuncHash, new FileReader(cHeaderFilePaths[i])); + } + + emitJavaCode(output, packageName, groupToFuncHash); + } + + protected void process(HashMap groupToFuncHash, FileReader headerFile) throws IOException + { + BufferedReader reader = new BufferedReader(headerFile); + String line, activeAssociation = null; + Matcher m; + while ((line = reader.readLine()) != null) + { + // see if we're inside a #ifndef GL_XXX block and matching a function + if (activeAssociation != null && (m = funcPattern.matcher(line)).matches()) + { + // We found a new function associated with the last #ifndef block we + // were associated with + + String funcName = m.group(8); + HashSet funcsForGroup = (HashSet)groupToFuncHash.get(activeAssociation); + if (funcsForGroup == null) + { + funcsForGroup = new HashSet(8); + groupToFuncHash.put(activeAssociation, funcsForGroup); + } + funcsForGroup.add(funcName); + + //System.err.println("FOUND ASSOCIATION FOR " + activeAssociation + ": " + funcName); + } + else if ((m = associationPattern.matcher(line)).matches()) + { + // found a new #ifndef GL_XXX block + activeAssociation = m.group(1); + + //System.err.println("FOUND NEW ASSOCIATION BLOCK: " + activeAssociation); + } + } + } + + protected void emitJavaCode(PrintWriter output, String packageName, HashMap groupToFuncHash) + { + output.println("package " + packageName + ";"); + output.println(); + output.println("import java.util.*;"); + output.println(); + output.println("public final class StaticGLInfo"); + output.println("{"); + + output.println(" // maps function names to the extension string or OpenGL"); + output.println(" // specification version string to which they correspond."); + output.println(" private static HashMap funcToAssocMap = null;"); + output.println(); + + output.println(" /**"); + output.println(" * Returns the OpenGL extension string or GL_VERSION string with which the"); + output.println(" * given function is associated. <P>"); + output.println(" *"); + output.println(" * If the"); + output.println(" * function is part of the OpenGL core, the returned value will be"); + output.println(" * GL_VERSION_XXX where XXX represents the OpenGL version of which the"); + output.println(" * function is a member (XXX will be of the form \"A\" or \"A_B\" or \"A_B_C\";"); + output.println(" * e.g., GL_VERSION_1_2_1 for OpenGL version 1.2.1)."); + output.println(" *"); + output.println(" * If the function is an extension function, the returned value will the"); + output.println(" * OpenGL extension string for the extension to which the function"); + output.println(" * corresponds. For example, if glLoadTransposeMatrixfARB is the argument,"); + output.println(" * GL_ARB_transpose_matrix will be the value returned."); + output.println(" * Please see http://oss.sgi.com/projects/ogl-sample/registry/index.html for"); + output.println(" * a list of extension names and the functions they expose."); + output.println(" *"); + output.println(" * If the function specified is not part of any known OpenGL core version or"); + output.println(" * extension, then NULL will be returned."); + output.println(" */"); + output.println(" public static String getFunctionAssociation(String glFunctionName)"); + output.println(" {"); + output.println(" if (funcToAssocMap == null) { init(); }"); + output.println(" return (String)funcToAssocMap.get(glFunctionName);"); + output.println(" }"); + output.println(); + + output.println(" private static void init()"); + output.println(" {"); + output.println(" funcToAssocMap = new HashMap(1536); // approximate max capacity"); + output.println(" String group;"); + ArrayList sets = new ArrayList(groupToFuncHash.keySet()); + Collections.sort(sets); + for (int i = 0; i < sets.size(); ++i) + { + String groupName = (String) sets.get(i); + //System.err.println(groupName); // debug + output.println(); + output.println(" //----------------------------------------------------------------"); + output.println(" // " + groupName); + output.println(" //----------------------------------------------------------------"); + output.println(" group = \"" + groupName + "\";"); + HashSet funcs = (HashSet)groupToFuncHash.get(groupName); + Iterator funcIter = funcs.iterator(); + while (funcIter.hasNext()) + { + String funcName = (String)funcIter.next(); + //System.err.println(" " + funcName); // debug + output.println(" funcToAssocMap.put(\"" + funcName + "\", group);"); + } + } + output.println(" }"); + + output.println("} // end class StaticGLInfo"); + } + +} diff --git a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java new file mode 100644 index 000000000..cd0554dc3 --- /dev/null +++ b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java @@ -0,0 +1,201 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import java.io.*; +import java.util.*; +import net.java.games.gluegen.*; +import net.java.games.gluegen.cgram.types.*; + +public class CGLPAWrapperEmitter extends CMethodBindingEmitter +{ + private static final CommentEmitter defaultCommentEmitter = + new CGLPAWrapperCommentEmitter(); + + private CMethodBindingEmitter emitterBeingWrapped; + private String glFuncPtrTypedefValue; + private static String procAddressJavaTypeName = + JavaType.createForClass(Long.TYPE).jniTypeName(); + + public CGLPAWrapperEmitter(CMethodBindingEmitter methodToWrap) + { + super( + new MethodBinding(methodToWrap.getBinding()) { + public String getName() { + return GLEmitter.WRAP_PREFIX + super.getName(); + } + }, + methodToWrap.getIsOverloadedBinding(), + methodToWrap.getJavaPackageName(), + methodToWrap.getJavaClassName(), + methodToWrap.getIsJavaMethodStatic(), + methodToWrap.getDefaultOutput() + ); + + setCommentEmitter(defaultCommentEmitter); + } + + protected int emitArguments(PrintWriter writer) + { + int numEmitted = super.emitArguments(writer); + if (numEmitted > 0) + { + writer.print(", "); + } + //writer.print("long glProcAddress"); + writer.print(procAddressJavaTypeName); + writer.print(" glProcAddress"); + ++numEmitted; + + return numEmitted; + } + + protected void emitBodyVariableDeclarations(PrintWriter writer) + { + // create variable for the function pointer with the right type, and set + // it to the value of the passed-in glProcAddress + FunctionSymbol cSym = getBinding().getCSymbol(); + String funcPointerTypedefName = + GLEmitter.getGLFunctionPointerTypedefName(cSym); + + writer.print(" "); + writer.print(funcPointerTypedefName); + writer.print(" ptr_"); + writer.print(cSym.getName()); + writer.println(";"); + + super.emitBodyVariableDeclarations(writer); + } + + protected void emitBodyVariablePreCallSetup(PrintWriter writer) + { + super.emitBodyVariablePreCallSetup(writer); + + // set the function pointer to the value of the passed-in glProcAddress + FunctionSymbol cSym = getBinding().getCSymbol(); + String funcPointerTypedefName = + GLEmitter.getGLFunctionPointerTypedefName(cSym); + + String ptrVarName = "ptr_" + cSym.getName(); + + writer.print(" "); + writer.print(ptrVarName); + writer.print(" = ("); + writer.print(funcPointerTypedefName); + writer.println(") (intptr_t) glProcAddress;"); + + writer.println(" assert(" + ptrVarName + " != NULL);"); + } + + // FIXME: refactor this and the superclass version so we don't have to copy + // the whole function + 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 = getBinding().getCReturnType(); + + if (!cReturnType.isVoid()) { + writer.print("_res = "); + } + + // !!!!!!!!! BEGIN CHANGES FROM SUPERCLASS METHOD + + MethodBinding binding = getBinding(); + if (binding.hasContainingType()) { + // Cannot call GL func through function pointer + throw new IllegalStateException( + "Cannot call GL func through function pointer: " + binding); + } + + // call throught the run-time function pointer + writer.print("(* ptr_"); + writer.print(binding.getCSymbol().getName()); + writer.print(") "); + + // !!!!!!!!! END CHANGES FROM SUPERCLASS METHOD + + + writer.print("("); + for (int i = 0; i < binding.getNumArguments(); i++) { + if (i != 0) { + writer.print(", "); + } + JavaType javaType = binding.getJavaArgumentType(i); + // Handle case where only param is void. + if (javaType.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 (javaType.isJNIEnv()) { + writer.print("env"); + } else if (binding.isArgumentThisPointer(i)) { + writer.print(CMethodBindingEmitter.cThisArgumentName()); + } else { + writer.print("("); + writer.print(binding.getCSymbol().getArgumentType(i).getName()); + writer.print(") "); + if (binding.getCArgumentType(i).isPointer() && binding.getJavaArgumentType(i).isPrimitive()) { + writer.print("(intptr_t) "); + } + if (javaType.isArray() || javaType.isNIOBuffer()) { + writer.print(pointerConversionArgumentName(i)); + } else { + if (javaType.isString()) { writer.print("_UTF8"); } + writer.print(binding.getArgumentName(i)); + } + } + } + writer.println(");"); + } + + /** This class emits the comment for the wrapper method */ + private static class CGLPAWrapperCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { + protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { + writer.print(" -- FIXME: IMPLEMENT COMMENT FOR CGLPAWrapperCommentEmitter -- "); + } + } +} // end class CGLPAWrapperEmitter diff --git a/src/net/java/games/gluegen/opengl/ConvertFromGL4Java.java b/src/net/java/games/gluegen/opengl/ConvertFromGL4Java.java new file mode 100644 index 000000000..c026dbd50 --- /dev/null +++ b/src/net/java/games/gluegen/opengl/ConvertFromGL4Java.java @@ -0,0 +1,91 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import java.io.*; + +public class ConvertFromGL4Java { + public static void main(String[] args) throws IOException { + for (int i = 0; i < args.length; i++) { + convert(new File(args[i])); + } + } + + private static void convert(File src) throws IOException { + File orig = new File(src.getAbsolutePath() + ".orig"); + if (!src.renameTo(orig)) { + throw new IOException("Error renaming original file to " + orig); + } + File dest = src; + BufferedReader reader = new BufferedReader(new FileReader(orig)); + BufferedWriter writer = new BufferedWriter(new FileWriter(dest)); + boolean handledImports = false; + String line = null; + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + boolean isImport = false; + if (trimmed.startsWith("import gl4java")) { + line = "import net.java.games.jogl.*;"; + isImport = true; + } + if (!isImport || + (isImport && !handledImports)) { + line = line.replaceAll("GLFunc14", "GL"); + line = line.replaceAll("GLUFunc14", "GLU"); + line = line.replaceAll("GLFunc", "GL"); + line = line.replaceAll("GLUFunc", "GLU"); + line = line.replaceAll("implements GLEnum,", "implements "); + line = line.replaceAll(", GLEnum\\s", " "); + line = line.replaceAll("GLEnum,", ""); + line = line.replaceAll("GLEnum.", ""); + line = line.replaceAll("GLEnum", ""); + line = line.replaceAll("GL_", "GL.GL_"); + writer.write(line); + writer.newLine(); + if (isImport) { + handledImports = true; + } + } + } + writer.flush(); + reader.close(); + writer.close(); + } +} diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java new file mode 100644 index 000000000..27cc07e2c --- /dev/null +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -0,0 +1,330 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import net.java.games.gluegen.*; +import net.java.games.gluegen.cgram.types.*; + +/** + * A subclass of JavaEmitter that modifies the normal emission of C and Java + * code in order to allow a high-performance, cross-platform binding of Java + * to OpenGL. + */ +public class GLEmitter extends JavaEmitter +{ + public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; + protected static final String WRAP_PREFIX = "dispatch_"; + private TypeDictionary typedefDictionary; + private PrintWriter tableWriter; + private String tableClassName = "ProcAddressTable"; + private int numProcAddressEntries; + + public void beginFunctions(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map canonMap) throws Exception + { + this.typedefDictionary = typedefDictionary; + + if (getConfig().emitImpl()) { + cWriter().println("#include <assert.h> /* this include emitted by GLEmitter.java */"); + cWriter().println(); + } + + if (((GLConfiguration)getConfig()).emitProcAddressTable()) + { + beginGLProcAddressTable(); + } + super.beginFunctions(typedefDictionary, structDictionary, canonMap); + } + + public void endFunctions() throws Exception + { + if (((GLConfiguration)getConfig()).emitProcAddressTable()) + { + endGLProcAddressTable(); + } + super.endFunctions(); + } + + public void beginStructs(TypeDictionary typedefDictionary, + TypeDictionary structDictionary, + Map canonMap) throws Exception { + super.beginStructs(typedefDictionary, structDictionary, canonMap); + } + + protected JavaConfiguration createConfig() { + return new GLConfiguration(); + } + + protected Iterator generateMethodBindingEmitters(FunctionSymbol sym) throws Exception + { + Iterator defaultEmitters = super.generateMethodBindingEmitters(sym); + + // if the superclass didn't generate any bindings for the symbol, let's + // honor that (for example, the superclass might have caught an Ignore + // direction that matched the symbol's name). + if (!defaultEmitters.hasNext()) + { + return defaultEmitters; + } + + // Don't do anything special if this symbol doesn't require passing of + // Opengl procedure addresses in order to function correctly. + if (!needsProcAddressWrapper(sym) || getConfig().isUnimplemented(sym.getName())) + { + return defaultEmitters; + } + + // 9 is default # expanded bindings for void* + ArrayList modifiedEmitters = new ArrayList(9); + + if (((GLConfiguration)getConfig()).emitProcAddressTable()) + { + // emit an entry in the GL proc address table for this method. + emitGLProcAddressTableEntryForSymbol(sym); + } + + while (defaultEmitters.hasNext()) + { + FunctionEmitter emitter = (FunctionEmitter)defaultEmitters.next(); + if (emitter instanceof JavaMethodBindingEmitter) + { + JavaMethodBindingEmitter newEmitter = + generateModifiedEmitter((JavaMethodBindingEmitter)emitter); + if (newEmitter != null) { + modifiedEmitters.add(newEmitter); + } + } + else if (emitter instanceof CMethodBindingEmitter) + { + modifiedEmitters.add( + generateModifiedEmitter((CMethodBindingEmitter)emitter)); + } + else + { + throw new RuntimeException("Unexpected emitter type: " + + emitter.getClass().getName()); + } + } + + return modifiedEmitters.iterator(); + } + + /** + * Returns the name of the typedef for a pointer to the GL function + * represented by the argument. For example, 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. + */ + static String getGLFunctionPointerTypedefName(FunctionSymbol sym) + { + String symName = sym.getName(); + StringBuffer buf = new StringBuffer(symName.length() + 8); + buf.append("PFN"); + buf.append(symName.toUpperCase()); + buf.append("PROC"); + return buf.toString(); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private JavaMethodBindingEmitter generateModifiedEmitter(JavaMethodBindingEmitter baseJavaEmitter) + { + if (!(baseJavaEmitter instanceof JavaMethodBindingImplEmitter)) { + // We only want to wrap the native entry point in the implementation + // class, not the public interface in the interface class. + // + // If the superclass has generated a "0" emitter for this routine because + // it needs argument conversion or similar, filter that out since we will + // be providing such an emitter ourselves. Otherwise return the emitter + // unmodified. + if (baseJavaEmitter.isForNIOBufferBaseRoutine()) + return null; + return baseJavaEmitter; + } + return new JavaGLPAWrapperEmitter(baseJavaEmitter); + } + + private CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) + { + // 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 + CGLPAWrapperEmitter res = new CGLPAWrapperEmitter(baseCEmitter); + MessageFormat exp = baseCEmitter.getReturnValueCapacityExpression(); + if (exp != null) { + res.setReturnValueCapacityExpression(exp); + } + return res; + } + + private boolean needsProcAddressWrapper(FunctionSymbol sym) + { + String symName = sym.getName(); + + // We should only wrap the GL symbol if its function pointer typedef has + // been defined (most likely in glext.h). + String funcPointerTypedefName = getGLFunctionPointerTypedefName(sym); + boolean shouldWrap = typedefDictionary.containsKey(funcPointerTypedefName); + //System.err.println(funcPointerTypedefName + " defined: " + shouldWrap); + + if (!shouldWrap) + { + //System.err.println("WARNING (GL): *not* run-time linking: " + sym + + // "(" + funcPointerTypedefName + " undefined)"); + } + return shouldWrap; + } + + private void beginGLProcAddressTable() throws Exception + { + String implPackageName = getImplPackageName(); + String jImplRoot = + getJavaOutputDir() + File.separator + + CodeGenUtils.packageAsPath(implPackageName); + + // HACK: until we have a way to make the impl dir different from the + // WindowsGLImpl dir and the interface dir + //tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); + File tmpFile = new File(jImplRoot); + tmpFile = tmpFile.getParentFile(); + tmpFile = new File(tmpFile, tableClassName + ".java"); + tableWriter = openFile(tmpFile.getPath()); + // tableWriter = openFile(jImplRoot + File.separator + ".." + File.separator + tableClassName + ".java"); + + CodeGenUtils.emitAutogeneratedWarning(tableWriter, this); + + // HACK: until we have a way to make the impl dir different from the + // WindowsGLImpl dir and the interface dir + //tableWriter.println("package " + implPackageName + ";"); + tableWriter.println("package " + getJavaPackageName() + ".impl;"); + tableWriter.println(); + tableWriter.println("/**"); + tableWriter.println(" * This table is a cache of the native pointers to OpenGL extension"); + tableWriter.println(" * functions, to be used for run-time linking of these extensions. "); + tableWriter.println(" * These pointers are obtained by the OpenGL context via a "); + tableWriter.println(" * platform-specific function (e.g., wglGetProcAddress() on Win32,"); + tableWriter.println(" * glXGetProcAddress() on X11, etc). If the member variable "); + tableWriter.println(" * " + PROCADDRESS_VAR_PREFIX + "glFuncName is non-zero then function"); + tableWriter.println(" * \"glFuncName\" can be called through the associated GLContext; "); + tableWriter.println(" * if it is 0, then the extension is not available and cannot be called."); + tableWriter.println(" */"); + tableWriter.println("public class " + tableClassName); + tableWriter.println("{"); + numProcAddressEntries = 0; + } + + private void endGLProcAddressTable() throws Exception + { + PrintWriter w = tableWriter; + w.print(" protected static long __PROCADDRESSINDEX__LASTINDEX = "); + w.print(numProcAddressEntries-1); + w.println(';'); + + w.println(); + w.println(" /**"); + w.println(" * This is a convenience method to get (by name) the native function "); + w.println(" * pointer for a given extension function. It lets you avoid "); + w.println(" * having to manually compute the " + PROCADDRESS_VAR_PREFIX + "<glFunctionName>"); + w.println(" * member variable name and look it up via reflection; it also"); + w.println(" * will throw an exception if you try to get the address of an"); + w.println(" * unknown GL extension, or one that is statically linked "); + w.println(" * and therefore does not have a valid GL procedure address. "); + w.println(" */"); + w.println(" public long getAddressFor(String glFunctionName) {"); + w.println(" String addressFieldName = net.java.games.gluegen.opengl.GLEmitter.PROCADDRESS_VAR_PREFIX + glFunctionName;"); + w.println(" try { "); + w.println(" java.lang.reflect.Field addressField = this.getClass().getField(addressFieldName);"); + w.println(" return addressField.getLong(this);"); + w.println(" } catch (Exception e) {"); + w.println(" // The user is calling a bogus function or one which is not runtime"); + w.println(" // linked (extensions and core post-OpenGL 1.1 functions are runtime linked)"); + w.println(" if (!FunctionAvailabilityCache.isPartOfGLCore(\"1.1\", glFunctionName)) "); + w.println(" {"); + w.println(" throw new RuntimeException(" ); + w.println(" \"WARNING: Address query failed for \\\"\" + glFunctionName +"); + w.println(" \"\\\"; either it's not runtime linked or it is not a known \" +"); + w.println(" \"OpenGL function\", e);"); + w.println(" }"); + w.println(" } "); + w.println(" assert(false); // should never get this far"); + w.println(" return 0;"); + w.println(" }"); + + w.println("} // end of class " + tableClassName); + w.flush(); + w.close(); + } + + private void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) + { + tableWriter.print(" public static long "); + tableWriter.print(PROCADDRESS_VAR_PREFIX); + tableWriter.print(cFunc.getName()); + tableWriter.println(";"); + ++numProcAddressEntries; + } + + protected static class GLConfiguration extends JavaConfiguration + { + private boolean emitProcAddressTable = false; + + 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 + { + super.dispatch(cmd,tok,file,filename,lineNo); + } + } + + public boolean emitProcAddressTable() { return emitProcAddressTable; } + } // end class GLConfiguration +} + diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java new file mode 100644 index 000000000..e0a098f2a --- /dev/null +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -0,0 +1,178 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.opengl; + +import java.io.*; +import java.util.*; +import net.java.games.gluegen.*; +import net.java.games.gluegen.cgram.types.*; + +public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter +{ + private static final CommentEmitter commentEmitterForWrappedMethod = + new WrappedMethodCommentEmitter(); + + private JavaMethodBindingEmitter emitterBeingWrapped; + + public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap) + { + super(methodToWrap.getBinding(), methodToWrap.getDefaultOutput(), methodToWrap.getRuntimeExceptionType()); + + if (methodToWrap.getBinding().hasContainingType()) + { + throw new IllegalArgumentException( + "Cannot create OpenGL proc. address wrapper; method has containing type: \"" + + methodToWrap.getBinding() + "\""); + } + + // make a new emitter that will emit the original method's binding, but + // with WRAP_PREFIX before its name. If a body is needed (for array + // length checking, unwrapping of wrapper objects to java.nio.Buffers, + // etc.) then it will be generated; therefore the emitter being wrapped + // should be an "NIO buffer variant" (i.e., after all unpacking has + // occurred). + emitterBeingWrapped = + new JavaMethodBindingEmitter(methodToWrap.getBinding().createNIOBufferVariant(), + methodToWrap.getDefaultOutput(), + methodToWrap.getRuntimeExceptionType()) + { + protected void emitName(PrintWriter writer) + { + writer.print(GLEmitter.WRAP_PREFIX); + super.emitName(writer); + } + protected int emitArguments(PrintWriter writer) + { + int numEmitted = super.emitArguments(writer); + if (numEmitted > 0) + { + writer.print(", "); + } + writer.print("long glProcAddress"); + ++numEmitted; + + return numEmitted; + } + }; + + // copy the modifiers from the original emitter + emitterBeingWrapped.addModifiers(methodToWrap.getModifiers()); + + // Change the access of the method we're wrapping to PRIVATE + EmissionModifier origAccess = null; // null is equivalent if package access + if (emitterBeingWrapped.hasModifier(PUBLIC)) + { + origAccess = PUBLIC; + } + else if (emitterBeingWrapped.hasModifier(PROTECTED)) + { + origAccess = PROTECTED; + } + else if (emitterBeingWrapped.hasModifier(PRIVATE)) + { + origAccess = PRIVATE; + } + + if (origAccess != null) + { + emitterBeingWrapped.removeModifier(origAccess); + } + emitterBeingWrapped.addModifier(PRIVATE); + emitterBeingWrapped.addModifier(NATIVE); + + // Now make our binding use the original access of the wrapped method + this.addModifier(origAccess); + } + + protected boolean needsBody() { + return true; + } + + protected String getImplMethodName() { + return GLEmitter.WRAP_PREFIX + getBinding().getName(); + } + + public void emit(PrintWriter writer) + { + // Emit a wrapper that will call the method we want to wrap + //writer.println(" // Emitter being wrapped = " + emitterBeingWrapped.getClass().getName()); + super.emit(writer); + writer.println(); + + // emit the wrapped method + CommentEmitter origComment = emitterBeingWrapped.getCommentEmitter(); + emitterBeingWrapped.setCommentEmitter(commentEmitterForWrappedMethod); + emitterBeingWrapped.emit(writer); + emitterBeingWrapped.setCommentEmitter(origComment); + writer.println(); + } + + protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + super.emitPreCallSetup(binding, writer); + JavaType returnType = binding.getJavaReturnType(); + + MethodBinding wrappedBinding = emitterBeingWrapped.getBinding(); + String procAddressVariable = + GLEmitter.PROCADDRESS_VAR_PREFIX + wrappedBinding.getName(); + + writer.print(" final long addr = context.getGLProcAddressTable()."); + writer.print(procAddressVariable); + writer.println(';'); + writer.println(" if (addr == 0) {"); + writer.println(" throw new GLException(\"Method \\\"" + binding.getName() + "\\\" not available\");"); + writer.println(" }"); + } + + protected int emitCallArguments(MethodBinding binding, PrintWriter writer) { + int numEmitted = super.emitCallArguments(binding, writer); + if (numEmitted > 0) { + writer.print(", "); + } + writer.print("addr"); + return 1 + numEmitted; + } + + /** This class emits the comment for the wrapper method */ + private static class WrappedMethodCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { + protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { + writer.print("Encapsulates function pointer for OpenGL function <br>: "); + } + } +} // end class JavaGLPAWrapperEmitter diff --git a/src/net/java/games/gluegen/pcpp/PCPP.java b/src/net/java/games/gluegen/pcpp/PCPP.java new file mode 100644 index 000000000..4bddc976f --- /dev/null +++ b/src/net/java/games/gluegen/pcpp/PCPP.java @@ -0,0 +1,824 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.pcpp; + +import java.io.*; +import java.util.*; + +/** 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 boolean disableDebugPrint = true; + + public PCPP(List/*<String>*/ includePaths) { + this.includePaths = includePaths; + setOut(System.out); + } + + public OutputStream out() { return out; } + public void setOut(OutputStream out) { this.out = out; writer = new PrintWriter(out); } + + public void run(Reader reader, String filename) throws IOException { + StreamTokenizer tok = new StreamTokenizer(reader); + tok.resetSyntax(); + tok.wordChars('a', 'z'); + tok.wordChars('A', 'Z'); + tok.wordChars('0', '9'); + tok.wordChars('_', '_'); + tok.wordChars('.', '.'); + tok.wordChars(128 + 32, 255); + tok.whitespaceChars(0, ' '); + tok.quoteChar('"'); + tok.quoteChar('\''); + tok.eolIsSignificant(true); + tok.slashSlashComments(true); + tok.slashStarComments(true); + ParseState curState = new ParseState(tok, filename); + ParseState oldState = state; + state = curState; + lineDirective(); + parse(); + state = oldState; + if (state != null) { + lineDirective(); + } + } + + public static void main(String[] args) { + try { + Reader reader = null; + String filename = null; + + if (args.length == 0) { + usage(); + } + + List includePaths = new ArrayList(); + 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 { + 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).run(reader, filename); + } catch (IOException e) { + e.printStackTrace(); + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + 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.exit(1); + } + + /** 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(); + + /** List containing the #include paths as Strings */ + private List/*<String>*/ includePaths; + + // State + static class ParseState { + private StreamTokenizer tok; + private String filename; + private int lineNumber; + private boolean startOfLine; + private boolean startOfFile; + + ParseState(StreamTokenizer tok, String filename) { + this.tok = tok; + this.filename = filename; + lineNumber = 1; + startOfLine = true; + startOfFile = true; + } + + StreamTokenizer tok() { return tok; } + 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 ParseState state; + + // Accessors + + private void pushBackToken() throws IOException { + state.tok().pushBack(); + } + + /** 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 (curToken() == StreamTokenizer.TT_EOL) { + state.setStartOfLine(true); + } else if (!state.startOfFile()) { + state.setStartOfLine(false); + } + state.setStartOfFile(false); + int val = state.tok().nextToken(); + if (!returnEOLs) { + if (val == StreamTokenizer.TT_EOL) { + do { + // Consume and return next token, setting state appropriately + val = state.tok().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 int curToken() { + return state.tok().ttype; + } + + private String curTokenAsString() { + int t = curToken(); + if (t == StreamTokenizer.TT_WORD) { + return curWord(); + } + if (t == StreamTokenizer.TT_EOL) { + throw new RuntimeException("Should not be converting EOL characters to strings"); + } + char c = (char) t; + if (c == '"' || c == '\'') { + StringBuffer buf = new StringBuffer(); + buf.append(c); + buf.append(state.tok().sval); + buf.append(c); + return buf.toString(); + } + return new String(new char[] { c }); + } + + private String nextWord() throws IOException { + int val = nextToken(); + if (val != StreamTokenizer.TT_WORD) { + throw new RuntimeException("Expected word at file " + filename() + + ", line " + lineNumber()); + } + return curWord(); + } + + private String curWord() { + return state.tok().sval; + } + + 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) + print(" "); + String s = curTokenAsString(); + String newS = (String) defineMap.get(s); + if (newS == null) { + newS = s; + } + print(newS); + } + } + flush(); + } + + private void preprocessorDirective() throws IOException { + String w = nextWord(); + boolean shouldPrint = true; + 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 values = new ArrayList(); + while (nextToken(true) != StreamTokenizer.TT_EOL) { + values.add(curTokenAsString()); + } + + if (enabled()) { + String oldDef = (String)defineMap.remove(name); + if (oldDef == null) { + System.err.println("WARNING: ignoring redundant \"#undef " + + name + "\", at \"" + filename() + "\" line " + lineNumber() + + ": \"" + name + "\" was not previously defined"); + } else { + // System.err.println("UNDEFINED: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); + } + } + else System.err.println("FAILED TO UNDEFINE: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); + + } + + private void handleDefine() throws IOException { + // Next token is the name of the #define + String name = nextWord(); + //System.err.println("IN HANDLE_DEFINE: '" + name + "' (line " + lineNumber() + " file " + filename() + ")"); + // (Note that this is not actually proper handling for multi-line #defines) + List values = new ArrayList(); + 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 oldDef = (String)defineMap.put(name, ""); + if (oldDef != null) { + System.err.println("WARNING: \"" + name + "\" redefined from \"" + + oldDef + "\" to \"\""); + } + // 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 = (String) values.get(0); + if (isConstant(value)) { + // Value is numeric constant like "#define FOO 5". + // Put it in the #define map + String oldDef = (String)defineMap.put(name, value); + if (oldDef != null) { + System.err.println("WARNING: \"" + name + "\" redefined from \"" + + oldDef + "\" to \"" + value + "\""); + } + //System.out.println("//---DEFINED: " + name + " to \"" + value + "\""); + } else { + // Value is a symbolic constant like "#define FOO BAR". + // Try to look up the symbol's value + String newValue = (String) defineMap.get(value); + 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); + emitDefine = false; + } + } + } + else + { + // can't handle definitions that aren't constants or empty + emitDefine = false; + StringBuffer buf = new StringBuffer("#define "); + buf.append(name); + for (int i = 0; i < sz; ++i) { + buf.append(" "); + buf.append(values.get(i)); + } + System.err.println("WARNING: Ignoring \"" + buf.toString() + + "\" at \"" + filename() + "\" line " + lineNumber() + + ": Unable to handle definition to non-constant"); + 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 \"" + buf.toString() + "\""); + } + } + + if (emitDefine) + { + // Print name and value + print("# define "); + print(name); + for (Iterator iter = values.iterator(); iter.hasNext(); ) { + print(" "); + print((String) iter.next()); + } + println(); + } + + } // end if (enabled()) + + //System.err.println("OUT HANDLE_DEFINE: " + name); + } + + private boolean isConstant(String s) { + if (s.startsWith("0x") || s.startsWith("0X")) { + return checkHex(s); + } else { + return checkDecimal(s); + } + } + + private boolean checkHex(String s) { + for (int i = 2; i < s.length(); i++) { + char c = s.charAt(i); + if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'))) { + return false; + } + } + return true; + } + + private boolean checkDecimal(String s) { + try { + Float.valueOf(s); + } + catch (NumberFormatException e) { + // not parsable as a number + return false; + } + return true; + } + + //////////////////////////////////////////////// + // 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, "HANDLE_IFDEF: ifdef(" + symbolName + ") = " + 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(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 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 = (String)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. + return true; + } 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>!"); + 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 = curWord(); + } else if (t == '<') { + // Components of path name are coming in as separate tokens; + // concatenate them + StringBuffer buf = new StringBuffer(); + while ((t = nextToken()) != '>' && (t != StreamTokenizer.TT_EOF)) { + buf.append(curTokenAsString()); + } + if (t == StreamTokenizer.TT_EOF) { + System.err.println("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) { + System.err.println("WARNING: unable to find #include file \"" + 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); + } + } + + private String findFile(String filename) { + String sep = System.getProperty("file.separator"); + for (Iterator iter = includePaths.iterator(); iter.hasNext(); ) { + String inclPath = (String) iter.next(); + String fullPath = inclPath + sep + filename; + File file = new File(fullPath); + if (file.exists()) { + return fullPath; + } + } + return null; + } + + //////////// + // Output // + //////////// + + private OutputStream out; + private PrintWriter writer; + private ArrayList enabledBits = new ArrayList(); + + private static int debugPrintIndentLevel = 0; + private void debugPrint(boolean onlyPrintIfEnabled, String msg) + { + if (disableDebugPrint) { + 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(new Boolean(enabled)); + ++debugPrintIndentLevel; + //debugPrint(false, "PUSH_ENABLED, NOW: " + enabled()); + } + + private void popEnableBit() { + if (enabledBits.size() == 0) { + System.err.println("WARNING: mismatched #ifdef/endif pairs"); + return; + } + enabledBits.remove(enabledBits.size() - 1); + --debugPrintIndentLevel; + //debugPrint(false, "POP_ENABLED, NOW: " + enabled()); + } + + private boolean enabled() { + return (enabledBits.size() == 0 || + ((Boolean) enabledBits.get(enabledBits.size() - 1)).booleanValue()); + } + + 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("#line " + lineNumber() + " \"" + filename() + "\""); + println(); + } +} diff --git a/src/net/java/games/gluegen/runtime/BufferFactory.java b/src/net/java/games/gluegen/runtime/BufferFactory.java new file mode 100644 index 000000000..2efb78ca6 --- /dev/null +++ b/src/net/java/games/gluegen/runtime/BufferFactory.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 + * 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. + */ + +package net.java.games.gluegen.runtime; + +import java.nio.*; + +public class BufferFactory { + public static ByteBuffer newDirectByteBuffer(int size) { + ByteBuffer buf = ByteBuffer.allocateDirect(size); + buf.order(ByteOrder.nativeOrder()); + return buf; + } +} diff --git a/src/net/java/games/gluegen/runtime/StructAccessor.java b/src/net/java/games/gluegen/runtime/StructAccessor.java new file mode 100644 index 000000000..a5d1d1717 --- /dev/null +++ b/src/net/java/games/gluegen/runtime/StructAccessor.java @@ -0,0 +1,191 @@ +/* + * 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 + * 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. + */ + +package net.java.games.gluegen.runtime; + +import java.nio.*; + +public class StructAccessor { + private ByteBuffer bb; + private CharBuffer cb; + private DoubleBuffer db; + private FloatBuffer fb; + private IntBuffer ib; + private LongBuffer lb; + private ShortBuffer sb; + + public StructAccessor(ByteBuffer bb) { + // Setting of byte order is concession to native code which needs + // to instantiate these + this.bb = bb.order(ByteOrder.nativeOrder()); + } + + public ByteBuffer getBuffer() { + return bb; + } + + /** Return a slice of the current ByteBuffer starting at the + specified byte offset and extending the specified number of + bytes. Note that this method is not thread-safe with respect to + the other methods in this class. */ + public ByteBuffer slice(int byteOffset, int byteLength) { + bb.position(byteOffset); + bb.limit(byteOffset + byteLength); + ByteBuffer newBuf = bb.slice(); + bb.position(0); + bb.limit(bb.capacity()); + return newBuf; + } + + /** Retrieves the byte at the specified slot (byte offset). */ + public byte getByteAt(int slot) { + return bb.get(slot); + } + + /** Puts a byte at the specified slot (byte offset). */ + public void setByteAt(int slot, byte v) { + bb.put(slot, v); + } + + /** Retrieves the char at the specified slot (2-byte offset). */ + public char getCharAt(int slot) { + return charBuffer().get(slot); + } + + /** Puts a char at the specified slot (2-byte offset). */ + public void setCharAt(int slot, char v) { + charBuffer().put(slot, v); + } + + /** Retrieves the double at the specified slot (8-byte offset). */ + public double getDoubleAt(int slot) { + return doubleBuffer().get(slot); + } + + /** Puts a double at the specified slot (8-byte offset). */ + public void setDoubleAt(int slot, double v) { + doubleBuffer().put(slot, v); + } + + /** Retrieves the float at the specified slot (4-byte offset). */ + public float getFloatAt(int slot) { + return floatBuffer().get(slot); + } + + /** Puts a float at the specified slot (4-byte offset). */ + public void setFloatAt(int slot, float v) { + floatBuffer().put(slot, v); + } + + /** Retrieves the int at the specified slot (4-byte offset). */ + public int getIntAt(int slot) { + return intBuffer().get(slot); + } + + /** Puts a int at the specified slot (4-byte offset). */ + public void setIntAt(int slot, int v) { + intBuffer().put(slot, v); + } + + /** Retrieves the long at the specified slot (8-byte offset). */ + public long getLongAt(int slot) { + return longBuffer().get(slot); + } + + /** Puts a long at the specified slot (8-byte offset). */ + public void setLongAt(int slot, long v) { + longBuffer().put(slot, v); + } + + /** Retrieves the short at the specified slot (2-byte offset). */ + public short getShortAt(int slot) { + return shortBuffer().get(slot); + } + + /** Puts a short at the specified slot (2-byte offset). */ + public void setShortAt(int slot, short v) { + shortBuffer().put(slot, v); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private CharBuffer charBuffer() { + if (cb == null) { + cb = bb.asCharBuffer(); + } + return cb; + } + + private DoubleBuffer doubleBuffer() { + if (db == null) { + db = bb.asDoubleBuffer(); + } + return db; + } + + private FloatBuffer floatBuffer() { + if (fb == null) { + fb = bb.asFloatBuffer(); + } + return fb; + } + + private IntBuffer intBuffer() { + if (ib == null) { + ib = bb.asIntBuffer(); + } + return ib; + } + + private LongBuffer longBuffer() { + if (lb == null) { + lb = bb.asLongBuffer(); + } + return lb; + } + + private ShortBuffer shortBuffer() { + if (sb == null) { + sb = bb.asShortBuffer(); + } + return sb; + } +} |