diff options
Diffstat (limited to 'src/net/java/games/gluegen')
17 files changed, 491 insertions, 116 deletions
diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java index 45035f640..763fe6695 100644 --- a/src/net/java/games/gluegen/CMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -50,6 +50,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected static final CommentEmitter defaultCommentEmitter = new DefaultCommentEmitter(); + + protected static final String arrayResLength = "_array_res_length"; + protected static final String arrayRes = "_array_res"; + protected static final String arrayIdx = "_array_idx"; private MethodBinding binding; @@ -89,6 +93,12 @@ public class CMethodBindingEmitter extends FunctionEmitter */ private MessageFormat returnValueCapacityExpression = null; + /** + * Length of the returned array. Is ignored if + * binding.getJavaReturnType().isArray() is false. + */ + private MessageFormat returnValueLengthExpression = 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 @@ -168,6 +178,49 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Get the expression for the length of the returned array + */ + public final MessageFormat getReturnValueLengthExpression() + { + return returnValueLengthExpression; + } + + /** + * If this function returns an array, sets the expression for the + * length of the returned array. + * + * @param expression a MessageFormat which, when applied to an array + * of type String[] that contains each of the arguments names of the + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the length of the array returned from + * this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false + * </code> + */ + public final void setReturnValueLengthExpression(MessageFormat expression) + { + returnValueLengthExpression = expression; + + if (!binding.getJavaReturnType().isArray()) + { + throw new IllegalArgumentException( + "Cannot specify return value length for a method that does not " + + "return an array: \"" + binding + "\""); + } + } + + /** + * Returns the List of Strings containing declarations for temporary + * C variables to be assigned to after the underlying function call. + */ + public final List/*<String>*/ getTemporaryCVariableDeclarations() { + return temporaryCVariableDeclarations; + } + + /** * Sets up a List of Strings containing declarations for temporary C * variables to be assigned to after the underlying function call. A * null argument indicates that no manual declarations are to be made. @@ -177,6 +230,16 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Returns the List of Strings containing assignments for temporary + * C variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be + * made. + */ + public final List/*<String>*/ getTemporaryCVariableAssignments() { + return temporaryCVariableAssignments; + } + + /** * Sets up a List of Strings containing assignments for temporary C * variables which are made after the underlying function call. A * null argument indicates that no manual assignments are to be made. @@ -340,8 +403,6 @@ public class CMethodBindingEmitter extends FunctionEmitter 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(" "); @@ -349,24 +410,36 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(binding.getCSymbol().getReturnType().getName(true)); writer.println(" _res;"); if (javaReturnType.isArray()) { - writer.print(" int "); - writer.print(arrayResLength); - writer.println(";"); + if (javaReturnType.isNIOByteBufferArray()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + writer.print(" int "); + writer.print(arrayIdx); + writer.println(";"); + writer.print(" jobjectArray "); + writer.print(arrayRes); + writer.println(";"); + } else { + 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"); - } + 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(";"); + 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(";"); + } } } } @@ -568,6 +641,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -696,6 +774,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -826,35 +909,63 @@ public class CMethodBindingEmitter extends FunctionEmitter 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(";"); - + if (javaReturnType.isNIOByteBufferArray()) { + writer.println(" if (_res == NULL) return NULL;"); + if (returnValueLengthExpression == null) { + throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + + binding); + } + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNames) + ";"); + writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); + writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); + Type retType = binding.getCSymbol().getReturnType(); + Type baseType; + if (retType.isPointer()) { + baseType = retType.asPointer().getTargetType().asPointer().getTargetType(); + } else { + baseType = retType.asArray().getElementType().asPointer().getTargetType(); + } + int sz = baseType.getSize(); + if (sz < 0) + sz = 0; + writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + + ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], " + sz + "));"); + writer.println(" }"); + writer.println(" return " + arrayRes + ";"); + } else { + // 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(";"); + } } } } @@ -1034,6 +1145,11 @@ public class CMethodBindingEmitter extends FunctionEmitter throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); } } + else if (javaType.isArrayOfCompoundTypeWrappers()) + { + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } else { ptrTypeString = cType.getName(); diff --git a/src/net/java/games/gluegen/DebugEmitter.java b/src/net/java/games/gluegen/DebugEmitter.java index 677c3e7e6..8618e1576 100644 --- a/src/net/java/games/gluegen/DebugEmitter.java +++ b/src/net/java/games/gluegen/DebugEmitter.java @@ -95,8 +95,13 @@ public class DebugEmitter implements GlueEmitter { TypeDictionary structDictionary, Map canonMap) { } - public void emitStruct(CompoundType t) { - System.out.println("Referenced type \"" + t.getName() + "\""); + public void emitStruct(CompoundType t, String alternateName) { + String name = t.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + System.out.println("Referenced type \"" + name + "\""); } public void endStructs() {} } diff --git a/src/net/java/games/gluegen/GlueEmitter.java b/src/net/java/games/gluegen/GlueEmitter.java index 3d55474e0..e816ffc9d 100644 --- a/src/net/java/games/gluegen/GlueEmitter.java +++ b/src/net/java/games/gluegen/GlueEmitter.java @@ -93,7 +93,11 @@ public interface GlueEmitter { 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; + /** Emit glue code for the given CompoundType. alternateName is + provided when the CompoundType (e.g. "struct foo_t") has not + been typedefed to anything but the type of "pointer to struct + foo_t" has (e.g. "typedef struct foo_t {} *Foo"); in this case + alternateName would be set to Foo. */ + public void emitStruct(CompoundType t, String alternateName) throws Exception; public void endStructs() throws Exception; } diff --git a/src/net/java/games/gluegen/GlueGen.java b/src/net/java/games/gluegen/GlueGen.java index 374611c81..2e5f3421a 100644 --- a/src/net/java/games/gluegen/GlueGen.java +++ b/src/net/java/games/gluegen/GlueGen.java @@ -241,20 +241,35 @@ public class GlueGen implements GlueEmitterControls { 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()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.layoutStruct(t.asCompound()); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + emit.layoutStruct(c); + } } emit.endStructLayout(); // Emit structs emit.beginStructs(td, sd, headerParser.getCanonMap()); for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { - emit.emitStruct((CompoundType) iter.next()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.emitStruct(t.asCompound(), null); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p.getName()); + } } emit.endStructs(); diff --git a/src/net/java/games/gluegen/JavaConfiguration.java b/src/net/java/games/gluegen/JavaConfiguration.java index ecd953480..3950a7014 100644 --- a/src/net/java/games/gluegen/JavaConfiguration.java +++ b/src/net/java/games/gluegen/JavaConfiguration.java @@ -105,6 +105,7 @@ public class JavaConfiguration { private List/*<String>*/ forcedStructs = new ArrayList(); private Map/*<String,List<Integer>>*/ mirroredArgs = new HashMap(); private Map/*<String, String>*/ returnValueCapacities = new HashMap(); + private Map/*<String, String>*/ returnValueLengths = 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(); @@ -288,7 +289,9 @@ public class JavaConfiguration { /** 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. */ + name. Indicates that the given return value, which must be a + pointer to a CompoundType, is actually an array of the + CompoundType rather than a pointer to a single object. */ public String returnedArrayLength(String functionName) { return (String) returnedArrayLengths.get(functionName); } @@ -378,6 +381,13 @@ public class JavaConfiguration { return (String) returnValueCapacities.get(functionName); } + /** Returns a MessageFormat string of the C expression calculating + the length of the array being returned from a native method, or + null if no expression has been specified. */ + public String returnValueLength(String functionName) { + return (String) returnValueLengths.get(functionName); + } + /** Returns a List of Strings of expressions declaring temporary C variables in the glue code for the specified function. */ public List/*<String>*/ temporaryCVariableDeclarations(String functionName) { @@ -582,6 +592,10 @@ public class JavaConfiguration { readReturnValueCapacity(tok, filename, lineNo); // Warning: make sure delimiters are reset at the top of this loop // because ReturnValueCapacity changes them. + } else if (cmd.equalsIgnoreCase("ReturnValueLength")) { + readReturnValueLength(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnValueLength changes them. } else if (cmd.equalsIgnoreCase("Include")) { doInclude(tok, file, filename, lineNo); } else if (cmd.equalsIgnoreCase("IncludeAs")) { @@ -926,6 +940,18 @@ public class JavaConfiguration { } } + protected void readReturnValueLength(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnValueLengths.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnValueLength\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readTemporaryCVariableDeclaration(StringTokenizer tok, String filename, int lineNo) { try { String functionName = tok.nextToken(); diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java index 228b5aff2..26e2a4f23 100644 --- a/src/net/java/games/gluegen/JavaEmitter.java +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -451,18 +451,26 @@ public class JavaEmitter implements GlueEmitter { this.canonMap = canonMap; } - public void emitStruct(CompoundType structType) throws Exception { - if (structType.getName() == null) { + public void emitStruct(CompoundType structType, String alternateName) throws Exception { + String name = structType.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + if (name == null) { System.err.println("WARNING: skipping emission of unnamed struct \"" + structType + "\""); return; } - if (cfg.shouldIgnore(structType.getName())) { + if (cfg.shouldIgnore(name)) { return; } Type containingCType = canonicalize(new PointerType(machDesc.pointerSizeInBytes(), structType, 0)); JavaType containingType = typeToJavaType(containingCType, false); + if (!containingType.isCompoundTypeWrapper()) { + return; + } String containingTypeName = containingType.getName(); boolean needsNativeCode = false; @@ -473,7 +481,7 @@ public class JavaEmitter implements GlueEmitter { } } - String structClassPkg = cfg.packageForStruct(structType.getName()); + String structClassPkg = cfg.packageForStruct(name); PrintWriter writer = null; PrintWriter cWriter = null; try @@ -534,7 +542,7 @@ public class JavaEmitter implements GlueEmitter { 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 (!cfg.shouldIgnore(name + " " + field.getName())) { if (fieldType.isFunctionPointer()) { try { // Emit method call and associated native code @@ -566,7 +574,7 @@ public class JavaEmitter implements GlueEmitter { cWriter); cEmitter.emit(); } catch (Exception e) { - System.err.println("While processing field " + field + " of type " + structType.getName() + ":"); + System.err.println("While processing field " + field + " of type " + name + ":"); throw(e); } } else if (fieldType.isCompound()) { @@ -575,7 +583,7 @@ public class JavaEmitter implements GlueEmitter { // a name?) if (fieldType.getName() == null) { throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" + - field + "\" in type \"" + structType.getName() + "\")"); + field + "\" in type \"" + name + "\")"); } writer.println(); @@ -586,7 +594,7 @@ public class JavaEmitter implements GlueEmitter { // 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() + + System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + name + "\") not implemented yet"); } else { JavaType javaType = null; @@ -594,7 +602,7 @@ public class JavaEmitter implements GlueEmitter { javaType = typeToJavaType(fieldType, false); } catch (Exception e) { System.err.println("Error occurred while creating accessor for field \"" + - field.getName() + "\" in type \"" + structType.getName() + "\""); + field.getName() + "\" in type \"" + name + "\""); e.printStackTrace(); throw(e); } @@ -629,10 +637,6 @@ public class JavaEmitter implements GlueEmitter { 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 + @@ -663,6 +667,7 @@ public class JavaEmitter implements GlueEmitter { String bindingJavaClassName, PrintWriter output) { MessageFormat returnValueCapacityFormat = null; + MessageFormat returnValueLengthFormat = null; JavaType javaReturnType = binding.getJavaReturnType(); if (javaReturnType.isNIOBuffer()) { // See whether capacity has been specified @@ -670,6 +675,12 @@ public class JavaEmitter implements GlueEmitter { if (capacity != null) { returnValueCapacityFormat = new MessageFormat(capacity); } + } else if (javaReturnType.isArray()) { + // See whether length has been specified + String len = cfg.returnValueLength(binding.getName()); + if (len != null) { + returnValueLengthFormat = new MessageFormat(len); + } } CMethodBindingEmitter cEmitter; if (doingImplRoutine) { @@ -686,6 +697,9 @@ public class JavaEmitter implements GlueEmitter { if (returnValueCapacityFormat != null) { cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); } + if (returnValueLengthFormat != null) { + cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); + } cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); return cEmitter; @@ -758,12 +772,21 @@ public class JavaEmitter implements GlueEmitter { 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")) { + if (t.getName() != null && + t.getName().equals("jobject")) { return javaType(java.lang.Object.class); } - return JavaType.createForCStruct(cfg.renameJavaType(targetType.getName())); + String name = targetType.getName(); + if (name == null) { + // Try containing pointer type for any typedefs + name = t.getName(); + if (name == null) { + throw new RuntimeException("Couldn't find a proper type name for pointer type " + t); + } + } + + return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { throw new RuntimeException("Don't know how to convert pointer/array type \"" + t + "\""); @@ -775,15 +798,16 @@ public class JavaEmitter implements GlueEmitter { // Get the target type of the target type (targetType was computer earlier // as to be a pointer to the target type, so now we need to get its // target type) + Type bottomType; if (targetType.isPointer()) { // t is<type>**, targetType is <type>*, we need to get <type> - targetType = targetType.asPointer().getTargetType(); + bottomType = targetType.asPointer().getTargetType(); } else { // t is<type>[][], targetType is <type>[], we need to get <type> - targetType = targetType.asArray().getElementType(); + bottomType = targetType.asArray().getElementType(); } - if (targetType.isInt()) { - switch (targetType.getSize()) + if (bottomType.isInt()) { + switch (bottomType.getSize()) { case 1: return javaType(ArrayTypes.byteArrayArrayClass); // FIXME: handle 2,4,8-byte int types here @@ -793,10 +817,13 @@ public class JavaEmitter implements GlueEmitter { "Java type; Currently, the only supported depth=2 " + "pointer/array integer types are \"char**\" and \"char[][]\""); } + } else if (targetType.isPointer() && (targetType.pointerDepth() == 1)) { + // Array of pointers; convert as array of StructAccessors + return JavaType.createForCArray(targetType); } else { throw new RuntimeException( "Could not convert C type \"" + t + "\" " + - "to appropriate Java type; need to add support for " + + "to appropriate Java type; need to add more support for " + "depth=2 pointer/array types with non-integral target " + "types [debug info: targetType=\"" + targetType + "\"]"); } diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java index 003ea8643..850a2c701 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -122,7 +122,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter } protected String getReturnTypeString(boolean skipArray) { - if (skipArray || getReturnedArrayLengthExpression() == null) { + if (skipArray || (getReturnedArrayLengthExpression() == null && !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers())) { return binding.getJavaReturnType().getName(); } return binding.getJavaReturnType().getName() + "[]"; diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java index 2357e7c38..2ec2723bd 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java @@ -113,6 +113,9 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter returnType.isNIOByteBuffer()) { writer.println("ByteBuffer _res;"); writer.print(" _res = "); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("ByteBuffer[] _res;"); + writer.print(" _res = "); } else { writer.print("return "); } @@ -205,7 +208,7 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter 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(" _retarray[_count] = new " + getReturnTypeString(true) + "(_tmp);"); writer.println(" }"); writer.print (" return _retarray"); } @@ -213,6 +216,14 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter writer.println(";"); writer.println(" if (_res == null) return null;"); writer.print(" return _res.order(ByteOrder.nativeOrder())"); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println(";"); + writer.println(" if (_res == null) return null;"); + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); + writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_res[_count]);"); + writer.println(" }"); + writer.print (" return _retarray"); } writer.println(";"); } diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java index ac42aa0f1..ea771613e 100644 --- a/src/net/java/games/gluegen/JavaType.java +++ b/src/net/java/games/gluegen/JavaType.java @@ -39,6 +39,10 @@ package net.java.games.gluegen; +import java.nio.*; + +import net.java.games.gluegen.cgram.types.*; + /** * Describes a java-side representation of a type that is used to represent * the same data on both the Java-side and C-side during a JNI operation. Also @@ -47,8 +51,10 @@ package net.java.games.gluegen; 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 Type elementType; // Element type if this JavaType represents a C array private static JavaType nioBufferType; private static JavaType nioByteBufferType; + private static JavaType nioByteBufferArrayType; public boolean equals(Object arg) { if ((arg == null) || (!(arg instanceof JavaType))) { @@ -70,14 +76,28 @@ public class JavaType { return clazz.hashCode(); } + /** Creates a JavaType corresponding to the given Java type. This + can be used to represent arrays of primitive values or Strings; + the emitters understand how to perform proper conversion from + the corresponding C type. */ public static JavaType createForClass(Class clazz) { return new JavaType(clazz); } + /** Creates a JavaType corresponding to the specified C CompoundType + name; for example, if "Foo" is supplied, then this JavaType + represents a "Foo *" by way of a StructAccessor. */ public static JavaType createForCStruct(String name) { return new JavaType(name); } + /** Creates a JavaType corresponding to an array of the given + element type. This is used to represent arrays of "Foo **" which + should be mapped to Foo[] in Java. */ + public static JavaType createForCArray(Type elementType) { + return new JavaType(elementType); + } + public static JavaType createForVoidPointer() { return new JavaType(); } @@ -100,6 +120,14 @@ public class JavaType { return nioByteBufferType; } + public static JavaType forNIOByteBufferArrayClass() { + if (nioByteBufferArrayType == null) { + ByteBuffer[] tmp = new ByteBuffer[0]; + nioByteBufferArrayType = createForClass(tmp.getClass()); + } + return nioByteBufferArrayType; + } + /** * Returns the Java Class corresponding to this type. Returns null if this * object corresponds to a C "void*" type. @@ -119,6 +147,9 @@ public class JavaType { } return clazz.getName(); } + if (elementType != null) { + return elementType.getName(); + } return name; } @@ -151,6 +182,10 @@ public class JavaType { return "jobjectArray /*elements are String*/"; //return "jobjectArray"; } + else if (elementType == java.nio.ByteBuffer.class) + { + return "jobjectArray /*elements are ByteBuffer*/"; + } else if (elementType.isArray()) { // Type is array-of-arrays-of-something @@ -189,6 +224,10 @@ public class JavaType { return (clazz == java.nio.ByteBuffer.class); } + public boolean isNIOByteBufferArray() { + return (this == nioByteBufferArrayType); + } + public boolean isString() { return (clazz == java.lang.String.class); } @@ -205,17 +244,16 @@ public class JavaType { 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 isArrayOfCompoundTypeWrappers() { + return (elementType != null); + } + public boolean isVoidPointerType() { - return (clazz == null && name == null); + return (clazz == null && name == null && elementType == null); } public boolean isJNIEnv() { @@ -252,6 +290,11 @@ public class JavaType { this.name = name; } + /** Constructs a type representing an array of C pointers. */ + private JavaType(Type elementType) { + this.elementType = elementType; + } + /** * Default constructor; the type is initialized to the equivalent of a * C-language "void *". diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java index 9cdcd4b0b..b8f0eefdf 100644 --- a/src/net/java/games/gluegen/MethodBinding.java +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -174,7 +174,8 @@ public class MethodBinding { public boolean needsBody() { if (!computedNeedsBody) { if (javaReturnType.isCompoundTypeWrapper() || - javaReturnType.isNIOByteBuffer()) { + javaReturnType.isNIOByteBuffer() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { // Needs wrapping and/or setting of byte order (neither of // which can be done easily from native code) needsBody = true; @@ -204,6 +205,8 @@ public class MethodBinding { binding.thisPointerIndex = thisPointerIndex; if (javaReturnType.isCompoundTypeWrapper()) { binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); + } else if (javaReturnType.isArrayOfCompoundTypeWrappers()) { + binding.setJavaReturnType(JavaType.forNIOByteBufferArrayClass()); } else { binding.setJavaReturnType(javaReturnType); } diff --git a/src/net/java/games/gluegen/ReferencedStructs.java b/src/net/java/games/gluegen/ReferencedStructs.java index 1b3b75198..4a7a8aadf 100644 --- a/src/net/java/games/gluegen/ReferencedStructs.java +++ b/src/net/java/games/gluegen/ReferencedStructs.java @@ -54,7 +54,18 @@ public class ReferencedStructs implements TypeVisitor { } public void visitType(Type t) { - if (t.isCompound()) { + if (t.isPointer()) { + PointerType p = t.asPointer(); + if (p.hasTypedefedName()) { + CompoundType c = p.getTargetType().asCompound(); + if (c != null && c.getName() == null) { + // This otherwise-unnamed CompoundType is referred to by a + // PointerType that has a typedef name. Assume that it is + // referred to in the glue code and emit it. + results.add(p); + } + } + } else if (t.isCompound()) { results.add(t); } } diff --git a/src/net/java/games/gluegen/cgram/types/PointerType.java b/src/net/java/games/gluegen/cgram/types/PointerType.java index 62bfe9c1a..64acbade4 100644 --- a/src/net/java/games/gluegen/cgram/types/PointerType.java +++ b/src/net/java/games/gluegen/cgram/types/PointerType.java @@ -100,6 +100,10 @@ public class PointerType extends Type { } } + public boolean hasTypedefedName() { + return hasTypedefedName; + } + public PointerType asPointer() { return this; } public Type getTargetType() { return targetType; } diff --git a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java index 47476a54c..c6ac77c7b 100644 --- a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java +++ b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java @@ -209,7 +209,7 @@ public class BuildComposablePipeline { output.print(" public "); output.print(' '); - output.print(m.getReturnType().getName()); + output.print(JavaType.createForClass(m.getReturnType()).getName()); output.print(' '); output.print(m.getName()); output.print('('); diff --git a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java index cd0554dc3..2a1559e52 100644 --- a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java @@ -68,6 +68,15 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter methodToWrap.getIsJavaMethodStatic(), methodToWrap.getDefaultOutput() ); + + if (methodToWrap.getReturnValueCapacityExpression() != null) { + setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); + } + if (methodToWrap.getReturnValueLengthExpression() != null) { + setReturnValueLengthExpression(methodToWrap.getReturnValueLengthExpression()); + } + setTemporaryCVariableDeclarations(methodToWrap.getTemporaryCVariableDeclarations()); + setTemporaryCVariableAssignments (methodToWrap.getTemporaryCVariableAssignments ()); setCommentEmitter(defaultCommentEmitter); } @@ -192,6 +201,13 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter writer.println(");"); } + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(super.jniMangle(binding)); + jniMangle(Long.TYPE, buf); + return buf.toString(); + } + /** This class emits the comment for the wrapper method */ private static class CGLPAWrapperCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java index 27cc07e2c..f6e26fd3b 100644 --- a/src/net/java/games/gluegen/opengl/GLEmitter.java +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -44,6 +44,7 @@ import java.text.MessageFormat; import java.util.*; import net.java.games.gluegen.*; import net.java.games.gluegen.cgram.types.*; +import net.java.games.gluegen.runtime.*; /** * A subclass of JavaEmitter that modifies the normal emission of C and Java @@ -52,11 +53,12 @@ import net.java.games.gluegen.cgram.types.*; */ public class GLEmitter extends JavaEmitter { - public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; + public static final String PROCADDRESS_VAR_PREFIX = ProcAddressHelper.PROCADDRESS_VAR_PREFIX; protected static final String WRAP_PREFIX = "dispatch_"; private TypeDictionary typedefDictionary; private PrintWriter tableWriter; - private String tableClassName = "ProcAddressTable"; + private String tableClassPackage; + private String tableClassName; private int numProcAddressEntries; public void beginFunctions(TypeDictionary typedefDictionary, @@ -70,7 +72,7 @@ public class GLEmitter extends JavaEmitter cWriter().println(); } - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { beginGLProcAddressTable(); } @@ -79,7 +81,7 @@ public class GLEmitter extends JavaEmitter public void endFunctions() throws Exception { - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { endGLProcAddressTable(); } @@ -118,7 +120,7 @@ public class GLEmitter extends JavaEmitter // 9 is default # expanded bindings for void* ArrayList modifiedEmitters = new ArrayList(9); - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { // emit an entry in the GL proc address table for this method. emitGLProcAddressTableEntryForSymbol(sym); @@ -185,7 +187,7 @@ public class GLEmitter extends JavaEmitter return null; return baseJavaEmitter; } - return new JavaGLPAWrapperEmitter(baseJavaEmitter); + return new JavaGLPAWrapperEmitter(baseJavaEmitter, getGLConfig().getProcAddressTableExpr()); } private CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) @@ -205,11 +207,17 @@ public class GLEmitter extends JavaEmitter { String symName = sym.getName(); + GLConfiguration config = getGLConfig(); + // 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 (config.skipProcAddressGen(symName)) { + shouldWrap = false; + } if (!shouldWrap) { @@ -221,26 +229,27 @@ public class GLEmitter extends JavaEmitter private void beginGLProcAddressTable() throws Exception { - String implPackageName = getImplPackageName(); + tableClassPackage = getGLConfig().tableClassPackage(); + tableClassName = getGLConfig().tableClassName(); + + // Table defaults to going into the impl directory unless otherwise overridden + String implPackageName = tableClassPackage; + if (implPackageName == null) { + implPackageName = getImplPackageName(); + } String jImplRoot = getJavaOutputDir() + File.separator + CodeGenUtils.packageAsPath(implPackageName); - // 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"); + tableWriter = openFile(jImplRoot + 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("package " + implPackageName + ";"); + tableWriter.println(); + for (Iterator iter = getConfig().imports().iterator(); iter.hasNext(); ) { + tableWriter.println("import " + ((String) iter.next()) + ";"); + } tableWriter.println(); tableWriter.println("/**"); tableWriter.println(" * This table is a cache of the native pointers to OpenGL extension"); @@ -260,9 +269,6 @@ public class GLEmitter extends JavaEmitter 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(" /**"); @@ -301,30 +307,69 @@ public class GLEmitter extends JavaEmitter private void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) { - tableWriter.print(" public static long "); + tableWriter.print(" public long "); tableWriter.print(PROCADDRESS_VAR_PREFIX); tableWriter.print(cFunc.getName()); tableWriter.println(";"); ++numProcAddressEntries; } + private GLConfiguration getGLConfig() { + return (GLConfiguration) getConfig(); + } + protected static class GLConfiguration extends JavaConfiguration { private boolean emitProcAddressTable = false; - + private String tableClassPackage; + private String tableClassName = "ProcAddressTable"; + private Set/*<String>*/ skipProcAddressGen = new HashSet(); + private String getProcAddressTableExpr = "context.getGLProcAddressTable()"; + protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { if (cmd.equalsIgnoreCase("EmitProcAddressTable")) { emitProcAddressTable = readBoolean("EmitProcAddressTable", tok, filename, lineNo).booleanValue(); } + else if (cmd.equalsIgnoreCase("ProcAddressTablePackage")) + { + tableClassPackage = readString("ProcAddressTablePackage", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("ProcAddressTableClassName")) + { + tableClassName = readString("ProcAddressTableClassName", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("SkipProcAddressGen")) + { + String sym = readString("SkipProcAddressGen", tok, filename, lineNo); + skipProcAddressGen.add(sym); + } + else if (cmd.equalsIgnoreCase("GetProcAddressTableExpr")) + { + getProcAddressTableExpr = readGetProcAddressTableExpr(tok, filename, lineNo); + } else { super.dispatch(cmd,tok,file,filename,lineNo); } } - public boolean emitProcAddressTable() { return emitProcAddressTable; } + protected String readGetProcAddressTableExpr(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + return restOfLine.trim(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"GetProcAddressTableExpr\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + public boolean emitProcAddressTable() { return emitProcAddressTable; } + public String tableClassPackage() { return tableClassPackage; } + public String tableClassName() { return tableClassName; } + public boolean skipProcAddressGen (String name) { return skipProcAddressGen.contains(name); } + public String getProcAddressTableExpr() { return getProcAddressTableExpr; } } // end class GLConfiguration } diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java index e0a098f2a..c3b17f6cb 100644 --- a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -50,10 +50,12 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter new WrappedMethodCommentEmitter(); private JavaMethodBindingEmitter emitterBeingWrapped; + private String getProcAddressTableExpr; - public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap) + public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap, String getProcAddressTableExpr) { super(methodToWrap.getBinding(), methodToWrap.getDefaultOutput(), methodToWrap.getRuntimeExceptionType()); + this.getProcAddressTableExpr = getProcAddressTableExpr; if (methodToWrap.getBinding().hasContainingType()) { @@ -119,6 +121,9 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter // Now make our binding use the original access of the wrapped method this.addModifier(origAccess); + if (emitterBeingWrapped.hasModifier(STATIC)) { + this.addModifier(STATIC); + } } protected boolean needsBody() { @@ -152,9 +157,7 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter String procAddressVariable = GLEmitter.PROCADDRESS_VAR_PREFIX + wrappedBinding.getName(); - writer.print(" final long addr = context.getGLProcAddressTable()."); - writer.print(procAddressVariable); - writer.println(';'); + writer.println(" final long addr = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (addr == 0) {"); writer.println(" throw new GLException(\"Method \\\"" + binding.getName() + "\\\" not available\");"); writer.println(" }"); diff --git a/src/net/java/games/gluegen/runtime/ProcAddressHelper.java b/src/net/java/games/gluegen/runtime/ProcAddressHelper.java new file mode 100644 index 000000000..fec0eb366 --- /dev/null +++ b/src/net/java/games/gluegen/runtime/ProcAddressHelper.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.runtime; + +/** Contains constants used in glue code generation. */ + +public class ProcAddressHelper { + public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; +} |