aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/gluegen/JavaEmitter.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2010-11-06 23:13:39 +0100
committerSven Gothel <[email protected]>2010-11-06 23:13:39 +0100
commit6f2d046c8d532db94f6af5003e341104d5bf4aff (patch)
tree723c31b8f9c1097ae48486acbf68e4e06fab2517 /src/java/com/jogamp/gluegen/JavaEmitter.java
parentec6d61f4597af32c22319c4bda3c9dd9ab80bf25 (diff)
Renamed com.sun.gluegen -> com.jogamp.gluegen
Diffstat (limited to 'src/java/com/jogamp/gluegen/JavaEmitter.java')
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java1950
1 files changed, 1950 insertions, 0 deletions
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
new file mode 100644
index 0000000..250da79
--- /dev/null
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -0,0 +1,1950 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.jogamp.gluegen;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.os.DynamicLookupHelper;
+import java.io.*;
+import java.util.*;
+import java.text.MessageFormat;
+
+import com.jogamp.gluegen.cgram.types.*;
+import java.nio.Buffer;
+import java.util.logging.Logger;
+
+import static java.util.logging.Level.*;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*;
+
+// PROBLEMS:
+// - what if something returns 'const int *'? Could we
+// return an IntBuffer that has read-only behavior? Or do we copy the array
+// (but we don't know its size!). What do we do if it returns a non-const
+// int*? Should the user be allowed to write back to the returned pointer?
+//
+// - Non-const array types must be properly released with JNI_COMMIT
+// in order to see side effects if the array was copied.
+
+
+public class JavaEmitter implements GlueEmitter {
+
+ private StructLayout layout;
+ private TypeDictionary typedefDictionary;
+ private TypeDictionary structDictionary;
+ private Map<Type, Type> canonMap;
+ protected JavaConfiguration cfg;
+
+ /**
+ * Style of code emission. Can emit everything into one class
+ * (AllStatic), separate interface and implementing classes
+ * (InterfaceAndImpl), only the interface (InterfaceOnly), or only
+ * the implementation (ImplOnly).
+ */
+ public enum EmissionStyle {AllStatic, InterfaceAndImpl, InterfaceOnly, ImplOnly};
+
+ /**
+ * Access control for emitted Java methods.
+ */
+ public enum MethodAccess {PUBLIC, PROTECTED, PRIVATE, PACKAGE_PRIVATE, PUBLIC_ABSTRACT}
+
+ private PrintWriter javaWriter; // Emits either interface or, in AllStatic mode, everything
+ private PrintWriter javaImplWriter; // Only used in non-AllStatic modes for impl class
+ private PrintWriter cWriter;
+ private MachineDescription machDesc32;
+ private MachineDescription machDesc64;
+
+ protected final static Logger LOG = Logger.getLogger(JavaEmitter.class.getPackage().getName());
+
+ public void readConfigurationFile(String filename) throws Exception {
+ cfg = createConfig();
+ cfg.read(filename);
+ }
+
+ public void setMachineDescription(MachineDescription md32, MachineDescription md64) {
+
+ if ((md32 == null) && (md64 == null)) {
+ throw new RuntimeException("Must specify at least one MachineDescription");
+ }
+
+ machDesc32 = md32;
+ machDesc64 = md64;
+ }
+
+ class ConstantRenamer implements SymbolFilter {
+
+ private List<ConstantDefinition> constants;
+
+ public void filterSymbols(List<ConstantDefinition> constants, List<FunctionSymbol> functions) {
+ this.constants = constants;
+ doWork();
+ }
+
+ public List<ConstantDefinition> getConstants() {
+ return constants;
+ }
+
+ public List<FunctionSymbol> getFunctions() {
+ return null;
+ }
+
+ private void doWork() {
+ List<ConstantDefinition> newConstants = new ArrayList<ConstantDefinition>();
+ JavaConfiguration cfg = getConfig();
+ for (ConstantDefinition def : constants) {
+ def.rename(cfg.getJavaSymbolRename(def.getName()));
+ newConstants.add(def);
+ }
+ constants = newConstants;
+ }
+ }
+
+ public void beginEmission(GlueEmitterControls controls) throws IOException {
+
+ // Request emission of any structs requested
+ for (String structs : cfg.forcedStructs()) {
+ controls.forceStructEmission(structs);
+ }
+
+ if (!cfg.structsOnly()) {
+ try {
+ openWriters();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to open files for writing", e);
+ }
+ emitAllFileHeaders();
+
+ // Handle renaming of constants
+ controls.runSymbolFilter(new ConstantRenamer());
+ }
+ }
+
+ public void endEmission() {
+ if (!cfg.structsOnly()) {
+ emitAllFileFooters();
+
+ try {
+ closeWriters();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to close open files", e);
+ }
+ }
+ }
+
+ public void beginDefines() throws Exception {
+ if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) {
+ javaWriter().println();
+ }
+ }
+
+ protected static int getJavaRadix(String name, String value) {
+ // FIXME: need to handle when type specifier is in last char (e.g.,
+ // "1.0d or 2759L", because parseXXX() methods don't allow the type
+ // specifier character in the string.
+ //
+ //char lastChar = value.charAt(value.length()-1);
+
+ try {
+ // see if it's a long or int
+ int radix;
+ String parseValue;
+ // FIXME: are you allowed to specify hex/octal constants with
+ // negation, e.g. "-0xFF" or "-056"? If so, need to modify the
+ // following "if(..)" checks and parseValue computation
+ if (value.startsWith("0x") || value.startsWith("0X")) {
+ radix = 16;
+ parseValue = value.substring(2);
+ }
+ else if (value.startsWith("0") && value.length() > 1) {
+ // TODO: is "0" the prefix in C to indicate octal???
+ radix = 8;
+ parseValue = value.substring(1);
+ }
+ else {
+ radix = 10;
+ parseValue = value;
+ }
+ //System.err.println("parsing " + value + " as long w/ radix " + radix);
+ long longVal = Long.parseLong(parseValue, radix);
+ return radix;
+ } catch (NumberFormatException e) {
+ try {
+ // see if it's a double or float
+ double dVal = Double.parseDouble(value);
+ return 10;
+ } catch (NumberFormatException e2) {
+ throw new RuntimeException(
+ "Cannot emit define \""+name+"\": value \""+value+
+ "\" cannot be assigned to a int, long, float, or double", e2);
+ }
+ }
+ }
+
+ protected static Object getJavaValue(String name, String value) {
+
+ // "calculates" the result type of a simple expression
+ // example: (2+3)-(2.0f-3.0) -> Double
+ // example: (1 << 2) -> Integer
+
+ Scanner scanner = new Scanner(value).useDelimiter("[+-/*/></(/)]");
+
+ Object resultType = null;
+
+ while (scanner.hasNext()) {
+
+ String t = scanner.next().trim();
+
+ if(0<t.length()) {
+ Object type = getJavaValue2(name, t);
+
+ //fast path
+ if(type instanceof Double)
+ return type;
+
+ if(resultType != null) {
+
+ if(resultType instanceof Integer) {
+ if(type instanceof Long || type instanceof Float || type instanceof Double)
+ resultType = type;
+ }else if(resultType instanceof Long) {
+ if(type instanceof Float || type instanceof Double)
+ resultType = type;
+ }else if(resultType instanceof Float) {
+ if(type instanceof Float)
+ resultType = type;
+ }
+ }else{
+ resultType = type;
+ }
+
+ //fast path
+ if(resultType instanceof Double)
+ return type;
+ }
+ }
+
+ return resultType;
+ }
+
+ private static Object getJavaValue2(String name, String value) {
+ // FIXME: need to handle when type specifier is in last char (e.g.,
+ // "1.0d or 2759L", because parseXXX() methods don't allow the type
+ // specifier character in the string.
+ //
+ char lastChar = value.charAt(value.length()-1);
+
+ try {
+ // see if it's a long or int
+ int radix;
+ String parseValue;
+ // FIXME: are you allowed to specify hex/octal constants with
+ // negation, e.g. "-0xFF" or "-056"? If so, need to modify the
+ // following "if(..)" checks and parseValue computation
+ if (value.startsWith("0x") || value.startsWith("0X")) {
+ radix = 16;
+ parseValue = value.substring(2);
+ } else if (value.startsWith("0") && value.length() > 1) {
+ // TODO: is "0" the prefix in C to indicate octal???
+ radix = 8;
+ parseValue = value.substring(1);
+ } else {
+ radix = 10;
+ parseValue = value;
+ }
+ if(lastChar == 'u' || lastChar == 'U') {
+ parseValue = parseValue.substring(0, parseValue.length()-1);
+ }
+
+ //System.err.println("parsing " + value + " as long w/ radix " + radix);
+ long longVal = Long.parseLong(parseValue, radix);
+ // if constant is small enough, store it as an int instead of a long
+ if (longVal > Integer.MIN_VALUE && longVal < Integer.MAX_VALUE) {
+ return (int)longVal;
+ }
+ return longVal;
+
+ } catch (NumberFormatException e) {
+ try {
+ // see if it's a double or float
+ double dVal = Double.parseDouble(value);
+ double absVal = Math.abs(dVal);
+ // if constant is small enough, store it as a float instead of a double
+ if (absVal < Float.MIN_VALUE || absVal > Float.MAX_VALUE) {
+ return new Double(dVal);
+ }
+ return new Float((float) dVal);
+ } catch (NumberFormatException e2) {
+ throw new RuntimeException(
+ "Cannot emit define \""+name+"\": value \""+value+
+ "\" cannot be assigned to a int, long, float, or double", e2);
+ }
+ }
+ }
+
+
+ protected static String getJavaType(String name, String value) {
+ Object oval = getJavaValue(name, value);
+ return getJavaType(name, oval);
+ }
+
+ protected static String getJavaType(String name, Object oval) {
+ if(oval instanceof Integer) {
+ return "int";
+ } else if(oval instanceof Long) {
+ return "long";
+ } else if(oval instanceof Float) {
+ return "float";
+ } else if(oval instanceof Double) {
+ return "double";
+ }
+
+ throw new RuntimeException(
+ "Cannot emit define (2) \""+name+"\": value \""+oval+
+ "\" cannot be assigned to a int, long, float, or double");
+ }
+
+ public void emitDefine(ConstantDefinition def, String optionalComment) throws Exception {
+
+ if (cfg.allStatic() || cfg.emitInterface()) {
+ // TODO: Some defines (e.g., GL_DOUBLE_EXT in gl.h) are defined in terms
+ // of other defines -- should we emit them as references to the original
+ // define (not even sure if the lexer supports this)? Right now they're
+ // emitted as the numeric value of the original definition. If we decide
+ // emit them as references we'll also have to emit them in the correct
+ // order. It's probably not an issue right now because the emitter
+ // currently only emits only numeric defines -- if it handled #define'd
+ // objects it would make a bigger difference.
+
+ String name = def.getName();
+ String value = def.getValue();
+
+ if (!cfg.shouldIgnoreInInterface(name)) {
+ String type = getJavaType(name, value);
+ if (optionalComment != null && optionalComment.length() != 0) {
+ javaWriter().println(" /** " + optionalComment + " */");
+ }
+ String suffix = "";
+ if(!value.endsWith(")")) {
+ if (type.equals("float") && !value.endsWith("f")) {
+ suffix = "f";
+ }else if(value.endsWith("u") || value.endsWith("U")) {
+ value = value.substring(0, value.length()-1);
+ }
+ }
+
+ javaWriter().println(" public static final " + type + " " + name + " = " + value + suffix + ";");
+ }
+ }
+ }
+
+ public void endDefines() throws Exception {
+ }
+
+ public void beginFunctions(TypeDictionary typedefDictionary,
+ TypeDictionary structDictionary,
+ Map<Type, Type> canonMap) throws Exception {
+
+ this.typedefDictionary = typedefDictionary;
+ this.structDictionary = structDictionary;
+ this.canonMap = canonMap;
+
+ if ((cfg.allStatic() || cfg.emitInterface()) && !cfg.structsOnly()) {
+ javaWriter().println();
+ }
+ }
+
+ public Iterator<FunctionSymbol> emitFunctions(List<FunctionSymbol> originalCFunctions) throws Exception {
+
+ // Sometimes headers will have the same function prototype twice, once
+ // with the argument names and once without. We'll remember the signatures
+ // we've already processed we don't generate duplicate bindings.
+ //
+ // Note: this code assumes that on the equals() method in FunctionSymbol
+ // only considers function name and argument types (i.e., it does not
+ // consider argument *names*) when comparing FunctionSymbols for equality
+ Set<FunctionSymbol> funcsToBindSet = new HashSet<FunctionSymbol>(100);
+ for (FunctionSymbol cFunc : originalCFunctions) {
+ if (!funcsToBindSet.contains(cFunc)) {
+ funcsToBindSet.add(cFunc);
+ }
+ }
+
+ // validateFunctionsToBind(funcsToBindSet);
+
+ ArrayList<FunctionSymbol> funcsToBind = new ArrayList<FunctionSymbol>(funcsToBindSet);
+ // sort functions to make them easier to find in native code
+ Collections.sort(funcsToBind, new Comparator<FunctionSymbol>() {
+ public int compare(FunctionSymbol o1, FunctionSymbol o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+
+ // Bind all the C funcs to Java methods
+ HashSet<MethodBinding> methodBindingSet = new HashSet<MethodBinding>();
+ ArrayList<FunctionEmitter> methodBindingEmitters = new ArrayList<FunctionEmitter>(2*funcsToBind.size());
+ for (FunctionSymbol cFunc : funcsToBind) {
+ // Check to see whether this function should be ignored
+ if (!cfg.shouldIgnoreInImpl(cFunc.getName())) {
+ methodBindingEmitters.addAll(generateMethodBindingEmitters(methodBindingSet, cFunc));
+ }
+
+ }
+
+ // Emit all the methods
+ for (FunctionEmitter emitter : methodBindingEmitters) {
+ try {
+ if (!emitter.isInterface() || !cfg.shouldIgnoreInInterface(emitter.getName())) {
+ emitter.emit();
+ emitter.getDefaultOutput().println(); // put newline after method body
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Error while emitting binding for \"" + emitter.getName() + "\"", e);
+ }
+ }
+
+ // Return the list of FunctionSymbols that we generated gluecode for
+ return funcsToBind.iterator();
+ }
+
+ /**
+ * Create the object that will read and store configuration information for
+ * this JavaEmitter.
+ */
+ protected JavaConfiguration createConfig() {
+ return new JavaConfiguration();
+ }
+
+ /**
+ * Get the configuration information for this JavaEmitter.
+ */
+ protected JavaConfiguration getConfig() {
+ return cfg;
+ }
+
+ /**
+ * Generates the public emitters for this MethodBinding which will
+ * produce either simply signatures (for the interface class, if
+ * any) or function definitions with or without a body (depending on
+ * whether or not the implementing function can go directly to
+ * native code because it doesn't need any processing of the
+ * outgoing arguments).
+ */
+ protected void generatePublicEmitters(MethodBinding binding,
+ List<FunctionEmitter> allEmitters,
+ boolean signatureOnly) {
+ PrintWriter writer = ((signatureOnly || cfg.allStatic()) ? javaWriter() : javaImplWriter());
+
+ if (cfg.manuallyImplement(binding.getName()) && !signatureOnly) {
+ // We only generate signatures for manually-implemented methods;
+ // user provides the implementation
+ return;
+ }
+
+ MethodAccess accessControl = cfg.accessControl(binding.getName());
+ // We should not emit anything except public APIs into interfaces
+ if (signatureOnly && (accessControl != PUBLIC)) {
+ return;
+ }
+
+ // It's possible we may not need a body even if signatureOnly is
+ // set to false; for example, if the routine doesn't take any
+ // arrays or buffers as arguments
+ boolean isUnimplemented = cfg.isUnimplemented(binding.getName());
+ List<String> prologue = cfg.javaPrologueForMethod(binding, false, false);
+ List<String> epilogue = cfg.javaEpilogueForMethod(binding, false, false);
+ boolean needsBody = (isUnimplemented ||
+ (binding.needsNIOWrappingOrUnwrapping() ||
+ binding.signatureUsesJavaPrimitiveArrays()) ||
+ (prologue != null) ||
+ (epilogue != null));
+
+ JavaMethodBindingEmitter emitter =
+ new JavaMethodBindingEmitter(binding,
+ writer,
+ cfg.runtimeExceptionType(),
+ cfg.unsupportedExceptionType(),
+ !signatureOnly && needsBody,
+ cfg.tagNativeBinding(),
+ false,
+ cfg.nioDirectOnly(binding.getName()),
+ false,
+ false,
+ false,
+ isUnimplemented,
+ signatureOnly,
+ cfg);
+ switch (accessControl) {
+ case PUBLIC: emitter.addModifier(JavaMethodBindingEmitter.PUBLIC); break;
+ case PROTECTED: emitter.addModifier(JavaMethodBindingEmitter.PROTECTED); break;
+ case PRIVATE: emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); break;
+ default: break; // package-private adds no modifiers
+ }
+ if (cfg.allStatic()) {
+ emitter.addModifier(JavaMethodBindingEmitter.STATIC);
+ }
+ if (!isUnimplemented && !needsBody && !signatureOnly) {
+ emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
+ }
+ emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ emitter.setPrologue(prologue);
+ emitter.setEpilogue(epilogue);
+ allEmitters.add(emitter);
+ }
+
+ /**
+ * Generates the private emitters for this MethodBinding. On the
+ * Java side these will simply produce signatures for native
+ * methods. On the C side these will create the emitters which will
+ * write the JNI code to interface to the functions. We need to be
+ * careful to make the signatures all match up and not produce too
+ * many emitters which would lead to compilation errors from
+ * creating duplicated methods / functions.
+ */
+ protected void generatePrivateEmitters(MethodBinding binding,
+ List<FunctionEmitter> allEmitters) {
+ if (cfg.manuallyImplement(binding.getName())) {
+ // Don't produce emitters for the implementation class
+ return;
+ }
+
+ boolean hasPrologueOrEpilogue =
+ ((cfg.javaPrologueForMethod(binding, false, false) != null) ||
+ (cfg.javaEpilogueForMethod(binding, false, false) != null));
+
+ // If we already generated a public native entry point for this
+ // method, don't emit another one
+ if (!cfg.isUnimplemented(binding.getName()) &&
+ (binding.needsNIOWrappingOrUnwrapping() ||
+ binding.signatureUsesJavaPrimitiveArrays() ||
+ hasPrologueOrEpilogue)) {
+ PrintWriter writer = (cfg.allStatic() ? javaWriter() : javaImplWriter());
+
+ // If the binding uses primitive arrays, we are going to emit
+ // the private native entry point for it along with the version
+ // taking only NIO buffers
+ if (!binding.signatureUsesJavaPrimitiveArrays()) {
+ // (Always) emit the entry point taking only direct buffers
+ JavaMethodBindingEmitter emitter =
+ new JavaMethodBindingEmitter(binding,
+ writer,
+ cfg.runtimeExceptionType(),
+ cfg.unsupportedExceptionType(),
+ false,
+ cfg.tagNativeBinding(),
+ true,
+ cfg.nioDirectOnly(binding.getName()),
+ true,
+ true,
+ false,
+ false,
+ false,
+ cfg);
+ emitter.addModifier(JavaMethodBindingEmitter.PRIVATE);
+ if (cfg.allStatic()) {
+ emitter.addModifier(JavaMethodBindingEmitter.STATIC);
+ }
+ emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
+ emitter.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName()));
+ allEmitters.add(emitter);
+ }
+ }
+
+ // Now generate the C emitter(s). We need to produce one for every
+ // Java native entry point (public or private). The only
+ // situations where we don't produce one are (a) when the method
+ // is unimplemented, and (b) when the signature contains primitive
+ // arrays, since the latter is handled by the method binding
+ // variant taking only NIO Buffers.
+ if (!cfg.isUnimplemented(binding.getName()) &&
+ !binding.signatureUsesJavaPrimitiveArrays()) {
+ CMethodBindingEmitter cEmitter;
+ // Generate a binding without mixed access (NIO-direct, -indirect, array)
+ cEmitter =
+ new CMethodBindingEmitter(binding,
+ cWriter(),
+ cfg.implPackageName(),
+ cfg.implClassName(),
+ true, // NOTE: we always disambiguate with a suffix now, so this is optional
+ cfg.allStatic(),
+ (binding.needsNIOWrappingOrUnwrapping() || hasPrologueOrEpilogue),
+ !cfg.nioDirectOnly(binding.getName()),
+ machDesc64);
+ prepCEmitter(binding, cEmitter);
+ allEmitters.add(cEmitter);
+ }
+ }
+
+ protected void prepCEmitter(MethodBinding binding, CMethodBindingEmitter cEmitter)
+ {
+ // See whether we need an expression to help calculate the
+ // length of any return type
+ JavaType javaReturnType = binding.getJavaReturnType();
+ if (javaReturnType.isNIOBuffer() ||
+ javaReturnType.isCompoundTypeWrapper()) {
+ // See whether capacity has been specified
+ String capacity = cfg.returnValueCapacity(binding.getName());
+ if (capacity != null) {
+ cEmitter.setReturnValueCapacityExpression( new MessageFormat(capacity) );
+ }
+ } else if (javaReturnType.isArray() ||
+ javaReturnType.isArrayOfCompoundTypeWrappers()) {
+ // NOTE: adding a check here because the CMethodBindingEmitter
+ // also doesn't yet handle returning scalar arrays. In order
+ // to implement this, return the type as a Buffer instead
+ // (i.e., IntBuffer, FloatBuffer) and add code as necessary.
+ if (javaReturnType.isPrimitiveArray()) {
+ throw new RuntimeException("Primitive array return types not yet supported");
+ }
+
+ // See whether length has been specified
+ String len = cfg.returnValueLength(binding.getName());
+ if (len != null) {
+ cEmitter.setReturnValueLengthExpression( new MessageFormat(len) );
+ }
+ }
+ cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName()));
+ cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName()));
+ }
+
+ /**
+ * Generate all appropriate Java bindings for the specified C function
+ * symbols.
+ */
+ protected List<? extends FunctionEmitter> generateMethodBindingEmitters(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception {
+
+ ArrayList<FunctionEmitter> allEmitters = new ArrayList<FunctionEmitter>();
+
+ try {
+ // Get Java binding for the function
+ MethodBinding mb = bindFunction(sym, null, null, machDesc64);
+
+ // JavaTypes representing C pointers in the initial
+ // MethodBinding have not been lowered yet to concrete types
+ List<MethodBinding> bindings = expandMethodBinding(mb);
+
+ for (MethodBinding binding : bindings) {
+
+ if(!methodBindingSet.add(binding)) {
+ // skip .. already exisiting binding ..
+ continue;
+ }
+
+ if (cfg.allStatic() && binding.hasContainingType()) {
+ // This should not currently happen since structs are emitted using a different mechanism
+ throw new IllegalArgumentException("Cannot create binding in AllStatic mode because method has containing type: \"" +
+ binding + "\"");
+ }
+
+ // The structure of the generated glue code looks something like this:
+ // Simple method (no arrays, void pointers, etc.):
+ // Interface class:
+ // public void fooMethod();
+ // Implementation class:
+ // public native void fooMethod();
+ //
+ // Method taking void* argument:
+ // Interface class:
+ // public void fooMethod(Buffer arg);
+ // Implementation class:
+ // public void fooMethod(Buffer arg) {
+ // ... bounds checks, etc. ...
+ //
+ // boolean arg_direct = arg != null && Buffers.isDirect(arg);
+ //
+ // fooMethod0(arg_direct?arg:Buffers.getArray(arg),
+ // arg_direct?Buffers.getDirectBufferByteOffset(arg):Buffers.getIndirectBufferByteOffset(arg),
+ // arg_direct,
+ // ... );
+ // }
+ // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...);
+ //
+ // Method taking primitive array argument:
+ // Interface class:
+ // public void fooMethod(int[] arg, int arg_offset);
+ // public void fooMethod(IntBuffer arg);
+ // Implementing class:
+ // public void fooMethod(int[] arg, int arg_offset) {
+ // ... range checks, etc. ...
+ // fooMethod1(arg, SIZEOF_INT * arg_offset);
+ // }
+ // public void fooMethod(IntBuffer arg) {
+ // ... bounds checks, etc. ...
+ //
+ // boolean arg_direct = BufferFactory.isDirect(arg);
+ //
+ // fooMethod1(arg_direct?arg:BufferFactory.getArray(arg),
+ // arg_direct?BufferFactory.getDirectBufferByteOffset(arg):BufferFactory.getIndirectBufferByteOffset(arg),
+ // arg_direct,
+ // ... );
+ // }
+ // private native void fooMethod1(Object arg, int arg_byte_offset, boolean arg_is_direct, ...);
+ //
+ // Note in particular that the public entry point taking an
+ // array is merely a special case of the indirect buffer case.
+
+ if (cfg.emitInterface()) {
+ generatePublicEmitters(binding, allEmitters, true);
+ }
+ if (cfg.emitImpl()) {
+ generatePublicEmitters(binding, allEmitters, false);
+ generatePrivateEmitters(binding, allEmitters);
+ }
+ } // end iteration over expanded bindings
+ } catch (Exception e) {
+ throw new RuntimeException("Error while generating bindings for \"" + sym + "\"", e);
+ }
+
+ return allEmitters;
+ }
+
+
+ public void endFunctions() throws Exception {
+ if (!cfg.structsOnly()) {
+ if (cfg.allStatic() || cfg.emitInterface()) {
+ emitCustomJavaCode(javaWriter(), cfg.className());
+ }
+ if (!cfg.allStatic() && cfg.emitImpl()) {
+ emitCustomJavaCode(javaImplWriter(), cfg.implClassName());
+ }
+ }
+ }
+
+ public void beginStructLayout() throws Exception {}
+ public void layoutStruct(CompoundType t) throws Exception {
+ getLayout().layout(t);
+ }
+ public void endStructLayout() throws Exception {}
+
+ public void beginStructs(TypeDictionary typedefDictionary,
+ TypeDictionary structDictionary,
+ Map<Type, Type> canonMap) throws Exception {
+ this.typedefDictionary = typedefDictionary;
+ this.structDictionary = structDictionary;
+ this.canonMap = canonMap;
+ }
+
+ public void emitStruct(CompoundType structType, String alternateName) throws Exception {
+ // Emit abstract base class delegating to 32-bit or 64-bit implementations
+ emitStructImpl(structType, alternateName, machDesc32, machDesc64, true, false);
+ // Emit concrete implementing class for each variant
+ emitStructImpl(structType, alternateName, machDesc32, machDesc64, false, true);
+ emitStructImpl(structType, alternateName, machDesc32, machDesc64, false, false);
+ }
+
+ public void emitStructImpl(CompoundType structType,
+ String alternateName,
+ MachineDescription md32,
+ MachineDescription md64,
+ boolean doBaseClass,
+ boolean do32Bit) throws Exception {
+ String name = structType.getName();
+ if (name == null && alternateName != null) {
+ name = alternateName;
+ }
+
+ if (name == null) {
+ LOG.log(WARNING, "skipping emission of unnamed struct \"{0}\"", structType);
+ return;
+ }
+
+ if (cfg.shouldIgnoreInInterface(name)) {
+ return;
+ }
+
+ Type containingCType = canonicalize(new PointerType(SizeThunk.POINTER, structType, 0));
+ JavaType containingType = typeToJavaType(containingCType, false, null);
+ if (!containingType.isCompoundTypeWrapper()) {
+ return;
+ }
+ String containingTypeName = containingType.getName();
+
+ if ((md32 == null) || (md64 == null)) {
+ throw new RuntimeException("Must supply both 32- and 64-bit MachineDescriptions to emitStructImpl");
+ }
+ String suffix = "";
+
+ // The "external" MachineDescription is the one used to determine
+ // the sizes of the primitive types seen in the public API. For
+ // example, if a C long is an element of a struct, it is the size
+ // of a Java int on a 32-bit machine but the size of a Java long
+ // on a 64-bit machine. To support both of these sizes with the
+ // same API, the abstract base class must take and return a Java
+ // long from the setter and getter for this field. However the
+ // implementation on a 32-bit platform must downcast this to an
+ // int and set only an int's worth of data in the struct. The
+ // "internal" MachineDescription is the one used to determine how
+ // much data to set in or get from the struct and exactly from
+ // where it comes.
+ //
+ // Note that the 64-bit MachineDescription is always used as the
+ // external MachineDescription.
+
+ MachineDescription extMachDesc = md64;
+ MachineDescription intMachDesc = null;
+
+ if (!doBaseClass) {
+ if (do32Bit) {
+ intMachDesc = md32;
+ suffix = "32";
+ } else {
+ intMachDesc = md64;
+ suffix = "64";
+ }
+ }
+
+ boolean needsNativeCode = false;
+ // Native code for calls through function pointers gets emitted
+ // into the abstract base class; Java code which accesses fields
+ // gets emitted into the concrete classes
+ if (doBaseClass) {
+ for (int i = 0; i < structType.getNumFields(); i++) {
+ if (structType.getField(i).getType().isFunctionPointer()) {
+ needsNativeCode = true;
+ break;
+ }
+ }
+ }
+
+ String structClassPkg = cfg.packageForStruct(name);
+ PrintWriter writer = null;
+ PrintWriter newWriter = null;
+ try {
+ writer = openFile(
+ cfg.javaOutputDir() + File.separator +
+ CodeGenUtils.packageAsPath(structClassPkg) +
+ File.separator + containingTypeName + suffix + ".java");
+ CodeGenUtils.emitAutogeneratedWarning(writer, this);
+ if (needsNativeCode) {
+ String nRoot = cfg.nativeOutputDir();
+ if (cfg.nativeOutputUsesJavaHierarchy()) {
+ nRoot += File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
+ }
+ newWriter = openFile(nRoot + File.separator + containingTypeName + "_JNI.c");
+ CodeGenUtils.emitAutogeneratedWarning(newWriter, this);
+ emitCHeader(newWriter, containingTypeName);
+ }
+ } catch(Exception e) {
+ throw new RuntimeException("Unable to open files for emission of struct class", e);
+ }
+
+ writer.println();
+ writer.println("package " + structClassPkg + ";");
+ writer.println();
+ writer.println("import java.nio.*;");
+ writer.println();
+
+ writer.println("import " + cfg.gluegenRuntimePackage() + ".*;");
+ writer.println("import " + DynamicLookupHelper.class.getPackage().getName() + ".*;");
+ writer.println("import " + Buffers.class.getPackage().getName() + ".*;");
+ writer.println();
+ List<String> imports = cfg.imports();
+ for (String str : imports) {
+ writer.print("import ");
+ writer.print(str);
+ writer.println(";");
+ }
+ writer.println();
+ List<String> javadoc = cfg.javadocForClass(containingTypeName);
+ for (String doc : javadoc) {
+ writer.println(doc);
+ }
+ writer.print((doBaseClass ? "public " : "") + (doBaseClass ? "abstract " : "") + "class " + containingTypeName + suffix + " ");
+ if (!doBaseClass) {
+ writer.print("extends " + containingTypeName + " ");
+ }
+ boolean firstIteration = true;
+ List<String> userSpecifiedInterfaces = cfg.implementedInterfaces(containingTypeName);
+ for (String userInterface : userSpecifiedInterfaces) {
+ if (firstIteration) {
+ writer.print("implements ");
+ }
+ firstIteration = false;
+ writer.print(userInterface);
+ writer.print(" ");
+ }
+ writer.println("{");
+ writer.println();
+ if (doBaseClass) {
+ writer.println(" StructAccessor accessor;");
+ writer.println();
+ }
+
+ writer.println(" public static int size() {");
+ if (doBaseClass) {
+ writer.println(" if (Platform.is32Bit()) {");
+ writer.println(" return " + containingTypeName + "32" + ".size();");
+ writer.println(" } else {");
+ writer.println(" return " + containingTypeName + "64" + ".size();");
+ writer.println(" }");
+ } else {
+ writer.println(" return " + structType.getSize(intMachDesc) + ";");
+ }
+ writer.println(" }");
+ writer.println();
+ if (doBaseClass) {
+ writer.println(" public static " + containingTypeName + " create() {");
+ writer.println(" return create(Buffers.newDirectByteBuffer(size()));");
+ writer.println(" }");
+ writer.println();
+ writer.println(" public static " + containingTypeName + " create(java.nio.ByteBuffer buf) {");
+ writer.println(" if (Platform.is32Bit()) {");
+ writer.println(" return new " + containingTypeName + "32(buf);");
+ writer.println(" } else {");
+ writer.println(" return new " + containingTypeName + "64(buf);");
+ writer.println(" }");
+ writer.println(" }");
+ writer.println();
+ writer.println(" " + containingTypeName + "(java.nio.ByteBuffer buf) {");
+ writer.println(" accessor = new StructAccessor(buf);");
+ writer.println(" }");
+ writer.println();
+ writer.println(" public java.nio.ByteBuffer getBuffer() {");
+ writer.println(" return accessor.getBuffer();");
+ writer.println(" }");
+ } else {
+ writer.println(" " + containingTypeName + suffix + "(java.nio.ByteBuffer buf) {");
+ writer.println(" super(buf);");
+ writer.println(" }");
+ writer.println();
+ }
+ for (int i = 0; i < structType.getNumFields(); i++) {
+
+ Field field = structType.getField(i);
+ Type fieldType = field.getType();
+
+ if (!cfg.shouldIgnoreInInterface(name + " " + field.getName())) {
+
+ String renamed = cfg.getJavaSymbolRename(field.getName());
+ String fieldName = renamed==null ? field.getName() : renamed;
+
+ if (fieldType.isFunctionPointer()) {
+
+ if (doBaseClass) {
+ try {
+ // Emit method call and associated native code
+ FunctionType funcType = fieldType.asPointer().getTargetType().asFunction();
+ FunctionSymbol funcSym = new FunctionSymbol(fieldName, funcType);
+ MethodBinding binding = bindFunction(funcSym, containingType, containingCType, machDesc64);
+ binding.findThisPointer(); // FIXME: need to provide option to disable this on per-function basis
+ writer.println();
+
+ // Emit public Java entry point for calling this function pointer
+ JavaMethodBindingEmitter emitter =
+ new JavaMethodBindingEmitter(binding,
+ writer,
+ cfg.runtimeExceptionType(),
+ cfg.unsupportedExceptionType(),
+ true,
+ cfg.tagNativeBinding(),
+ false,
+ true, // FIXME: should unify this with the general emission code
+ false,
+ false, // FIXME: should unify this with the general emission code
+ false, // FIXME: should unify this with the general emission code
+ false, // FIXME: should unify this with the general emission code
+ false,
+ cfg);
+ emitter.addModifier(JavaMethodBindingEmitter.PUBLIC);
+ emitter.emit();
+
+ // Emit private native Java entry point for calling this function pointer
+ emitter =
+ new JavaMethodBindingEmitter(binding,
+ writer,
+ cfg.runtimeExceptionType(),
+ cfg.unsupportedExceptionType(),
+ false,
+ cfg.tagNativeBinding(),
+ true,
+ true, // FIXME: should unify this with the general emission code
+ true,
+ true, // FIXME: should unify this with the general emission code
+ false, // FIXME: should unify this with the general emission code
+ false, // FIXME: should unify this with the general emission code
+ false,
+ cfg);
+ emitter.addModifier(JavaMethodBindingEmitter.PRIVATE);
+ emitter.addModifier(JavaMethodBindingEmitter.NATIVE);
+ emitter.emit();
+
+ // Emit (private) C entry point for calling this function pointer
+ CMethodBindingEmitter cEmitter =
+ new CMethodBindingEmitter(binding,
+ newWriter,
+ structClassPkg,
+ containingTypeName,
+ true, // FIXME: this is optional at this point
+ false,
+ true,
+ false, // FIXME: should unify this with the general emission code
+ machDesc64);
+ prepCEmitter(binding, cEmitter);
+ cEmitter.emit();
+ } catch (Exception e) {
+ System.err.println("While processing field " + field + " of type " + name + ":");
+ throw(e);
+ }
+ }
+ } else if (fieldType.isCompound()) {
+ // FIXME: will need to support this at least in order to
+ // handle the union in jawt_Win32DrawingSurfaceInfo (fabricate
+ // a name?)
+ if (fieldType.getName() == null) {
+ throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" +
+ field + "\" in type \"" + name + "\")");
+ }
+
+ writer.println();
+ generateGetterSignature(writer, doBaseClass, fieldType.getName(), capitalizeString(fieldName));
+ if (doBaseClass) {
+ writer.println(";");
+ } else {
+ writer.println(" {");
+ writer.println(" return " + fieldType.getName() + ".create(accessor.slice(" +
+ field.getOffset(intMachDesc) + ", " + fieldType.getSize(intMachDesc) + "));");
+ writer.println(" }");
+ }
+
+ } else if (fieldType.isArray()) {
+
+ Type baseElementType = field.getType().asArray().getBaseElementType();
+
+ if(!baseElementType.isPrimitive())
+ break;
+
+ String paramType = typeToJavaType(baseElementType, false, extMachDesc).getName();
+ String capitalized = capitalizeString(fieldName);
+
+ int slot = -1;
+ if(!doBaseClass) {
+ slot = slot(fieldType, (int) field.getOffset(intMachDesc), intMachDesc);
+ }
+
+ // Setter
+ writer.println();
+ generateSetterSignature(writer, doBaseClass, containingTypeName, capitalized, paramType+"[]");
+ if (doBaseClass) {
+ writer.println(";");
+ } else {
+ writer.println(" {");
+ writer.print (" accessor.set" + capitalizeString(paramType) + "sAt(" + slot + ", ");
+ writer.println("val);");
+ writer.println(" return this;");
+ writer.println(" }");
+ }
+ writer.println();
+ // Getter
+ generateGetterSignature(writer, doBaseClass, paramType+"[]", capitalized);
+ if (doBaseClass) {
+ writer.println(";");
+ } else {
+ writer.println(" {");
+ writer.print (" return ");
+ writer.println("accessor.get" + capitalizeString(paramType) + "sAt(" + slot + ", new " +paramType+"["+fieldType.asArray().getLength()+"]);");
+ writer.println(" }");
+ }
+
+ } else {
+ JavaType internalJavaType = null;
+ JavaType externalJavaType = null;
+
+ try {
+ externalJavaType = typeToJavaType(fieldType, false, extMachDesc);
+ if (!doBaseClass) {
+ internalJavaType = typeToJavaType(fieldType, false, intMachDesc);
+ }
+ } catch (Exception e) {
+ System.err.println("Error occurred while creating accessor for field \"" +
+ field.getName() + "\" in type \"" + name + "\"");
+ throw(e);
+ }
+ if (externalJavaType.isPrimitive()) {
+ // Primitive type
+ String externalJavaTypeName = null;
+ String internalJavaTypeName = null;
+ externalJavaTypeName = externalJavaType.getName();
+ if (!doBaseClass) {
+ internalJavaTypeName = internalJavaType.getName();
+ }
+ if (isOpaque(fieldType)) {
+ externalJavaTypeName = compatiblePrimitiveJavaTypeName(fieldType, externalJavaType, extMachDesc);
+ if (!doBaseClass) {
+ internalJavaTypeName = compatiblePrimitiveJavaTypeName(fieldType, internalJavaType, intMachDesc);
+ }
+ }
+ String capitalized = null;
+ if (!doBaseClass) {
+ capitalized = capitalizeString(internalJavaTypeName);
+ }
+ int slot = -1;
+ if (!doBaseClass) {
+ slot = slot(fieldType, (int) field.getOffset(intMachDesc), intMachDesc);
+ }
+ writer.println();
+ String capitalizedFieldName = capitalizeString(fieldName);
+ // Setter
+ generateSetterSignature(writer, doBaseClass, containingTypeName, capitalizedFieldName, externalJavaTypeName);
+ if (doBaseClass) {
+ writer.println(";");
+ } else {
+ writer.println(" {");
+ writer.print (" accessor.set" + capitalized + "At(" + slot + ", ");
+ if (!externalJavaTypeName.equals(internalJavaTypeName)) {
+ writer.print("(" + internalJavaTypeName + ") ");
+ }
+ writer.println("val);");
+ writer.println(" return this;");
+ writer.println(" }");
+ }
+ writer.println();
+ // Getter
+ generateGetterSignature(writer, doBaseClass, externalJavaTypeName, capitalizedFieldName);
+ if (doBaseClass) {
+ writer.println(";");
+ } else {
+ writer.println(" {");
+ writer.print (" return ");
+ if (!externalJavaTypeName.equals(internalJavaTypeName)) {
+ writer.print("(" + externalJavaTypeName + ") ");
+ }
+ writer.println("accessor.get" + capitalized + "At(" + slot + ");");
+ writer.println(" }");
+ }
+ } else {
+ // FIXME
+ LOG.log(WARNING, "Complicated fields (field \"{0}\" of type \"{1}\") not implemented yet", new Object[]{field, name});
+ // throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t +
+ // "\") not implemented yet");
+ }
+ }
+ }
+ }
+ if (doBaseClass) {
+ emitCustomJavaCode(writer, containingTypeName);
+ }
+ writer.println("}");
+ writer.flush();
+ writer.close();
+ if (needsNativeCode) {
+ newWriter.flush();
+ newWriter.close();
+ }
+ }
+ public void endStructs() throws Exception {}
+
+ public static int addStrings2Buffer(StringBuilder buf, String sep, String first, Collection<String> col) {
+ int num = 0;
+ if(null==buf) {
+ buf = new StringBuilder();
+ }
+
+ Iterator<String> iter = col.iterator();
+ if(null!=first) {
+ buf.append(first);
+ if( iter.hasNext() ) {
+ buf.append(sep);
+ }
+ num++;
+ }
+ while( iter.hasNext() ) {
+ buf.append(iter.next());
+ if( iter.hasNext() ) {
+ buf.append(sep);
+ }
+ num++;
+ }
+ return num;
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void generateGetterSignature(PrintWriter writer, boolean baseClass, String returnTypeName, String capitalizedFieldName) {
+ writer.print(" public " + (baseClass ? "abstract " : "") + returnTypeName + " get" + capitalizedFieldName + "()");
+ }
+
+ private void generateSetterSignature(PrintWriter writer, boolean baseClass, String returnTypeName, String capitalizedFieldName, String paramTypeName) {
+ writer.print(" public " + (baseClass ? "abstract " : "") + returnTypeName + " set" + capitalizedFieldName + "(" + paramTypeName + " val)");
+ }
+
+ private JavaType typeToJavaType(Type cType, boolean outgoingArgument, MachineDescription curMachDesc) {
+ // Recognize JNIEnv* case up front
+ PointerType opt = cType.asPointer();
+ if ((opt != null) &&
+ (opt.getTargetType().getName() != null) &&
+ (opt.getTargetType().getName().equals("JNIEnv"))) {
+ return JavaType.createForJNIEnv();
+ }
+ Type t = cType;
+
+ // Opaque specifications override automatic conversions
+ // in case the identity is being used .. not if ptr-ptr
+ TypeInfo info = cfg.typeInfo(t, typedefDictionary);
+ if (info != null) {
+ boolean isPointerPointer = false;
+ if (t.pointerDepth() > 0 || t.arrayDimension() > 0) {
+ Type targetType; // target type
+ if (t.isPointer()) {
+ // t is <type>*, we need to get <type>
+ targetType = t.asPointer().getTargetType();
+ } else {
+ // t is <type>[], we need to get <type>
+ targetType = t.asArray().getElementType();
+ }
+ if (t.pointerDepth() == 2 || t.arrayDimension() == 2) {
+ // Get the target type of the target type (targetType was computer earlier
+ // as to be a pointer to the target type, so now we need to get its
+ // target type)
+ if (targetType.isPointer()) {
+ isPointerPointer = true;
+
+ // t is<type>**, targetType is <type>*, we need to get <type>
+ Type bottomType = targetType.asPointer().getTargetType();
+ LOG.log(INFO, "Opaque Type: {0}, targetType: {1}, bottomType: {2} is ptr-ptr", new Object[]{t, targetType, bottomType});
+ }
+ }
+ }
+ if(!isPointerPointer) {
+ return info.javaType();
+ }
+ }
+
+ if (t.isInt() || t.isEnum()) {
+ switch ((int) t.getSize(curMachDesc)) {
+ case 1: return javaType(Byte.TYPE);
+ case 2: return javaType(Short.TYPE);
+ case 4: return javaType(Integer.TYPE);
+ case 8: return javaType(Long.TYPE);
+ default: throw new RuntimeException("Unknown integer type of size " +
+ t.getSize(curMachDesc) + " and name " + t.getName());
+ }
+ } else if (t.isFloat()) {
+ return javaType(Float.TYPE);
+ } else if (t.isDouble()) {
+ return javaType(Double.TYPE);
+ } else if (t.isVoid()) {
+ return javaType(Void.TYPE);
+ } else {
+ if (t.pointerDepth() > 0 || t.arrayDimension() > 0) {
+ Type targetType; // target type
+ if (t.isPointer()) {
+ // t is <type>*, we need to get <type>
+ targetType = t.asPointer().getTargetType();
+ } else {
+ // t is <type>[], we need to get <type>
+ targetType = t.asArray().getElementType();
+ }
+
+ // Handle Types of form pointer-to-type or array-of-type, like
+ // char* or int[]; these are expanded out into Java primitive
+ // arrays, NIO buffers, or both in expandMethodBinding
+ if (t.pointerDepth() == 1 || t.arrayDimension() == 1) {
+ if (targetType.isVoid()) {
+ return JavaType.createForVoidPointer();
+ } else if (targetType.isInt()) {
+ // size_t and intptr_t is always a PointerBuffer since size is arch dependent
+ if ("size_t".equals(targetType.getName()) || "intptr_t".equals(targetType.getName())) {
+ return JavaType.forNIOPointerBufferClass();
+ }
+ switch ((int) targetType.getSize(curMachDesc)) {
+ case 1: return JavaType.createForCCharPointer();
+ case 2: return JavaType.createForCShortPointer();
+ case 4: return JavaType.createForCInt32Pointer();
+ case 8: return JavaType.createForCInt64Pointer();
+ default: throw new RuntimeException("Unknown integer array type of size " +
+ t.getSize(curMachDesc) + " and name " + t.getName());
+ }
+ } else if (targetType.isFloat()) {
+ return JavaType.createForCFloatPointer();
+ } else if (targetType.isDouble()) {
+ return JavaType.createForCDoublePointer();
+ } else if (targetType.isCompound()) {
+ if (t.isArray()) {
+ throw new RuntimeException("Arrays of compound types not handled yet");
+ }
+ // Special cases for known JNI types (in particular for converting jawt.h)
+ if (t.getName() != null &&
+ t.getName().equals("jobject")) {
+ return javaType(java.lang.Object.class);
+ }
+
+ String name = targetType.getName();
+ if (name == null) {
+ // Try containing pointer type for any typedefs
+ name = t.getName();
+ if (name == null) {
+ throw new RuntimeException("Couldn't find a proper type name for pointer type " + t);
+ }
+ }
+
+ return JavaType.createForCStruct(cfg.renameJavaType(name));
+ } else {
+ throw new RuntimeException("Don't know how to convert pointer/array type \"" +
+ t + "\"");
+ }
+ }
+ // Handle Types of form pointer-to-pointer-to-type or
+ // array-of-arrays-of-type, like char** or int[][]
+ else if (t.pointerDepth() == 2 || t.arrayDimension() == 2) {
+ // Get the target type of the target type (targetType was computer earlier
+ // as to be a pointer to the target type, so now we need to get its
+ // target type)
+ Type bottomType;
+ if (targetType.isPointer()) {
+ // t is<type>**, targetType is <type>*, we need to get <type>
+ bottomType = targetType.asPointer().getTargetType();
+ return JavaType.forNIOPointerBufferClass();
+ } else {
+ // t is<type>[][], targetType is <type>[], we need to get <type>
+ bottomType = targetType.asArray().getElementType();
+ LOG.log(WARNING, "typeToJavaType(ptr-ptr): {0}, targetType: {1}, bottomType: {2} -> Unhandled!", new Object[]{t, targetType, bottomType});
+ }
+
+ // Warning: The below code is not backed up by an implementation,
+ // the only working variant is a ptr-ptr type which results in a PointerBuffer.
+ //
+ if (bottomType.isPrimitive()) {
+ if (bottomType.isInt()) {
+ switch ((int) bottomType.getSize(curMachDesc)) {
+ case 1: return javaType(ArrayTypes.byteBufferArrayClass);
+ case 2: return javaType(ArrayTypes.shortBufferArrayClass);
+ case 4: return javaType(ArrayTypes.intBufferArrayClass);
+ case 8: return javaType(ArrayTypes.longBufferArrayClass);
+ default: throw new RuntimeException("Unknown two-dimensional integer array type of element size " +
+ bottomType.getSize(curMachDesc) + " and name " + bottomType.getName());
+ }
+ } else if (bottomType.isFloat()) {
+ return javaType(ArrayTypes.floatBufferArrayClass);
+ } else if (bottomType.isDouble()) {
+ return javaType(ArrayTypes.doubleBufferArrayClass);
+ } else {
+ throw new RuntimeException("Unexpected primitive type " + bottomType.getName() +
+ " in two-dimensional array");
+ }
+ } else if (bottomType.isVoid()) {
+ return javaType(ArrayTypes.bufferArrayClass);
+ } else if (targetType.isPointer() && (targetType.pointerDepth() == 1) &&
+ targetType.asPointer().getTargetType().isCompound()) {
+ // Array of pointers; convert as array of StructAccessors
+ return JavaType.createForCArray(bottomType);
+ } else {
+ throw new RuntimeException(
+ "Could not convert C type \"" + t + "\" " +
+ "to appropriate Java type; need to add more support for " +
+ "depth=2 pointer/array types [debug info: targetType=\"" +
+ targetType + "\"]");
+ }
+ } else {
+ // can't handle this type of pointer/array argument
+ throw new RuntimeException(
+ "Could not convert C pointer/array \"" + t + "\" to " +
+ "appropriate Java type; types with pointer/array depth " +
+ "greater than 2 are not yet supported [debug info: " +
+ "pointerDepth=" + t.pointerDepth() + " arrayDimension=" +
+ t.arrayDimension() + " targetType=\"" + targetType + "\"]");
+ }
+
+ } else {
+ throw new RuntimeException(
+ "Could not convert C type \"" + t + "\" (class " +
+ t.getClass().getName() + ") to appropriate Java type");
+ }
+ }
+ }
+
+ private static boolean isIntegerType(Class<?> c) {
+ return ((c == Byte.TYPE) ||
+ (c == Short.TYPE) ||
+ (c == Character.TYPE) ||
+ (c == Integer.TYPE) ||
+ (c == Long.TYPE));
+ }
+
+ private int slot(Type t, int byteOffset, MachineDescription curMachDesc) {
+ if (t.isInt()) {
+ switch ((int) t.getSize(curMachDesc)) {
+ case 1:
+ case 2:
+ case 4:
+ case 8: return byteOffset / (int) t.getSize(curMachDesc);
+ default: throw new RuntimeException("Illegal type");
+ }
+ } else if (t.isFloat()) {
+ return byteOffset / 4;
+ } else if (t.isDouble()) {
+ return byteOffset / 8;
+ } else if (t.isPointer()) {
+ return byteOffset / curMachDesc.pointerSizeInBytes();
+ } else if (t.isArray()) {
+ return slot(t.asArray().getBaseElementType(), byteOffset, curMachDesc);
+ } else {
+ throw new RuntimeException("Illegal type " + t);
+ }
+ }
+
+ private StructLayout getLayout() {
+ if (layout == null) {
+ layout = StructLayout.createForCurrentPlatform();
+ }
+ return layout;
+ }
+
+ protected PrintWriter openFile(String filename) throws IOException {
+ //System.out.println("Trying to open: " + filename);
+ File file = new File(filename);
+ String parentDir = file.getParent();
+ if (parentDir != null)
+ {
+ File pDirFile = new File(parentDir);
+ pDirFile.mkdirs();
+ }
+ return new PrintWriter(new BufferedWriter(new FileWriter(file)));
+ }
+
+ private boolean isOpaque(Type type) {
+ return (cfg.typeInfo(type, typedefDictionary) != null);
+ }
+
+ private String compatiblePrimitiveJavaTypeName(Type fieldType,
+ JavaType javaType,
+ MachineDescription curMachDesc) {
+ Class<?> c = javaType.getJavaClass();
+ if (!isIntegerType(c)) {
+ // FIXME
+ throw new RuntimeException("Can't yet handle opaque definitions of structs' fields to non-integer types (byte, short, int, long, etc.)");
+ }
+ switch ((int) fieldType.getSize(curMachDesc)) {
+ case 1: return "byte";
+ case 2: return "short";
+ case 4: return "int";
+ case 8: return "long";
+ default: throw new RuntimeException("Can't handle opaque definitions if the starting type isn't compatible with integral types");
+ }
+ }
+
+ private void openWriters() throws IOException {
+ String jRoot = null;
+ if (cfg.allStatic() || cfg.emitInterface()) {
+ jRoot = cfg.javaOutputDir() + File.separator +
+ CodeGenUtils.packageAsPath(cfg.packageName());
+ }
+ String jImplRoot = null;
+ if (!cfg.allStatic()) {
+ jImplRoot =
+ cfg.javaOutputDir() + File.separator +
+ CodeGenUtils.packageAsPath(cfg.implPackageName());
+ }
+ String nRoot = cfg.nativeOutputDir();
+ if (cfg.nativeOutputUsesJavaHierarchy())
+ {
+ nRoot +=
+ File.separator + CodeGenUtils.packageAsPath(cfg.packageName());
+ }
+
+ if (cfg.allStatic() || cfg.emitInterface()) {
+ javaWriter = openFile(jRoot + File.separator + cfg.className() + ".java");
+ }
+ if (!cfg.allStatic() && cfg.emitImpl()) {
+ javaImplWriter = openFile(jImplRoot + File.separator + cfg.implClassName() + ".java");
+ }
+ if (cfg.emitImpl()) {
+ cWriter = openFile(nRoot + File.separator + cfg.implClassName() + "_JNI.c");
+ }
+
+ if (javaWriter != null) {
+ CodeGenUtils.emitAutogeneratedWarning(javaWriter, this);
+ }
+ if (javaImplWriter != null) {
+ CodeGenUtils.emitAutogeneratedWarning(javaImplWriter, this);
+ }
+ if (cWriter != null) {
+ CodeGenUtils.emitAutogeneratedWarning(cWriter, this);
+ }
+ }
+
+ protected PrintWriter javaWriter() {
+ if (!cfg.allStatic() && !cfg.emitInterface()) {
+ throw new InternalError("Should not call this");
+ }
+ return javaWriter;
+ }
+
+ protected PrintWriter javaImplWriter() {
+ if (cfg.allStatic() || !cfg.emitImpl()) {
+ throw new InternalError("Should not call this");
+ }
+ return javaImplWriter;
+ }
+
+ protected PrintWriter cWriter() {
+ if (!cfg.emitImpl()) {
+ throw new InternalError("Should not call this");
+ }
+ return cWriter;
+ }
+
+ private void closeWriter(PrintWriter writer) throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+ private void closeWriters() throws IOException {
+ if (javaWriter != null) {
+ closeWriter(javaWriter);
+ }
+ if (javaImplWriter != null) {
+ closeWriter(javaImplWriter);
+ }
+ if (cWriter != null) {
+ closeWriter(cWriter);
+ }
+ javaWriter = null;
+ javaImplWriter = null;
+ cWriter = null;
+ }
+
+ /**
+ * Returns the value that was specified by the configuration directive
+ * "JavaOutputDir", or the default if none was specified.
+ */
+ protected String getJavaOutputDir() {
+ return cfg.javaOutputDir();
+ }
+
+ /**
+ * Returns the value that was specified by the configuration directive
+ * "Package", or the default if none was specified.
+ */
+ protected String getJavaPackageName() {
+ return cfg.packageName();
+ }
+
+ /**
+ * Returns the value that was specified by the configuration directive
+ * "ImplPackage", or the default if none was specified.
+ */
+ protected String getImplPackageName() {
+ return cfg.implPackageName();
+ }
+
+ /**
+ * Emit all the strings specified in the "CustomJavaCode" parameters of
+ * the configuration file.
+ */
+ protected void emitCustomJavaCode(PrintWriter writer, String className) throws Exception {
+ List<String> code = cfg.customJavaCodeForClass(className);
+ if (code.isEmpty())
+ return;
+
+ writer.println();
+ writer.println(" // --- Begin CustomJavaCode .cfg declarations");
+ for (String line : code) {
+ writer.println(line);
+ }
+ writer.println(" // ---- End CustomJavaCode .cfg declarations");
+ }
+
+ /**
+ * Write out any header information for the output files (class declaration
+ * and opening brace, import statements, etc).
+ */
+ protected void emitAllFileHeaders() throws IOException {
+ try {
+ List<String> imports = new ArrayList<String>(cfg.imports());
+ imports.add(cfg.gluegenRuntimePackage()+".*");
+ imports.add(DynamicLookupHelper.class.getPackage().getName()+".*");
+ imports.add(Buffers.class.getPackage().getName()+".*");
+ imports.add(Buffer.class.getPackage().getName()+".*");
+
+ if (cfg.allStatic() || cfg.emitInterface()) {
+
+ String[] interfaces;
+ List<String> userSpecifiedInterfaces = null;
+ if (cfg.emitInterface()) {
+ userSpecifiedInterfaces = cfg.extendedInterfaces(cfg.className());
+ } else {
+ userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.className());
+ }
+ interfaces = new String[userSpecifiedInterfaces.size()];
+ userSpecifiedInterfaces.toArray(interfaces);
+
+ final List<String> intfDocs = cfg.javadocForClass(cfg.className());
+ CodeGenUtils.EmissionCallback docEmitter =
+ new CodeGenUtils.EmissionCallback() {
+ public void emit(PrintWriter w) {
+ for (Iterator iter = intfDocs.iterator(); iter.hasNext(); ) {
+ w.println((String) iter.next());
+ }
+ }
+ };
+
+ String[] accessModifiers = null;
+ if(cfg.accessControl(cfg.className()) == PUBLIC_ABSTRACT) {
+ accessModifiers = new String[] { "public", "abstract" };
+ } else {
+ accessModifiers = new String[] { "public" };
+ }
+
+ CodeGenUtils.emitJavaHeaders(
+ javaWriter,
+ cfg.packageName(),
+ cfg.className(),
+ cfg.allStatic() ? true : false,
+ imports,
+ accessModifiers,
+ interfaces,
+ cfg.extendedParentClass(cfg.className()),
+ docEmitter);
+ }
+
+ if (!cfg.allStatic() && cfg.emitImpl()) {
+ final List<String> implDocs = cfg.javadocForClass(cfg.implClassName());
+ CodeGenUtils.EmissionCallback docEmitter =
+ new CodeGenUtils.EmissionCallback() {
+ public void emit(PrintWriter w) {
+ for (Iterator iter = implDocs.iterator(); iter.hasNext(); ) {
+ w.println((String) iter.next());
+ }
+ }
+ };
+
+ String[] interfaces;
+ List<String> userSpecifiedInterfaces = null;
+ userSpecifiedInterfaces = cfg.implementedInterfaces(cfg.implClassName());
+ int additionalNum = 0;
+ if (cfg.className() != null) {
+ additionalNum = 1;
+ }
+ interfaces = new String[additionalNum + userSpecifiedInterfaces.size()];
+ userSpecifiedInterfaces.toArray(interfaces);
+ if (additionalNum == 1) {
+ interfaces[userSpecifiedInterfaces.size()] = cfg.className();
+ }
+
+ String[] accessModifiers = null;
+ if(cfg.accessControl(cfg.implClassName()) == PUBLIC_ABSTRACT) {
+ accessModifiers = new String[] { "public", "abstract" };
+ } else {
+ accessModifiers = new String[] { "public" };
+ }
+
+ CodeGenUtils.emitJavaHeaders(
+ javaImplWriter,
+ cfg.implPackageName(),
+ cfg.implClassName(),
+ true,
+ imports,
+ accessModifiers,
+ interfaces,
+ cfg.extendedParentClass(cfg.implClassName()),
+ docEmitter);
+ }
+
+ if (cfg.emitImpl()) {
+ emitCHeader(cWriter(), cfg.implClassName());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Error emitting all file headers: cfg.allStatic()=" + cfg.allStatic() +
+ " cfg.emitImpl()=" + cfg.emitImpl() + " cfg.emitInterface()=" + cfg.emitInterface(),
+ e);
+ }
+
+ }
+
+ protected void emitCHeader(PrintWriter cWriter, String className) {
+ cWriter.println("#include <jni.h>");
+ cWriter.println("#include <stdlib.h>");
+ cWriter.println();
+
+ if (getConfig().emitImpl()) {
+ cWriter.println("#include <assert.h>");
+ cWriter.println();
+ }
+
+ for (String code : cfg.customCCode()) {
+ cWriter.println(code);
+ }
+ cWriter.println();
+ }
+
+ /**
+ * Write out any footer information for the output files (closing brace of
+ * class definition, etc).
+ */
+ protected void emitAllFileFooters(){
+ if (cfg.allStatic() || cfg.emitInterface()) {
+ javaWriter().println();
+ javaWriter().println("} // end of class " + cfg.className());
+ }
+ if (!cfg.allStatic() && cfg.emitImpl()) {
+ javaImplWriter().println();
+ javaImplWriter().println("} // end of class " + cfg.implClassName());
+ }
+ }
+
+ private JavaType javaType(Class<?> c) {
+ return JavaType.createForClass(c);
+ }
+
+ /** Maps the C types in the specified function to Java types through
+ the MethodBinding interface. Note that the JavaTypes in the
+ returned MethodBinding are "intermediate" JavaTypes (some
+ potentially representing C pointers rather than true Java types)
+ and must be lowered to concrete Java types before creating
+ emitters for them. */
+ private MethodBinding bindFunction(FunctionSymbol sym,
+ JavaType containingType,
+ Type containingCType,
+ MachineDescription curMachDesc) {
+
+ MethodBinding binding = new MethodBinding(sym, containingType, containingCType);
+
+ binding.renameMethodName(cfg.getJavaSymbolRename(sym.getName()));
+
+ // System.out.println("bindFunction(0) "+sym.getReturnType());
+
+ if (cfg.returnsString(binding.getName())) {
+ PointerType prt = sym.getReturnType().asPointer();
+ if (prt == null ||
+ prt.getTargetType().asInt() == null ||
+ prt.getTargetType().getSize(curMachDesc) != 1) {
+ throw new RuntimeException(
+ "Cannot apply ReturnsString configuration directive to \"" + sym +
+ "\". ReturnsString requires native method to have return type \"char *\"");
+ }
+ binding.setJavaReturnType(javaType(java.lang.String.class));
+ } else {
+ binding.setJavaReturnType(typeToJavaType(sym.getReturnType(), false, curMachDesc));
+ }
+
+ // System.out.println("bindFunction(1) "+binding.getJavaReturnType());
+
+ // List of the indices of the arguments in this function that should be
+ // converted from byte[] or short[] to String
+ List<Integer> stringArgIndices = cfg.stringArguments(binding.getName());
+
+ for (int i = 0; i < sym.getNumArguments(); i++) {
+ Type cArgType = sym.getArgumentType(i);
+ JavaType mappedType = typeToJavaType(cArgType, true, curMachDesc);
+ // System.out.println("C arg type -> \"" + cArgType + "\"" );
+ // System.out.println(" Java -> \"" + mappedType + "\"" );
+
+ // Take into account any ArgumentIsString configuration directives that apply
+ if (stringArgIndices != null && stringArgIndices.contains(i)) {
+ // System.out.println("Forcing conversion of " + binding.getName() + " arg #" + i + " from byte[] to String ");
+ if (mappedType.isCVoidPointerType() ||
+ mappedType.isCCharPointerType() ||
+ mappedType.isCShortPointerType() ||
+ mappedType.isNIOPointerBuffer() ||
+ (mappedType.isArray() &&
+ (mappedType.getJavaClass() == ArrayTypes.byteBufferArrayClass) ||
+ (mappedType.getJavaClass() == ArrayTypes.shortBufferArrayClass))) {
+ // convert mapped type from:
+ // void*, byte[], and short[] to String
+ // ByteBuffer[] and ShortBuffer[] to String[]
+ if (mappedType.isArray() || mappedType.isNIOPointerBuffer()) {
+ mappedType = javaType(ArrayTypes.stringArrayClass);
+ } else {
+ mappedType = javaType(String.class);
+ }
+ }
+ else {
+ throw new RuntimeException(
+ "Cannot apply ArgumentIsString configuration directive to " +
+ "argument " + i + " of \"" + sym + "\": argument type is not " +
+ "a \"void*\", \"char *\", \"short *\", \"char**\", or \"short**\" equivalent");
+ }
+ }
+ binding.addJavaArgumentType(mappedType);
+ //System.out.println("During binding of [" + sym + "], added mapping from C type: " + cArgType + " to Java type: " + mappedType);
+ }
+
+ // System.out.println("---> " + binding);
+ // System.out.println(" ---> " + binding.getCSymbol());
+ // System.out.println("bindFunction(3) "+binding);
+ return binding;
+ }
+
+ private MethodBinding lowerMethodBindingPointerTypes(MethodBinding inputBinding,
+ boolean convertToArrays,
+ boolean[] canProduceArrayVariant) {
+ MethodBinding result = inputBinding;
+ boolean arrayPossible = false;
+
+ // System.out.println("lowerMethodBindingPointerTypes(0): "+result);
+
+ for (int i = 0; i < inputBinding.getNumArguments(); i++) {
+ JavaType t = inputBinding.getJavaArgumentType(i);
+ if (t.isCPrimitivePointerType()) {
+ if (t.isCVoidPointerType()) {
+ // These are always bound to java.nio.Buffer
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOBufferClass());
+ } else if (t.isCCharPointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.byteArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOByteBufferClass());
+ }
+ } else if (t.isCShortPointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.shortArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOShortBufferClass());
+ }
+ } else if (t.isCInt32PointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.intArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOIntBufferClass());
+ }
+ } else if (t.isCInt64PointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.longArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOInt64BufferClass());
+ }
+ } else if (t.isCFloatPointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.floatArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIOFloatBufferClass());
+ }
+ } else if (t.isCDoublePointerType()) {
+ arrayPossible = true;
+ if (convertToArrays) {
+ result = result.replaceJavaArgumentType(i, javaType(ArrayTypes.doubleArrayClass));
+ } else {
+ result = result.replaceJavaArgumentType(i, JavaType.forNIODoubleBufferClass());
+ }
+ } else {
+ throw new RuntimeException("Unknown C pointer type " + t);
+ }
+ }
+ }
+
+ // System.out.println("lowerMethodBindingPointerTypes(1): "+result);
+
+ // Always return primitive pointer types as NIO buffers
+ JavaType t = result.getJavaReturnType();
+ if (t.isCPrimitivePointerType()) {
+ if (t.isCVoidPointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass());
+ } else if (t.isCCharPointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOByteBufferClass());
+ } else if (t.isCShortPointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOShortBufferClass());
+ } else if (t.isCInt32PointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOIntBufferClass());
+ } else if (t.isCInt64PointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOInt64BufferClass());
+ } else if (t.isCFloatPointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIOFloatBufferClass());
+ } else if (t.isCDoublePointerType()) {
+ result = result.replaceJavaArgumentType(-1, JavaType.forNIODoubleBufferClass());
+ } else {
+ throw new RuntimeException("Unknown C pointer type " + t);
+ }
+ }
+
+ // System.out.println("lowerMethodBindingPointerTypes(2): "+result);
+
+ if (canProduceArrayVariant != null) {
+ canProduceArrayVariant[0] = arrayPossible;
+ }
+
+ return result;
+ }
+
+ // Expands a MethodBinding containing C primitive pointer types into
+ // multiple variants taking Java primitive arrays and NIO buffers, subject
+ // to the per-function "NIO only" rule in the configuration file
+ protected List<MethodBinding> expandMethodBinding(MethodBinding binding) {
+
+ List<MethodBinding> result = new ArrayList<MethodBinding>();
+ // Indicates whether it is possible to produce an array variant
+ // Prevents e.g. char* -> String conversions from emitting two entry points
+ boolean[] canProduceArrayVariant = new boolean[1];
+
+ if (binding.signatureUsesCPrimitivePointers() ||
+ binding.signatureUsesCVoidPointers() ||
+ binding.signatureUsesCArrays()) {
+
+ result.add(lowerMethodBindingPointerTypes(binding, false, canProduceArrayVariant));
+
+ // FIXME: should add new configuration flag for this
+ if (canProduceArrayVariant[0] && (binding.signatureUsesCPrimitivePointers() || binding.signatureUsesCArrays()) &&
+ !cfg.nioDirectOnly(binding.getName()) && !cfg.nioOnly(binding.getName())) {
+ result.add(lowerMethodBindingPointerTypes(binding, true, null));
+ }
+ } else {
+ result.add(binding);
+ }
+
+ return result;
+ }
+
+ private String resultName() {
+ return "_res";
+ }
+
+ private Type canonicalize(Type t) {
+ Type res = canonMap.get(t);
+ if (res != null) {
+ return res;
+ }
+ canonMap.put(t, t);
+ return t;
+ }
+
+ /**
+ * Converts first letter to upper case.
+ */
+ private final String capitalizeString(String string) {
+ return Character.toUpperCase(string.charAt(0)) + string.substring(1);
+ }
+
+}