/* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package net.java.games.gluegen; import java.util.*; import net.java.games.gluegen.cgram.types.*; /** Represents the binding of a C function to a Java method. Also used to represent calls through function pointers contained in structs. */ public class MethodBinding { private FunctionSymbol sym; private JavaType javaReturnType; private List javaArgumentTypes; private boolean computedSignatureProperties; private boolean signatureUsesNIO; private boolean signatureUsesCArrays; private boolean signatureUsesPrimitiveArrays; private JavaType containingType; private Type containingCType; private int thisPointerIndex = -1; /** * Constructs a new MethodBinding that is an exact clone of the * argument, including the java return type and java argument * types. It's safe to modify this binding after construction. */ public MethodBinding(MethodBinding bindingToCopy) { this.sym = bindingToCopy.sym; this.containingType = bindingToCopy.containingType; this.containingCType = bindingToCopy.containingCType; this.javaReturnType = bindingToCopy.javaReturnType; this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; this.signatureUsesNIO = bindingToCopy.signatureUsesNIO; this.signatureUsesCArrays = bindingToCopy.signatureUsesCArrays; this.signatureUsesPrimitiveArrays = bindingToCopy.signatureUsesPrimitiveArrays; this.thisPointerIndex = bindingToCopy.thisPointerIndex; } /** Constructor for calling a C function. */ public MethodBinding(FunctionSymbol sym) { this.sym = sym; } /** Constructor for calling a function pointer contained in a struct. */ public MethodBinding(FunctionSymbol sym, JavaType containingType, Type containingCType) { this.sym = sym; this.containingType = containingType; this.containingCType = containingCType; } public void setJavaReturnType(JavaType type) { javaReturnType = type; computedSignatureProperties = false; } public void addJavaArgumentType(JavaType type) { if (javaArgumentTypes == null) { javaArgumentTypes = new ArrayList(); } javaArgumentTypes.add(type); computedSignatureProperties = false; } public JavaType getJavaReturnType() { return javaReturnType; } public int getNumArguments() { return sym.getNumArguments(); } public JavaType getJavaArgumentType(int i) { return (JavaType) javaArgumentTypes.get(i); } public Type getCReturnType() { return sym.getReturnType(); } public Type getCArgumentType(int i) { return sym.getArgumentType(i); } public FunctionSymbol getCSymbol() { return sym; } /** Returns either the argument name specified by the underlying FunctionSymbol or a fabricated argument name based on the position. Note that it is currently not guaranteed that there are no namespace clashes with these fabricated argument names. */ public String getArgumentName(int i) { String ret = sym.getArgumentName(i); if (ret != null) { return ret; } return "arg" + i; } public String getName() { return sym.getName(); } /** Replaces the C primitive pointer argument at slot argumentNumber (0..getNumArguments() - 1) with the specified type. If argumentNumber is less than 0 then replaces the return type. */ public MethodBinding createCPrimitivePointerVariant(int argumentNumber, JavaType newArgType) { MethodBinding binding = new MethodBinding(sym); if (argumentNumber < 0) { binding.setJavaReturnType(newArgType); } else { binding.setJavaReturnType(javaReturnType); } for (int i = 0; i < getNumArguments(); i++) { JavaType type = getJavaArgumentType(i); if (i == argumentNumber) { type = newArgType; } binding.addJavaArgumentType(type); } return binding; } /** * Returns true if the return type or any of the outgoing arguments * in the method's signature require conversion or checking due to * the use of New I/O. */ public boolean signatureUsesNIO() { computeSignatureProperties(); return signatureUsesNIO; } /** * Returns true if any of the outgoing arguments in the method's * signature represent fixed-length C arrays which require length * checking during the call. */ public boolean signatureUsesCArrays() { computeSignatureProperties(); return signatureUsesCArrays; } /** * Returns true if any of the outgoing arguments in the method's * signature represent primitive arrays which require a * GetPrimitiveArrayCritical or similar operation during the call. */ public boolean signatureUsesPrimitiveArrays() { computeSignatureProperties(); return signatureUsesPrimitiveArrays; } /** * Computes summary information about the method's C and Java * signatures. */ protected void computeSignatureProperties() { if (computedSignatureProperties) return; signatureUsesNIO = false; signatureUsesCArrays = false; signatureUsesPrimitiveArrays = false; if (javaReturnType.isCompoundTypeWrapper() || javaReturnType.isNIOByteBuffer() || javaReturnType.isArrayOfCompoundTypeWrappers()) { // Needs wrapping and/or setting of byte order (neither of // which can be done easily from native code) signatureUsesNIO = true; } for (int i = 0; i < getNumArguments(); i++) { JavaType javaArgType = getJavaArgumentType(i); Type cArgType = getCArgumentType(i); if (javaArgType.isCompoundTypeWrapper() || javaArgType.isNIOBuffer() || javaArgType.isNIOBufferArray()) { // Needs unwrapping of accessors or checking of direct // buffer property signatureUsesNIO = true; } if (cArgType.isArray()) { // Needs checking of array lengths signatureUsesCArrays = true; } if (javaArgType.isPrimitiveArray()) { // Needs getPrimitiveArrayCritical or similar construct // depending on native code calling convention signatureUsesPrimitiveArrays = true; } } computedSignatureProperties = true; } public MethodBinding createNIOBufferVariant() { if (!signatureUsesNIO()) { return this; } MethodBinding binding = new MethodBinding(sym, containingType, containingCType); binding.thisPointerIndex = thisPointerIndex; if (javaReturnType.isCompoundTypeWrapper()) { binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); } else if (javaReturnType.isArrayOfCompoundTypeWrappers()) { binding.setJavaReturnType(JavaType.forNIOByteBufferArrayClass()); } else { binding.setJavaReturnType(javaReturnType); } for (int i = 0; i < getNumArguments(); i++) { JavaType type = getJavaArgumentType(i); if (type.isCompoundTypeWrapper()) { type = JavaType.forNIOBufferClass(); } binding.addJavaArgumentType(type); } return binding; } /** Indicates whether this MethodBinding is for a function pointer contained in a struct. */ public boolean hasContainingType() { return (getContainingType() != null); } /** Retrieves the containing type of this MethodBinding if it is for a function pointer contained in a struct. */ public JavaType getContainingType() { return containingType; } /** Retrieves the containing C type of this MethodBinding if it is for a function pointer contained in a struct. */ public Type getContainingCType() { return containingCType; } /** Find the leftmost argument matching the type of the containing type (for function pointer MethodBindings) and record that as a "this" pointer, meaning that it does not need to be explicitly passed at the Java level. */ public void findThisPointer() { clearThisPointer(); for (int i = 0; i < getNumArguments(); i++) { JavaType arg = getJavaArgumentType(i); if (arg.equals(containingType)) { thisPointerIndex = i; break; } if (!arg.isJNIEnv()) { break; // this pointer must be leftmost argument excluding JNIEnvs } } } /** Clears any record of a this pointer for this MethodBinding. */ public void clearThisPointer() { thisPointerIndex = -1; } /** Indicates whether the ith argument to this MethodBinding is actually a "this" pointer. */ public boolean isArgumentThisPointer(int i) { return (thisPointerIndex == i); } public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null || ! (obj instanceof MethodBinding)) { return false; } MethodBinding other = (MethodBinding)obj; if (!(sym.equals(other.sym))) { return false; } if (!(javaReturnType.equals(other.getJavaReturnType()))) { return false; } if (containingType != null && other.getContainingCType() != null && (!(containingCType.equals(other.getContainingCType())))) { return false; } if (javaArgumentTypes.size() != other.javaArgumentTypes.size()) { return false; } for (int i = 0; i < javaArgumentTypes.size(); ++i) { Object typeThis = javaArgumentTypes.get(i); Object typeOther = other.getJavaArgumentType(i); if (!(typeThis.equals(typeOther))) { return false; } } return true; } // FIXME!! Implement hashCode() to match equals(Object) /** Returns the signature of this binding. */ public String toString() { StringBuffer buf = new StringBuffer(200); buf.append(getJavaReturnType().getName()); buf.append(" "); buf.append(getName()); buf.append("("); boolean needComma = false; for (int i = 0; i < getNumArguments(); i++) { JavaType type = getJavaArgumentType(i); if (type.isVoid()) { // Make sure this is the only param to the method; if it isn't, // there's something wrong with our parsing of the headers. assert(getNumArguments() == 1); continue; } if (type.isJNIEnv() || isArgumentThisPointer(i)) { // Don't need to expose these at the Java level continue; } if (needComma) { buf.append(", "); } buf.append(type.getName()); buf.append(" "); buf.append(getArgumentName(i)); needComma = true; } buf.append(")"); return buf.toString(); } public final Object clone() { return new MethodBinding(this); } }